她在丛中笑 發表於 2025-11-13 09:43:53

深入理解Redis 延迟监控的项目实践

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">1 为什么需要内建延迟监控?</a></li><li><a href="#_label1">2 事件模型与时间序列</a></li><li><a href="#_label2">3 一键启用监控</a></li><li><a href="#_label3">4 LATENCY 指令族速查表</a></li><ul class="second_class_ul"><li><a href="#_lab2_3_0">样例:fork 事件图</a></li></ul><li><a href="#_label4">5 实战排障流程</a></li><ul class="second_class_ul"><li><a href="#_lab2_4_1">5.1 高频命令延迟</a></li><li><a href="#_lab2_4_2">5.2 fork 卡顿</a></li><li><a href="#_lab2_4_3">5.3 AOF fsync 峰值</a></li><li><a href="#_lab2_4_4">5.4 过期/淘汰抖动</a></li></ul><li><a href="#_label5">6 监控 &amp; 告警集成</a></li><ul class="second_class_ul"></ul><li><a href="#_label6">7 总结 &amp; 最佳实践</a></li><ul class="second_class_ul"></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>1 为什么需要内建延迟监控?</h2>
<p>Redis 单线程+磁盘持久化+多种算法复杂度共存,一旦:</p>
<ul><li>遇到 <strong>O(N) 大命令</strong></li><li><strong>AOF fsync / fork</strong> 挂起主线程</li><li>大量 Key 同秒过期 / 主动淘汰</li><li>宿主机抖动,导致系统调用耗时飙升</li></ul>
<p>就会让所有客户端排队等待,出现毫秒到秒级 &ldquo;雪崩&rdquo;。</p>
<p><strong>Latency Monitor</strong> 将这些 &ldquo;卡点&rdquo; 做成事件钩子并存储时间序列,让你可精确回放 spike 发生的时刻与持续时间,配合 <code>LATENCY DOCTOR</code> 产出可读结论。</p>
<p class="maodian"><a name="_label1"></a></p><h2>2 事件模型与时间序列</h2>
<table><thead><tr><th>事件名</th><th>监控对象</th><th>典型根因</th></tr></thead><tbody><tr><td>command</td><td>所有命令</td><td>KEYS / ZINTERSTORE 等 O(N) 阻塞</td></tr><tr><td>fast-command</td><td>O(1)/O(logN) 命令</td><td>基线抖动</td></tr><tr><td>fork</td><td>fork() 复制页表</td><td>BGSAVE / BGREWRITEAOF</td></tr><tr><td>aof-write / aof-fsync-*</td><td>write() &amp; fsync()</td><td>磁盘 IO / 共振</td></tr><tr><td>expire-cycle</td><td>主动过期采样</td><td>同批 EXPIREAT</td></tr><tr><td>eviction-*</td><td>内存淘汰</td><td>高频淘汰、热点 key 巨大</td></tr><tr><td>active-defrag-cycle</td><td>在线碎片整理</td><td>大碎片 + defrag aggressiveness</td></tr></tbody></table>
<blockquote><p><strong>记录规则</strong></p>
<ul><li>每类事件独立 160 个桶:(timestamp, cost_ms)</li><li>同 1 秒内多次 spike 取 最大,保证最少 160 秒历史</li><li>额外维护 &ldquo;历史最大值&rdquo; 方便基准比较</li></ul></blockquote>
<p class="maodian"><a name="_label2"></a></p><h2>3 一键启用监控</h2>
<div class="jb51code"><pre class="brush:sql;">127.0.0.1:6379&gt; CONFIG SET latency-monitor-threshold 100   # 只记录 ≥100 ms
OK
</pre></div>
<ul><li><strong>阈值 = SLA-可接受延迟</strong>。例如业务要求&ldquo;单条 &le;80 ms&rdquo;,阈值可设 50 ms,提前预警。</li><li>线上零成本;禁用只需 <code>CONFIG SET latency-monitor-threshold 0</code>。</li></ul>
<p class="maodian"><a name="_label3"></a></p><h2>4 LATENCY 指令族速查表</h2>
<table><thead><tr><th>子命令</th><th>作用</th><th>常用姿势</th></tr></thead><tbody><tr><td>LATEST</td><td>输出最近一次 spike<br />格式:</td><td>Dashboard 抓最新高延迟项</td></tr><tr><td>HISTORY &lt;event&gt;</td><td>取整条时间序列</td><td>时序图 / Promotheus Export</td></tr><tr><td>GRAPH &lt;event&gt;</td><td>终端 ASCII 图</td><td>SSH 即时观测</td></tr><tr><td>RESET </td><td>清空某事件历史</td><td>回收内存 / 做 A/B 测试</td></tr><tr><td>DOCTOR</td><td>智能诊断报告</td><td>快速定位+给出 tuning 建议</td></tr></tbody></table>
<p class="maodian"><a name="_lab2_3_0"></a></p><h3>样例:fork 事件图</h3>
<div class="jb51code"><pre class="brush:sql;">127.0.0.1:6379&gt; LATENCY GRAPH fork
</pre></div>
<div class="jb51code"><pre class="brush:plain;">fork: (microseconds)
% &gt; (msec) 999th cummulative latency distribution
99.4% ██████████████████████████████████████████████████ 44
...
100%██████████████████████████████████████████████████ 68
</pre></div>
<p class="maodian"><a name="_label4"></a></p><h2>5 实战排障流程</h2>
<p class="maodian"><a name="_lab2_4_1"></a></p><h3>5.1 高频命令延迟</h3>
<p><code>LATENCY LATEST</code> 出现 <code>command</code> &gt; SLA</p>
<p><code>SLOWLOG GET 10</code> 找具体指令</p>
<p>如果是</p>
<ul><li><strong>SCAN 替代 KEYS</strong></li><li>把<strong>大集合运算</strong>放到 Replica / 后台脚本</li><li>引入 Lua + 分批写,避免长事务</li></ul>
<p class="maodian"><a name="_lab2_4_2"></a></p><h3>5.2 fork 卡顿</h3>
<p><code>LATENCY GRAPH fork</code> 有 50-500 ms 峰</p>
<p><code>INFO persistence</code> 查看 <code>latest_fork_usec</code></p>
<p>动作:</p>
<ul><li>使用 HVM / 物理机、关闭 THP</li><li>大实例开启 <code>linux-readahead 0</code>, 避免写时极端 COW</li><li>避开业务高峰做持久化/重写</li></ul>
<p class="maodian"><a name="_lab2_4_3"></a></p><h3>5.3 AOF fsync 峰值</h3>
<p><code>aof-fsync-always</code> or <code>aof-write-pending-fsync</code> spikes</p>
<p>解决:</p>
<ul><li>改为 <code>appendfsync everysec + no-appendfsync-on-rewrite yes</code></li><li>独立 NVMe 盘、开启 <code>direct-io</code></li><li>若不要求秒级丢失,可用 <code>appendfsync no</code> + 双机热备</li></ul>
<p class="maodian"><a name="_lab2_4_4"></a></p><h3>5.4 过期/淘汰抖动</h3>
<p><code>expire-cycle</code> 抖高:大量 key 同时过期,做 TTL 随机抖动</p>
<p><code>eviction-del</code> 抖高:</p>
<ul><li>优化 maxmemory 策略 &rarr; <code>allkeys-lfu</code></li><li>检查是否有超大 value 导致单次 DEL 久</li></ul>
<p class="maodian"><a name="_label5"></a></p><h2>6 监控 &amp; 告警集成</h2>
<div class="jb51code"><pre class="brush:sql;"># example: 导出为 Prometheus 指标
redis-cli --raw LATENCY LATEST \
| awk '{printf "redis_latency_spike{event=\"%s\"} %d\n", $1, $3}'
</pre></div>
<ul><li>结合 <code>redis_exporter</code> 可自动拉取 <code>LATENCY LATEST</code> 指标。</li><li>建议对以下事件配置告警:<code>command</code>、<code>fork</code>、<code>aof-fsync-always</code>、<code>expire-cycle</code>。</li><li>触发后自动执行 <code>LATENCY DOCTOR</code>,邮件/钉钉输出诊断。</li></ul>
<p class="maodian"><a name="_label6"></a></p><h2>7 总结 &amp; 最佳实践</h2>
<ol><li><strong>阈值 = SLA 提前量</strong>,建议&emsp;<strong>TPS 高时 &lt;1/2 SLA</strong>。</li><li>日常持续开 <code>latency-monitor-threshold</code>,日志轮转保留 7-30 天。</li><li>spike 首先看 <strong>事件类型&rarr;慢日志&rarr;系统调用</strong> 三步定位。</li><li>fork 与 fsync 难免有抖动 &rarr; 减峰就靠 磁盘隔离 + 业务错峰。</li><li>用 <code>LATENCY RESET</code> 在每次调优后清零,再观测新曲线。</li></ol>
<p>有了 Latency Monitor,你能把 Redis 从黑盒变成可观测白盒 &mdash;&mdash; 慢在那里、卡多久、为何卡,一查便知,助你稳稳守住延迟红线。祝线上永不宕,&ldquo;毫&rdquo;无压力!</p>
頁: [1]
查看完整版本: 深入理解Redis 延迟监控的项目实践