在分布式系统架构里,服务发现、配置中心、分布式锁、 leader选举这些需求几乎绕不开底层存储组件的选型。提到候选方案,Redis 和 Etcd 是被讨论最多的两个名字。有人认为 Redis 足够快,用它做分布式锁简单省事;也有人坚持 Etcd 才是正统的分布式协调组件。事实上,两者虽然都能存储键值数据,但设计哲学完全不同,盲目混用很容易埋下一致性隐患。本文将从多个维度展开对比,帮你理清选型思路。

一、设计目标与架构原理的差异
Redis 诞生于 2009 年,本质是一个基于内存的键值数据库。它的核心诉求是性能,单线程处理命令请求,数据常驻内存,官方公布的基准测试中单机 QPS 可以轻松达到 10 万以上。Redis 的高可用方案经历了主从复制、哨兵模式到 Redis Cluster 的演进,其复制方式默认是异步的,主节点写入后立即返回成功,不等从节点确认,这保证了速度,但也意味着主节点宕机时存在丢数据的风险。
Etcd 则是完全不同的技术路线。它是 CoreOS 团队开发的分布式键值存储,底层使用 Go 编写,数据库引擎基于 boltdb(3.x 版本后演进为 bbolt),数据最终落盘。Etcd 的灵魂在于 Raft 共识算法:集群通常部署奇数个节点(3 个或 5 个),任何一次写请求都必须获得集群多数派节点的确认才算成功。这种多数派机制保证了即使少数节点故障,数据也不会丢失,任何已提交的写入对外永远可见。Kubernetes 就是把 Etcd 作为唯一的状态存储,足以说明其一致性能力经过了大规模生产验证。
简单概括:Redis 追求的是 AP 系统的极致性能,而 Etcd 是标准的 CP 系统,宁可拒绝服务也不牺牲一致性。这个根本差异决定了后续所有行为的不同。
二、一致性与 Watch 机制的对比
1. 一致性模型
Redis 的主从复制默认是异步的,哨兵触发故障切换时需要从从节点中选举新主,已经写入但尚未同步的数据可能丢失。对于缓存场景这无所谓,但用于分布式锁就可能出问题:客户端 A 在主节点拿到锁后主节点宕机,锁信息还没同步到从节点,故障切换后客户端 B 又能拿到同一把锁,互斥性被破坏。著名的 Redlock 算法就是为了缓解这个问题提出的,但包括 Martin Kleppmann 在内的多位专家都指出 Redlock 存在时钟漂移等固有缺陷,业界对其安全性仍有争论。
Etcd 的一切操作都建立在 Raft 之上。写入一旦返回成功,就意味着多数派节点已经持久化该值,之后无论怎么读,读到的一定是这次写入之后的状态(线性一致性读)。用于分布式锁时,Etcd 通过 lease(租约)机制绑定锁的持有者,客户端断线后租约到期自动释放锁,既避免了死锁,又不会出现两个客户端同时持锁的情况。
2. Watch 机制
两个组件都支持监听 key 变化,但实现深度不同。Redis 从 2.2 版本开始支持 pub/sub,但它是“即发即弃”的:订阅者掉线的瞬间消息就丢失了,没有重放机制。后来 Redis 5.0 引入的 Streams 弥补了一部分短板,支持持久化和消费者组,但本质上仍是消息队列的思路。
Etcd 的 Watch 则是基于 MVCC 多版本机制实现的。每个 key 的每次修改都有全局递增的 revision 版本号,Watch 可以指定从任意历史 revision 开始监听,客户端断线重连后会自动从上次收到的 revision 续传,事件绝不丢失。配合 compact 策略控制历史版本数量,既能保证可靠性又能限制磁盘占用。对于配置中心、服务发现这类“一个变更不能漏”的场景,这个特性是决定性的优势。
三、性能表现与功能特性对比
性能层面 Redis 占据压倒性优势。Redis 数据在内存中,读写延迟通常在亚毫秒级;Etcd 每次写都要走 Raft 提交、多数派落盘,默认要求 fsync,单次写延迟通常在几毫秒到十几毫秒,官方建议的写入规模上限是每秒几千次。因此 Etcd 明确不适合高频写入的场景,比如计数器、限流这类操作就不该放进 Etcd。
| 对比维度 | Redis | Etcd |
|---|---|---|
| 存储介质 | 内存为主,可持久化 | 磁盘为主,内存缓存 |
| 一致性模型 | 最终一致(主从异步复制) | 线性一致(Raft 多数派) |
| 写入吞吐 | 10 万级 QPS | 数千 QPS |
| 数据结构 | String、Hash、List、Set、ZSet 等 | 扁平 key-value,支持前缀范围查询 |
| 事务能力 | MULTI/EXEC、Lua 脚本 | 迷你事务(mod revision 条件比较) |
| 典型用途 | 缓存、队列、限流、锁 | 服务发现、配置中心、leader 选举 |
功能层面两者侧重也不同。Redis 提供丰富的数据结构,Lua 脚本可以组合出复杂的原子操作;Etcd 的数据模型非常朴素,只有二进制 key-value,但它支持按前缀的范围查询、按 revision 的事务比较以及 lease 租约管理。Etcd 的事务示例如下:
etcdctl put --lease=1234abc /services/user/instance1 "10.0.0.5:8080"
etcdctl txn <<'EOF'
val("/services/leader", "MOD", "5")
put /services/leader "node-a"
get /services/leader
EOF
上面第一条命令把服务实例地址和租约绑定,进程崩溃后租约超时,key 自动删除,服务自动下线;第二条命令展示了事务用法,只有当 key 的修改版本满足条件时才会执行写入,天然适合实现抢锁和抢占式注册。
四、部署运维与选型建议
部署复杂度上,两者都不算难,但运维要点不同。Redis 主从或哨兵模式部署简单,单机即可运行,备份用 RDB 或 AOF;Redis Cluster 分片部署相对复杂,涉及槽位迁移。Etcd 建议至少三节点集群,运维时要注意磁盘性能(fsync 延迟直接影响写性能和集群稳定性)、定期 compact 和 defrag 控制数据库大小(默认配额 2GB)、以及妥善备份 snapshot。Etcd 集群对网络延迟敏感,跨机房部署时需要谨慎评估。
选型建议可以从几个判断题出发。第一问:业务能否容忍极小概率的数据丢失或锁互斥失效?如果答案是能,比如纯缓存、排行榜、计数,选 Redis,快就是正义。第二问:是否依赖“变更事件绝不丢失”的监听能力?配置中心、服务发现强烈建议 Etcd。第三问:写入频率有多高?高频写入直接排除 Etcd。第四问:团队是否已有 Kubernetes 体系?如果已经用 K8s,Etcd 就在那里,复用它做配置和服务注册的边际成本很低。
一个务实的结论是:两者并不互斥,多数中大型系统里它们各司其职。Etcd 负责元数据、服务发现、配置分发这类低频但强一致的场景;Redis 承担缓存、会话、限流、高频计数这类性能敏感的场景。真正要避免的是把强一致的协调需求压到 Redis 上(比如用主从 Redis 实现金融级互斥锁),或者反过来把高并发计数写入 Etcd,把集群拖垮。理解了 CAP 取舍的底层逻辑,这个选择题其实并不难。