Redis之乐观锁和悲观锁
悲观锁&乐观锁
1、悲观锁
顾名思义,就是比较悲观的锁,总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
2、乐观锁
反之,总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
案例
- 正常的事务
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #监视money对象
OK
127.0.0.1:6379> MULTI #事务正常结束,数据期间没有发送变动。
OK
127.0.0.1:6379(TX)> DECRBY money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
127.0.0.1:6379(TX)> EXEC
1) (integer) 80
2) (integer) 20
- 测试多线程修改值,使用watch 可以当做redis的乐观锁操作
开启两个会话
第一步会话1执行:
127.0.0.1:6379> watch money #监视money,获取值进行监控,值
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY money 10
QUEUED
127.0.0.1:6379(TX)> INCRBY out 10
QUEUED
第二步会话2执行:
会话1的事务还没有执行之前,会话2对money进行修改
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
第三步,会话1进行事务执行
127.0.0.1:6379(TX)> exec #会话1事务没有执行之前,另外的线程对值进行了修改,导致事务失败
(nil)
如果修改失败,获取最新的值
127.0.0.1:6379> UNWATCH #如果事务执行失败,先解锁
OK
127.0.0.1:6379> watch money #再次监视,获取乐观锁
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY money 1
QUEUED
127.0.0.1:6379(TX)> INCRBY money 1
QUEUED
127.0.0.1:6379(TX)> exec #比对监视的值是否发生了变化,如果没有变化可以执行成功,如果变化了执行失败
1) (integer) 989
2) (integer) 990
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?