latency fragment 带宽 more 日志文件 随机 2.6 one
info 命令输出的数据可分为 10 个分类,分别是:
server,clients,memory,persistence,stats,replication,cpu,commandstats,cluster,keyspace
为了快速定位并解决性能问题,这里选择 5 个关键性的数据指标,它包含了大多数人在使用 Redis 上会经常碰到的性能问题
上图中 used_memory 字段数据表示的是:由 Redis 分配器分配的内存总量,以字节(byte)为单位。 其中 used_memory_human 和 used_memory 是一样的,以 M 为单位显示
- 127.0.0.1:6379[20]>info memory
- # Memory
- used_memory:8589645288
- used_memory_human:8.00G
- used_memory_rss:9439997952
- used_memory_peak:9082282776
- used_memory_peak_human:8.46G
- used_memory_lua:35840
- mem_fragmentation_ratio:1.10
- mem_allocator:jemalloc-3.6.0
used_memory 是 Redis 使用的内存总量,包含了实际缓存占用的内存和 Redis 自身运行所占用的内存 (如元数据、lua),是由 Redis 使用内存分配器分配的内存,所以这个数据不包括内存碎片浪费掉的内存
其他字段代表的含义,都以字节为单位:
内存使用率是 Redis 服务最关键的一部分。如果 Redis 实例的内存使用率超过可用最大内存 (used_memory> 可用最大内存),那么操作系统开始进行内存与 swap 空间交换,把内存中旧的或不再使用的内容写入硬盘上(硬盘上的这块空间叫 Swap 分区),以便留出新的物理内存给新页或活动页 (page) 使用。
如果 Redis 进程上发生内存交换,那么 Redis 和依赖 Redis 上数据的应用会受到严重的性能影响。 通过查看 used_memory 指标可知道 Redis 正在使用的内存情况,如果 used_memory > 可用最大内存,那就说明 Redis 实例正在进行内存交换或者已经内存交换完毕。
若是在使用 Redis 期间没有开启 rdb 快照或 aof 持久化策略,那么缓存数据在 Redis 崩溃时就有丢失的危险。因为当 Redis 内存使用率超过可用内存的 95% 时,部分数据开始在内存与 swap 空间来回交换,这时就可能有丢失数据的危险。
当开启并触发快照功能时,Redis 会 fork 一个子进程把当前内存中的数据完全复制一份写入到硬盘上。因此若是当前使用内存超过可用内存的 45% 时触发快照功能,那么此时进行的内存交换会变的非常危险 (可能会丢失数据)。 倘若在这个时候实例上有大量频繁的更新操作,问题会变得更加严重。
通过减少 Redis 的内存占用率,来避免这样的问题,或者使用下面的技巧来避免内存交换发生:
当内存使用达到设置的最大阀值时,需要选择一种 key 的回收策略,可在 Redis.conf 配置文件中修改 "maxmemory-policy" 属性值。 若是 Redis 数据集中的 key 都设置了过期时间,那么 "volatile-ttl" 策略是比较好的选择。但如果 key 在达到最大内存限制时没能够迅速过期,或者根本没有设置过期时间。那么设置为 "allkeys-lru" 值比较合适,它允许 Redis 从整个数据集中挑选最近最少使用的 key 进行删除 (LRU 淘汰算法)。Redis 还提供了一些其他淘汰策略,如下:
通过设置 maxmemory 为系统可用内存的 45% 或 95%(取决于持久化策略) 和设置 "maxmemory-policy" 为 "volatile-ttl" 或 "allkeys-lru"(取决于过期设置),可以比较准确的限制 Redis 最大内存使用率,在绝大多数场景下使用这 2 种方式可确保 Redis 不会进行内存交换。倘若你担心由于限制了内存使用率导致丢失数据的话,可以设置 noneviction 值禁止淘汰数据。
在 info 信息里的 total_commands_processed 字段显示了 Redis 服务处理命令的总数,其命令来自一个或多个 Redis 客户端
- info stats
- # Stats
- total_connections_received:843391006
- total_commands_processed:3946780282
- instantaneous_ops_per_sec:1447
- total_net_input_bytes:5060670300797
- total_net_output_bytes:13788457111609
- instantaneous_input_kbps:1399.63
- instantaneous_output_kbps:2863.71
- rejected_connections:0
- sync_full:2
- sync_partial_ok:1
- sync_partial_err:0
- expired_keys:231497375
- evicted_keys:0
- keyspace_hits:613100363
- keyspace_misses:252710911
- pubsub_channels:0
- pubsub_patterns:0
- latest_fork_usec:60179
在 Redis 实例中,跟踪命令处理总数是解决响应延迟问题最关键的部分,因为 Redis 是个单线程模型,客户端过来的命令是按照顺序执行的。比较常见的延迟是带宽,通过千兆网卡的延迟大约有 200μs。倘若明显看到命令的响应时间变慢,延迟高于 200μs,那可能是 Redis 命令队列里等待处理的命令数量比较多。 如上所述,延迟时间增加导致响应时间变慢可能是由于一个或多个慢命令引起的,这时可以看到每秒命令处理数在明显下降,甚至于后面的命令完全被阻塞,导致 Redis 性能降低。要分析解决这个性能问题,需要跟踪命令处理数的数量和延迟时间。
比如可以写个脚本,定期记录 total_commands_processed 的值。当客户端明显发现响应时间过慢时,可以通过记录的 total_commands_processed 历史数据值来判断命理处理总数是上升趋势还是下降趋势,以便排查问题。
通过与记录的历史数据比较得知,命令处理总数确实是处于上升或下降状态,那么可能是有 2 个原因引起的:
下面有三个办法可以解决,因上面 2 条原因引起的响应延迟问题。
- set -> mset
- get -> mget
- lset -> lpush, rpush
- lindex -> lrange
- hset -> hmset
- hget -> hmget
Redis 的延迟数据是无法从 info 信息中获取的。可以用 Redis-cli 工具加 --latency 参数运行,如:
- redis-cli --latency -h 127.0.0.1 -p 6379
由于当前服务器不同的运行情况,延迟时间可能有所误差,通常 1G 网卡的延迟时间是 200μs,Redis 的响应延迟时间以毫秒为单位
- [[email protected]~]#redis - cli--latency - h 127.0.0.1 - p 6379 min: 0,
- max: 1,
- avg: 0.07(12596 samples)
Redis 之所以这么流行的主要原因之一就是低延迟特性带来的高性能,所以说解决延迟问题是提高 Redis 性能最直接的办法。拿 1G 带宽来说,若是延迟时间远高于 200μs,那明显是出现了性能问题。 虽然在服务器上会有一些慢的 IO 操作,但 Redis 是单核接受所有客户端的请求,所有请求是按良好的顺序排队执行。因此若是一个客户端发过来的命令是个慢操作,那么其他所有请求必须等待它完成后才能继续执行。
一旦确定延迟时间是个性能问题后,这里有几个办法可以用来分析解决性能问题。
1. 使用 slowlog 查出引发延迟的慢命令:Redis 中的 slowlog 命令可以让我们快速定位到那些超出指定执行时间的慢命令,默认情况下命令若是执行时间超过 10ms 就会被记录到日志。slowlog 只会记录其命令执行的时间,不包含 io 往返操作,也不记录单由网络延迟引起的响应慢。通常 1gb 带宽的网络延迟,预期在 200μs 左右,倘若一个命令仅执行时间就超过 10ms,那比网络延迟慢了近 50 倍。 想要查看所有执行时间比较慢的命令,可以通过使用 Redis-cli 工具,输入 slowlog get 命令查看,返回结果的第三个字段以微妙位单位显示命令的执行时间。假如只需要查看最后 10 个慢命令,输入 slowlog get 10 即可
- slowlog get
- 1) 1) (integer) 12849
- 2) (integer) 1495630160
- 3) (integer) 61916
- 4) 1) "KEYS"
- 2) "20170524less*"
- 2) 1) (integer) 12848
- 2) (integer) 1495629901
- 3) (integer) 59368
- 4) 1) "KEYS"
- 2) "20170524more*"
- 3) 1) (integer) 12847
- 2) (integer) 1495629504
- 3) (integer) 59522
- 4) 1) "KEYS"
- 2) "sou_dzmore_16_*"
- 4) 1) (integer) 12846
- 2) (integer) 1495629504
- 3) (integer) 57941
- 4) 1) "KEYS"
- 2) "sou_dz_16_*"
- 5) 1) (integer) 12845
- 2) (integer) 1495629504
- 3) (integer) 15053
- 4) 1) "KEYS"
- 2) "list_dingzhis_16_*"
- 6) 1) (integer) 12844
- 2) (integer) 1495629504
- 3) (integer) 24391
- 4) 1) "KEYS"
- 2) "cache_kwnew_*"
- 7) 1) (integer) 12843
- 2) (integer) 1495629469
- 3) (integer) 57001
- 4) 1) "KEYS"
- 2) "sou_dzmore_15_*"
- 8) 1) (integer) 12842
- 2) (integer) 1495629469
- 3) (integer) 61131
- 4) 1) "KEYS"
- 2) "sou_dz_15_*"
- 9) 1) (integer) 12841
- 2) (integer) 1495629469
- 3) (integer) 10035
- 4) 1) "KEYS"
- 2) "ztlistnew_dingzhi_15_*"
- 10) 1) (integer) 12840
- 2) (integer) 1495629469
- 3) (integer) 17974
- 4) 1) "KEYS"
- 2) "list_dingzhis_15_*"
图中字段分别意思是:
倘若你想自定义慢命令的标准,可以调整触发日志记录慢命令的阀值。若是很少或没有命令超过 10ms,想降低记录的阀值,比如 5 毫秒,可在 Redis-cli 工具中输入下面的命令配置:
- config set slowlog-log-slower-than 5000
也可以在 Redis.config 配置文件中设置,以微妙位单位。
2. 监控客户端的连接:因为 Redis 是单线程模型 (只能使用单核),来处理所有客户端的请求, 但由于客户端连接数的增长,处理请求的线程资源开始降低分配给单个客户端连接的处理时间,这时每个客户端需要花费更多的时间去等待 Redis 共享服务的响应。这种情况下监控客户端连接数是非常重要的,因为客户端创建连接数的数量可能超出预期的数量,也可能是客户端端没有有效的释放连接。在 Redis-cli 工具中输入 info clients 可以查看到当前实例的所有客户端连接信息。如下图,第一个字段(connected_clients) 显示当前实例客户端连接的总数:
- info clients
- # Clients
- connected_clients:21
- client_longest_output_list:0
- client_biggest_input_buf:13856
- blocked_clients:0
Redis 默认允许客户端连接的最大数量是 10000。若是看到连接数超过 5000 以上,那可能会影响 Redis 的性能。倘若一些或大部分客户端发送大量的命令过来,这个数字会低的多。
3. 限制客户端连接数:自 Redis2.6 以后,允许使用者在配置文件 (Redis.conf)maxclients 属性上修改客户端连接的最大数,也可以通过在 Redis-cli 工具上输入 config set maxclients 去设置最大连接数。根据连接数负载的情况,这个数字应该设置为预期连接数峰值的 110 到 150 之间,若是连接数超出这个数字后,Redis 会拒绝并立刻关闭新来的连接。通过设置最大连接数来限制非预期数量的连接数增长,是非常重要的。另外,新连接尝试失败会返回一个错误消息,这可以让客户端知道,Redis 此时有非预期数量的连接数,以便执行对应的处理措施。 上述二种做法对控制连接数的数量和持续保持 Redis 的性能最优是非常重要的,
4. 加强内存管理:较少的内存会引起 Redis 延迟时间增加。如果 Redis 占用内存超出系统可用内存,操作系统会把 Redis 进程的一部分数据,从物理内存交换到硬盘上,内存交换会明显的增加延迟时间。关于怎么监控和减少内存使用,可查看 used_memory 介绍章节。
5. 性能数据指标:分析解决 Redis 性能问题,通常需要把延迟时间的数据变化与其他性能指标的变化相关联起来。命令处理总数下降的发生可能是由慢命令阻塞了整个系统,但如果命令处理总数的增加,同时内存使用率也增加,那么就可能是由于内存交换引起的性能问题。对于这种性能指标相关联的分析,需要从历史数据上来观察到数据指标的重要变化,此外还可以观察到单个性能指标相关联的所有其他性能指标信息。这些数据可以在 Redis 上收集,周期性的调用内容为 Redis info 的脚本,然后分析输出的信息,记录到日志文件中。当延迟发生变化时,用日志文件配合其他数据指标,把数据串联起来排查定位问题。
info 信息中的 mem_fragmentation_ratio 给出了内存碎片率的数据指标,它是由操系统分配的内存除以 Redis 分配的内存得出:
- mem_fragmentation_ratio = used_memory_rss / used_memory
used_memory 和 used_memory_rss 都包含的内存分配有:
used_memory_rss 的 rss 是 Resident Set Size 的缩写,表示该进程所占物理内存的大小,是操作系统分配给 Redis 实例的内存大小。除了用户定义的数据和内部开销以外,used_memory_rss 指标还包含了内存碎片的开销,内存碎片是由操作系统低效的分配 / 回收物理内存导致的。
操作系统负责分配物理内存给各个应用进程,Redis 使用的内存与物理内存的映射是由操作系统上虚拟内存管理分配器完成的。举个例子来说,Redis 需要分配连续内存块来存储 1G 的数据集,这样的话更有利,但可能物理内存上没有超过 1G 的连续内存块,那操作系统就不得不使用多个不连续的小内存块来分配并存储这 1G 数据,也就导致内存碎片的产生。
内存分配器另一个复杂的层面是,它经常会预先分配一些内存块给引用,这样做会使加快应用程序的运行。
跟踪内存碎片率对理解 Redis 实例的资源性能是非常重要的。内存碎片率稍大于 1 是合理的,这个值表示内存碎片率比较低,也说明 redis 没有发生内存交换。但如果内存碎片率超过 1.5,那就说明 Redis 消耗了实际需要物理内存的 150%,其中 50% 是内存碎片率。若是内存碎片率低于 1 的话,说明 Redis 内存分配超出了物理内存,操作系统正在进行内存交换。内存交换会引起非常明显的响应延迟,可查看 used_memory 介绍章节。
- info memory
- # Memory
- used_memory:21189222536
- used_memory_human:19.73G
- used_memory_rss:21901688832
- used_memory_peak:27350156888
- used_memory_peak_human:25.47G
- used_memory_lua:35840
- mem_fragmentation_ratio:1.03
- mem_allocator:jemalloc-3.6.0
倘若内存碎片率超过了 1.5,那可能是操作系统或 Redis 实例中内存管理变差的表现。下面有 3 种方法解决内存管理变差的问题,并提高 Redis 性能:
1. 重启 Redis 服务器:如果内存碎片率超过 1.5,重启 Redis 服务器可以让额外产生的内存碎片失效并重新作为新内存来使用,使操作系统恢复高效的内存管理。额外碎片的产生是由于 Redis 释放了内存块,但内存分配器并没有返回内存给操作系统,这个内存分配器是在编译时指定的,可以是 libc、jemalloc 或者 tcmalloc。 通过比较 used_memory_peak, used_memory_rss 和 used_memory_metrics 的数据指标值可以检查额外内存碎片的占用。从名字上可以看出,used_memory_peak 是过去 Redis 内存使用的峰值,而不是当前使用内存的值。如果 used_memory_peak 和 used_memory_rss 的值大致上相等,而且二者明显超过了 used_memory 值,这说明额外的内存碎片正在产生。 在 Redis-cli 工具上输入 info memory 可以查看上面三个指标的信息:
在重启服务器之前,需要在 Redis-cli 工具上输入 shutdown save 命令,意思是强制让 Redis 数据库执行保存操作并关闭 Redis 服务,这样做能保证在执行 Redis 关闭时不丢失任何数据。 在重启后,Redis 会从硬盘上加载持久化的文件,以确保数据集持续可用。
2. 限制内存交换: 如果内存碎片率低于 1,Redis 实例可能会把部分数据交换到硬盘上。内存交换会严重影响 Redis 的性能,所以应该增加可用物理内存或减少实 Redis 内存占用。 可查看 used_memory 章节的优化建议。
3. 修改内存分配器:Redis 支持 glibc's malloc、jemalloc11、tcmalloc 几种不同的内存分配器,每个分配器在内存分配和碎片上都有不同的实现。不建议普通管理员修改 Redis 默认内存分配器,因为这需要完全理解这几种内存分配器的差异,也要重新编译 Redis。这个方法更多的是让其了解 Redis 内存分配器所做的工作,当然也是改善内存碎片问题的一种办法。
info 信息中的 evicted_keys 字段显示的是,因为 maxmemory 限制导致 key 被回收删除的数量。回收 key 的情况只会发生在设置 maxmemory 值后,不设置会发生内存交换。 当 Redis 由于内存压力需要回收一个 key 时,Redis 首先考虑的不是回收最旧的数据,而是在最近最少使用的 key 或即将过期的 key 中随机选择一个 key,从数据集中删除。
这可以在配置文件中设置 maxmemory-policy 值为 "volatile-lru" 或 "volatile-ttl",来确定 Redis 是使用 lru 策略还是过期时间策略。 倘若所有的 key 都有明确的过期时间,那过期时间回收策略是比较合适的。若是没有设置 key 的过期时间或者说没有足够的过期 key,那设置 lru 策略是比较合理的,这可以回收 key 而不用考虑其过期状态。
- # Stats
- total_connections_received:843708918
- total_commands_processed:3947987793
- instantaneous_ops_per_sec:1360
- total_net_input_bytes:5061895225788
- total_net_output_bytes:13791028024582
- instantaneous_input_kbps:1247.52
- instantaneous_output_kbps:2756.92
- rejected_connections:0
- sync_full:2
- sync_partial_ok:1
- sync_partial_err:0
- expired_keys:231544806
- evicted_keys:0
- keyspace_hits:613324172
- keyspace_misses:252815503
- pubsub_channels:0
- pubsub_patterns:0
- latest_fork_usec:60179
跟踪 key 回收是非常重要的,因为通过回收 key,可以保证合理分配 Redis 有限的内存资源。如果 evicted_keys 值经常超过 0,那应该会看到客户端命令响应延迟时间增加,因为 Redis 不但要处理客户端过来的命令请求,还要频繁的回收满足条件的 key。需要注意的是,回收 key 对性能的影响远没有内存交换严重,若是在强制内存交换和设置回收策略做一个选择的话,选择设置回收策略是比较合理的,因为把内存数据交换到硬盘上对性能影响非常大 (见前面章节)。
减少回收 key 的数量是提升 Redis 性能的直接办法,下面有 2 种方法可以减少回收 key 的数量:
1. 增加内存限制:倘若开启快照功能,maxmemory 需要设置成物理内存的 45%,这几乎不会有引发内存交换的危险。若是没有开启快照功能,设置系统可用内存的 95% 是比较合理的,具体参考前面的快照和 maxmemory 限制章节。如果 maxmemory 的设置是低于 45% 或 95%(视持久化策略),通过增加 maxmemory 的值能让 Redis 在内存中存储更多的 key,这能显著减少回收 key 的数量。 若是 maxmemory 已经设置为推荐的阀值后,增加 maxmemory 限制不但无法提升性能,反而会引发内存交换,导致延迟增加、性能降低。 maxmemory 的值可以在 Redis-cli 工具上输入 config set maxmemory 命令来设置。需要注意的是,这个设置是立即生效的,但重启后丢失,需要永久化保存的话,再输入 config rewrite 命令会把内存中的新配置刷新到配置文件中。
2. 对实例进行分片:分片是把数据分割成合适大小,分别存放在不同的 Redis 实例上,每一个实例都包含整个数据集的一部分。通过分片可以把很多服务器联合起来存储数据,相当于增加总的物理内存,使其在没有内存交换和回收 key 的策略下也能存储更多的 key。假如有一个非常大的数据集,maxmemory 已经设置,实际内存使用也已经超过了推荐设置的阀值,那通过数据分片能明显减少 key 的回收,从而提高 Redis 的性能。 分片的实现有很多种方法,下面是 Redis 实现分片的几种常见方式:
关于 redis 性能问题分析和优化
来源: http://www.bubuko.com/infodetail-2086778.html