性能分析及优化
内存诊断
内存使用率是Redis服务最关键的一部分。
如果Redis实例的内存使用率超过最大可用内存,即“used_memory”>最大可用内存,那么操作系统会将内存与Swap空间交换,把内存中旧的或不再使用的内容写入硬盘上的Swap分区,以便留出新的物理内存给新页或活动页(page)使用。
通过查看“used_memory”指标可知道Redis的内存情况,当“used_memory”>最大可用内存时,Redis实例正在进行内存交换或者已经内存交换完毕。如果Redis进程上发生内存交换,那么Redis及使用Redis数据的应用性能都会受到严重影响。
理想情况下,“used_memory_rss”的值应该比“used_memory”略微高一点。
“used_memory_rss”>“used_memory”,且两者的值相差较大时,表示存在(内部或外部的)内存碎片。内存碎片的比率可以通过“mem_fragmentation_ratio”的值确定。
“used_memory”>“used_memory_rss”,表示Redis的部分内存被操作系统换出到Swap空间了,在这种情况下,操作可能会产生明显延迟。
当Redis释放内存时,分配器有可能会将内存返还给操作系统,也可能不会。
如果Redis释放了内存,却没有将内存返还给操作系统,那么“used_memory”的值可能和操作系统显示的Redis内存占用并不一致,通过查看“used_memory_peak”可以验证这种情况是否发生。
①跟踪内存使用率
当Redis内存使用率超过可用内存的95%时,部分数据开始在内存与Swap空间来回交换,如果没有开启RDB快照或AOF持久化策略,缓存数据在Redis崩溃时会有丢失风险。
当开启并触发快照功能时,Redis会fork一个子进程,复制当前内存中的数据到硬盘,若当前使用内存超过可用内存的45%时触发快照功能,那么此时进行的内存交换可能会丢失数据。如果此时Redis实例上有大量频繁的更新操作,问题会更加严重。
我们可以减少Redis的内存占用率来解决该问题,或者使用下面的技巧来避免内存交换:
尽可能的使用Hash数据结构
Redis在储存小于100个字段的Hash结构时,存储效率非常高,因此在不需要集合“set”操作或“list”的“push/pop”操作时,我们应尽可能地使用Hash结构。
设置“key”的过期时间
通过在存储对象时设置“key”的过期时间可以减少内存使用率。倘若“key”在明确的时间周期内使用或者旧“key”不大可能被使用时,就可以用Redis过期时间命令(expire,expireat, pexpire, pexpireat)去设置过期时间,这样Redis会在“key”过期时自动将其删除。
回收“key”。
在Redis配置文件中(一般为“redis.conf”文件),设置“maxmemory”的值可以限制Redis最大使用内存,修改后重启实例生效。
当内存使用达到设置的最大阀值时,需要选择一种“key”的回收策略,可在“redis.conf”配置文件中修改“maxmemory-policy”属性。若是Redis数据集中的“key”都设置了过期时间,那么“volatile-ttl”策略是比较好的选择,但如果“key”在达到最大内存限制时没能迅速过期,或根本没有设置过期时间,那么设置为“allkeys-lru”更加合适,它允许Redis从整个数据集中挑选近期最少使用的“key”进行删除(LRU淘汰算法)。
Redis还提供了一些其他淘汰策略,如下:
“volatile-lru”:使用LRU算法从已设置过期时间的数据集合中淘汰数据
“volatile-ttl”:从已设置过期时间的数据集合中淘汰即将过期的数据
“volatile-random”:从已设置过期时间的数据集合中随机淘汰数据
“allkeys-lru”:使用LRU算法从所有数据集合中淘汰数据
“allkeys-random”:从数据集合中任意淘汰数据
“no-enviction”:禁止淘汰数据
设置“maxmemory”的值为系统可用内存的45%或95%(取决于持久化策略),设置“maxmemory-policy”为“volatile-ttl”或“allkeys-lru”(取决于过期设置),可以比较准确的限制Redis最大内存使用率,在绝大多数场景下使用这2种方式可确保Redis不会进行内存交换。
延迟诊断
Redis之所以这么流行的主要原因之一就是低延迟特性带来的高性能,所以说解决延迟问题是提高Redis性能最直接的办法。
以1G带宽来说,若是延迟时间远高于200μs,那明显是出现了性能问题。Redis是单核执行所有客户端的请求的, 即使在服务器上会有一些慢的IO操作,这些请求也都是按序排队等待执行的。
①通过slowlog查出引发延迟的慢命令
Redis中的“slowlog”命令可以让我们快速定位到超出指定执行时间的慢命令,默认情况下命令若是执行时间超过10ms就会被记录到日志。“slowlog”只会记录其命令执行的时间,不包含IO往返操作及由网络延迟引起的慢响应 。
通常1GB带宽的网络延迟预期在200μs左右,倘若一个命令仅执行时间就超过10ms(近网络延迟的50倍),此时可以使用“redis-cli”工具,输入“slowlog get”命令进行查看,此时返回结果的第三个字段将以微秒为单位显示命令的执行时间,假如只需要查看最后10个慢命令,输入“slowlog get 10”即可。
②客户端连接监控
Redis是单线程模型,只能单核处理客户端的请求。由于客户端连接数的增长,处理请求的线程资源将降低分配给单个客户端连接的处理时间,这时每个客户端等待Redis共享服务的响应时间将延长。
因此,监控客户端连接数是非常有必要的,通过监控,可以确定客户端创建连接数的数量是否超出预期,以及客户端是否没有有效释放连接。
查看命令如下:
“./redis-cli -p 6379 info |grep connected_clients”
Redis默认允许客户端连接的最大数量是10000,连接数超过5000以上,可能会影响Redis的性能。
可尝试通过如下办法降低客户端连接数:
服务器内存足够:可增加Redis的实例个数,均摊客户端连接。
服务器内存足够:可增加maxmemory配置,提高处理速度。
应用通过连接池调用Redis:可适当降低Redis连接的空闲数量。
若以上方法未能降低Redis的连接数,可限制客户端连接数来提高性能。
③限制客户端连接数
设置最大连接数可限制非预期的连接数增长,并保持Redis的性能最优。
Redis2.6版本及以上允许使用者通过配置文件(redis.conf)配置“maxclients”属性,来修改客户端连接的最大值。根据连接数负载的情况,这个数字应该设置为预期连接数峰值的110到150之间,若连接数超出这个数值,Redis会拒绝并立刻关闭新来的连接。
如新连接尝试失败,将返回一个错误消息,客户端可执行对应的处理措施。
内存碎片诊断
“info”信息中的“mem_fragmentation_ratio”给出了内存碎片率的数据指标,该指标由操作系统分配的内存除Redis分配的内存得出:
“mem_fragmentation_ratio = used_memory_rss / used_memory”
“used_memory”和“used_memory_rss”都包含的内存分配有:
用户定义的数据:内存被用来存储“key-value”值
内部开销:存储内部Redis信息来表示不同的数据类型
“used_memory_rss”的“rss”是“Resident Set Size”的缩写,表示该进程所占物理内存的大小是操作系统分配给Redis实例的内存大小。
除用户定义的数据和内部开销外,“used_memory_rss”指标还包含了内存碎片的开销,内存碎片是由操作系统低效的分配除回收物理内存得到的。
如内存碎片率超过1.5,可能是操作系统或Redis实例中内存管理变差的表现。
下面列举3种解决内存管理变差并提高Redis性能的方法:
①重启Redis服务器
如内存碎片率超过1.5,重启Redis服务器可让额外产生的内存碎片失效并作为新内存使用,使操作系统恢复高效的内存管理。
额外碎片的产生是由于Redis释放了内存块,但编译时制定的内存分配器(“libc”、“jemalloc”或“tcmalloc”)并没有返回内存给操作系统。
通过比较“used_memory_peak”、“used_memory_rss”和“used_memory_metrics”的数据指标可检查额外内存碎片的占用。如果过去Redis内存使用的峰值“used_memory_peak”和“used_memory_rss”的值大致相等,且二者明显超过“used_memory”的值,则额外的内存碎片正在产生。 在“redis-cli”工具上输入“info memory”可以查看上面三个指标的信息。
重启服务器前,需在“redis-cli”工具上输入“shutdown save”命令,强制让Redis数据库执行保存操作并关闭Redis服务,这样能保证关闭Redis时不丢失任何数据。在重启后,Redis会从硬盘上加载持久化的文件,确保数据集持续可用。
②限制内存交换
如果内存碎片率低于1,Redis实例可能会把部分数据交换到硬盘上,将严重影响Redis的性能。我们可以增加可用物理内存或减少Redis实例内存占用。具体可查看“used_memory”章节的优化建议。
③修改内存分配器
Redis支持“glibc’s malloc”、“jemalloc11”和“tcmalloc”等几种不同的内存分配器,每个分配器在内存分配和碎片上都有不同的实现。
修改Redis默认内存分配器,需要完全理解这几种内存分配器的差异,也需重新编译Redis,因此,不建议普通管理员修改。通过这个方法可了解Redis内存分配器所做的工作,改善内存碎片问题。
Redis优化总结
①根据业务需要选择合适的数据类型,并为不同的应用场景设置相应的紧凑存储参数。
②若业务场景不需要数据持久化,关闭持久化方式用以提高处理性能及内存使用率。
③不要让你的Redis所在机器物理内存使用超过实际内存总量的60%。
④默认情况下,尽量不要让Redis实例的客户端连接数超出5000