Redis 性能排查

一 查看Redis相关信息
一般可以通过Redis-cli使用info命令获取所有与Redis服务相关的信息。
info memory
127.0.0.1:6379> info memory
# Memory
used_memory:16106032 # redis分配器 分配的内存总量,单位byte
used_memory_human:15.36M
used_memory_rss:29167616 # 从操作系统上显示已经分配的内存总量
maxmemory_policy:noeviction # 淘汰数据策略
mem_fragmentation_ratio:1.81 #内存碎片率
mem_allocator:jemalloc-3.6.0 内存分配器,libc,jemalloc,tcmalloc
active_defrag_running:0 # 是否有内存碎片整理开启
lazyfree_pending_objects:0
   通过查看used_memory指标可知道Redis正在使用的内存情况,如果used_memory>可用最大内存(used_memory_rss),那就说明Redis实例正在进行内存交换或者已经内存交换完毕。
used_memory是Redis使用的内存总量,它包含了实际缓存占用的内存和Redis自身运行所占用的内存(如元数据、lua)。它是由Redis使用内存分配器分配的内存,所以这个数据并没有把内存碎片浪费掉的内存给统计进去。

    如果一个Redis实例的内存超过可用最大内存,那么操作系统开始进行内存与swap空间交换,把内存中旧的或不再使用的内容写入硬盘上(硬盘上的这块空间叫Swap分区),以便腾出新的物理内存给新页或活动页(page)使用。 
在硬盘上进行读写操作要比在内存上进行读写操作,时间上慢了近5个数量级,内存是0.1μs单位、而硬盘是10ms。如果Redis进程上发生内存交换,那么Redis和依赖Redis上数据的应用会受到严重的性能影响。
当内存使用达到设置的最大阀值时(maxmemory),需要选择一种key的回收策略,可在Redis.conf配置文件中修改“maxmemory-policy”属性值。 若是Redis数据集中的key都设置了过期时间,那么“volatile-ttl”策略是比较好的选择。但如果key在达到最大内存限制时没能够迅速过期,或者根本没有设置过期时间。那么设置为“allkeys-lru”值比较合适,它允许Redis从整个数据集中挑选最近最少使用的key进行删除(LRU淘汰算法)。Redis还提供了一些其他淘汰策略,如下:
  • volatile-lru:从已设置过期时间的数据集合中使用LRU算法淘汰数据。 [volatile:挥发物]
  • volatile-ttl:从已设置过期时间的数据集合中挑选即将过期的数据淘汰。
  • volatile-random:从已设置过期时间的数据集合中随机挑选数据淘汰。
  • allkeys-lru:使用LRU算法从所有数据集合中淘汰数据。
  • allkeys-random:从数据集合中任意选择数据淘汰
  • no-enviction:禁止淘汰数据。
  • 新版本新增两种
  • volatile-lfu:从已设置了过期时间的数据集合中,驱逐使用频率最少的键
  • allkeys-lfu:从所有键中驱逐使用频率最少的键

通过设置maxmemory为系统可用内存的45%或95%(取决于持久化策略)和设置“maxmemory-policy”为“volatile-ttl”或“allkeys-lru”(取决于过期设置),可以比较准确的限制Redis最大内存使用率,在绝大多数场景下使用这2种方式可确保Redis不会进行内存交换。倘若你担心由于限制了内存使用率导致丢失数据的话,可以设置noneviction值禁止淘汰数据。
二 分析命令处理总数,诊断响应延迟。
在Redis实例中,跟踪命令处理总数是解决响应延迟问题最关键的部分,因为Redis是个单线程模型,客户端过来的命令是按照顺序执行的。比较常见的延迟是带宽,通过千兆网卡的延迟大约有200μs。倘若明显看到命令的响应时间变慢,延迟高于200μs,那可能是Redis命令队列里等待处理的命令数量比较多。 如上所述,延迟时间增加导致响应时间变慢可能是由于一个或多个慢命令引起的,这时可以看到每秒命令处理数在明显下降,甚至于后面的命令完全被阻塞,导致Redis性能降低。要分析解决这个性能问题,需要跟踪命令处理数的数量和延迟时间。
比如可以写个脚本,定期记录total_commands_processed的值。当客户端明显发现响应时间过慢时,可以通过记录的total_commands_processed历史数据值来判断命理处理总数是上升趋势还是下降趋势,以便排查问题。
1) 命令过多会一直等待队列中,所以通过单命令多参数的形式取代多命令单参数的形式。
例如:
set 变成 mset
get变成 mget
hset 变成 hmset
hget 变成 hmget
2) 使用管道命令,pipeline,把几个命令合并执行,从而减少网络开销引起的延迟问题。
3) 避免大集合的慢命令,如果命令处理频率过低导致延迟时间增加,这可能是因为使用了高时间复杂度的命令操作导致,这意味着每个命令从集合中获取数据的时间增大。 所以减少使用高时间复杂的命令,能显著的提高的Redis的性能。
查看redis的延迟时间和慢命令
redis-cli --latency -h 127.0.0.1 -p 6379
1G 带宽 延迟不高于200微妙
查询redis慢命令
redis命令超过10ms的的都会记录到慢查询日志,这个数值由参数slowlog-log-slower-than(单位微秒默认是10000)
slowlog-max-len 设置慢查询日志存储长度,如果达到最大值,最老的那日志将被清除掉。
slowslog get 10 表示查看最后10个慢命令
    • 1) (integer) 179
    • 2) (integer) 1587011792
    • 3) (integer) 1516526
    • 4) 1) "INFO"
    • 5) "192.168.12.85:36978"
    • 6) "sentinel-4e85c44b-cmd"
    1. 第一个字段是每个慢查询唯一标识
    2. 处理完命令后,Unix时间戳
    3. 执行命令所需要的时间,单位微秒
    4. 命令的参数列表,是个数组类型
    5. 发起请求的客户端IP 和端口
    6. 客户端的名称
查看当前慢日志的日志长度
slowlog len
清空慢查询日志内容
slowlog reset
四 监控客户端的连接,
因为redis是单线程模型,来处理所有客户端请求,但由于客户端连接数增长,处理请求的线程资源开始降低分配给单个客户端连接的处理时间,这时每个客户端需要花费更多的时间去等待Redis共享服务的响应。这种情况下监控客户端连接数是非常重要的,因为客户端创建连接数的数量可能超出预期数量,也可能是客户端没有有效的释放连接。
查看当前实例所有客户端连接信息
info clients
# Clients
connected_clients:8 //当前有8个
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
Redis 默认允许客户端连接的最大数量是10000,若是看到超过5000以上,那可能会影响Redis性能。相关参数为 # maxclients 10000。
根据连接数负载的情况,这个数字应该设为预期连接数峰值的110%到150之间,若连接数超出这个数后,Redis 会拒绝并立刻关闭新来的连接。通过设置最大连接数来限制非预期数量的连接数,是非常重要的。另外新连接尝试失败会返回一个错误消息,这可以让客户端知道,Redis此时有非预期数量的连接数,以便执行对应的处理措施。
五 加强内存管理
Redis 内存划分大致为为,数据,进程本身运行内存(代码,常量池),缓冲内存,内存碎片。其中进程本身运行内存和内存碎片不会保存在used_memory中。
较少的内存会引起Redis进行swap从而导致延迟时间增加。
性能数据指标:
分析解决Redis性能问题,通常需要把延迟时间的数据变化与其他性能指标的变化相关联起来。
命令处理总数下降的发生可能是由慢命令阻塞了整个系统。
对于这种性能指标相关联的分析,需要从历史数据上来观察到数据指标的重要变化,此外还可以观察到单个性能指标相关联的所有其他性能指标信息。
这些数据可以在Redis上收集,周期性的调用内容为Redis info的脚本,然后分析输出的信息,记录到日志文件中。当延迟发生变化时,用日志文件配合其他数据指标,把数据串联起来排查定位问题。
内存碎片率:
info 信息中 mem_fragmentation_ratio 给出了内存碎片率的数据指标,算法是OS分配的内存除以Redis 分配的内存:
Memory Fragmentation Ratio = UsedMemoryRss/UsedMemory
used_memory_rss的rss是Resident Set Size的缩写,表示该进程所占物理内存的大小,是操作系统分配给Redis实例的内存大小。
而UsedMemory 由 Redis 分配器分配的内存总量,包含了redis进程内部的开销和数据占用的内存,以字节(byte)为单位redis分配器 分配的内存总量。
两者包括了实际缓存占用的内存和Redis自身运行所占用的内存,used_memory_rss指标还包含了内存碎片的开销,内存碎片是由操作系统低效的分配/回收物理内存导致的。
mem_fragmentation_ratio < 1 表示Redis内存分配超出了物理内存,操作系统正在进行内存交换,内存交换会引起非常明显的响应延迟;
mem_fragmentation_ratio > 1 是合理的;(有一个数值是1.03是比较合理的)
mem_fragmentation_ratio > 1.5 说明Redis消耗了实际需要物理内存的150%以上,其中50%是内存碎片率,可能是操作系统或Redis实例中内存管理变差的表现。
解决方案是
1 重启redis.....
2 开启自动内存碎片整理 config set activedefrag yes
3 手动清理内存碎片 memeory purge
如果碎片率小于1 证明redis内存用的很多,要么增加可用物理内存,要么减少Redis内存占用。
KEY的回收
info stats 有个evicted_keys(驱逐key的数量),因为maxmemory 限制导致key被回收删除的数量。如果没有设置或者设置了noeviction 可能在超过maxmemory 之后会发生swap,导致Redis延迟过高
如果大多数key 都有明确的过期时间,那过期时间回收策略是比较ok的,如果没有设置过期时间那么lru策略比较合理的。
根据key回收定位性能问题:
通过回收key 可以保证合理分配Redis 有限内存资源。如果evicted_keys 值经常超过0,那应该会看到客户端命令响应延迟时间增加,因为Redis 不但要处理客户端过来的命令请求,可以频繁的回收满足条件的key。回收key对性能的影响远没有内存交换严重,若是在强制内存交换和设置回收策略做一个选择的话,选择设置回收策略是比较合理的,因为把内存数据交换到硬盘上对性能影响非常大。
减少key的回收数量是提升Redis性能的直接办法,那么如何减少key的回收数量就显得尤为重要。
1) 增加内存限制:如果开启了RDB,maxmemory 需要设置成物理内存45%,这几乎不会有引发内存交换的危险。若是没有开启快照功能,设置可用内存的95%是比较合理的。通过增加maxmemory的值能让Redis在内存中存储更多的key,这能显著减少回收key的数量。设置分两种,第一种redis-cli 中通过config set maxmemory 命令来设置,会立即生效,重启后丢失,可以通过config rewrite 命令会把内存中的新配置刷新到配置文件中.第二种就是在redis.conf中配置.
2) 对实例进行分片: 分片是把数据分割成合适大小,分别存放在不同的Redis实例上,通过分片可以把很多服务器联合起来存储数据,相当于增加总的物理内存,使其在没有内存交换和回收key的策略下也能存储更多的key。假如有一个非常大的数据集,maxmemory已经设置,实际内存使用也已经超过了推荐设置的阀值,那通过数据分片能明显减少key的回收,从而提高Redis的性能。 分片的实现有很多种方法,下面是Redis实现分片的几种常见方式:
  • a. Hash分片:一个比较简单的方法实现,通过Hash函数计算出key的Hash值,然后值所在范围对应特定的Redis实例。
  • b. 代理分片:客户端把请求发送到代理上,代理通过分片配置表选择对应的Redis实例。 如Twitter的Twemproxy,豌豆荚的codis。
  • c. 一致性Hash分片:
  • d. 虚拟桶分片:
总结: redis性能参数 可以通过info命令查看
要结合maxmemory配置 防止redis进行swap
redis 内存碎片率=系统分配内存/redis自身内存
redis 内存碎片率 在1 左右是最理想的状态,小于1会发生swap,大于1 导致内存浪费。
redis 内存碎片率过高,可以通过手动,自动内存碎片整理,或者重启redis来解决。
可以通过周期性的调用Redis info的脚本,记录综合分析定位问题.
通过设置maxmemory和实例分片方法来减少key回收。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注