← 返回最佳实践列表

*Redis 监控与告警方案:生产环境可观测性实战指南

本文深入讲解 Redis 生产环境监控的核心维度、底层原理与可观测性架构,涵盖 Prometheus + Grafana + Redis Exporter 的完整部署方案,提供 Shell、Python、Java 多语言监控采集代码,以及从延迟告警到自动故障自愈的实战案例。

*目录


*一、为什么 Redis 需要专业化监控

Redis 作为内存数据库,性能极高但容错窗口极短。一次内存耗尽、一次主从切换失败、一次慢查询堆积,都可能在秒级导致服务雪崩。不同于磁盘型数据库有较多缓冲时间,Redis 的问题往往在业务可感知之前就已恶化。

生产环境中,仅靠 redis-cli ping 判断存活是远远不够的。你需要知道:

  • 内存使用率是否接近上限,驱逐策略是否已触发?
  • 连接数是否达到瓶颈,是否存在连接泄露?
  • 哪些命令执行缓慢,是否拖垮了主线程?
  • 主从复制延迟多少,是否已影响读写一致性?
  • 持久化(RDB/AOF)是否阻塞了正常请求?

专业化监控的目标不是"看图表",而是提前发现趋势、快速定位根因、自动触发止损。本文将从监控维度、底层原理、采集方法、可视化方案到告警规则,给出可直接落地的完整方案。


*二、Redis 监控的六大核心维度

维度 关键指标 风险等级 说明
内存 used_memory, used_memory_rss, maxmemory 🔴 高 内存耗尽直接导致 OOM 或数据丢失
连接 connected_clients, rejected_connections 🟡 中 连接泄露会耗尽文件描述符
命令 instantaneous_ops_per_sec, slowlog 🟡 中 慢查询和热点命令影响吞吐量
延迟 latency, latest_fork_usec 🔴 高 延迟是 Redis 最核心的 SLA 指标
持久化 rdb_last_bgsave_status, aof_last_write_status 🟡 中 持久化失败意味着数据风险
复制 master_link_status, master_last_io_seconds_ago 🔴 高 主从断开导致读写不一致

监控不是越多越好,而是围绕业务 SLA 选择关键指标。对于缓存场景,延迟和命中率是核心;对于消息队列,持久化和复制延迟更重要。


*三、监控原理与关键指标解析

*3.1 INFO 命令:Redis 的监控数据总线

Redis 的 INFO 命令是所有监控体系的基石。它输出数十个模块的指标,按段分类:

INFO [section]

常用段落:

段落 监控内容 典型用途
server 版本、运行时间、监听端口 版本管理和巡检
clients 连接数、最大输出缓冲、阻塞客户端 连接泄露检测
memory 内存使用、RSS、碎片率、驱逐统计 内存预警和优化
stats 总命令数、QPS、网络流量、驱逐数 容量规划和热点分析
replication 主从状态、复制偏移、延迟 高可用监控
persistence RDB/AOF 状态、最后一次持久化结果 数据安全审计
commandstats 各命令的调用次数和耗时 慢命令定位
cpu 主线程和子线程 CPU 耗时 资源消耗分析
keyspace 各 DB 的 key 数量、过期 key、平均 TTL 数据分布和过期策略

*3.2 内存监控:从 used_memory 到 RSS 的真相

used_memory 是 Redis 逻辑使用的内存(含对象开销、缓冲区等),used_memory_rss 是操作系统实际分配的物理内存。两者的差异反映了内存碎片外部开销(如客户端输出缓冲)。

关键指标:

  • used_memory / maxmemory:内存使用率,超过 80% 需预警
  • used_memory_rss / used_memory:比值 > 1.5 说明碎片严重,需考虑 MEMORY PURGE 或重启
  • mem_fragmentation_ratio:官方推荐 1.0 ~ 1.5,超过 2.0 需关注
  • evicted_keys:被驱逐的 key 数量,持续非零说明内存不足

*3.3 慢查询监控:SLOWLOG 与 LATENCY DOCTOR

Redis 是单线程模型,任何慢命令都会阻塞后续请求。SLOWLOG 记录超过阈值的命令:

CONFIG SET slowlog-log-slower-than 10000  # 10ms
CONFIG SET slowlog-max-len 128

Redis 2.8.13+ 提供了 LATENCY DOCTOR,自动诊断延迟异常:

LATENCY DOCTOR

它会分析 LATENCY LATEST 中的事件(如 fork, aof-write, rdb-save),并给出人类可读的建议。

*3.4 复制延迟与主从一致性

主从架构下,复制延迟是读写分离场景的关键指标:

  • master_repl_offset vs slave_repl_offset:偏移差反映数据同步进度
  • master_last_io_seconds_ago:从库多久没收到主库数据,超过 10 秒需告警
  • master_link_statusupdown,直接反映复制链路健康

*四、可观测性架构设计

*4.1 整体架构图

┌─────────────────────────────────────────────────────────────────────┐
│                        Redis 监控可观测性架构                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐      │
│  │ Redis    │    │ Redis    │    │ Redis    │    │ Redis    │      │
│  │ Node 1   │    │ Node 2   │    │ Node 3   │    │ Sentinel │      │
│  └────┬─────┘    └────┬─────┘    └────┬─────┘    └────┬─────┘      │
│       │               │               │               │              │
│       │  INFO /       │  INFO /       │  INFO /       │  INFO /      │
│       │  SLOWLOG      │  SLOWLOG      │  SLOWLOG      │  SLOWLOG     │
│       └───────┬───────┴───────┬───────┴───────┬───────┴───────┘    │
│               │               │               │                      │
│       ┌───────▼───────────────▼───────────────▼───────┐              │
│       │       Redis Exporter (oliver006/redis_exporter)  │              │
│       │       - 端口: 9121                                │              │
│       │       - 采集周期: 15s                             │              │
│       └───────┬───────────────────────────────────────────┘              │
│               │                                                       │
│               │  Prometheus 拉取 (scrape)                               │
│               ▼                                                       │
│       ┌─────────────────────────────┐                                │
│       │    Prometheus Server          │                                │
│       │    - 存储: 15天本地 TSDB       │                                │
│       │    - 告警规则: 10+ 条          │                                │
│       │    - 预聚合: recording rules  │                                │
│       └──────────┬──────────────────┘                                │
│                  │                                                    │
│       ┌──────────┴──────────┐                                        │
│       │                     │                                        │
│       ▼                     ▼                                        │
│  ┌──────────┐      ┌──────────────┐                                 │
│  │ Grafana  │      │ Alertmanager │                                 │
│  │ 可视化   │      │ 告警路由      │                                 │
│  │ 仪表盘   │      │ 静默/抑制     │                                 │
│  └──────────┘      └──────┬───────┘                                 │
│                           │                                          │
│                           ▼                                          │
│                  ┌────────────────┐                                 │
│                  │ 钉钉/飞书/邮件/  │                                 │
│                  │ PagerDuty/Slack │                                 │
│                  └────────────────┘                                 │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

*4.2 组件选型说明

组件 选型 理由
指标采集 Redis Exporter 社区最成熟,覆盖 200+ 指标,支持 Sentinel
时序存储 Prometheus 云原生标准,Pull 模型适合 Redis 短连接
可视化 Grafana 模板丰富,官方提供 Redis 专用 Dashboard
告警引擎 Prometheus + Alertmanager 规则灵活,支持分级告警和静默
日志采集 Promtail + Loki 可选,用于慢查询日志聚合

*4.3 部署拓扑建议

  • 单实例:Exporter 与 Redis 同机或同容器,Prometheus 远程拉取
  • 主从:Exporter 部署在主从节点各一个,分别监控
  • Cluster:每个分片主节点部署 Exporter,Prometheus 配置全部目标
  • Sentinel:单独监控 Sentinel 进程,同时监控其管理的 Redis 实例

*五、核心命令与指标采集

*5.1 Redis CLI:INFO 全量采集

# 采集全部监控信息
redis-cli INFO

# 仅采集内存段(适合脚本过滤)
redis-cli INFO memory

# 采集复制状态(主从监控)
redis-cli INFO replication

# 持续监控 QPS(每秒采样)
redis-cli INFO stats | grep instantaneous_ops_per_sec

# 查看命令统计(找出热点命令)
redis-cli INFO commandstats

*5.2 Shell:自动化监控脚本

#!/bin/bash
# redis-health-check.sh - Redis 健康巡检脚本
# 用法: ./redis-health-check.sh 127.0.0.1 6379

HOST=${1:-127.0.0.1}
PORT=${2:-6379}
ALERT_THRESHOLD=80  # 内存告警阈值(%)

# 获取内存使用率
USED=$(redis-cli -h $HOST -p $PORT INFO memory | grep "used_memory:" | cut -d: -f2 | tr -d '\r')
MAX=$(redis-cli -h $HOST -p $PORT CONFIG GET maxmemory | tail -1)

if [ "$MAX" -eq 0 ]; then
    MAX=$USED  # 未设置 maxmemory,使用当前内存作为基线
fi

USAGE=$(echo "scale=2; $USED / $MAX * 100" | bc)

# 获取连接数
CLIENTS=$(redis-cli -h $HOST -p $PORT INFO clients | grep "connected_clients:" | cut -d: -f2 | tr -d '\r')
MAX_CLIENTS=$(redis-cli -h $HOST -p $PORT CONFIG GET maxclients | tail -1)

# 获取复制延迟
REPL_LAG=$(redis-cli -h $HOST -p $PORT INFO replication | grep "master_last_io_seconds_ago:" | cut -d: -f2 | tr -d '\r')

# 输出 JSON 格式监控数据
cat <<EOF
{
  "timestamp": "$(date -Iseconds)",
  "host": "$HOST:$PORT",
  "memory_usage_percent": $USAGE,
  "connected_clients": $CLIENTS,
  "maxclients": $MAX_CLIENTS,
  "replication_lag_seconds": ${REPL_LAG:-0},
  "alert": $(echo "$USAGE > $ALERT_THRESHOLD" | bc)
}
EOF

*5.3 Python:应用层监控客户端

#!/usr/bin/env python3
"""
redis_monitor.py - Python 应用层 Redis 监控采集
依赖: pip install redis
"""

import redis
import json
import time
from datetime import datetime


def collect_metrics(host='127.0.0.1', port=6379, password=None):
    """采集 Redis 核心监控指标"""
    r = redis.Redis(host=host, port=port, password=password, decode_responses=True)

    # 采集 INFO 数据
    info = r.info()

    metrics = {
        'timestamp': datetime.now().isoformat(),
        'host': f'{host}:{port}',
        'version': info.get('redis_version'),
        'uptime_seconds': info.get('uptime_in_seconds'),

        # 内存指标
        'memory': {
            'used_memory': info.get('used_memory'),
            'used_memory_rss': info.get('used_memory_rss'),
            'used_memory_peak': info.get('used_memory_peak'),
            'maxmemory': info.get('maxmemory', 0),
            'fragmentation_ratio': info.get('mem_fragmentation_ratio'),
            'evicted_keys': info.get('evicted_keys', 0),
        },

        # 连接指标
        'clients': {
            'connected': info.get('connected_clients'),
            'blocked': info.get('blocked_clients'),
            'maxclients': info.get('maxclients'),
        },

        # 性能指标
        'performance': {
            'instantaneous_ops_per_sec': info.get('instantaneous_ops_per_sec'),
            'total_commands_processed': info.get('total_commands_processed'),
            'keyspace_hits': info.get('keyspace_hits'),
            'keyspace_misses': info.get('keyspace_misses'),
            'hit_rate': (
                info.get('keyspace_hits', 0) / 
                (info.get('keyspace_hits', 0) + info.get('keyspace_misses', 1)) * 100
            ),
        },

        # 复制指标
        'replication': {
            'role': info.get('role'),
            'master_link_status': info.get('master_link_status'),
            'master_last_io_seconds_ago': info.get('master_last_io_seconds_ago'),
            'master_repl_offset': info.get('master_repl_offset'),
            'slave_repl_offset': info.get('slave_repl_offset'),
        },

        # 持久化指标
        'persistence': {
            'rdb_last_bgsave_status': info.get('rdb_last_bgsave_status'),
            'aof_last_write_status': info.get('aof_last_write_status'),
            'aof_current_size': info.get('aof_current_size'),
            'aof_base_size': info.get('aof_base_size'),
        }
    }

    return metrics


def check_slowlog(r, threshold_ms=10, limit=10):
    """检查慢查询日志"""
    slowlog = r.slowlog_get(limit)
    slow_commands = []

    for entry in slowlog:
        duration_ms = entry['duration'] / 1000  # 微秒转毫秒
        if duration_ms > threshold_ms:
            slow_commands.append({
                'id': entry['id'],
                'command': ' '.join(entry['command'].decode() if isinstance(entry['command'], bytes) else str(c) for c in entry['command']),
                'duration_ms': duration_ms,
                'timestamp': datetime.fromtimestamp(entry['time']).isoformat()
            })

    return slow_commands


if __name__ == '__main__':
    metrics = collect_metrics()
    print(json.dumps(metrics, indent=2, ensure_ascii=False))

    # 慢查询检查示例
    r = redis.Redis(decode_responses=True)
    slow = check_slowlog(r, threshold_ms=10)
    if slow:
        print(f"\n⚠️ 发现 {len(slow)} 条慢查询:")
        for cmd in slow[:5]:
            print(f"  - {cmd['command']}: {cmd['duration_ms']:.2f}ms")

*5.4 Java:Spring Boot 生产级监控集成

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tags;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * RedisMetricsCollector - Spring Boot 生产级 Redis 监控采集
 * 依赖: io.micrometer:micrometer-core, redis.clients:jedis
 */
## 

public class RedisMetricsCollector {

    private final JedisPool jedisPool;
    private final MeterRegistry meterRegistry;

    public RedisMetricsCollector(JedisPool jedisPool, MeterRegistry meterRegistry) {
        this.jedisPool = jedisPool;
        this.meterRegistry = meterRegistry;
    }

    /**
     * 每 15 秒采集一次 Redis 指标,写入 Micrometer
     */
    @Scheduled(fixedRate = 15000)
    public void collectMetrics() {
        try (Jedis jedis = jedisPool.getResource()) {
            // 内存指标
            long usedMemory = Long.parseLong(
                jedis.info("memory").split("used_memory:")[1].split("\r\n")[0]
            );
            meterRegistry.gauge("redis.memory.used", Tags.of("instance", "main"), usedMemory);

            // QPS 指标
            long ops = Long.parseLong(
                jedis.info("stats").split("instantaneous_ops_per_sec:")[1].split("\r\n")[0]
            );
            meterRegistry.gauge("redis.ops.per_second", Tags.of("instance", "main"), ops);

            // 连接数
            long clients = Long.parseLong(
                jedis.info("clients").split("connected_clients:")[1].split("\r\n")[0]
            );
            meterRegistry.gauge("redis.clients.connected", Tags.of("instance", "main"), clients);

            // 复制延迟(仅 Slave 节点)
            String replicationInfo = jedis.info("replication");
            if (replicationInfo.contains("role:slave")) {
                long lag = Long.parseLong(
                    replicationInfo.split("master_last_io_seconds_ago:")[1].split("\r\n")[0]
                );
                meterRegistry.gauge("redis.replication.lag_seconds", 
                    Tags.of("instance", "main"), lag);
            }

            // 命中率
            long hits = Long.parseLong(
                jedis.info("stats").split("keyspace_hits:")[1].split("\r\n")[0]
            );
            long misses = Long.parseLong(
                jedis.info("stats").split("keyspace_misses:")[1].split("\r\n")[0]
            );
            double hitRate = (hits + misses) > 0 ? (double) hits / (hits + misses) * 100 : 0;
            meterRegistry.gauge("redis.cache.hit_rate", Tags.of("instance", "main"), hitRate);

        } catch (Exception e) {
            // 采集失败时记录错误,避免影响业务
            meterRegistry.counter("redis.metrics.collection.errors",
                Tags.of("error", e.getClass().getSimpleName())).increment();
        }
    }

    /**
     * 慢查询检查(每分钟执行)
     */
    @Scheduled(fixedRate = 60000)
    public void checkSlowLog() {
        try (Jedis jedis = jedisPool.getResource()) {
            // 获取最近 10 条慢查询(slowlog-log-slower-than 需预先配置)
            var slowlog = jedis.slowlogGet(10);

            for (var entry : slowlog) {
                long durationMs = entry.getExecutionTime() / 1000; // 微秒转毫秒
                if (durationMs > 100) {  // 超过 100ms 视为严重慢查询
                    meterRegistry.counter("redis.slowlog.severe",
                        Tags.of("command", entry.getArgs().get(0)))
                        .increment();
                }
            }
        } catch (Exception e) {
            // 静默处理,避免告警风暴
        }
    }
}

*六、实战:Prometheus + Grafana 监控体系搭建

*6.1 部署 Redis Exporter

# 下载并启动 Redis Exporter(二进制部署)
wget https://github.com/oliver006/redis_exporter/releases/download/v1.55.0/redis_exporter-v1.55.0.linux-amd64.tar.gz
tar xzf redis_exporter-v1.55.0.linux-amd64.tar.gz
cd redis_exporter-v1.55.0.linux-amd64

# 启动 Exporter(支持密码、TLS、多实例)
./redis_exporter \
  -redis.addr redis://127.0.0.1:6379 \
  -redis.password 'your_password' \
  -web.listen-address :9121

Docker 部署方式:

docker run -d \
  --name redis_exporter \
  -p 9121:9121 \
  oliver006/redis_exporter:latest \
  --redis.addr redis://redis:6379

*6.2 Prometheus 配置

# /etc/prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'redis'
    static_configs:
      - targets:
        - 'redis-01:9121'
        - 'redis-02:9121'
        - 'redis-03:9121'
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance

  - job_name: 'redis-sentinel'
    static_configs:
      - targets:
        - 'sentinel-01:9121'
        - 'sentinel-02:9121'
        - 'sentinel-03:9121'
    metrics_path: /scrape
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - target_label: __address__
        replacement: sentinel-exporter:9121

*6.3 Grafana Dashboard 导入

官方推荐的 Dashboard ID:763,或搜索 "Redis Dashboard for Prometheus Redis Exporter"(作者:oliver006)。

核心面板应包含:

  • Overview:运行时间、版本、QPS、客户端数
  • Memory:内存使用趋势、RSS、碎片率、峰值
  • Connections:连接数、拒绝连接数、阻塞客户端
  • Persistence:RDB/AOF 状态、最后一次保存时间
  • Replication:主从偏移量、复制延迟、连接状态
  • Performance:命令处理耗时、命中率、慢查询数量
  • Alerts:实时告警事件列表

*6.4 告警规则(Prometheus Rule)

# /etc/prometheus/rules/redis.yml
groups:
  - name: redis_alerts
    rules:
      # 内存使用率超过 85%
      - alert: RedisMemoryUsageHigh
        expr: |
          (
            redis_memory_used_bytes / 
            (redis_memory_max_bytes > 0 or redis_memory_used_bytes * 1.2)
          ) * 100 > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Redis 内存使用率过高"
          description: "实例 {{ $labels.instance }} 内存使用率 {{ $value | humanizePercentage }},接近 maxmemory 限制。"

      # 连接数超过 80% maxclients
      - alert: RedisConnectionsNearLimit
        expr: |
          redis_connected_clients / redis_config_maxclients * 100 > 80
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "Redis 连接数接近上限"
          description: "实例 {{ $labels.instance }} 连接数 {{ $value | humanizePercentage }},可能存在连接泄露。"

      # 复制延迟超过 10 秒
      - alert: RedisReplicationLagHigh
        expr: redis_master_last_io_seconds_ago > 10
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Redis 主从复制延迟过高"
          description: "从库 {{ $labels.instance }} 已 {{ $value }} 秒未收到主库数据。"

      # 主从连接断开
      - alert: RedisMasterLinkDown
        expr: redis_master_link_up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Redis 主从连接断开"
          description: "从库 {{ $labels.instance }} 与主库连接已断开。"

      # 持久化失败
      - alert: RedisPersistenceFailed
        expr: |
          redis_rdb_last_bgsave_status != 1 or 
          redis_aof_last_write_status != 1
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Redis 持久化失败"
          description: "实例 {{ $labels.instance }} 的 RDB 或 AOF 持久化状态异常。"

      # QPS 骤降(对比 1 小时前下降 50%)
      - alert: RedisQPSDrop
        expr: |
          (
            redis_instantaneous_ops_per_sec 
            / 
            avg_over_time(redis_instantaneous_ops_per_sec[1h]) 
          ) < 0.5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Redis QPS 异常下降"
          description: "实例 {{ $labels.instance }} QPS 较 1 小时前下降超过 50%,可能存在业务异常。"

      # 慢查询数量激增
      - alert: RedisSlowLogIncrease
        expr: |
          increase(redis_slowlog_length[5m]) > 10
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Redis 慢查询数量激增"
          description: "实例 {{ $labels.instance }} 5 分钟内新增 {{ $value }} 条慢查询,需检查热点命令。"

*6.5 Alertmanager 告警路由

# /etc/alertmanager/alertmanager.yml
global:
  smtp_smarthost: 'smtp.example.com:587'
  smtp_from: 'alert@example.com'

route:
  group_by: ['alertname', 'instance']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: 'default'
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty'
      continue: true
    - match:
        severity: warning
      receiver: 'slack'

receivers:
  - name: 'default'
    email_configs:
      - to: 'ops@example.com'

  - name: 'pagerduty'
    pagerduty_configs:
      - service_key: '<your-pagerduty-key>'

  - name: 'slack'
    slack_configs:
      - api_url: '<your-slack-webhook>'
        channel: '#redis-alerts'
        title: '{{ .GroupLabels.alertname }}'
        text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'

*七、多语言客户端监控采集

除了服务端监控,应用层客户端监控同样重要。以下是各语言客户端的监控集成方案。

*7.1 Python:基于 redis-py 的连接池监控

import redis
import psutil

class MonitoredRedisClient:
    """带监控能力的 Redis 客户端包装器"""

    def __init__(self, **kwargs):
        self.pool = redis.ConnectionPool(**kwargs)
        self.client = redis.Redis(connection_pool=self.pool)

    def get_pool_stats(self):
        """获取连接池状态"""
        return {
            'pool_size': self.pool.max_connections,
            'in_use_connections': len(self.pool._in_use_connections),
            'available_connections': len(self.pool._available_connections),
            'connection_utilization': (
                len(self.pool._in_use_connections) / self.pool.max_connections * 100
            )
        }

    def execute_with_timing(self, command, *args, **kwargs):
        """执行命令并记录耗时"""
        import time
        start = time.time()
        try:
            result = getattr(self.client, command)(*args, **kwargs)
            latency_ms = (time.time() - start) * 1000

            # 上报指标(可接入 Prometheus Pushgateway 或 StatsD)
            print(f"[METRIC] redis_command_duration{{cmd={command}}} {latency_ms:.2f}ms")

            if latency_ms > 100:
                print(f"[WARN] 慢查询: {command} 耗时 {latency_ms:.2f}ms")

            return result
        except redis.RedisError as e:
            print(f"[ERROR] Redis 命令失败: {command}, 错误: {e}")
            raise

*7.2 Java:基于 Lettuce 的指标采集

import io.lettuce.core.metrics.DefaultCommandLatencyCollectorOptions;
import io.lettuce.core.metrics.MicrometerCommandLatencyRecorder;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;

/**
 * Lettuce 客户端监控配置
 * Lettuce 原生支持 Micrometer 指标集成
 */
public class MonitoredLettuceClient {

    public static ClientResources createMonitoredClientResources() {
        MeterRegistry meterRegistry = new SimpleMeterRegistry();

        // 配置命令延迟采集选项
        DefaultCommandLatencyCollectorOptions options = 
            DefaultCommandLatencyCollectorOptions.builder()
                .targetUnit(io.lettuce.core.metrics.Microseconds)  // 微秒精度
                .resetLatenciesAfterInterval(java.time.Duration.ofMinutes(1))  // 每分钟重置
                .build();

        return DefaultClientResources.builder()
            .commandLatencyCollectorOptions(options)
            .commandLatencyRecorder(new MicrometerCommandLatencyRecorder(meterRegistry, options))
            .build();
    }
}

*7.3 Go:基于 go-redis 的 Hook 监控

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/redis/go-redis/v9"
)

// MetricsHook 实现 go-redis 的 Hook 接口,用于采集命令延迟
type MetricsHook struct{}

func (h *MetricsHook) DialHook(next redis.DialHook) redis.DialHook {
    return func(ctx context.Context, net, addr string) (redis.Conn, error) {
        return next(ctx, net, addr)
    }
}

func (h *MetricsHook) ProcessHook(next redis.ProcessHook) redis.ProcessHook {
    return func(ctx context.Context, cmd redis.Cmder) error {
        start := time.Now()
        err := next(ctx, cmd)
        latency := time.Since(start)

        // 上报到 Prometheus 或日志
        fmt.Printf("[METRIC] redis_cmd_latency{cmd=%s} %.3fms\n", 
            cmd.FullName(), float64(latency.Microseconds())/1000)

        if latency > 100*time.Millisecond {
            fmt.Printf("[WARN] 慢查询: %s 耗时 %.3fms\n", 
                cmd.FullName(), float64(latency.Microseconds())/1000)
        }

        return err
    }
}

func (h *MetricsHook) ProcessPipelineHook(next redis.ProcessPipelineHook) redis.ProcessPipelineHook {
    return func(ctx context.Context, cmds []redis.Cmder) error {
        start := time.Now()
        err := next(ctx, cmds)
        latency := time.Since(start)

        fmt.Printf("[METRIC] redis_pipeline_latency cmds=%d %.3fms\n", 
            len(cmds), float64(latency.Microseconds())/1000)

        return err
    }
}

func main() {
    // 创建带 Hook 的 Redis 客户端
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    // 添加监控 Hook
    rdb.AddHook(&MetricsHook{})

    // 此后所有命令自动采集延迟
    val, err := rdb.Get(ctx, "key").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println(val)
}

*八、故障排查与告警规则

*8.1 常见故障场景与诊断路径

故障现象 可能原因 诊断命令 解决方案
内存突增 大 Key 写入 / 批量操作 redis-cli --bigkeys 拆分大 Key,启用内存上限
延迟飙升 慢查询 / fork 阻塞 LATENCY DOCTOR 优化慢命令,升级硬件
连接被拒绝 连接泄露 / 并发过高 INFO clients 检查连接池配置,扩容
主从断开 网络故障 / 缓冲区溢出 INFO replication 检查 repl-backlog-size
命中率骤降 缓存穿透 / 大量新 Key INFO stats 启用布隆过滤器,优化缓存策略
持久化失败 磁盘满 / 权限问题 INFO persistence 清理磁盘,检查目录权限

*8.2 告警阈值建议(生产环境)

指标 警告阈值 严重阈值 说明
内存使用率 > 80% > 90% 预留 10% 缓冲,避免突发写入导致 OOM
连接数占比 > 70% > 90% 连接泄露往往在 70% 时开始恶化
复制延迟 > 5s > 10s 读写分离场景,超过 10s 可能读到旧数据
慢查询数量 > 5/min > 20/min 持续慢查询意味着单线程瓶颈
命中率(缓存) < 80% < 50% 低于 50% 缓存几乎失去意义
拒绝连接数 > 0 - 任何拒绝连接都应立即排查
持久化状态 != ok - 持久化失败 = 数据安全风险

*8.3 故障自愈脚本示例

#!/bin/bash
# redis-auto-heal.sh - 自动故障自愈脚本
# 与告警系统集成,接收 Alertmanager webhook 触发

INSTANCE=$1
ALERT_TYPE=$2

case $ALERT_TYPE in
  "memory_high")
    # 自动触发内存整理
    redis-cli -h $INSTANCE MEMORY PURGE
    # 记录日志
    echo "$(date) [$INSTANCE] 自动执行 MEMORY PURGE" >> /var/log/redis-heal.log
    ;;

  "connection_leak")
    # 检查并关闭空闲连接
    redis-cli -h $INSTANCE CLIENT LIST | grep "idle=" | \
      awk -F' ' '{for(i=1;i<=NF;i++) if($i ~ /id=/) print $i}' | \
      sed 's/id=//' | xargs -I {} redis-cli -h $INSTANCE CLIENT KILL {} skipme
    echo "$(date) [$INSTANCE] 自动清理空闲连接" >> /var/log/redis-heal.log
    ;;

  "slowlog_spike")
    # 临时降低慢查询阈值以捕获更多日志
    redis-cli -h $INSTANCE CONFIG SET slowlog-log-slower-than 5000
    echo "$(date) [$INSTANCE] 慢查询激增,临时降低阈值至 5ms" >> /var/log/redis-heal.log
    ;;

  *)
    echo "$(date) [$INSTANCE] 未知告警类型: $ALERT_TYPE" >> /var/log/redis-heal.log
    ;;
esac

⚠️ 生产环境注意:自动故障自愈需谨慎评估副作用。建议先在测试环境验证,并配合人工审批流程。


*九、FAQ 常见问题

*Q1:Prometheus 采集 Redis 指标会影响性能吗?

A:影响极小。Redis Exporter 通过 INFO 命令采集,单次执行耗时通常在 1ms 以内。默认 15 秒采集间隔,对 Redis 主线程的压力可以忽略。对于极高并发场景(> 100 万 QPS),可将间隔调整为 30 秒。

*Q2:内存使用率 80% 时,为什么还没有触发驱逐?

A:Redis 的驱逐只在 used_memory > maxmemory 时触发。80% 是预警阈值,给运维留出响应时间。如果未设置 maxmemory,Redis 将持续使用内存直到系统 OOM,因此生产环境必须设置 maxmemorymaxmemory-policy

*Q3:监控显示复制延迟 0 秒,但业务仍读到旧数据?

Amaster_last_io_seconds_ago 只表示从库收到数据的网络延迟,不代表数据已完全写入从库内存。高并发场景下,从库处理命令队列可能有延迟。更精确的指标是 master_repl_offset - slave_repl_offset,偏移差 > 1000 说明存在处理延迟。

*Q4:为什么 Grafana 显示内存使用率 100%,但 used_memory 远小于 maxmemory

Aused_memory 是 Redis 逻辑内存,而 used_memory_rss 是系统物理内存。如果 RSS 远大于 used_memory,说明存在严重内存碎片或客户端输出缓冲堆积。此时应关注 RSS 而非 used_memory,并检查 mem_fragmentation_ratio

*Q5:客户端监控和服务端监控,哪个更重要?

A:两者缺一不可。服务端监控回答"Redis 是否健康",客户端监控回答"应用使用 Redis 是否正确"。很多性能问题(如连接池过小、Pipeline 未使用、缓存穿透)在服务端指标上表现不明显,但客户端监控能直接发现。建议服务端做基线告警,客户端做精细诊断

*Q6:Redis Cluster 模式下如何监控?

A:每个节点部署独立的 Redis Exporter,Prometheus 配置所有节点为目标。Grafana Dashboard 使用变量($instance)切换节点视图。额外关注 cluster_slots_assignedcluster_slots_ok,确保所有 16384 个槽位正常分配。任何节点槽位异常(cluster_slots_pfail > 0)都应立即告警。


*十、总结与下一步

本文从 Redis 监控的六大核心维度出发,深入解析了 INFO 命令的数据结构、内存与复制延迟的监控原理,给出了基于 Prometheus + Grafana + Redis Exporter 的完整可观测性架构方案,并提供了 Shell、Python、Java、Go 多语言的监控采集代码。

核心要点回顾:

  1. 监控不是堆砌指标:围绕内存、连接、命令、延迟、持久化、复制六个维度,选择对业务 SLA 有直接影响的指标
  2. 服务端 + 客户端双管齐下:服务端监控发现系统异常,客户端监控发现使用问题
  3. 告警分级:Critical(立即响应)vs Warning(趋势关注),避免告警疲劳
  4. 从监控到自愈:基于告警触发自动化脚本,缩短 MTTR(平均恢复时间)

下一步建议:

  • 短期:部署 Redis Exporter + Grafana Dashboard,建立基线监控
  • 中期:根据业务场景定制告警规则,接入企业告警通道(钉钉/飞书/邮件)
  • 长期:建立 Redis 监控 SLO(如 P99 延迟 < 10ms、命中率 > 90%),将监控纳入 CI/CD 质量门禁

监控的本质不是"出了问题再查",而是"让问题在发生前就被看见"。一套成熟的 Redis 监控体系,是保障缓存服务稳定运行的最后一道,也是最重要的一道防线。