分布式锁的粒度问题

分布式锁的粒度(Granularity)是分布式系统中一个非常重要的概念,它指的是锁所保护的资源或数据范围的大小

简单来说,就像现实生活中的锁:

  • 粗粒度锁:像锁住整个大楼的大门
  • 细粒度锁:像锁住大楼里每个办公室的门

粒度级别的详细说明

1. 粗粒度锁(Coarse-Grained Lock)

特点:一把锁保护大量资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 例子:整个系统级别锁
public class OrderSystem {
private DistributedLock globalLock = new RedisLock("ORDER_SYSTEM_LOCK");

public void processOrder() {
globalLock.lock();
try {
// 任何订单操作都需要这把锁
// 保护所有订单数据
} finally {
globalLock.unlock();
}
}
}

2. 细粒度锁(Fine-Grained Lock)

特点:针对特定资源加锁

1
2
3
4
5
6
7
8
9
10
11
12
13
// 例子:按订单ID加锁
public class OrderService {
public void updateOrder(String orderId) {
DistributedLock orderLock = new RedisLock("ORDER_LOCK:" + orderId);
orderLock.lock();
try {
// 只保护这个特定订单的操作
// 其他订单可以并发处理
} finally {
orderLock.unlock();
}
}
}

3. 中等粒度锁(Medium-Grained Lock)

介于两者之间

1
2
3
4
5
6
7
8
9
10
11
12
// 例子:按用户级别加锁
public class UserService {
public void processUserOrder(Long userId) {
DistributedLock userLock = new RedisLock("USER_LOCK:" + userId);
userLock.lock();
try {
// 保护该用户的所有相关操作
} finally {
userLock.unlock();
}
}
}

粒度选择的权衡

维度 粗粒度锁 细粒度锁
并发性能 ❌ 低(串行化严重) ✅ 高(并发度高)
系统吞吐量 ❌ 低 ✅ 高
实现复杂度 ✅ 简单 ❌ 复杂
死锁风险 ✅ 低(锁数量少) ❌ 高(锁数量多)
资源争用 ❌ 高 ✅ 低
内存开销 ✅ 低 ❌ 高

实际应用场景

适合粗粒度锁的场景

1
2
3
4
5
6
7
8
9
10
11
12
// 系统初始化、全局配置更新、全量数据迁移
public class SystemManager {
public void systemMaintenance() {
DistributedLock maintenanceLock = new RedisLock("SYSTEM_MAINTENANCE");
maintenanceLock.lock();
try {
// 执行系统维护操作,需要独占整个系统
} finally {
maintenanceLock.unlock();
}
}
}

适合细粒度锁的场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 电商库存扣减、银行账户转账
public class InventoryService {
public boolean deductStock(Long itemId, Integer quantity) {
String lockKey = "INVENTORY_LOCK:" + itemId;
DistributedLock lock = new RedisLock(lockKey);

lock.lock();
try {
// 只锁定这个具体商品的库存
// 其他商品可以正常交易
Item item = itemRepository.findById(itemId);
if (item.getStock() >= quantity) {
item.setStock(item.getStock() - quantity);
itemRepository.update(item);
return true;
}
return false;
} finally {
lock.unlock();
}
}
}

最佳实践建议

  1. 从业务需求出发

    • 如果业务允许一定程度的数据不一致,考虑更细的粒度
    • 如果需要强一致性,可能需要较粗的粒度
  2. 渐进式优化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 先使用中等粒度,根据性能监控再决定是否细化
    public class PaymentService {
    // 版本1:用户粒度
    public void processPayment(Long userId, String orderId) {
    String lockKey = "PAYMENT_USER:" + userId;
    // ...
    }

    // 版本2:如果需要更高并发,细化到订单粒度
    public void processPaymentV2(Long userId, String orderId) {
    String lockKey = "PAYMENT_ORDER:" + orderId;
    // ...
    }
    }
  3. 避免过度细化

    • 太细的粒度会增加系统复杂度
    • 可能引入死锁问题
    • 增加锁管理的开销

总结

分布式锁的粒度本质是在并发性能与系统复杂度之间的权衡

  • 粗粒度:简单可靠,但性能差
  • 细粒度:性能好,但复杂度高

选择合适粒度的关键是深入理解业务场景、数据访问模式和性能要求,通过监控和测试找到最适合的平衡点。