1. 当缓存遇到高并发:为什么我们需要锁?
在电商秒杀场景中,假设某商品库存缓存在Redis中。当1000个请求同时扣减库存时,如果不加控制,可能发生这样的情形:
# Python示例(技术栈:Redis + Python)
import redis
r = redis.Redis()
def reduce_stock():
stock = int(r.get('product_001'))
if stock > 0:
r.decr('product_001') # 非原子操作导致超卖
这个经典案例暴露了三个致命问题:
- 读取库存与实际扣减存在时间差
- 非原子操作导致数据不一致
- 并发请求未正确排队
2. 读写锁的四种经典模式
2.1 基础读写锁模式
# 互斥写锁实现(Python)
def safe_reduce_stock():
with r.lock('product_lock'): # 获取互斥锁
stock = int(r.get('product_001'))
if stock > 0:
r.decr('product_001')
这种简单实现存在明显缺陷:
- 单点性能瓶颈
- 死锁风险
- 锁超时难控制
2.2 乐观锁实践
# 使用WATCH实现CAS(技术栈:Redis事务)
def optimistic_reduce_stock():
with r.pipeline() as pipe:
while True:
try:
pipe.watch('product_001')
stock = int(pipe.get('product_001'))
if stock <= 0:
break
pipe.multi()
pipe.decr('product_001')
pipe.execute()
break
except redis.WatchError:
continue
特点分析:
- 适合读多写少场景
- 需要重试机制保障
- 无法完全避免冲突
2.3 分布式读写锁
# Redisson实现(技术栈:Java + Redisson)
RLock lock = redisson.getLock("productLock");
try {
lock.lock();
// 业务操作
} finally {
lock.unlock();
}
进阶特性:
- 自动续期机制
- 可重入设计
- 锁等待队列
2.4 分段锁优化
# 库存分桶示例(Python)
def sharded_lock(stock_key):
slot = hash(stock_key) % 16
lock_key = f"lock_{slot}"
with r.lock(lock_key):
# 操作对应分片
优势体现:
- 将全局锁拆分为多个槽位
- 并发度提升N倍(N为分片数)
- 需要配合数据分片使用
3. 锁冲突的四种典型场景
3.1 热点数据风暴
某社交平台的热门帖子缓存,每秒数万次读取请求。采用读写分离策略:
# 读写分离示例
def get_hot_post():
# 读锁(共享)
with r.read_lock('post_lock'):
return r.get('hot_post_123')
def update_post():
# 写锁(排他)
with r.write_lock('post_lock'):
# 更新操作
3.2 缓存雪崩时的锁风暴
当大量缓存同时失效时:
# 缓存重建锁(Java伪代码)
public Object getData(String key) {
Object value = redis.get(key);
if (value == null) {
RLock lock = redisson.getLock(key + "_lock");
if (lock.tryLock()) {
try {
// 二次检查
value = db.query(key);
redis.setex(key, 300, value);
} finally {
lock.unlock();
}
} else {
Thread.sleep(50);
return getData(key);
}
}
return value;
}
3.3 分布式事务中的锁应用
订单支付场景示例:
# 事务锁组合(Python)
def pay_order():
with r.lock('user_balance_lock'):
with r.lock('order_status_lock'):
# 扣减余额
# 修改订单状态
3.4 缓存与数据库双写
# 双写一致性保障(技术栈:MySQL + Redis)
def update_product(info):
with r.lock('product_lock'):
# 先更新数据库
db.update(info)
# 再删除缓存
r.delete('product_cache')
4. 性能优化五板斧
- 锁粒度控制:从全局锁到字段级锁
- 超时策略:动态调整锁持有时间
- 异步队列:非关键操作延迟执行
- 本地缓存:减少Redis访问频率
- 监控报警:实时跟踪锁等待时间
5. 踩坑经验总结
最近在物流系统中遇到的真实案例:
# 死锁示例(错误示范)
def process_order():
lock1 = r.lock('A')
lock2 = r.lock('B')
# 获取锁顺序不一致导致死锁
解决方案:
- 统一锁获取顺序
- 设置锁等待超时
- 使用全局排序算法
6. 技术选型指南
根据业务场景选择策略:
场景特征 | 推荐方案 | 适用案例 |
---|---|---|
QPS < 1000 | 简单互斥锁 | 后台管理系统 |
1000 < QPS < 5000 | 读写锁+分段 | 电商商品详情页 |
QPS > 5000 | 无锁CAS+本地缓存 | 社交平台点赞数 |
强一致性要求 | RedLock+版本控制 | 金融账户余额 |
7. 未来演进方向
- 基于AI的锁预测系统
- 动态锁粒度调整算法
- 跨数据中心的锁同步
- 量子加密锁技术探索