Redis 最牛实践:业务层面和运维层面优化!
Java技术栈
www.javastack.cn
关注阅读更多优质文章
出处:kaito-kidd.com/2020/07/04/redis-best-practices/
在上一篇文章:
常见延迟问题定位与分析,主要分析了常见的导致变慢的场景以及问题定位和分析,主要是由业务使用不合理和运维不当导致的。
我们在了解了导致变慢的原因之后,针对性地优化,就可以让稳定发挥出更高性能。
这篇文章我们就来总结一下,在使用Redis时的最佳实践方式,主要包含两个层面:业务层面、运维层面。
由于我之前写过很多UGC后端服务,在大量场景下用到了Redis,这个过程中也踩过很多坑,所以在使用过程中也总结了一套合理的使用方法。
后来做基础架构,开发Codis、相关的中间件,在这个阶段关注领域从使用层面下沉到Redis的开发和运维,更多聚焦在的内部实现和运维过程中产生的各种问题,在这块也积累了一些经验。
业务层面主要是开发人员需要关注,也就是开发人员在写业务代码时,如何合理地使用Redis。开发人员需要对有基本的了解,才能在合适的业务场景使用,从而避免业务层面导致的延迟问题。
在开发过程中,业务层面的优化建议如下:
-
key的长度尽量要短,在数据量非常大时,过长的key名会占用更多的内存
-
一定避免存储过大的数据(大value),过大的数据在分配内存和释放内存时耗时严重,会阻塞主线程
-
Redis 4.0以上建议开启
lazy-free
机制,释放大value时异步操作,不阻塞主线程 -
建议设置过期时间,把Redis当做缓存使用,尤其在数量很大的时,不设置过期时间会导致内存的无限增长
-
不使用复杂度过高的命令,例如
SORT
、SINTER
、SINTERSTORE
、ZUNIONSTORE
、ZINTERSTORE
,使用这些命令耗时较久,会阻塞主线程 -
查询数据时,一次尽量获取较少的数据,在不确定容器元素个数的情况下,避免使用
LRANGE key 0 -1
,ZRANGE key 0 -1
这类操作,应该设置具体查询的元素个数,推荐一次查询100个以下元素 -
写入数据时,一次尽量写入较少的数据,例如
HSET key value1 value2 value3...
,控制一次写入元素的数量,推荐在100以下,大数据量分多个批次写入 -
批量操作数据时,用
MGET/MSET
替换GET/SET
、HMGET/MHSET
替换HGET/HSET
,减少请求来回的网络IO次数,降低延迟,对于没有批量操作的命令,推荐使用pipeline
,一次性发送多个命令到服务端 -
禁止使用
KEYS
命令,需要扫描实例时,建议使用SCAN
,线上操作一定要控制扫描的频率,避免对产生性能抖动 -
避免某个时间点集中过期大量的key,集中过期时推荐增加一个随机时间,把过期时间打散,降低集中过期key时的压力,避免阻塞主线程
-
根据业务场景,选择合适的淘汰策略,通常随机过期要比LRU过期淘汰数据更快
-
使用连接池访问,并配置合理的连接池参数,避免短连接,TCP三次握手和四次挥手的耗时也很高
-
只使用
db0
,不推荐使用多个db,使用多个db会增加的负担,每次访问不同的db都需要执行SELECT
命令,如果业务线不同,建议拆分多个实例,还能提高单个实例的性能 -
读的请求量很大时,推荐使用读写分离,前提是可以容忍从节数据更新不及时的问题
-
写请求量很大时,推荐使用集群,部署多个实例分摊写压力
运维层面主要是DBA需要关注的,目的是合理规划Redis的部署和保障Redis的稳定运行,主要优化如下:
-
不同业务线部署不同的实例,各自独立,避免混用,推荐不同业务线使用不同的机器,根据业务重要程度划分不同的分组来部署,避免某一个业务线出现问题影响其他业务线
-
保证机器有足够的CPU、内存、带宽、磁盘资源,防止负载过高影响Redis性能
-
以master-slave集群方式部署实例,并分布在不同机器上,避免单点,slave必须设置为
readonly
-
master和slave节点所在机器,各自独立,不要交叉部署实例,通常备份工作会在slave上做,做备份时会消耗机器资源,交叉部署会影响到master的性能
-
推荐部署哨兵节点增加可用性,节点数量至少3个,并分布在不同机器上,实现故障自动故障转移
-
提前做好容量规划,一台机器部署实例的内存上限,最好是机器内存的一半,主从全量同步时会占用最多额外一倍的内存空间,防止网络大面积故障引发所有master-slave的全量同步导致机器内存被吃光
-
做好机器的CPU、内存、带宽、磁盘监控,在资源不足时及时报警处理,Redis使用Swap后性能急剧下降,网络带宽负载过高访问延迟明显增大,磁盘IO过高时开启AOF会拖慢的性能
-
设置最大连接数上限,防止过多的客户端连接导致服务负载过高
-
单个实例的使用内存建议控制在20G以下,过大的实例会导致备份时间久、资源消耗多,主从全量同步数据时间阻塞时间更长
-
设置合理的
slowlog
阈值,推荐10毫秒,并对其进行监控,产生过多的慢日志需要及时报警 -
设置合理的复制缓冲区
repl-backlog
大小,适当调大repl-backlog
可以降低主从全量复制的概率 -
设置合理的slave节点
client-output-buffer-limit
大小,对于写入量很大的实例,适当调大可以避免主从复制中断问题 -
备份时推荐在slave节点上做,不影响master性能
-
不开启AOF或开启AOF配置为每秒刷盘,避免磁盘IO消耗降低Redis性能
-
当实例设置了内存上限,需要调大内存上限时,先调整slave再调整master,否则会导致主从节点数据不一致
-
对Redis增加监控,监控采集
info
信息时,使用长连接,频繁的短连接也会影响Redis性能,,这篇推荐看下 -
线上扫描整个实例数时,记得设置休眠时间,避免扫描时QPS突增对Redis产生性能抖动
-
做好Redis的运行时监控,尤其是
expired_keys
、evicted_keys
、latest_fork_usec
指标,短时间内这些指标值突增可能会阻塞整个实例,引发性能问题
以上就是我在使用和开发相关中间件时,总结出来Redis推荐的实践方法,以上提出的这些方面,都或多或少在实际使用中遇到过。
如果对上面的建议产生疑问,可以我之前写的这篇文章:常见延迟问题定位与分析,里面详细描述了产生问题的原因。
可见,要想稳定发挥的高性能,需要在各个方面做好工作,但凡某一个方面出现问题,必然会影响到的性能,这对我们使用和运维提出了更高的要求。
如果你在使用过程中,遇到更多的问题或者有更好的使用经验,可以留言一起探讨!
关注Java技术栈看更多干货