文章

Redis 开发规范

键值设计

key名设计

(1)【强制】不要包含特殊字符

(2)【建议】为了可读性,以业务名或数据库名为前缀,用冒号分隔

例如:trade:order:{id}(业务名:表名:id)

(3)【建议】为了减少内存占用,保证语义的情况下,控制key的长度

例如:将 user:{uid}:friends:messages:{mid} 简化为 u:{uid}:fr:msg:{mid}

(4)【建议】设置过期时间

value设计

(1)【推荐】使用合适的数据结构

(2)【强制】避免出现bigkey

一般认为下面这些key是bigkey:

  1. string类型的value大于10KB

  2. hash、list、set、zset等元素超过5000个

在Redis中,string最大支持512MB,hash、list、set、zset等支持大约40亿个元素。

bigkey的危害:

  1. 导致Redis阻塞:因为bigkey比较大,获取或手动删除bigkey都可能造成Redis阻塞(Redis4.0之后过期删除不会造成阻塞,因为key过期默认使用异步删除:`lazyfree-lazy-expire yes`)

手动删除bigkey会造成阻塞,避免直接使用del删除,也要避免bigkey过期删除,一般使用hscan、sscan、zscan等方式渐进式删除

  1. 导致网络拥塞:bigkey本身比较大,每次获取bigkey需要占用网卡大量的带宽,当该key的访问量稍微大一些时,就会造成网络拥塞(如一个bigkey大小为1MB,服务器一般是千兆网卡,即处理能力为125MB/s,也就是说一秒最多处理125个对这个bigkey的请求,超过这个请求数网络就会拥塞)

bigkey的产生:

  1. 大V的粉丝列表

  2. 统计系统中所有的用户

  3. 将数据库中的数据直接加载到缓存:如果字段比较多,会产生bigkey

bigkey的优化:

  1. 拆分:可以拆分bigkey,用多个key来存储

  2. 适量取用:如果不可拆分,尽量避免一次性取出key中的所有数据,如可用 hmget 操作代替`hgetall`操作

命令使用

(1) 线上禁止使用keys、flushall、flushdb等命令(可通过Redis的rename机制禁用这些命令)

(2) 使用批量操作提高效率(注意控制一次批量操作的元素个数)

  1. 原生命令:如mget、mset

  2. 非原生命令:可用pipeline

注意原生命令是原子的,pipeline不是原子的。

pipeline可以打包不同的命令,但pipeline需要客户端和服务端同时支持。

(3) 注意hgetall、lrange、zrange等O(n)命令的n值(需要遍历应用hscan、sscan、zscan等命令)

(4) 慎用多数据库:select命令可以切换数据库(0~15),但要注意一个Redis节点的多数据库实际还是单线程处理

(5) 不推荐使用Redis事务:Redis事务功能较弱,推荐使用lua替代

客户端使用

(1) 避免不同的应用使用同一个Redis实例

(2) 使用带连接池的客户端,如Jedis

下面介绍下Jedis重要的3个连接池参数maxTotal、maxIdle和minIdle:(连接池基于Apache的commons-pool2

1⃣️ maxTotal:允许的最大连接数,包括正在使用的和空闲的连接(默认值为8)

可以考虑以下因素大致设定这个参数值:

  1. 业务希望的Redis并发量

  2. 一个连接执行命令耗时:假设一个连接执行命令平均耗时为1ms,则一个连接的QPS约为1000,如业务期望并发量为50000,则最多要50个连接

  3. Redis资源:应用数 * maxTotal 不超过 Redis的maxclients配置值

  4. 资源开销:在控制一定的空闲连接的情况下,不希望因为连接频繁释放和创建造成不必要的开销

2⃣️ maxIdle:允许的最大空闲连接数(默认值为8)

这个参数定义了连接池中允许的最大空闲连接数量。如果连接池中的空闲连接数量超过了这个值,那么多余的空闲连接将会被关闭。

它限制了连接池中的空闲连接数量,以避免资源过度浪费。

3⃣️ minIdle:允许的最小空闲连接数(默认值为0)

这个参数定义了连接池中保持的最少空闲连接数量。如果当前空闲连接数低于minIdle,连接池就会尝试创建新的连接以满足这个最小值。

它确保在低负载时至少保持一定数量的空闲连接,以提高连接获取的速度。

Jedis 连接池的空闲连接数量通常是动态的,根据maxIdleTime释放长时间空闲的连接,使得空闲连接在 minidle 和 maxidle 之间动态调整

连接池maxTotal=maxIdle时性能最佳,因为可以避免弹性伸缩带来的性能损耗,但是如果并发不高或者maxTotal设置偏大,会导致资源浪费

一般推荐maxIdle根据业务期望的并发计算,然后maxTotal为maxIdle的两倍

如果系统启动马上就会有很多请求过来,可以执行简单的ping()命令进行预热,快速创建一些连接

(3) 高并发下客户端添加熔断功能

(4) 设置访问密码

Redis清除策略配置

  1. 被动删除:访问一个已经过期的key时,会触发惰性删除,会直接删除掉这个过期的key(lazyfree指异步删除,惰性删除特指访问时删除)

  2. 主动删除:光有被动删除还不够,有些过期的key可能很长时间都不会被访问到,因此Redis会定期淘汰一批过期的key

  3. 当已用内存超过maxmemory限定值时,会触发主动清理(在redis.cong中配置maxmemory

主动清理一共有 8 种内存淘汰策略(maxmemory-policy,默认 noeviction,推荐 volatile-lru):

(1) 针对设置了过期时间的key

  1. volatile-ttl:在设置了过期时间的键值对中,根据过期时间先后进行删除,越早过期的越先被删除

  2. volatile-random:在设置了过期时间的键值对中,进行随机删除

  3. volatile-lru:在设置了过期时间的键值对中,使用LRU算法筛选并删除

  4. volatile-lfu:在设置了过期时间的键值对中,使用LFU算法筛选并删除

(2) 针对所有key

  1. allkeys-random:在所有键值对中,进行随机删除

  2. allkeys-lru:在所有键值对中,使用LRU算法筛选并删除

  3. allkeys-lfu:在所有键值对中,使用LFU算法筛选并删除

(3) 不处理

  1. noeviction:不会删除任何数据,而是拒绝所有写入请求并返回错误信息:`(error) OOM command not allowed when used memory`

LRU(Least Recently Used,最近最少使用):淘汰很久没有使用的数据,以最近一次访问时间为准

LFU(Least Frequently Used,最不经常使用):淘汰最近一段时间被访问次数最少的数据,以访问次数为准

一般推荐使用LRU,但是如果有周期性批量操作,会导致LRU命中率急剧下降,此时使用LFU可能更好;当有大量热点数据时,使用LFU效果更好

如果不设置maxmemory,当Redis使用内存超过机器物理内存时,内存和磁盘会发生大量的数据交换(swap),此时Redis性能会急剧下降

当运行在主从模式时,只有主节点会执行清除策略,从节点只用同步删除操作就行

License:  CC BY 4.0