Redis分布式缓存规范

适用范围

本规范主要面向用友云的Redis开发规范,从键值设计、命令使用、客户端使用、相关工具等方面进行说明,遵循此规范可以减少使用Redis过程带来的问题。

 

一、缓存设计

1.【推荐】避免缓存穿透

  • 数据库中未查询到的数据,可在Redis中设置特殊标识,以避免因缓存中无数据而导致每次请求均达到数据库。

2.【推荐】避免缓存雪崩

  • 当大量缓存集中在某一个时间段失效,这样在失效的时候也会给数据库带来很大压力。

3.【推荐】避免缓存击穿

  • 某个key的缓存过期后,同一时间内有大量的请求均访问该key,由于缓存过期,大量的请求均会访问数据库,并重建缓存;重建缓存的过程加锁,保证只有一个人执行,其他人等待。

4.【推荐】可以进行适当的缓存预热

  • 对于上线后可能会有大量读请求的应用,在上线之前可预先将数据写入缓存中

5.【推荐】数据一致性问题

  • 数据源发生变更时可能导致缓存中数据与数据源中数据不一致,应根据实际业务需求来选择适当的缓存更新策略:

a)   主动更新:在数据源发生变更时同步更新缓存数据或将缓存数据过期。一致性高,维护成本较高。

b)   被动删除:根据缓存设置的过期时间有Redis负责数据的过期删除。一致性较低,维护成本较低。

c)   推荐策略:主动更新,数据源发生变更时将缓存数据过期。缓存和DB的更新不在同一个事务问题:

    • 利用Spring的事务管理机制,在事务管理器上注册一个事务提交后回调,在回调方法中进行缓存清理;
    • 使用异步消息队列,并且支持重试补偿机制。(如果缓存里数据时间与数据库时间不能匹配,意味着另外一个服务更新了该数据,那么就先从DB里读取最新数据版本,然后在新版本上提交数据)

二、键值设计

1.强制key不要包含特殊字符

  • Redis以“\n”作为命令结束符,以空格作为命令和参数分隔符。反例:包含空格、换行、单双引号以及其他转义字符

2.【强制】拒绝bigkey(防止网卡流量、慢查询)

  • 反例:一个包含200万个元素的list。非字符串的bigkey,不要使用del删除,使用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞,而且该操作不会出现在慢查询中(latency可查))

3.【推荐】key值可读性和可管理性

  • 以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如[模块名]:[业务描述]:[ID]

4.【推荐】key值简洁性

  • 保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视。

5.【推荐】根据业务场景合理使用不同数据结构类型

  • 目前Redis支持的数据库结构类型较多:字符串(String),哈希(Hash),列表(List),集合(Set),有序集合(Sorted Set), Bitmap, HyperLogLog和地理空间索引(geospatial)等,需要根据业务场景选择合适的类型,常见的如:String可以用作普通的K-V、计数类;Hash可以用作对象如商品、经纪人等,包含较多属性的信息;List可以用作消息队列等;Set可以用于推荐;Sorted Set可以用于排行榜等。

6.【推荐】控制key的生命周期

  • redis不是垃圾桶,建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注idletime。

7.【推荐】冷热数据分离,不要将所有数据全部都放到Redis

  • 虽然Redis支持持久化,但是Redis的数据存储全部都是在内存中的,成本昂贵,建议根据业务只将高频热数据存储到Redis中。

三、命令使用

1.【强制】线上禁止使用keys命令

  • Redis是单线程处理,在线上KEY数量较多时,操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求,而且在高QPS情况下会直接造成Redis服务崩溃。如果有类似需求,请使用scan命令代替。

2.【强制】禁用命令

  • 禁止线上使用flushall、flushdb等,flushall、flushdb会清空redis数据。通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。

3.【推荐】O(N)命令关注N的数量

  • 例如hgetall、lrange、smembers、zrange、sinter等并非不能使用,但是需要明确N的值。有遍历的需求可以使用hscan、sscan、zscan代替。

4.【推荐】合理使用select

  • redis的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰。

5.【推荐】使用批量操作提高效率

  • 原生命令mset、mget,非原生命令pipline;但要注意控制一次批量操作的元素个数(例如500以内,实际也和元素字节数有关)

6.【推荐】Redis事务功能较弱,不建议过多使用

  • Redis的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的key必须在一个slot上(可以使用hashtag功能解决)

7.【推荐】Redis集群版本在使用Lua上有特殊要求:

  • Redis从2.6版本开始引入对Lua脚本的支持
  • 所有key都应该由 KEYS 数组来传递,redis.call/pcall 里面调用的redis命令,key的位置,必须是KEYS array, 否则直接返回error
  • Redis集群对多key操作有限制,要求命令中所有的key都属于一个slot,才可以被执行。

8.【推荐】必要情况下使用monitor命令时,要注意不要长时间使用

  • monitor命令在开启的情况下会降低redis的吞吐量,根据压测结果大概会降低redis50%的吞吐量,越多客户端开启该命令,吞吐量下降会越多。

四、客户端使用

1.【推荐】避免多个应用使用一个实例

  • 避免多个应用使用一个Redis实例。不要将不相关的业务数据都放到一个实例中,建议新业务申请新的单独实例。因为Redis为单线程处理,独立存储会减少不同业务相互操作的影响,提高请求响应速度;同时也避免单个实例内存数据量膨胀过大,在出现异常情况时可以更快恢复服务,公共数据做服务化。

2.【推荐】使用连接池

  • 使用带有连接池的数据库,可以有效控制连接,同时提高效率

3.【推荐】添加熔断功能

  • 高并发下建议客户端添加熔断功能(例如netflix hystrix)

4.【强制】设置合理的密码

  • 设置合理的密码,如有必要可以使用SSL加密访问(阿里云Redis支持)

5.【推荐】根据业务类型选择淘汰策略

  • 根据自身业务类型,选好maxmemory-policy(最大内存淘汰策略),设置好过期时间。默认策略是volatile-lru,即超过最大内存后,在过期键中使用lru算法进行key的剔除,保证不过期数据不被删除,但是可能会出现OOM问题。
  • 其他策略如下:

a)     allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。

b)     allkeys-random:随机删除所有键,直到腾出足够空间为止。

c)      volatile-random:随机删除过期键,直到腾出足够空间为止。

d)     volatile-ttl:根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。

e)     noeviction:不会剔除任何数据,拒绝所有写入操作并返回客户端错误信息"(error) OOM command not allowed when used memory",此时Redis只响应读操作。

6.【强制】设置连接池时,如果非默认database,直接在URI中指定,不能每次查询selectDb

五、相关工具推荐

1.【推荐】数据同步

  • redis间数据同步可以使用:redis-port

2.【推荐】big key搜索

  • redis大key搜索工具

3.【推荐】热点key寻找

  • 内部实现使用monitor,建议短时间使用;facebook的redis-faina 。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章