- C (Consistency) - 一致性:所有节点同一时刻看到相同数据
- A (Availability) - 可用性:每个请求都能得到响应(成功或失败)
- P (Partition Tolerance) - 分区容错性:网络分区时系统仍能工作
分布式系统在网络分区(P)存在时,只能在一致性(C)和可用性(A)之间权衡取舍!
⚠️ 注意:常见的"三选二"表述并不准确。在分布式环境中,网络分区是客观存在的,因此P是必选项。实际上是在P的前提下,在C和A之间进行权衡。
C
/ \
/ \
/ \
/ CP AP \
A ------- P
(CA不现实)
网络分区不可避免 → 必须选择 P
因此实际选择:
- CP系统:牺牲可用性,保证一致性
- AP系统:牺牲一致性,保证可用性
- 网络分区时,少数节点不可用
- 保证数据强一致性
1. Zookeeper / etcd
// etcd事务:保证强一致性
_, err := cli.Txn(ctx).
If(clientv3.Compare(clientv3.Value(key), "=", oldValue)).
Then(clientv3.OpPut(key, newValue)).
Commit()2. HBase
- Region Server宕机时,该Region不可用
- 保证读取到最新数据
3. MongoDB(默认配置)
// 写入时等待主节点确认
db.collection.insertOne({...}, {writeConcern: {w: "majority"}})- 网络分区时,所有节点仍可用
- 允许数据短暂不一致
- 最终一致性
1. Cassandra
-- 调整一致性级别
SELECT * FROM users WHERE id = 1
USING CONSISTENCY ONE; -- 只需一个节点响应2. DynamoDB
- 多副本架构
- Quorum机制:W + R > N
3. Eureka(服务注册中心)
- AP优先
- 允许短暂的服务信息不一致
- BA (Basically Available) - 基本可用
- S (Soft state) - 软状态
- E (Eventually consistent) - 最终一致性
放弃强一致性,追求最终一致性
func ReadWithRepair(key string, replicas []Node) (value string, err error) {
responses := make(map[string]int)
// 并发读取所有副本
for _, node := range replicas {
val, _ := node.Get(key)
responses[val]++
}
// 找到最新值(版本号最大)
latestValue := findLatest(responses)
// 修复不一致的节点
for _, node := range replicas {
val, _ := node.Get(key)
if val != latestValue {
node.Set(key, latestValue) // 修复
}
}
return latestValue, nil
}Vector Clock(向量时钟)
type VectorClock map[string]int
func (vc VectorClock) Increment(nodeID string) {
vc[nodeID]++
}
func (vc VectorClock) Compare(other VectorClock) string {
less, greater := false, false
for k, v := range vc {
if otherV, ok := other[k]; ok {
if v < otherV {
less = true
} else if v > otherV {
greater = true
}
}
}
if less && !greater {
return "LESS" // vc < other
} else if greater && !less {
return "GREATER" // vc > other
} else if !less && !greater {
return "EQUAL"
} else {
return "CONCURRENT" // 冲突
}
}// 通过异步消息保证最终一致性
func UpdateUser(userID string, data UserData) error {
// 1. 更新主库
if err := db.Update(userID, data); err != nil {
return err
}
// 2. 发送同步消息
msg := SyncMessage{
UserID: userID,
Data: data,
}
return messageQueue.Publish("user.sync", msg)
}
// 消费者同步到其他系统
func Consumer() {
messageQueue.Subscribe("user.sync", func(msg SyncMessage) {
cache.Update(msg.UserID, msg.Data)
searchEngine.Update(msg.UserID, msg.Data)
// ... 其他系统
})
}需求:
- 高可用(不能因为网络问题导致无法下单)
- 最终一致(允许短暂超卖,后续补偿)
方案:AP + 最终一致性
// 1. 预扣库存(本地)
func PreDeductStock(productID string, quantity int) bool {
localStock := localCache.Get(productID)
if localStock >= quantity {
localCache.Decr(productID, quantity)
return true
}
return false
}
// 2. 异步同步到数据库
func AsyncSyncStock(productID string, quantity int) {
msg := StockMessage{
ProductID: productID,
Quantity: quantity,
Timestamp: time.Now(),
}
mq.Publish("stock.sync", msg)
}
// 3. 定时对账
func ReconcileStock() {
ticker := time.NewTicker(10 * time.Minute)
for range ticker.C {
// 对比本地缓存和数据库
// 发现超卖则退款补偿
}
}| 场景 | 选择 | 理由 |
|---|---|---|
| 金融交易 | CP | 强一致性要求 |
| 配置中心 | CP | 配置错误影响大 |
| 社交Feed | AP | 可用性优先 |
| 电商库存 | AP | 高可用,允许补偿 |
| 分布式锁 | CP | 互斥性要求 |
| 服务注册 | AP | 可用性优先 |
关键要点:
- ✅ CAP定理:分布式系统的铁律
- ✅ 网络分区不可避免,必须选择CP或AP
- ✅ BASE理论:放弃强一致,追求最终一致
- ✅ 根据业务场景选择合适的一致性模型
💡 思考题:
- 为什么CA系统在分布式环境中不现实?
- 如何设计一个最终一致性系统?
- 电商秒杀应该选择CP还是AP?