1. 为什么监控Redis不能靠INFO命令,得专门拉一个exporter
干运维或者后端时间长了,几乎都会遇到这种场景:线上Redis突然响应变慢,或者内存飙升,你第一反应是登录服务器敲redis-cli INFO看两眼。内存满了?连接数涨了?还是有大key在作妖?信息确实能看到一点,但也就仅此而已。
redis exporter 这个工具,就是专门解决"Redis状态看不全、看不及时、没法历史回溯"的问题。它把Redis的运行数据转成Prometheus能识别的指标格式,再由Prometheus持续抓取,最终在Grafana上画成趋势图、配上告警。简单说,INFO是一条一条手动查的状态快照,redis exporter是一套自动化的、带时间序列的监控数据采集器。
这篇手册适合谁?正在给Redis做基础监控的运维、想排查线上Redis问题的后端开发、以及搭了K8s或Docker环境需要统一观测Redis状态的平台工程师。我会从部署方式、配置项、核心指标、告警规则到踩坑记录,把整套东西讲透。
1.1 INFO的四个section够不够用
Redis自带的INFO命令其实内容很全,Memory、Clients、Persistence、Stats、Replication、Keyspace这些section都有。但问题不在内容全不全,而是它的使用方式。靠手动敲命令,只能看到当前这一刻的状态,Redis是不是五分钟前就内存告急了?连接数是什么时候开始涨的?缓存穿透是什么时候开始频繁发生的?这些问题INFO给不了答案。
另一个痛点是多实例场景。一个业务环境里往往有主从、哨兵、集群,可能有十几个Redis节点。一台一台登录上去敲INFO,运维效率极低,而且容易漏。就算写成脚本循环执行,数据也只能落在本地文件里,缺乏统一的采集、存储、可视化链路。
redis exporter的做法是:每个Redis节点旁边挂一个采集器,你只要配置好它的连接信息,它会周期性去执行类似INFO的命令,把结果整理成结构化的指标,通过HTTP端口暴露出来。Prometheus那边设置一个target,到点自动来抓数据,数据落在时序数据库里,随时可以查历史趋势。这才是监控该有的形态。
1.2 exporter的定位:把Redis"翻译"成Prometheus时序数据
很多人第一次看到redis exporter会困惑:它到底是一个独立的服务,还是一个Redis插件?答案是前者,完全不用改动Redis本身。它就是一个独立的进程,通过Redis的TCP协议和Redis服务端通信。你可以把它理解成一个"翻译官"——Redis吐出来的内部状态数据,经过这个翻译官,变成Prometheus的metrics格式。
这套设计非常巧妙的一点是解耦。Redis那边不需要装任何额外的模块,exporter这边也不依赖特定的Redis版本,只要Redis的INFO、CONFIG这些命令还能用,exporter就能正常工作。我见过从Redis 3.x到7.x的混合环境,一个exporter进程配了多个实例参数,全部正常采集,兼容性很稳。
数据链路也不复杂:
Redis节点 <--TCP INFO/CLIENT LIST/SLOWLOG--> redis_exporter进程 <--HTTP :9121--> Prometheus <--查询--> Grafana整个过程里你只需要关注两件事:exporter怎么部署、Prometheus怎么配抓取。后面几章我就按这个顺序展开。
2. 部署前先搞懂redis_exporter怎么走通数据链路
部署redis exporter没那么玄乎,但选错方式、配错参数,后续排查会比较难受。这一章先把数据是怎么流的讲清楚,再对比三种常见的部署方式,你按场景选一种就行。
2.1 一个请求进来,数据是怎么流转的
redis exporter默认监听在9121端口,每次Prometheus来抓取(默认15秒一次),exporter就会执行一系列Redis命令,把拿到的数据处理成指标。具体流程可以分成四步:
- 建立连接:exporter根据配置的地址、密码、TLS证书,和Redis实例建立TCP连接。连接方式支持redis:// 和 rediss:// 协议前缀,如果Redis启用了TLS,前缀要写对。
- 采集数据:exporter会执行INFO ALL、CLIENT LIST、CONFIG GET、SLOWLOG GET等命令。注意它拿到的不仅仅是INFO输出的那些东西,CLIENT LIST能拿到每个连接的详细状态,SLOWLOG能看到慢命令,这些都是INFO里面没有的维度。
- 格式转换:拿到原始数据后,exporter把Redis的文本格式解析成Prometheus指标格式。比如INFO里的
used_memory: 123456,会被解析成一个名为redis_memory_used_bytes的Gauge指标,数值保持123456不变。 - 等待抓取:转换完的指标缓存在exporter进程内,Prometheus通过HTTP请求拉走。整个交互是Pull模型,不是Push,所以exporter自己是不会主动把数据发给Prometheus的。
理解这四步之后,很多问题就迎刃而解了。比如你发现Grafana上只有点状数据没有连续曲线,大概率是Prometheus的抓取间隔和exporter的响应时间不匹配;如果某些指标永远是0,可能是Redis版本不支持某个命令,被exporter静默降级了。
2.2 三种部署方式的选型对比
redis exporter本身是Go语言编译的单个二进制文件,部署极其方便。但不同环境下有不同最优解,我把三种常见方式列在下面。
| 部署方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 二进制直接跑 | 物理机或虚拟机上的Redis | 部署最快,资源占用最低 | 进程守护和自愈要自己管 |
| Docker Compose | 开发环境、单机多实例 | 配置清晰,环境隔离 | 需要维护镜像和网络 |
| Kubernetes Deployment | K8s集群内Redis | 弹性调度、滚动更新 | 需要理解K8s网络模型 |
二进制方式最直接。去GitHub Release页面下载对应平台的安装包,解压后直接启动:
wget https://github.com/oliver006/redis_exporter/releases/download/v1.61.0/redis_exporter-v1.61.0.linux-amd64.tar.gz tar -zxvf redis_exporter-v1.61.0.linux-amd64.tar.gz cd redis_exporter-v1.61.0.linux-amd64 REDIS_ADDR=redis://127.0.0.1:6379 REDIS_PASSWORD=你的密码 ./redis_exporter启动后先别急着配Prometheus,先用curl确认指标能正常拉取:
curl -s http://127.0.0.1:9121/metrics | grep -E "redis_up|redis_memory_used_bytes"看到redis_up 1说明exporter成功连上了Redis,redis_memory_used_bytes这类指标也出来了,链路就通了。
Docker方式适合已经有容器化习惯的团队。一条命令就能起:
docker run -d --name redis-exporter \ -p 9121:9121 \ -e REDIS_ADDR=redis://172.17.0.2:6379 \ oliver006/redis_exporter:latest我建议开发环境用这种方式,干净利落,不污染宿主机。但记得把--restart=always加上,不然Docker守护进程重启后exporter不会自动拉起来。
K8s方式是生产环境最常见的选择。redis exporter作为Deployment部署,Prometheus通过Service发现来抓取。K8s下有个小技巧:如果你只是监控K8s集群内的Redis,可以不用给exporter单独配Service,直接写Pod的annotations,配合Prometheus的Pod注解发现机制,它会自动找上来。
apiVersion: apps/v1 kind: Deployment metadata: name: redis-exporter namespace: monitoring spec: replicas: 1 selector: matchLabels: app: redis-exporter template: metadata: annotations: prometheus.io/scrape: "true" prometheus.io/port: "9121" labels: app: redis-exporter spec: containers: - name: redis-exporter image: oliver006/redis_exporter:latest env: - name: REDIS_ADDR value: "redis://redis-service:6379" ports: - containerPort: 9121选型没有标准答案,我自己的习惯是:开发环境用Docker,测试和生产如果是虚拟机就二进制加systemd托管,如果上了K8s就直接Deployment。核心思路是让exporter尽可能贴近Redis部署,网络路径短,稳定性高。
3. 核心参数逐个拆解,这些配置直接影响监控结果
很多人用redis exporter就是配个地址和密码就完事了,但真正把参数吃透之后,能做的事情多很多。这一章我把高频使用的参数按功能分组拆开讲,顺便说一下哪些参数是容易踩坑的。
3.1 连接相关:地址、密码、TLS
先看最基本的几个连接参数:
REDIS_ADDR:Redis实例地址,支持逗号分隔多个地址,格式如redis://127.0.0.1:6379,redis://127.0.0.1:6380。多实例场景可以共用一个exporter进程,但我后面会讲为什么不太建议这么做。REDIS_PASSWORD:Redis密码。如果密码里有特殊字符,记得在URL里做URL编码,或者用环境变量传入,避免空格和#引起的解析问题。REDIS_TLS:是否启用TLS,设为true时地址前缀要改成rediss://。自签名证书环境下,还需要配合REDIS_TLS_CA_CERT、REDIS_TLS_CERT、REDIS_TLS_PRIVATE_KEY这几个参数。REDIS_USER:Redis 6.0以上支持ACL,默认用户是default,如果你创建了专门的monitor用户,这里填用户名。
如果你管理的Redis是在云上(比如自建的云主机或者托管的Redis实例),REDIS_ADDR一定要用能走内网的地址。我见过有人把公网地址填进去,然后发现Prometheus抓取延迟特别高,因为exporter到Redis的每次命令往返都要过公网,监控本身的延迟比被监控对象的状态变化还夸张。
3.2 采集逻辑:默认姿势和check-keys的取舍
redis exporter默认会采集所有以redis_前缀开头的指标,涵盖内存、CPU、连接数、命令统计、持久化、复制等维度。如果你只是想快速搭一套基础监控,什么都不用配,默认采集姿势已经足够。
但如果想监控某个具体key的长度或者某个key是否存在,就需要用到REDIS_CHECK_KEYS参数。这个参数的格式是key的pattern,支持glob风格的通配符。比如你想监控消息队列的积压情况,队列key的pattern是queue:*:
REDIS_CHECK_KEYS="queue:*" ./redis_exporterexporter会遍历匹配的key,对每个key执行LLEN、HLEN、SCARD这类命令,把结果输出成redis_key_length指标,并带上db和key两个label。
注意:
REDIS_CHECK_KEYS用起来要克制。如果Pattern写得太宽,比如直接写*,那等于要对Redis里所有key做一次SCAN加各种LEN操作,在key数量很大的实例上,这个采集动作本身就会拖慢Redis,属于典型的监控反噬。
还有个参数叫REDIS_CHECK_KEYS_BATCH_SIZE,默认100。它控制exporter每批检查多少个key,如果key数量多,可以把批次调大减少命令轮次,但每轮的时间会变长。我建议保持默认,除非你很清楚自己在做什么。
3.3 运行姿态:多实例、日志、超时
REDIS_EXPORTER_IS_AWS_ELASTICACHE:如果监控的是AWS ElastiCache,这个参数应该设为true,它会开启Elasticache特有的指标采集。自建环境不要开。REDIS_EXPORTER_DEBUG:设为true会输出详细的DEBUG日志,排错很有用。正常运行时建议关闭,不然日志量有点大。REDIS_EXPORTER_REDIS_TIMEOUT:exporter和Redis通信的超时时间,默认60秒。如果Redis负载很高,INFO命令本身可能超过1秒,这个超时留够空间就行,不要设太小。REDIS_EXPORTER_WEB_LISTEN_ADDRESS:exporter自身的监听地址,默认0.0.0.0:9121。如果你只要本机Prometheus抓取,改成127.0.0.1:9121更安全。
除了环境变量,所有这些参数也支持命令行flag方式,比如--redis.addr--redis.password。两种方式等价,我习惯用环境变量,因为K8s里配置env比传参更自然。
4. 看得懂的指标才算监控,关键指标和告警要这么配
exporter装好、指标也抓到了,接下来就要面对一个实际问题:那么多以redis_开头的指标,哪些值得盯,哪些看一眼就行?这一章我按运维视角把指标分分类,给出我认为最需要关注的几个,以及对应的告警规则。
4.1 内存和淘汰策略指标,撑起Redis最重要的告警
内存是Redis最核心的资源,指标redis_memory_used_bytes直接对应INFO里的used_memory。这个值接近maxmemory时,Redis会进入淘汰状态,根据maxmemory-policy的配置决定是LRU淘汰还是拒绝写入。告警阈值不能直接按百分比算,因为不同实例的maxmemory设置不一样。
# 内存使用率,假设maxmemory配置为8GB (redis_memory_used_bytes / 8589934592) * 100另外两个内存指标值得看:
redis_memory_max_bytes:Redis能用的最大内存,如果这个值为0说明没限制,告警规则里要处理这种情况。redis_memory_fragmentation_ratio:内存碎片率,大于1.5说明碎片化严重,可能是频繁修改大key导致的。
内存碎片率这个指标我实际用过,碎片率飙到2.x的时候,Redis明明没存多少数据,used_memory却居高不下。这时候重启Redis实例最直接,或者调整activedefrag参数让Redis自己整理碎片。
4.2 持久化与主从复制指标,最容易出问题的环节
持久化这块,redis_rdb_changes_since_last_save这个指标很常用。它表示上次RDB快照之后有多少次写操作。如果这个值持续增长,同时redis_rdb_last_bgsave_status是1(表示成功,0表示失败),说明RDB一直在正常执行,不用担心。最怕的是redis_rdb_last_bgsave_timestamp_seconds和当前时间差越来越大,说明很久没有成功的RDB保存了,数据安全就有隐患。
主从复制的关键指标是redis_connected_slaves和redis_replication_connected_slaves。如果Redis节点配置了复制,这两个值会持续波动说明主从连接不稳定。配合redis_master_link_up看主从通道是否确实断开。
我遇到过一个典型的踩坑场景:某个从库因为网络抖动和主库断连,但由于断线重连的逻辑设置了比较长的超时,从库和主库的数据差已经到了几个小时。如果只盯connected_slaves是发现不了的,因为连接还在,只是数据同步积压了。这时候要盯redis_master_repl_offset和redis_slave_repl_offset,两者差值就是同步延迟的字节数。告警规则可以这么写:
# 主从复制延迟超过100MB就告警 abs(redis_master_repl_offset - redis_slave_repl_offset) > 1048576004.3 客户端连接和命令统计,定位性能瓶颈的关键
redis_connected_clients是当前客户端连接数。这个值如果持续上涨而且接近maxclients,说明应用层的连接池管理可能出了问题,比如连接被泄漏了没有释放。结合redis_connected_clients_evicted(因为超过maxclients而被拒绝的连接数)一起看,能很快判断是哪种情况。
命令统计指标里有意思的是redis_commands_total,它带上cmd这个label,统计每种命令的执行次数。通过PromQL可以算慢命令占比、热点命令分布:
# 每秒执行次数Top5的命令 topk(5, rate(redis_commands_total[1m]))另一个和慢查询相关的指标是redis_slowlog_last_id,它表示SLOWLOG最新的日志ID。如果这个值在快速跳动,说明Redis正在大量执行超过慢查询阈值的命令,此时性能多半已经出问题了。
4.4 一套可以直接抄的告警规则
Prometheus告警规则可以写成一个yml文件,示例配置如下:
groups: - name: redis-exporter-alerts rules: - alert: Redis内存使用率过高 expr: (redis_memory_used_bytes / redis_memory_max_bytes) * 100 > 85 for: 5m labels: severity: warning annotations: summary: "Redis实例内存使用率超过85%" description: "实例 {{ $labels.instance }} 当前内存使用率 {{ $value }}%" - alert: Redis主从复制延迟过大 expr: abs(redis_master_repl_offset - redis_slave_repl_offset) > 104857600 for: 2m labels: severity: critical annotations: summary: "Redis复制延迟超过100MB" description: "实例 {{ $labels.instance }} 主从数据同步积压超过100MB"告警阈值没有放之四海而皆准的,还是得根据业务情况调。内存阈值85%对那种"内存就是拿来缓存、淘汰策略是LRU"的实例就太保守了,它对90%以上也未必有影响。但如果是用Redis做复杂集合运算的,内存超过80%就该高度重视,因为新增数据可能直接触发OOM。
5. 实战中的坑:从密码认证到高并发,这些我都踩过
用redis exporter这些年,遇到的坑不少。大部分问题不是exporter本身的问题,而是监控系统和Redis的联动方式没想清楚。写几个真实的踩坑经历,你遇到类似的可以少走弯路。
5.1 Redis 6.0 ACL认证导致scrape全部失败
Redis 6.0开始支持ACL(Access Control List),如果运维为了安全把默认用户给禁了,而exporter还在用老密码连接,就会出现一个很诡异的现象:exporter进程正常启动,但Prometheus抓回来的所有指标全都没有值,或者在普罗米修斯里看到redis_up跳变。
原因是exporter连接Redis时默认用的用户名是default,如果Redis把default用户禁掉了,连接直接抛WRONGPASS。排查方法是看exporter日志:
ERR max number of clients reached不对,这个错误是连接数问题。ACL失败时日志一般是:
ERR invalid password解决方式很简单,用专门的监控账号代替default。在Redis里创建账号,按照最小权限原则来:
ACL SETUSER redis_exporter on >你的强密码 +@all -@admin ~* &*然后在exporter里配置REDIS_USER=redis_exporter和REDIS_PASSWORD=你的强密码。这一步做对了,后续无论Redis侧怎么改权限,监控都不会受影响。
5.2 采集频率太高把Redis连接池打满
这是我自己踩的最深的一个坑。某次监控平台统一调整抓取频率,把15秒改成5秒,结果那天下午Redis突然出现大量连接拒绝的报错。查下来发现,Prometheus每个实例配置了多个target,每个target抓取时还会触发exporter多个并发TCP连接,叠加起来瞬间连接数翻了好几倍。
Redis的maxclients默认是10000,一般不会打满,但如果你的Redis实例本身链路就很多,加上exporter引入的额外连接,还是可能触顶。有两种解法,方案A是给Prometheus单独开一个抓取配置,降低抓取频率:
scrape_configs: - job_name: redis metrics_path: /metrics scrape_interval: 30s static_configs: - targets: ['127.0.0.1:9121']方案B是给exporter起一个独立的Pod或进程,和业务流量隔离。生产环境我倾向两个都做:抓取频率保持30秒以上,exporter单独部署一套,避免因为监控系统自身的问题影响业务访问Redis。
这个坑的底层逻辑是:监控系统本身也会消耗被监控对象的能力,采集频率、采集命令数量都要纳入容量评估。
5.3 K8s环境下多实例的exporter管理
在K8s里跑Redis Cluster时,一个集群通常有6个节点。最省事的做法是一个exporter进程配6个地址,用逗号分隔。但这样有个问题:Prometheus抓一次会把6个实例的指标全部拉回来,排错时候分不清哪个指标对应哪个节点,label也乱。
我更推荐的做法是一对一部署:每个Redis节点配一个exporter容器,通过StatefulSet编排,exporter的地址指向本地回环的Redis实例,这样指标往上送的时候,Prometheus能通过pod标签或者instance标签精确区分出是哪个节点。
StatefulSet ├── redis-0 + exporter-0 (REDIS_ADDR=redis://redis-0:6379) ├── redis-1 + exporter-1 (REDIS_ADDR=redis://redis-1:6379) └── redis-2 + exporter-2 (REDIS_ADDR=redis://redis-2:6379)这么做,牺牲了一点资源(多了几个轻量进程),换来了指标的可区分性,后续做节点维度告警、排错都方便很多。
5.4 exporter自身不是免费的:资源占用和性能影响
redis exporter虽然是个轻量进程,但它在采集过程中会对Redis执行额外命令,还是有开销的。默认情况下,它每轮采集会执行INFO ALL,这个命令本身在实例很大时(比如几万个key或者很多客户端连接)会消耗几十毫秒的CPU时间,如果抓取频率太快,对高负载Redis是有影响的。
还有一点容易忽略,exporter默认还带--redis.include-command参数,可以自定义要执行的命令。如果你需要采集某些自定义指标,比如业务写入的自增计数器,可以用这个参数。但加命令意味着每轮采集多一轮Redis往返,命令写得不好(比如用了KEYS),反而帮了倒忙。
我自己用下来,监控Redis的key基数最好不要超过10万级,超过后INFO和SCAN的开销开始变得明显。如果你管理的Redis超大,建议考虑用Redis自带的latency monitor和memory doctor这类内部诊断工具来做补充,不要把所有压力都压在exporter一轮轮的采集上。
6. 最后分享一点个人的使用习惯
把这些年用redis exporter攒下来的一些小习惯放在这里。谈不上教程,就是纯经验分享。
习惯一:用systemd托管二进制进程时,写好环境变量文件。别把密码直接写在启动命令里,那样进程列表里都能看到。写一个/etc/redis_exporter.conf然后通过EnvironmentFile加载,配置和代码分离,维护也好做。
REDIS_ADDR=redis://127.0.0.1:6379 REDIS_PASSWORD=你的强密码 REDIS_EXPORTER_WEB_LISTEN_ADDRESS=0.0.0.0:9121习惯二:监控指标分两层看。第一层是"资源层",内存、CPU、连接数,这些告诉你Redis是不是还健康;第二层是"业务层",缓存命中率、key积压数量、慢命令频率,这些告诉你Redis在业务slo中的表现。资源层指标异常往往是业务层指标异常的原因,两个维度要联合分析。
结合redis缓存穿透的场景举个例子:当缓存命中率指标(可以用redis_keyspace_hits_total/redis_keyspace_hits_total + redis_keyspace_misses_total算出来)持续走低,同时Redis整体QPS还在涨,这就说明大量请求没命中缓存直接打到数据库了。这时候如果只看资源层指标,你可能会去扩容Redis,但真正的修复方案是去查热点key是不是被穿透了。
习惯三:告警一定要配for关键字。Prometheus告警规则里的for: 5m表示持续5分钟才触发告警。这个参数非常重要,它能把瞬时的抖动过滤掉,避免告警疲劳。Redis内存偶尔冲到90%然后又掉下来,如果一看到超过阈值就告警,夜班同事会被你折磨疯。
redis exporter不是什么高精尖的东西,它的价值在于把Redis这个每天都要用、但状态经常被忽视的组件,纳入了统一的观测体系。和缓存穿透的治理一样——如果连缓存层有异常请求都看不见,治理就永远只能靠事后救火,谈不上主动维护。
最后提醒一句,exporter和Prometheus的版本尽量保持更新。这个项目虽然稳定,但Redis新版本有时会调整内部命令的返回结构,老版本exporter解析新格式可能出现指标缺失。隔一段时间去Release页面看看,有更新就顺手升一下,成本很低,收益明确。