Redis缓存设计与性能优化最佳实践

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
开发规范与性能优化
一、键值设计
1. key名设计
(1)【建议】: 可读性和可管理性
以业务名(或数据库名)为前缀(防止key冲突)用冒号分隔比如业务名:表名:id
(2)【建议】简洁性
保证语义的前提下控制key的长度当key较多时内存占用也不容忽视例如
(3)【强制】不要包含特殊字符
反例包含空格、换行、单双引号以及其他转义字符
2. value设计
(1)【强制】拒绝bigkey(防止网卡流量、慢查询)
在Redis中一个字符串最大512MB一个二级数据结构例如hash、list、set、zset可以存
储大约40亿个(2^32-1)个元素但实际中如果下面两种情况我就会认为它是bigkey。
1. 字符串类型它的big体现在单个value值很大一般认为超过10KB就是bigkey。
2. 非字符串类型哈希、列表、集合、有序集合它们的big体现在元素个数太多。
一般来说string类型控制在10KB以内hash、list、set、zset元素个数不要超过5000。
反例一个包含200万个元素的list。 非字符串的bigkey不要使用del删除使用hscan、sscan、zscan方式渐进式删除同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期会触发del操作造成阻塞
bigkey的危害
1.导致redis阻塞
2.网络拥塞
bigkey也就意味着每次获取要产生的网络流量较大假设一个bigkey为1MB客户端每秒访问
量为1000那么每秒产生1000MB的流量对于普通的千兆网卡(按照字节算是128MB/s)的服务
器来说简直是灭顶之灾而且一般服务器会采用单机多实例的方式来部署也就是说一个bigkey
可能会对其他实例也造成影响其后果不堪设想。
3. 过期删除
有个bigkey它安分守己只执行简单的命令例如hget、lpop、zscore等但它设置了过
期时间当它过期后会被删除如果没有使用Redis 4.0的过期异步删除( lazyfree-lazy- expire yes )就会存在阻塞Redis的可能性。
bigkey的产生
一般来说bigkey的产生都是由于程序设计不当或者对于数据规模预料不清楚造成的来看几
个例子
(1) 社交类粉丝列表如果某些明星或者大v不精心设计下必是bigkey。
(2) 统计类例如按天存储某项功能或者网站的用户集合除非没几个人用否则必是bigkey。
(3) 缓存类将数据从数据库load出来序列化放到Redis里这个方式非常常用但有两个地方需
要注意第一是不是有必要把所有字段都缓存第二有没有相关关联的数据有的同学为了
图方便把相关数据都存一个key下产生bigkey。
如何优化bigkey
1. 拆
big list list1、list2、...listN
big hash可以讲数据分段存储比如一个大的key假设存了1百万的用户数据可以拆分成
200个key每个key下面存放5000个用户数据
2. 如果bigkey不可避免也要思考一下要不要每次把所有元素都取出来(例如有时候仅仅需要
hmget而不是hgetall)删除也是一样尽量使用优雅的方式来处理。
(2)【推荐】选择适合的数据类型。
例如实体类型(要合理控制和使用数据结构但也要注意节省内存和性能之间的平衡)
反例
正例:
3.【推荐】控制key的生命周期redis不是垃圾桶。
建议使用expire设置过期时间(条件允许可以打散过期时间防止集中过期)。
二、命令使用
1.【推荐】 O(N)命令关注N的数量
例如hgetall、lrange、smembers、zrange、sinter等并非不能使用但是需要明确N的值。有
遍历的需求可以使用hscan、sscan、zscan代替。
2.【推荐】禁用命令
禁止线上使用keys、flushall、flushdb等通过redis的rename机制禁掉命令或者使用scan的
方式渐进式处理。
3.【推荐】合理使用select
redis的多数据库较弱使用数字进行区分很多客户端支持较差同时多业务用多数据库实际还
是单线程处理会有干扰。
4.【推荐】使用批量操作提高效率
但要注意控制一次批量操作的元素个数(例如500以内实际也和元素字节数有关)。
注意两者不同
5.【建议】Redis事务功能较弱不建议过多使用可以用lua替代
三、客户端使用
1.【推荐】
避免多个应用使用一个Redis实例
正例不相干的业务拆分公共数据做服务化。
2.【推荐】
使用带有连接池的数据库可以有效控制连接同时提高效率标准使用方式
连接池参数含义
优化建议
1 maxTotal 最大连接数早期的版本叫maxActive
实际上这个是一个很难回答的问题考虑的因素比较多
业务希望Redis并发量
客户端执行命令时间
Redis资源例如 nodes(例如应用个数) * maxTotal 是不能超过redis的最大连接数
maxclients。
资源开销例如虽然希望控制 空闲连接 (连接池此刻可马上使用的连接)但是不希望因
为连接池的频繁释放创建连接造成不必靠开销。
以一个例子说明 假设: 一次命令时间borrow|return resource + Jedis执行命令(含网络) 的平均耗时约为1ms一个连接的QPS大约是1000业务期望的QPS是50000那么理论上需要的资源池大小是50000 / 1000 = 50个。但事实上这是个理论值还要考虑到要比理论值预留一些资源通常来讲maxTotal可以比理论值大一些。
但这个值不是越大越好一方面连接太多占用客户端和服务端资源另一方面对于Redis这种高
QPS的服务器一个大命令的阻塞即使设置再大资源池仍然会无济于事。
2 maxIdle和minIdle
maxIdle实际上才是业务需要的最大连接数maxTotal是为了 给出余量 所以maxIdle不要设置
过小否则会有new Jedis(新连接)开销。
连接池的最佳性能是maxTotal = maxIdle 这样就避免连接池伸缩带来的性能干扰。但是如果
并发量不大或者maxTotal设置过高会导致不必要的连接资源浪费。一般推荐maxIdle可以设置
为按上面的业务期望QPS计算出来的理论连接数maxTotal可以再放大一倍。
minIdle最小空闲连接数与其说是最小空闲连接数不如说是" 至少需要保持的空闲连接
"在使用连接的过程中如果连接数超过了minIdle那么继续建立连接如果超过了
maxIdle当超过的连接执行完业务后会慢慢被移出连接池释放掉。
如果系统启动完马上就会有很多的请求过来那么可以给redis连接池做 预热 比如快速的创建一
些redis连接执行简单命令类似ping()快速的将连接池里的空闲连接提升到minIdle的数量。
连接池预热 示例代码
总之要根据实际系统的QPS和调用redis客户端的规模整体评估每个节点所使用的连接池大小。
3.【建议】
高并发下建议客户端添加熔断功能(例如sentinel、hystrix)
4.【推荐】
设置合理的密码如有必要可以使用SSL加密访问
5.【建议】
Redis对于过期键有三种清除策略
1. 被动删除当读/写一个已经过期的key时会触发惰性删除策略直接删除掉这个过期
key
2. 主动删除由于惰性删除策略无法保证冷数据被及时删掉所以Redis会定期主动淘汰一
已过期 的key
3. 当前已用内存超过maxmemory限定时触发 主动清理策略
主动清理策略 在Redis 4.0 之前一共实现了 6 种内存淘汰策略在 4.0 之后又增加了 2 种策
略总共8种
a) 针对设置了过期时间的key做处理
1. volatile-ttl在筛选时会针对设置了过期时间的键值对根据过期时间的先后进行删
除越早过期的越先被删除。
2. volatile-random就像它的名称一样在设置了过期时间的键值对中进行随机删除。
3. volatile-lru会使用 LRU 算法筛选设置了过期时间的键值对删除。
4. volatile-lfu会使用 LFU 算法筛选设置了过期时间的键值对删除。
b) 针对所有的key做处理
5. allkeys-random从所有键值对中随机选择并删除数据。
6. allkeys-lru使用 LRU 算法在所有数据中进行筛选删除。
7. allkeys-lfu使用 LFU 算法在所有数据中进行筛选删除。
c) 不处理
8. noeviction不会剔除任何数据拒绝所有写入操作并返回客户端错误信息"(error)
OOM command not allowed when used memory"此时Redis只响应读操作。
LRU 算法 Least Recently Used 最近最少使用
淘汰很久没被访问过的数据以 最近一次访问时间 作为参考。
LFU 算法 Least Frequently Used 最不经常使用
淘汰最近一段时间被访问次数最少的数据以 次数 作为参考。 当存在热点数据时LRU的效率很好但偶发性的、周期性的批量操作会导致LRU命中率急剧下
降缓存污染情况比较严重。这时使用LFU可能更好点。
根据自身业务类型配置好maxmemory-policy(默认是noeviction)推荐使用volatile-lru。如
果不设置最大内存当 Redis 内存超出物理内存限制时内存的数据会开始和磁盘产生频繁的交
换 (swap)会让 Redis 的性能急剧下降。
当Redis运行在主从模式时只有主结点才会执行过期删除策略然后把删除操作”del key”同
步到从结点删除数据。
四、系统内核参数优化
vm.swapiness
swap对于操作系统来说比较重要当物理内存不足时可以将一部分内存页进行swap到硬盘
上以解燃眉之急。但世界上没有免费午餐swap空间由硬盘提供对于需要高并发、高吞吐的
应用来说磁盘IO通常会成为系统瓶颈。在Linux中并不是要等到所有物理内存都使用完才会
使用到swap系统参数swppiness会决定操作系统使用swap的倾向程度。swappiness的取值范
围是0~100swappiness的值越大说明操作系统可能使用swap的概率越高swappiness值越
低表示操作系统更加倾向于使用物理内存。swappiness的取值越大说明操作系统可能使用
swap的概率越高越低则越倾向于使用物理内存。
如果linux内核版本<3.5那么swapiness设置为0这样系统宁愿swap也不会oom killer杀掉
进程如果linux内核版本>=3.5那么swapiness设置为1这样系统宁愿swap也不会oom killer
一般需要保证redis不会被kill掉
PSOOM killer 机制是指Linux操作系统发现可用内存不足时强制杀死一些用户进程非内核
进程来保证系统有足够的可用内存进行分配。
vm.overcommit_memory(默认0)
0表示内核将检查是否有足够的可用物理内存(实际不一定用满)供应用进程使用如果有足够的
可用物理内存内存申请允许否则内存申请失败并把错误返回给应用进程
1表示内核允许分配所有的物理内存而不管当前的内存状态如何
如果是0的话可能导致类似fork等操作执行失败申请不到足够的内存空间
Redis建议把这个值设置为1就是为了让fork操作能够在低内存下也执行成功。
合理设置文件句柄数
操作系统进程试图打开一个文件(或者叫句柄)但是现在进程打开的句柄数已经达到了上限继
续打开会报错“
Too many open files”
慢查询日志slowlog
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: redis