根本原因是docker默认bridge网络的多层转发开销:veth→docker0网桥→iptables→nat,每跳增加0.5–2ms,累计达3–8ms;而redis pub/sub本为内存级广播,理想延迟应低于1ms。

Redis PUB/SUB 在 Docker 里延迟高,根本不是 Redis 的锅
实测端到端延迟从 0.3ms 拉到 6–12ms,问题出在 Docker 默认 bridge 网络栈:每条消息要经过 veth → docker0 → iptables → NAT 四跳,光转发就吃掉 3–8ms。而 PUB/SUB 本质是内存广播,理想延迟应 1ms。
关键陷阱:docker run --network bridge 启动的容器,哪怕两个都在同一宿主机,通信也必须走虚拟网桥;用 localhost:6379 连宿主机 Redis 更糟——实际走的是 -p 6379:6379 触发的 DNAT + conntrack,延迟更不可控。
用 --network host 是最快解法,但得清楚代价
命令示例:docker run --network host -e REDIS_URL=redis://localhost:6379 myapp
这时容器直接复用宿主机网络命名空间,localhost 就是宿主机,redis-py 的 pubsub.listen() 能直连,延迟压到 0.3–0.8ms。
- 必须让客户端连
localhost:6379,不能写容器 IP 或服务名(DNS 解析无效) - 多个容器若都监听
6379会端口冲突,需协调或改 Redis 绑定端口 - 容器可直接操作宿主机网络接口、抓包、修改路由,权限模型变松,不适合多租户或强隔离场景
非要网络隔离?别碰默认 bridge,用自定义桥接网络
创建时关掉冗余功能:docker network create --driver bridge --opt com.docker.network.bridge.enable_ip_masquerade=false mynet
启动容器显式指定网络和 DNS 搜索域:docker run --network mynet --dns-search mynet redis-srv redis:7-alpine
这样其他容器能用 redis-srv 直接解析,避免走 docker0 的额外转发。
- 自定义网络默认不启用 IP 伪装(masquerade),省掉一次 NAT
- 若想彻底禁用 iptables 规则注入,需在
/etc/docker/daemon.json加"iptables": false,否则仍可能被 runtime 动态加规则 -
--dns-search让容器内ping redis-srv能命中,不用硬编码 IP 或依赖外网 DNS
键空间通知(Keyspace Notifications)必须手动开启配置
Docker 官方镜像不带 redis.conf,notify-keyspace-events 默认关闭。不配就收不到 __keyevent@0__:expired 这类消息。
典型配置(只开过期和删除事件):notify-keyspace-events "Ex"
- 挂载配置文件启动:
docker run -v $(pwd)/redis.conf:/usr/local/etc/redis/redis.conf redis:7 redis-server /usr/local/etc/redis/redis.conf - 进容器验证:
docker exec -it redis redis-cli config get notify-keyspace-events,返回["notify-keyspace-events","Ex"]才生效 - 字母含义别记混:
E=过期,x=删除,K=键事件本身,A=所有;Ex和AKE效果不同,后者会多发大量冗余消息
真正卡点不在 Redis 配置,而在网络路径选择——host 模式快但裸奔,自定义 bridge 安全但参数容易漏;键空间通知看似简单,但挂载后不验证 config get,90% 的人以为开了其实没开。











