从强到弱排序:
强一致性 → 顺序一致性 → 因果一致性 → 最终一致性 → 弱一致性
任何时刻,所有节点看到的数据完全一致。
- 读操作总能读到最新写入的值
- 就像只有一个副本
1. 同步复制
func WriteWithStrongConsistency(key, value string, replicas []Node) error {
var wg sync.WaitGroup
errChan := make(chan error, len(replicas))
// 并发写入所有副本
for _, node := range replicas {
wg.Add(1)
go func(n Node) {
defer wg.Done()
if err := n.Write(key, value); err != nil {
errChan <- err
}
}(node)
}
wg.Wait()
close(errChan)
// 只要有一个失败,回滚所有
if len(errChan) > 0 {
rollback(key, replicas)
return <-errChan
}
return nil
}2. Paxos / Raft
- 通过共识算法保证强一致性
- 牺牲部分性能
典型系统:etcd, ZooKeeper, Spanner
所有节点看到的操作顺序一致,但不保证实时性。
进程1: Write(X, 1) → Write(X, 2)
进程2: Read(X) → Read(X)
顺序一致: Read可能返回 (1,2) 或 (2,2),但不能是 (2,1)
有因果关系的操作必须按顺序可见。
用户A: 发帖子 → 删除帖子
用户B: 看到帖子 → 帖子消失 ✅
错误: 用户B先看到帖子消失,后看到帖子出现 ❌
type VectorClock map[string]int
type CausalStore struct {
data map[string]string
clock VectorClock
mu sync.RWMutex
}
func (cs *CausalStore) Write(key, value string, clientID string) {
cs.mu.Lock()
defer cs.mu.Unlock()
cs.clock[clientID]++
cs.data[key] = value
}
func (cs *CausalStore) Read(key string) (string, VectorClock) {
cs.mu.RLock()
defer cs.mu.RUnlock()
return cs.data[key], cs.clock
}停止写入后,经过一段时间,所有副本最终达到一致。
DNS系统
# 更新DNS记录
# 全球DNS服务器最终(几小时)会同步社交网络点赞数
func IncrementLikes(postID string) {
// 本地计数器递增
localCounter.Incr(postID)
// 异步同步到数据库
go func() {
db.IncrBy(postID, 1)
}()
}
// 定期合并
func MergeLikes() {
ticker := time.NewTicker(10 * time.Second)
for range ticker.C {
for postID, count := range localCounter.GetAll() {
db.IncrBy(postID, count)
localCounter.Reset(postID)
}
}
}同一会话内保证读到自己的写入。
type SessionStore struct {
sessions map[string]*Session
mu sync.RWMutex
}
type Session struct {
lastWriteTime time.Time
lastWriteNode string
}
func (ss *SessionStore) Write(sessionID, key, value string, node string) {
// 写入数据
node.Write(key, value)
// 记录会话信息
ss.mu.Lock()
ss.sessions[sessionID] = &Session{
lastWriteTime: time.Now(),
lastWriteNode: node,
}
ss.mu.Unlock()
}
func (ss *SessionStore) Read(sessionID, key string) string {
ss.mu.RLock()
session := ss.sessions[sessionID]
ss.mu.RUnlock()
// 如果刚写入,从同一节点读取
if session != nil && time.Since(session.lastWriteTime) < time.Second {
return session.lastWriteNode.Read(key)
}
// 否则随机选择节点
return randomNode().Read(key)
}应用:Web应用中的用户会话
- N:副本数
- W:写入确认数
- R:读取节点数
一致性保证:W + R > N
N = 3, W = 2, R = 2
副本: [A, B, C]
写入:
Client → A ✅
Client → B ✅ (W=2, 成功)
Client → C (异步)
读取:
Client → A (value=1, version=5)
Client → B (value=1, version=5)
返回: value=1 (两个节点达成一致)
type QuorumConfig struct {
N int // 副本数
W int // 写确认数
R int // 读节点数
}
// 强一致性
strongConsistency := QuorumConfig{N: 3, W: 3, R: 1}
// 最终一致性
eventualConsistency := QuorumConfig{N: 3, W: 1, R: 1}
// 平衡
balanced := QuorumConfig{N: 3, W: 2, R: 2}| 模型 | 延迟 | 可用性 | 实现复杂度 | 应用场景 |
|---|---|---|---|---|
| 强一致性 | 高 | 低 | 高 | 金融、订单 |
| 顺序一致性 | 中 | 中 | 中 | 分布式锁 |
| 因果一致性 | 中 | 中 | 中 | 社交网络 |
| 最终一致性 | 低 | 高 | 低 | DNS、点赞 |
| 会话一致性 | 低 | 高 | 低 | Web应用 |
- 下单:强一致性(不能重复扣款)
- 订单列表:会话一致性(看到自己的订单)
- 商品浏览量:最终一致性(允许误差)
type OrderService struct {
strongDB *StrongConsistentDB // 订单、支付
cacheDB *CacheDB // 商品信息
searchDB *SearchDB // 搜索引擎
}
// 创建订单(强一致性)
func (os *OrderService) CreateOrder(order Order) error {
// 写入主库,等待多副本确认
return os.strongDB.WriteWithQuorum(order, W=3)
}
// 查询订单(会话一致性)
func (os *OrderService) GetOrder(userID, orderID string) (Order, error) {
// 优先从主库读取
return os.strongDB.Read(orderID)
}
// 更新浏览量(最终一致性)
func (os *OrderService) IncrementViews(productID string) {
// 异步更新,不阻塞用户
go os.cacheDB.Incr(productID)
}关键要点:
- ✅ 一致性有多种级别,根据场景选择
- ✅ 强一致性牺牲性能,最终一致性牺牲实时性
- ✅ Quorum机制提供灵活的一致性配置
- ✅ 同一系统可对不同数据使用不同一致性模型
💡 思考题:
- 为什么W + R > N能保证一致性?
- 因果一致性如何实现?
- 电商系统哪些数据需要强一致性?