引言
你是否在工作中遇到过redis的bigkey导致的内存占用严重、查询耗时大大增加?
同时bigkey还可能导致redis实例的崩溃,因为内存不够用了,redis就会不堪重负,像是被大象坐了一样,瞬间崩溃!
所以,大家在使用redis的时候一定要小心,redis内存是十分宝贵的资源,我们在使用时,要特别注意对内存资源的合理应用。
下面带大家了解下redis极大节省内存空间的10个实用技巧。
正文
什么是bigkey
在面试过程中,发现很多人都对bigkey的概念搞错了,认为bigkey是一个key比较大的存储对象。
其实不是的,我们不能用英文直译的方式来理解它。所以这里还是给大家科普下:
bigkey,其实就是redis中占用大量内存空间的键,具体来说,就是那些因为存储了大量数据,或者单个数据项太大,导致内存占用严重的键。
简单点来说:就是这个redis的某个键(key)存了太多的数据,占用了太多的内存
redis节省内存的方式就是合理的运用redis的数据结构,减少redis的bigkey的产生, 以及做好内存的清理和淘汰策略。
1、使用hash数据结构
hash是redis提供的一种非常灵活的数据结构,可以存储键值对的集合。 如果我们存储对象类型的数据,建议使用hsah
假设我们有一个电商网站,需要存储商品的信息,比如商品名称、类目、属性等,这时候就可以用hash数据结构来存储,一个商品对应一个hash。
// 使用hash存储商品信息 string userid = "product:1001"; jedis.hset(userid, "productname", "xxxx"); jedis.hset(userid, "category", "手机"); jedis.hset(userid, "prop", "xxx");
2、使用压缩列表优化小规模列表
当你需要存储的列表元素数量不多时,redis会自动使用压缩列表来存储,这样可以节省内存空间。
比如在一个论坛系统中,用户发布了一篇文章,我们可以用列表来存储文章的评论,而且这篇文章评论不多的话,就可以充分利用压缩列表的优势。
// 使用压缩列表存储文章评论 string articleid = "article:2001"; jedis.lpush(articleid + ":comments", "评论1", "评论2", "评论3");
3、使用bitmaps存储布尔值信息
bitmaps是一种非常紧凑的数据结构,适合存储布尔值信息。
在我们平时开发过程中,会有一些 bool 型数据需要存取,比如用户一年的签到记录,签了是 1,没签是 0,要记录 365 天。如果使用普通的 key/value,每个用户要记录 365 个,当用户上亿的时候,需要的存储空间是惊人的。
为了解决这个问题,redis 提供了位图数据结构,这样每天的签到记录只占据一个位,365 天就是 365 个位,46 个字节 (一个稍长一点的字符串) 就可以完全容纳下,这就大大节约了存储空间。
// 使用bitmaps存储用户签到情况 string userid = "user:1001"; int day = 10; // 签到的天数,从0开始计算 jedis.setbit("sign_in:" + day, long.parselong(userid), true);
4、使用zset存储有序集合
zset是redis中的有序集合数据结构,可以按照指定的分数进行排序。
比如在一个新闻网站中,我们可以使用zset来存储新闻文章的阅读量排行榜,分数表示阅读量,文章id作为成员。
// 使用zset存储文章阅读量排行榜 string articleid = "article:3001"; jedis.zadd("article:views", 1000, articleid);
5、使用set存储唯一值
set数据结构适合存储唯一值,比如用户的喜好标签、商品的标签等。
在一个电商平台中,我们可以使用set来存储商品的标签,确保每个标签都是唯一的。
// 使用set存储商品标签 string productid = "product:5001"; jedis.sadd(productid + ":tags", "电子产品", "数码设备", "智能家居");
6、使用hyperloglog进行基数统计
hyperloglog是一种用于统计不重复元素数量的算法,在统计网站的uv(独立访客)数量时非常有用。 比如:
如果你负责开发维护一个大型的网站,有一天老板找产品经理要网站每个网页每天的 uv 数据,然后让你来开发这个统计模块,你会如何实现?
如果统计 pv 那非常好办,给每个网页一个独立的 redis 计数器就可以了,这个计数器的 key 后缀加上当天的日期。这样来一个请求,incrby 一次,最终就可以统计出所有的 pv 数据。
但是 uv 不一样,它要去重,同一个用户一天之内的多次访问请求只能计数一次。这就要求每一个网页请求都需要带上用户的 id,无论是登陆用户还是未登陆用户都需要一个唯一 id 来标识。
这时候就可以引入hyperloglog, hyperloglog 数据结构就是用来解决这种统计问题的。
hyperloglog 提供不精确的去重计数方案,虽然不精确但是也不是非常不精确,标准误差是 0.81%,这样的精确度已经可以满足上面的 uv 统计需求了,又大量节省了内存
hyperloglog 实现中用到的是 16384 个桶,也就是 2^14,每个桶的 maxbits 需要 6 个 bits 来存储,最大可以表示 maxbits=63,于是总共占用内存就是2^14 * 6 / 8 = 12k字节。
// 使用hyperloglog统计网站uv string today = "2024-04-23"; string userid = "user:1001"; jedis.pfadd("uv:" + today, userid);
7、使用string存储序列化的数据
虽然redis提供了丰富的数据结构,但有时候我们仍然需要将复杂的数据结构序列化为字符串存储。
比如在一个微博系统中,我们可以将用户的微博对象序列化为json字符串,然后存储到redis中。
// 使用string存储序列化后的用户微博对象 userpost userpost = new userpost("user:1001", "今天天气真好!", new date()); string userpostjson = objectmapper.writevalueasstring(userpost); jedis.set("user:1001:post:1", userpostjson);
8、使用pipeline批量操作命令
pipeline是一种批量操作命令的机制,可以减少网络通信开销,提高操作效率。
在需要进行大量操作时,使用pipeline可以有效节省内存空间和提升性能。
//使用pipeline批量设置键值对 pipeline pipeline = jedis.pipelined(); for (int i = 0; i < 10000; i++) { pipeline.set("key:" + i, "value:" + i); } pipeline.sync();
9、定期清理过期数据
定期清理过期数据是保持redis内存空间有效利用的重要方法之一。通过设置合适的过期时间,并定期清理过期数据,可以释放内存空间。
// 定期清理过期数据 jedis.setex("key:1001", 600, "value");
10、合理设置内存策略
根据实际需求和系统情况,合理设置redis的内存策略,比如最大内存限制、淘汰策略等,可以更好地管理和利用内存空间。
本文总结
redis内存是非常宝贵的资源,我们在日常开发中,要合理运用redis的数据结构以达到节省内存的目的。
以上就是redis节省内存的十个技巧分享的详细内容,更多关于redis节省内存技巧的资料请关注代码网其它相关文章!
发表评论