往期分享
RDS MySQL
RDS PostgreSQL
RDS SQL Server
Redis
概述
默认情况下,社区版Redis使用单线程模型处理读写请求,这使得CPU的使用率显得尤为重要。当实例的CPU打满时会导致数据库响应缓慢,严重影响线上业务。
本文将由浅入深帮您查看、分析和优化云数据库Redis的CPU使用率。
查看CPU使用
部署架构为主备模式下,只提供当前主节点的CPU使用。通过监控图查看CPU的使用率非常方便,点击控制台"性能监控"后一目了然。
部署架构为Redis集群/读写分离模式下,由于多个物理节点组成了一个逻辑实例,且可能通过多个Proxy节点做路由转发和负载均衡,通过监控图查看CPU使用率时一般只需要关注该集群数据节点的CPU使用率,"数据节点聚合指标","Proxy节点聚合指标","Proxy"中的CPU使用率请忽略。
CPU使用率高的一般排查步骤
由于社区版Redis6.0以下的版本使用单线程处理IO请求,所以基本上Redis进程本身也只能用到CPU的一个核,当CPU使用率超过80%时,就需要引起开发者的高度重视。一般可以大概分为以下几个步骤
确认是否存在流量突增/热点Key
在没有明显慢SQL的情况下,读写流量突增是引起Redis High CPU的常见原因,云数据库Redis不同的产品形态和实例规格对应了不同的QPS上限。
详情建议参考:
- https://help.aliyun.com/document_detail/100453.html
- https://help.aliyun.com/document_detail/188013.html
但需要注意的,这里提供的结果仅仅作为基准压测的参考,在实际的生产环境中,某个Redis实例能够承载的QPS上限往往与key和value的平均长度,key的数据类型,内网带宽等息息相关。也就是说,当前Redis实例的处理能力上限要以实际的业务压测为准,当业务代码没有任何变动的情况下CPU突增,建议您结合监控图先观察当前Redis实例的各项QPS指标是否同样存在突增。
另外阿里云支持Redis热点key的实时和历史情况查看,详细可参考:
- https://help.aliyun.com/document_detail/160585.html
- https://help.aliyun.com/document_detail/181195.html
如果纯粹是由于业务增长/热点Key引起的Redis CPU使用率高,那么优化思路一般有以下几种,本质上就是通过多线程或者架构升级的方式解决:
- 进行业务拆分,不同的业务使用不同的Redis实例
- 针对一些hash结构的超热点key(单key请求过10w),可以考虑将其拆分成string类型并存放至多个Redis实例中
- 针对string类型的超热点key(单key写请求过10w),在业务架构无法限流的情况下,必须使用阿里云“性能增强版”,多线程IO处理模型可以抗住20w以上的单key请求量
- 针对读多写少的场景,可以考虑使用阿里云Redis读写分离版,通过添加只读实例的方式抗住高并发读请求
- 针对写多读少的场景,可以考虑使用阿里云Redis集群版,注意使用读写分离/集群版以后,可能存在少部分Redis命令的兼容性
我们做一下简单对比,比如阿里云Redis社区标准版能够承载的QPS为K(社区标准版性能基本上与社区开源保持一致,基准压测的情况下一般在10w左右),如下表格即为阿里云主流Redis产品形态下的读写QPS总数参考。
Redis社区版 集群 |
Redis社区版 读写分离 |
Redis企业版-性能增强型主备 |
Redis企业版-性能增强型集群 |
Redis企业版-性能增强型读写分离 |
|
写(key均匀情况) |
K*分片数 |
K |
K*3 |
K*3*分片数 |
K*3 |
读(key均匀情况) |
K*分片数 |
K*只读节点数 |
K*3 |
K*3*分片数 |
K*3*只读节点 |
写(热key) |
K(最坏情况) |
K |
K*3 |
K*3 |
K*3 |
读(热key) |
K(最坏情况) |
K*只读节点数 |
K*3 |
K*3 |
K*3*只读节点 |
确认是否存在慢SQL
在阿里云数据库Redis中,默认执行时间超过20ms的称为慢SQL,会记录到慢SQL FIFO队列以供查看。查询执行时间指的是不包括像客户端响应(talking)、发送回复等 IO 操作,而单单是执行一个查询命令所耗费的时间,您可以通过控制台提供的参数slowlog-log-slower-than和slowlog-max-len自定义配置慢SQL阈值和保存条数。您可以通过Redis自带得slowlog get指令查看慢SQL,也可以通过阿里云控制台CloudDBA查看,详细内容请参考:
通常Redis指令消耗的CPU与指令的时间复杂度息息相关,一般认为O(N)或以上的复杂度在业务代码设计时需要谨慎对待它对应的流量评估。具体每个Redis指令的时间复杂度请参考官方文档:https://redis.io/commands
最常见的慢SQL包括KEYS LRANGE EVAL HGETALL PUBSUB等,一般都是扫描量比较大,KEY比较大,涉及到Redis计算逻辑的指令,这些较为消耗CPU资源的指令详情和原理请参考:
- https://redis.io/commands/keys
- https://redis.io/commands/lrange
- https://redis.io/commands/eval
- https://redis.io/commands/hgetall
由于Redis提供有相对较多的数据类型,而诸如Hash,List等常见数据类型中列表长度上限较高,所以在实际使用中很容易导致big key问题,阿里云Redis控制台CloudDBA提供有"缓存分析"找出实例中的存在性能隐患的大Key,详情可参考:https://help.aliyun.com/document_detail/102093.html
确认是否存在流量不均
在使用读写分离/集群架构模式下,当集群出现响应延迟时,首先需要确认是集群下所有对外提供服务的物理节点均出现High CPU还是部分节点出现热点的情况。
除了上述提到的big key和热点key容易引起的集群数据倾斜和流量不均以外,还有几种情况可能导致读写分离架构下的各个主节点,只读节点之间的流量不均:
- scan系列命令由于存在上下文关系,需要将该类命令全部转发至第一个只读节点
- bitfield命令混合了读写属性,默认情况下只会转发至主节点,这点阿里云Redis已优化,可提工单开启,详细参考:https://zhuanlan.zhihu.com/p/139747397
通用最佳实践
关于以上可能引起的CPU打满的情况,通用的最佳实践有以下几点:
- 优先设计合理的数据结构和逻辑,各指令的流量评估,并进行实际的业务压测。
- 尽量不要使用pubsub做消息订阅和分发,Redis实际上并不适合。
- 尽量避免使用lua做事务,脚本编译和加载会非常消耗CPU,建议使用扩展数据结构代替,如果需要实现高性能分布式锁建议使用企业版Redis,详细可参考:https://help.aliyun.com/document_detail/146758.html。
- Hash结构不适合存放通质且大量的key,容易引起数据倾斜并造成big key问题。
- 尽量避免使用blocking的API,如blpop,brpop等。
- 尽量避免keys命令的模糊匹配,当数据量上涨后一定会出现问题,建议使用scan命令代替,针对危险命令可以在控制台禁用,详细可参考:https://help.aliyun.com/document_detail/107695.html。
特殊场景下的High CPU
在一些特殊情况下,Redis也会更加消耗CPU,我们在实际运维的过程中一般有遇到以下情况:
- 节点宕机导致的全量同步,期间存在部分对外服务节点无法提供读负载均衡。
- Redis超高频使用incr指令作为计数器应用时,master节点CPU使用率可能比slave高很多,因为master向slave传输数据时可能做了合并,类似于pipeline,所以slave节点需要回放的请求数更少,消耗的CPU更低。
- 高频建连操作可能消耗更多的CPU,因为连接的建立和释放均会消耗一定的CPU,建议使用连接池,或者升级至Proxy集群,企业性能增强版等Redis产品。
- 在连接数较多的情况下频繁调用info指令查看各项监控信息可能导致CPU使用率更高,因为部分info子命令的监控信息统计需要遍历每一个client连接,所以当连接数超过5k的情况下建议控制info的频率。
- 频繁的AOF可能导致更高的CPU消耗,如果是将Redis纯粹当作Cache使用的场景,建议关闭AOF持久化,详细参考:https://redis.io/commands/bgrewriteaof。