redis pub/sub 不能用于 binlog 同步,因其不解析binlog格式、无位点管理、无ack重试、无法过滤表/操作类型;可靠方案需用canal等cdc工具对接mysql并投递至kafka,redis仅作终端执行缓存删除。

Redis 的发布订阅(Pub/Sub)本身不能直接用于 Binlog 变更同步,它只是进程内或同集群内的轻量消息广播机制,不具备可靠投递、重试、位点管理能力——用它接 Binlog 就是拿错工具拧螺丝。
为什么不能用 redis.PubSub 订阅 MySQL Binlog
MySQL 的 Binlog 是二进制日志文件,必须由支持 MySQL 复制协议的客户端(比如 Canal、Maxwell、Debezium)去连接主库、伪装成 slave、拉取并解析日志。Redis 的 PUBSUB 命令只负责在 Redis 实例内部转发字符串消息,它:
- 不理解 Binlog 格式(
ROW/STATEMENT/MIXED),无法解析INSERT行记录或UPDATE前后镜像 - 没有 offset 位点管理,消息一丢就永远丢失,无法做到“至少一次”投递
- 不支持 ACK 确认,下游消费失败时上游无感知,无法触发重试
- 无法区分表名、库名、操作类型,连
WHERE id = ?这种基本过滤都做不到
真正能接 Binlog 的组件长什么样
生产环境必须用专为 CDC(Change Data Capture)设计的中间件,它们会:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 主动连接 MySQL,配置
server-id,开启binlog_format = ROW和binlog_row_image = FULL - 把解析后的变更事件(含
database、table、type、before/after字段)转成结构化消息(JSON 或 Avro) - 投递到 Kafka / Pulsar / RocketMQ 等有持久化和重平衡能力的消息队列,而非 Redis Pub/Sub
- 提供 client SDK,让你在消费者里写逻辑:
if event.table == "user" and event.type == "UPDATE": redis.delete(f"user:{event.after.id}")
典型链路是:MySQL → Canal → Kafka → 消费服务 → redis.delete(),其中 Redis 只作为最终执行缓存清理的终端,不是消息总线。
如果硬要用 Redis 做“兜底补偿”,该怎么做
Redis 的 PUBSUB 不适合做主同步通道,但可以作为轻量级异步通知的补充手段,比如在补偿任务中广播“某条记录已校准完成”。这时要注意:
- 不要依赖
SUBSCRIBE的实时性——网络抖动或 consumer 重启会导致消息丢失,必须搭配 DB 记录状态表或 RedisSETNX去重 - 消息体必须带唯一 trace_id 和时间戳,避免重复消费导致误删
- 例如:消费 Kafka 中的
user_update事件后,执行redis.publish("cache:invalidate", '{"table":"user","id":123,"ts":1748294186}'),下游监听者仅作日志或触发监控告警,不依赖它更新数据 - 真正可靠的缓存失效动作,必须走直连 Redis 的
DEL或EXPIRE命令,而不是靠SUBSCRIBE回调再删
最容易被忽略的坑:Binlog 解析精度与缓存粒度不匹配
MySQL 的 ROW 格式 Binlog 只记录变更行的主键和修改字段,但你的缓存 key 可能是复合的:user:profile:123、user:order_list:123、user:summary:123。一个 UPDATE user SET name = ? WHERE id = 123 只会触发 user:profile:123 失效,但不会自动连带清掉 user:summary:123 ——这需要你在消费端显式定义映射规则,或者用 Lua 脚本批量 DEL 匹配 user:*:123 的 key(注意 KEYS 命令线上禁用,改用 SCAN + DEL 分批)。










