社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
对于任何应用服务和组件,都需要一套完善可靠谱监控方案。
尤其redis这类敏感的纯内存、高并发和低延时的服务,一套完善的监控告警方案,是精细化运营的前提。
redis故障快速通知,定位故障点;对于DBA,redis的可用性和性能故障需快速发现和定位解决。
分析redis故障的Root cause
redis容量规划和性能管理
redis硬件资源利用率和成本
当redis出现故障时,DBA应在尽可能短时间内发现告警;如果故障对服务是有损的(如大面积网络故障或程序BUG),需立即通知SRE和RD启用故障预案(如切换机房或启用emergency switch)止损。
如果没完善监控告警;假设由RD发现服务故障,再排查整体服务调用链去定位;甚于用户发现用问题,通过客服投诉,再排查到redis故障的问题;整个redis故障的发现、定位和解决时间被拉长,把一个原本的小故障被”无限”放大。
任何一个故障和性能问题,其根本“诱因”往往只有一个,称为这个故障的Root cause。
一个故障从DBA发现、止损、分析定位、解决和以后规避措施;最重要一环就是DBA通过各种问题表象,层层分析到Root cause;找到问题的根据原因,才能根治这类问题,避免再次发生。
完善的redis监控数据,是我们分析root cause的基础和证据。
备注:Troubleshtooing定位Root cause,就像医生通过病人的病历和检查报告找到“真正的病灶”,让病人康复和少受苦,一样有意思和复杂;或像刑警通过案件的证据分析和推理,寻找那个唯一的真相,一样惊心动魄。(快看DBA又在吹牛了),其实在大型商业系统中,一次故障轻松就达直接损失数十万(间接损失更大),那“抓住元凶”,避免它再次“作案”,同样是“破案”。
问题表现是综合情的,一般可能性较复杂,这里举2个例子:
Root cause是要直观的监控数据和证据,而非有技术支撑的推理分析。
说明:粮厂有个习惯,在分析root cause尽量能拿到直观证据。因为一旦引入推理步骤,每一步的推理结果都可能出现偏差,最终可能给出错误root cause. “元凶”又逃过一劫,它下次作案估计就会更大。所以建议任何小的故障或抖动,至少从个人或小组内部,深入分析找到root cause;这样个人或组织都会成长快; 形成良好的氛围。
通过分析redis资源使用和性能指标的监控历史趋势数据;对集群进行合理扩容(Scale-out)、缩容(Scale-back);对性能瓶颈优化处理等。
Redis资源使用饱和度监控,设置合理阀值;
一些常用容量指标:redis内存使用比例,swap使用,cpu单核的饱和度等;当资源使用容量预警时,能及时扩容,避免因资源使用过载,导致故障。
另一方面,如果资源利用率持续过低,及时通知业务,并进行redis集群缩容处理,避免资源浪费。
进一步,容器化管理redis后,根据监控数据,系统能自动地弹性扩容和缩容。
Redis性能监控管理,及时发现性能瓶颈,进行优化或扩容,把问题扼杀在”萌芽期“,避免它”进化“成故障。
从老板角度来看,最关心的是成本和资源利用率是否达标。
如果资源不达标,就得推进资源优化整合;提高硬件利用率,减少资源浪费。砍预算,减成本。
资源利用率是否达标的数据,都是通过监控系统采集的数据。
这一小节,扯了这么多; 只是强调redis不是只有一个端口存活监控就可以了。
下面进入主题,怎么采集redsis监控数。
老板曾说:监控告警和数据备份,是对DBA和SRE最基础也是最高的要求;
当服务和存储达到产品规模后,可认为“无监控,不服务;无备份,不存储”。
针对redis监控,可以分为几个层面:
1、服务器系统相关:服务器宕机,CPU,
2、redis应用进程。
3、redis性能指标
4、应用的响应时间:Redis慢查询监控
1)、服务器存活监控:
2)CPU
3)内存和swap
说明:系统内存余量合理,给各种缓冲区,fork cow足够的内存空间。
另一个问题:我的系统使用Redis缓存集群,”不怕挂,就怕慢“,或redis集群高可用做得厉害;这样redis的服务器是否能关闭swap呢?
4)磁盘
5)网络
1)、端口存活
2)、进程占用的cpu和内存
3)、网络连接数
可以通过info 命令获取相关性能指标。
info命令输出的数据可分为10个类别,分别是:
我们主要关注信息:
connected_clients_pct(连接数使用率): 连接数使用百分比,通过(connected_clients/macclients)计算;如果达到1,redis开始拒绝新连接创建。
rejected_connections(拒绝的连接个数): redis连接个数达到maxclients限制,拒绝新连接的个数。
说明:
1、如果内存使用很小时,mem_fragmentation_ratio可以远大于1的情况,这个告警值不好设置,需参考used_memory大小。
2、如果mem_fragmentation_ratio小于1,表示redis已使用swap分区
1、因内存交换引起的性能问题
如果Redis实例的内存使用率超过可用最大内存 (used_memory > 可用最大内存),那么操作系统开始进行内存与swap空间交换,把内存中旧的或不再使用的内容写入硬盘上(硬盘上的这块空间叫Swap分区),以便留出新的物理内存给新页或活动页(page)使用。
如果Redis进程上发生内存交换,那么Redis和依赖Redis上数据的应用会受到严重的性能影响。 通过查看used_memory指标可知道Redis正在使用的内存情况,如果used_memory>可用最大内存,那就说明Redis实例正在进行内存交换或者已经内存交换完毕。
2、跟踪内存使用率
若是在使用Redis期间没有开启rdb快照或aof持久化策略,那么缓存数据在Redis崩溃时就有丢失的危险。因为当Redis内存使用率超过可用内存的95%时,部分数据开始在内存与swap空间来回交换,这时就可能有丢失数据的危险。
当开启并触发快照功能时,Redis会fork一个子进程把当前内存中的数据完全复制一份写入到硬盘上。因此若是当前使用内存超过可用内存的45%时触发快照功能,那么此时进行的内存交换会变的非常危险(可能会丢失数据)。 倘若在这个时候实例上有大量频繁的更新操作,问题会变得更加严重。
通过减少Redis的内存占用率,来避免这样的问题,或者使用下面的技巧来避免内存交换发生:
尽可能的使用Hash数据结构。因为Redis在储存小于100个字段的Hash结构上,其存储效率是非常高的。
设置key的过期时间。一个减少内存使用率的简单方法就是,每当存储对象时确保设置key的过期时间。
回收key。 若是启用了Redis快照功能,应该设置“maxmemory”值为系统可使用内存的45%,因为快照时需要一倍的内存来复制整个数据集,也就是说如果当前已使用45%,在快照期间会变成95%(45%+45%+5%),其中5%是预留给其他的开销。 如果没开启快照功能,maxmemory最高能设置为系统可用内存的95%。
当内存使用达到设置的最大阀值时,需要选择一种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不会进行内存交换。倘若你担心由于限制了内存使用率导致丢失数据的话,可以设置noneviction值禁止淘汰数据。
redis键空间的状态监控:
Redis qps:
在Redis实例中,跟踪命令处理总数是解决响应延迟问题最关键的部分,因为Redis是个单线程模型,客户端过来的命令是按照顺序执行的。比较常见的延迟是带宽,通过千兆网卡的延迟大约有200μs。倘若明显看到命令的响应时间变慢,延迟高于200μs,那可能是Redis命令队列里等待处理的命令数量比较多。 如上所述,延迟时间增加导致响应时间变慢可能是由于一个或多个慢命令引起的,这时可以看到每秒命令处理数在明显下降,甚至于后面的命令完全被阻塞,导致Redis性能降低。要分析解决这个性能问题,需要跟踪命令处理数的数量和延迟时间。
Redis cmdstat_xxx
redis记录执行过的所有命令; 通过info all的Commandstats节采集数据.
以下表示:每个命令执行次数,总共消耗的CPU时长(单个微秒),平均每次消耗的CPU时长(单位微秒)
# Commandstats
cmdstat_set:calls=6,usec=37,usec_per_call=6.17
cmdstat_lpush:calls=4,usec=32,usec_per_call=8.00
cmdstat_lpop:calls=4,usec=33,usec_per_call=8.25
Redis Keysapce hit ratio
redis键空间请求命中率监控,通过此监控来度量redis缓存的质量,如果未命中率或次数较高,可能因热点数据已大于redis的内存限制,导致请求落到后端存储组件,可能需要扩容redis缓存集群的内存容量。当然也有可能是业务特性导致。
Redis fork
redis在执行BGSAVE,BGREWRITEAOF命令时,redis进程有 fork 操作。而fork会对redis进程有个短暂的卡顿,这个卡顿redis不能响应任务请求。所以监控fork阻塞时长,是相当重要。
如果你的系统不能接受redis有500ms的阻塞,那么就要监控fork阻塞时长的变化,做好容量规划。
redis network traffic
redis一般单机多实例部署,当服务器网络流量增长很大,需快速定位是网络流量被哪个redis实例所消耗了; 另外redis如果写流量过大,可能导致slave线程“客户端输出缓冲区”堆积,达到限制后被Maser强制断开连接,出现复制中断故障。所以我们需监控每个redis实例网络进出口流量,设置合适的告警值。
说明:网络监控指标 ,需较高的版本才有,应该是2.8.2x以后
前两者是累计值,根据监控平台1个采集周期(如1分钟)内平均每秒的流量字节数。
redis存储场景的集群,就得 redis持久化 保障数据落地,减少故障时数据丢失。这里分析redis rdb数据持久化的几个监控指标。
不论使用何种redis集群方案, redis复制 都会被使用。
复制相关的监控告警项:
说明: 关于根据实际情况,设置合适大小的复制缓冲区。可以通过master_repl_offset指标计算每秒写入字节数,同时乘以希望多少秒内闪断使用“部分同步”方式。
这里所写 redis官方集群方案 的监控指标
数据基本通过cluster info和info命令采集。
响应时间 是衡量一个服务组件性能和质量的重要指标。使用redis的服务通常对响应时间都十分敏感,比如要求99%的响应时间达10ms以内。
因redis的慢查询日志只计算命令的cpu占用时间,不会考虑排队或其他耗时。
响应时间监控的方式建议,最简单方法,使用 Percona tcprstat
Redis慢查询监控
redis慢查询 是排查性能问题关键监控指标。因redis是单线程模型(single-threaded server),即一次只能执行一个命令,如果命令耗时较长,其他命令就会被阻塞,进入队列排队等待;这样对程序性能会较大。
redis慢查询保存在内存中,最多保存slowlog-max-len(默认128)个慢查询命令,当慢查询命令日志达到128个时,新慢查询被加入前,会删除最旧的慢查询命令。因慢查询不能持久化保存,且不能实时监控每秒产生的慢查询个数。
我们建议的慢查询监控方法:
redis慢查询的监控项:
Redis中的slowlog命令可以让我们快速定位到那些超出指定执行时间的慢命令,默认情况下命令若是执行时间超过10ms就会被记录到日志。slowlog只会记录其命令执行的时间,不包含io往返操作,也不记录单由网络延迟引起的响应慢。通常1gb带宽的网络延迟,预期在200μs左右,倘若一个命令仅执行时间就超过10ms,那比网络延迟慢了近50倍。 想要查看所有执行时间比较慢的命令,可以通过使用Redis-cli工具,使用slowlog get命令查看,返回结果的第三个字段以微妙位单位显示命令的执行时间。
图中字段分别意思是:
1)、日志的唯一标识符
2)、被记录命令的执行时间点,以 UNIX 时间戳格式表示
3)、查询执行时间,以微秒为单位
4)、执行的命令,以数组的形式排列。完整命令是config get *
# Server
redis_version:3.2.3 #redis版本号
redis_git_sha1:00000000 #git sha1摘要值
redis_git_dirty:0 #git dirty标识
redis_build_id:443e50c39cbcdbe0 #redis构建id
redis_mode:standalone #运行模式:standalone、sentinel、cluster
os:Linux 3.10.0-514.16.1.el7.x86_64 x86_64 #服务器宿主机操作系统
arch_bits:64 服务器宿主机CUP架构(32位/64位)
multiplexing_api:epoll #redis IO机制
gcc_version:4.8.5 #编译 redis 时所使用的 GCC 版本
process_id:1508 #服务器进程的 PID
run_id:b4ac0f9086659ce54d87e41d4d2f947e19c28401 #redis 服务器的随机标识符 (用于 Sentinel 和集群)
tcp_port:6380 #redis服务监听端口
uptime_in_seconds:520162 #redis服务启动以来经过的秒数
uptime_in_days:6 #redis服务启动以来经过的天数
hz:10 #redis内部调度(进行关闭timeout的客户端,删除过期key等等)频率,程序规定serverCron每秒运行10次
lru_clock:16109450 #自增的时钟,用于LRU管理,该时钟100ms(hz=10,因此每1000ms/10=100ms执行一次定时任务)更新一次
executable:/usr/local/bin/redis-server
config_file:/data/redis-6380/redis.conf 配置文件的路径
# Clients
connected_clients:2 #已连接客户端的数量(不包括通过从属服务器连接的客户端)
client_longest_output_list:0 #当前连接的客户端当中,最长的输出列表
client_biggest_input_buf:0 #当前连接的客户端当中,最大输入缓存
blocked_clients:0 #正在等待阻塞命令(BLPOP、BRPOP、BRPOPLPUSH)的客户端的数量
# Memory
used_memory:426679232 #由 redis 分配器分配的内存总量,以字节(byte)为单位
used_memory_human:406.91M #以可读的格式返回 redis 分配的内存总量(实际是used_memory的格式化)
used_memory_rss:443179008 #从操作系统的角度,返回 redis 已分配的内存总量(俗称常驻集大小)。这个值和 top 、 ps等命令的输出一致
used_memory_rss_human:422.65M # redis 的内存消耗峰值(以字节为单位)
used_memory_peak:426708912
used_memory_peak_human:406.94M
total_system_memory:16658403328
total_system_memory_human:15.51G
used_memory_lua:37888 # Lua脚本存储占用的内存
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:1.04 # used_memory_rss/ used_memory
mem_allocator:jemalloc-4.0.3
# Persistence
loading:0 #服务器是否正在载入持久化文件,0表示没有,1表示正在加载
rdb_changes_since_last_save:3164272 #离最近一次成功生成rdb文件,写入命令的个数,即有多少个写入命令没有持久化
rdb_bgsave_in_progress:0 #服务器是否正在创建rdb文件,0表示否
rdb_last_save_time:1559093160 #离最近一次成功创建rdb文件的时间戳。当前时间戳 - rdb_last_save_time=多少秒未成功生成rdb文件
rdb_last_bgsave_status:ok #最近一次rdb持久化是否成功
rdb_last_bgsave_time_sec:-1 #最近一次成功生成rdb文件耗时秒数
rdb_current_bgsave_time_sec:-1 #如果服务器正在创建rdb文件,那么这个域记录的就是当前的创建操作已经耗费的秒数
aof_enabled:0 #是否开启了aof
aof_rewrite_in_progress:0 #标识aof的rewrite操作是否在进行中
aof_rewrite_scheduled:0 #rewrite任务计划,当客户端发送bgrewriteaof指令,如果当前rewrite子进程正在执行,那么将客户端请求的bgrewriteaof变为计划任务,待aof子进程结束后执行rewrite
aof_last_rewrite_time_sec:-1 #最近一次aof rewrite耗费的时长
aof_current_rewrite_time_sec:-1 #如果rewrite操作正在进行,则记录所使用的时间,单位秒
aof_last_bgrewrite_status:ok #上次bgrewriteaof操作的状态
aof_last_write_status:ok #上次aof写入状态
# Stats
total_connections_received:10 #服务器已经接受的连接请求数量
total_commands_processed:9510792 #redis处理的命令数
instantaneous_ops_per_sec:1 #redis当前的qps,redis内部较实时的每秒执行的命令数
total_net_input_bytes:1104411373 #redis网络入口流量字节数
total_net_output_bytes:66358938 #redis网络出口流量字节数
instantaneous_input_kbps:0.04 #redis网络入口kps
instantaneous_output_kbps:3633.35 #redis网络出口kps
rejected_connections:0 #拒绝的连接个数,redis连接个数达到maxclients限制,拒绝新连接的个数
sync_full:0 #主从完全同步成功次数
sync_partial_ok:0 #主从部分同步成功次数
sync_partial_err:0 #主从部分同步失败次数
expired_keys:0 #运行以来过期的key的数量
evicted_keys:0 #运行以来剔除(超过了maxmemory后)的key的数量
keyspace_hits:87 #命中次数
keyspace_misses:17 #没命中次数
pubsub_channels:0 #当前使用中的频道数量
pubsub_patterns:0 #当前使用的模式的数量
latest_fork_usec:0 #最近一次fork操作阻塞redis进程的耗时数,单位微秒
migrate_cached_sockets:0 #是否已经缓存了到该地址的连接
# Replication
role:master #实例的角色,是master or slave
connected_slaves:0 #连接的slave实例个数
master_repl_offset:0 #主从同步偏移量,此值如果和上面的offset相同说明主从一致没延迟,与master_replid可被用来标识主实例复制流中的位置
repl_backlog_active:0 #复制积压缓冲区是否开启
repl_backlog_size:1048576 #复制积压缓冲大小
repl_backlog_first_byte_offset:0 #复制缓冲区里偏移量的大小
repl_backlog_histlen:0 #此值等于 master_repl_offset - repl_backlog_first_byte_offset,该值不会超过repl_backlog_size的大小
# CPU
used_cpu_sys:507.00 #将所有redis主进程在核心态所占用的CPU时求和累计起来
used_cpu_user:280.48 #将所有redis主进程在用户态所占用的CPU时求和累计起来
used_cpu_sys_children:0.00 #将后台进程在核心态所占用的CPU时求和累计起来
used_cpu_user_children:0.00 将后台进程在用户态所占用的CPU时求和累计起来
# Cluster
cluster_enabled:0
# Keyspace
db0:keys=5557407,expires=362,avg_ttl=604780497
db15:keys=1,expires=0,avg_ttl=0
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!