Redis系列之历史踩坑

本文大概记录了笔者在使用 Redis 过程中踩过的坑,以及相应的建议。

欢迎关注作者公众号:闲余说

历史踩坑

笔者参与搭建、使用过百T的 Reids 集群以及数百T的磁盘存储集群,这些集群,有的支持异地容灾,有的支持业务(业务多写等方式)异地多活,有自身支持异地多活。但基本都是双副本、三地部署。而且承担了上百个上游业务,业务数据类型各种各样、数据量也有大有小,读写比也不尽相同。因此,在使用过程中遇到各种各样的问题,但总结下来,无非是 大value、大key、集群配置等使用问题。

大value

上面章节说明了,大value的危害,那么怎么避免或者怎么解决呢?下面我通过几个例子做些说明。

踩坑-1

list value数目过多,笔者遇到过用list做模拟消息队列的业务,其中有个业务在模拟消息队列时,消费者(消费完删除对应的value)是一个离线脚本,某天跑脚本的机器挂了,脚本也就停了,导致该list膨胀,最终触发线上容量报警。

建议:此类情况,建议使用kafka等常规消息队列服务,毕竟Redis存储容量有限。

踩坑-2

hashfield过多,业务需要存储用户是否领取过礼物,同时,又想很快获取哪些用户领取了礼物,因此将这个礼物领取功能使用一个hash存储,key为giftfielduser_id,最终该hash大小可想而知。某天,该RD,手动删除了该hash导致整体可用性抖动持续数秒。

建议:1.此case中,线上用string,统计通过日志处理即可;2. hashfield最好不要上万,如果必须是用hash建议拆成多个hash,比如gift_1, ..., gitf_n业务侧通过hash算法将对应的user_idhash到不同的hashkey中,避免了大value;3. 最好不要执行hgetall等命令,建议使用scan + hmget组合操作;4. 不用直接del一个大value,建议用scan + hdel组合操作;4. 大value数据误删恢复,建议将RDB加载到例行Redis上然后获取全量数据,在写回线上。

大key

踩坑-1

遇到过把请求URL当缓存key的,key长度达500B以上,甚至比value还长。

建议:key的信息熵尽可能的高,去掉一些共性的,没有意义上的部分,建议做好逻辑规划,比如参考MySQL等关系型数据库,规划出来database、table的概念避免冲突,一个key可以是database:table:id这种结构,进一步可以精简databasedbtablet,最终key为db:t:id

热key

踩坑-1

业务将一份公共资源存储在一个set中,所以用户请求都会访问该公共资源,判断自己是否在其中,也是每次都通过smembers将整个set数据拉到本地最判断,导致存储该set的Redis CPU使用率100%,Redis实例hang死,进而影响该Redis实例上的其他业务。

建议:1. 使用sismember命令,避免拉取整个set数据;2. 将数据分成n份,比分key_1, ..., key_n这n份数据完全一样,业务通过hash函数确认每个请求该访问哪个key,该方法是典型的空间换时间的讨论,避免集中访问某一个key(该方案只对Redis集群方案有效),注意数据需要同步更新;3. Redis从实例承担部分读请求。强烈建议公共缓存,即大部分请求都会访问的数据复制多份保证热点key打散(对集群有效)。

集群运维

踩坑-1

线上Redis实例配置了stop-writes-on-bgsave-error参数,某天凌晨该实例部署机器磁盘故障,导致生成rdb失败,由于配置了该参数是的该机器上所有实例成为只读实例,写入失败。

建议:如果非必须,不建议设置该参数为on.

踩坑-2

线上实例混布,部分机器上实例部署密集并且部分实例每30min执行一次bgsave导致流量高峰期可用性抖动。

建议:部署时,需要预留一些CPU、内存等资源,不要过于密集。同时,非必要情况下不要频繁执行bgsave.

其他

踩坑-1

业务将一次通过n条资源id去查询后端数据库并将结果缓存(key为资源id拼接的字符串,value为返回的资源详情列表),这样导致资源详情信息重复缓存(比如,很多请求返回结果中都包括相同的资源详情信息)进而导致存储数据膨胀,而且后续只有资源id列表相同(id、顺序、个数等完全相同)才能命中缓存。

建议:按资源维度缓存,每个资源id为key,资源详情信息为value,一次拿n个资源的详情信息可以通过mget获取。这样每个资源的详情信息只存储一份,同时请求的命中率也大大提高。

踩坑-2

几个业务都使用一份相同资源(比如用户的信息),为了方面可能每个业务都缓存了一份公共信息,导致资源浪费

建议:结合业务抽取出公共数据,比如资源数据,可以抽取出来,所有用户共享

踩坑-3

业务缓存如{"app_id":"xxx", "app_key":"yyy", "app_secret":"zzz"}此类信息json结构固定的数据,key反复存储导致浪费。

建议:可以考虑去掉json中的key,直接存储json中的value"xxx,yyy,zzz"。另外,高并发写入场景中,在条件允许的情况下,建议字符串长度控制在39字节(Redis 5.0 时可以设置为44,也可以通过适当改动源码调整该值,但可能会导致内存碎片)以内,减少创建redisObject内存分配次数,从而提高性能。

reference

Redis官网

Redis开发与运维

How Twitter Uses Redis To Scale - 105TB RAM, 39MM QPS, 10,000+ Instances

Latency Numbers Every Programmer Should Know

你可能感兴趣的:(Redis,redis,数据库)