前言
本篇谈一谈笔者对于redis优化的一些思考和方向。
提示:写博客是自己对知识梳理,目前是写给自己看,算是自己学习后的作业,也是为了养成一个良好的习惯。
一、分析
1. 由于redis都是基于内存的操作,CPU不是redis的性能瓶颈,则服务器的内存利用率、
网络IO和磁盘读写是redis主要性能瓶颈点;
2. 由于redis处理请求和指令时是单线程,则它的性能瓶颈点是同步阻塞问题。
综上所述:redis优化应从 网络IO、磁盘读写、内存利用率及同步阻塞点这几个维度来做优化。
二、网络优化
1.客户端---->服务端
请求批处理减少网络IO开销:
1. 原子性m批处理指令
string类型有批量处理时,推荐用mget/mset代替get/set;
hash类型有批量处理时,推荐使用hmget/hmset代替hget/hset。
2. pipeline技术
在使用list、set和zset时有批量操作时可以使用pipeline技术。
3. redis事务
在特殊业务要求保证多个指令的事务时推荐使用redis事务
4. lua脚本
在需要保证多条指令原子性时推荐使用lua脚本,具体案例有分布式解锁、秒杀扣减库存。
2.集群节点—>节点
节点间的网络优化:
1. 在同一个局域网中搭建集群
2. 控制切分集群的节点数
redis实例上分配的哈希槽信息需要在不同实例之间传递,以及负载均衡,实例增删时,数据会在不同实例间传递。
但哈希槽信息不大,而数据迁移是渐进式的,一般不是主要问题。
三、内存利用率
1. 控制key的长度
建议开发前先定义好规范,保证key在简单、清晰的前提下,尽可能把key按业务缩写。
2. 杜绝bigkey
string类型的大小建议在20KB以内;
hash、list、set和zset建议控制好元素阈值,推荐控制在5000以内。
3. key设置过期时间
除非业务强要求,否则所有key都得设置过期时间,尽可能减少内存的占用。
4. 选择合适的数据结构
string类型,推荐使用整数型,它底层编码会选择整数型编码,内存开销小;
hash类型,推荐控制元素阈值,元素少时底层会用压缩列表数据结构,内存开销小;
list类型,推荐控制元素阈值,元素少时底层会用压缩列表数据结构,内存开销小;
set类型,推荐存储整数型,它底层编码会选择整数型编码,内存开销小;
zset类型,推荐控制元素阈值,元素少时底层会用压缩列表数据结构,内存开销小;
5. 数据压缩
客户端在写入redis前可以采用snappy、gzip 等压缩算法对数据压缩,减少内存占用,
但客户端在读取数据后需要对数据做解压,会消耗更多的CPU。
6. 开启内存淘汰策略
杜绝使用默认的淘汰策略,按照业务特性选择合适淘汰策略。
7. 内存碎片化
原因:
一个是内存分配器的分配策略造成的,内存分配器是按照固定大小分配的,
而不是按照实际申请的大小分配的;
另一个是redis键值对删除之后会释放部分空间带来的内存碎片。
定位:
通过指令INFO memory 来观察 mem_fragmentation_ratio的指标,
当指标在 1-1.5之间则属于正常的(内存分配策略问题);
当指标大于 1.5 时 则表明内存碎片率已经超过了50%,需要处理内存碎片了
方案:
重启Redis实例;
开启redis自动内存碎片清理功能.
四、磁盘优化
1. 不开启持久化机制
尽可能将redis当做缓存使用,除非业务强要求,否则都推荐不开启持久化机制
2. 后台处理AOF
如果业务需要开启AOF,建议配置为 appendfsync everysec 把数据持久化的刷盘操作,
放到后台线程中去执行,尽量降低 Redis 写磁盘对性能的影响。
3. 实例大小建议不超过6G
redis实例太大在主从同步会有的卡顿现象,严重时会拖垮master;
redis在异常重启时会重放AOF,如果实例过大数据恢复会异常的缓慢。
五、性能优化
1. bigkey问题
bigkey危害:
读写bigkey可能会导致超时,而redis是单线程操作数据,严重的会导致阻塞整个redis服务。
而且一个key只会被分片到一个节点,无法分摊读写压力。
bigkey探测:
1. 自带命令redis-cli --bigkeys
redis自带指令,只能找出五种数据类型里最大的key,并没有太大作用,不推荐。
2. python扫描脚本
可以定位到具体key,但准确度不高,不推荐。
3. rdb_bigkeys工具
go写的一款工具,时间执行快且准确度高,还可以可直接导出到csv文件,方便查看,推荐。
bigkey优化:
1. 对于非string类型bigkey,可以对元素集合进行分割,拆分成功多个,如将一个bigkey拆分成1000个key,则key的后缀使用hash取模1000;
2. 使用本地缓存,如redis中之存放业务id+版本号,将具体内容放在本地缓存,每次查询先查redis缓存,再同本地缓存核对版本号;
3. 优化bigkey一般都是伤筋动骨,推荐在开发时就定义好规范,避免bigkey问题。
2. 过期策略优化
过期策略具体细节可以看我之前的博客,有详细介绍,这里就带过了。
redis默认是开启惰性删除+定期删除。
优化方案:
开启lazy-free,释放内存的耗时操作,将会放到后台线程中去执行,redis4.0支持;
开启多线程模式,在redis6.0之前过期策略都是主线程的同步操作,6.0之后采用多线程去处理。
3. 复杂度高指令
优化方案:
1. 推荐使用scan 分批次查询,不要使用keys;
2. 不使用聚合操作;
2.1 redis是单线程模型处理请求,在执行复杂度过高的命令(消耗更多cpu资源)时后面的
请求会排队导致延迟,如SINTER、SINTERSTORE、ZUNIONSTORE、ZINTERSTORE等。
2.2 推荐用scan分批次查出集合中元素,在客户端做聚合计算
3. 关注容器类数据操作
3.1 查询 当容器类元素非常多时,直接查询会存在由于网络网络问题导致的延迟,推荐分批次查询;
3.2 删除 当容器类元素非常多时,直接删除key时有可能会导致redis卡顿,推荐分批次删除。
总结
大纲基本上出来了,会定期更新核对以上内容是否有错误或遗漏地方,最后加上我画的思维导图(可能内容和图有些不一样,图会比较新,有时间再来修改博客吧)。
|