当前位置: 首页 > news >正文

悲观锁,乐观锁和redis分布式锁

​​悲观锁(Pessimistic Lock)​​

​​为什么叫 "悲观"?​​

因为它 ​​"悲观" 地认为并发操作一定会发生冲突​​,所以在操作数据之前,​​先加锁​​,确保其他事务无法修改这条数据,直到当前事务完成。

​​实现方式​​(数据库层面):

  • SELECT ... FOR UPDATE(MySQL)

  • SELECT ... WITH (UPDLOCK)(SQL Server)

  • 其他数据库的排他锁机制

​​特点​​:

  • ​​先锁再操作​​,防止并发修改

  • ​​适用于高并发写操作​​(如抢购、库存扣减)

  • ​​可能降低并发性能​​(锁会阻塞其他事务)

​​示例​​(ThinkPHP):

 
Db::startTrans(); // 开启事务 $info = $this->where(['id' => $id])->lock(true)->find(); // 加悲观锁 // ... 处理数据 ... Db::commit(); // 提交事务(释放锁)
 
 

​​乐观锁(Optimistic Lock)​​

​​为什么叫 "乐观"?​​

因为它 ​​"乐观" 地认为并发操作不会冲突​​,所以 ​​不加锁​​,而是在更新时检查数据是否被修改过(通常用版本号或时间戳)。

​​实现方式​​:

  • ​​版本号机制​​(version字段)

  • ​​时间戳机制​​(update_time字段)

  • ​​CAS(Compare-And-Swap)​​(如 Redis)

​​特点​​:

  • ​​不加锁,先操作再检查冲突​​

  • ​​适用于读多写少的场景​​

  • ​​冲突时需要重试或回滚​​

​​示例​​(ThinkPHP):

 
// 假设数据表有 `version` 字段 $data = $this->where(['id' => $id])->find(); $newData = ['name' => 'new_name', 'version' => $data['version'] + 1]; $result = $this->where(['id' => $id, 'version' => $data['version']])->save($newData); if (!$result) { // 更新失败,说明数据被其他人修改过,可以重试或报错 }
 
 

​​对比总结​​

特性

悲观锁(Pessimistic Lock)

乐观锁(Optimistic Lock)

​​加锁方式​​

先加锁再操作(FOR UPDATE

不加锁,更新时检查冲突

​​适用场景​​

高并发写操作(如抢购)

读多写少(如文章编辑)

​​性能影响​​

可能降低并发性能(锁阻塞)

无锁,冲突时才处理

​​实现方式​​

数据库锁机制

版本号、时间戳、CAS

​​冲突处理​​

其他事务会被阻塞

需要重试或回滚

​​如何选择?​​

  • ​​悲观锁​​:数据竞争激烈,必须保证数据一致性(如支付、库存扣减)。

  • ​​乐观锁​​:冲突概率低,希望提高并发性能(如文章编辑、评论更新)。

 

redis分布式锁:

这段代码的作用是 ​​使用 Redis 的 SETNX(SET if Not eXists)实现分布式锁​​,防止并发重复提交(如重复提现)。具体解析如下:


​​代码解析​​

 
if (!setLockNx($withdrawLockKey, 30)) { //提现锁,处理完手动解锁 json_exit_Base64(401, lang("处理中,请勿重复点击")); }
 
 
  1. ​​setLockNx($withdrawLockKey, 30)​​

    • 调用一个自定义函数(可能是封装了 Redis 的 SETNX命令)。

    • ​​$withdrawLockKey​​:锁的唯一标识(如用户ID + 业务类型,例如 withdraw:user123)。

    • ​​30​​:锁的自动过期时间(单位:秒),防止锁未释放导致死锁。

  2. ​​!setLockNx(...)​​

    • 如果获取锁失败(返回 false),说明锁已存在(即当前有其他请求正在处理相同业务)。

    • 直接返回错误提示,阻止重复操作。

  3. ​​json_exit_Base64(401, ...)​​

    • 返回 HTTP 401 状态码和 Base64 编码的 JSON 错误消息(可能是项目约定的通信格式)。

    • 提示用户:"处理中,请勿重复点击"。

  4. ​​注释说明​​

    • //提现锁,处理完手动解锁:提示开发者需要在业务逻辑完成后 ​​手动释放锁​​(如调用 delLock($withdrawLockKey)),否则锁会在 30 秒后自动过期。


​​实现原理(Redis 分布式锁)​​

  1. ​​加锁​​

    通过 Redis 的 SETNX命令实现原子性锁:

     
    SETNX $withdrawLockKey 1 # 如果 key 不存在则设置成功(返回1),否则失败(返回0) EXPIRE $withdrawLockKey 30 # 设置过期时间,避免程序崩溃导致死锁
     
     

    (注:高版本 Redis 支持 SET $withdrawLockKey 1 NX EX 30一步完成)

  2. ​​解锁​​

    业务处理完成后需手动删除 key:

     
    DEL $withdrawLockKey
     
     

​​适用场景​​

  • ​​提现/支付防重​​:防止用户多次点击导致重复扣款。

  • ​​秒杀/库存扣减​​:避免超卖问题。

  • ​​任何需要分布式环境下的互斥操作​​(如定时任务防并发执行)。


​​注意事项​​

  1. ​​锁的粒度​​

    • 锁的 key 需要足够具体(例如包含用户ID),避免不同用户互相阻塞。

  2. ​​锁的过期时间​​

    • 过期时间(如30秒)需大于业务处理时间,但不宜过长(否则阻塞其他请求)。

  3. ​​原子性问题​​

    • 确保 SETNX + EXPIRE是原子操作(如用 Redis 2.6.12+ 的 SET命令带 NX和 EX参数)。

  4. ​​手动解锁​​

    • 如果业务逻辑异常退出,需在 finally块中释放锁,或依赖自动过期。

http://www.hskmm.com/?act=detail&tid=17503

相关文章:

  • sql练习笔记
  • 算法练习
  • 数据库基础
  • 【System Beats!】第三章 程序的机器级表示
  • 苍穹外卖-day06(HttpClient) - a
  • Python 虚拟环境管理-学习笔记分享
  • 元人文AI的领域化部署:从哲学构想到实践应用的完整路径
  • 做题记录3
  • oucaiclub_cheapter1
  • navicat
  • 20250925 之所思 - 人生如梦
  • 在CodeBolcks下wxSmith的C++编程教程——在屏幕上绘图和保存绘图
  • 苍穹外卖-day07(缓存菜品,缓存套餐,添加购物车,查看购物车,清空购物车) - a
  • 一次CPU飙升问题排查定位
  • ros2 control 2
  • 基于洞察的智能编程法——从直觉到代码的原型炼成术
  • lc1036-逃离大迷宫
  • 9.25学习笔记
  • 新学期每日总结(第4天)
  • VSCode 升级 C++支持版本
  • 第四天
  • 25.9.25
  • 在electron-vite使用ShadCN
  • 每日博客(补)
  • 如何使用极限网关实现 Elasticsearch 集群迁移至 Easysearch
  • 文档抽取技术:实现金融保险业务流程自动化
  • 算法作业
  • C#学习3
  • 9-23
  • 9-26