Redis的锁

85 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

Redis的锁(一般用于分布式)(取钱这种场景都要加锁(watch))

1 悲观锁

很悲观,认为什么时候都会出问题,无论做什么(操作)都会加锁(这种情况会非常影响性能,无论干什么都会加锁,用完之后再去解锁)

2 乐观锁

很乐观,认为什么时候都不会出现问题,每次去拿数据的时候都会觉得不会进行修改,所以不会上锁,不过更新数据的时候会判断在此期间有没有进行数据的修改(是否有人修改这个数据 )

使用场景:秒杀业务

乐观锁在Redis中主要有两个操作步骤

1 获取version
2 更新的时候比较version

Redis的监视测试(监视:watch Redis加锁也是使用watch这个命令)

场景1(单线程操作),我有100元,另外一个人0元,我给另外一个人转20,我剩80,他有20

127.0.0.1:6379[2]> set money 100 #我有100

OK

127.0.0.1:6379[2]> set out 0 #他有0

OK

127.0.0.1:6379[2]> watch money #监视money对象(我的钱)

OK

127.0.0.1:6379[2]> multi #开始事务 事务正常结束 数据期间没有发生变动 这个时候就正常执行成功

OK

127.0.0.1:6379[2]> decrby money 20 #我自建少20

QUEUED

127.0.0.1:6379[2]> incrby out 20 #他自增20

QUEUED

127.0.0.1:6379[2]> exec #执行事务

1) (integer) 80 #我有80

2) (integer) 20 #他有20

场景2(多线程操作),我有100元,另外一个人0元,我给另外一个人转20,这时候我工资到账我的钱发送改动watch会告诉事务我的钱发生变化,所有的事务都会操作失败

主线程转钱

127.0.0.1:6379[2]> set money 100 #我有100

OK

127.0.0.1:6379[2]> set out 0 #他有0

OK

127.0.0.1:6379[2]> watch money #监视对象

OK

127.0.0.1:6379[2]> multi #开启事务

OK

127.0.0.1:6379[2]> decrby money 20 #我的钱自减20,但是这时候副线程执行,我的钱发生变化

QUEUED

127.0.0.1:6379[2]> incrby out 20 #他的钱自增20

QUEUED

127.0.0.1:6379[2]> exec #执行事务 执行之前,副线程修改了值,这个时候就会导致事务执行失败

(nil)    #执行失败,因为在转账后我的钱发生变化,事务执行失败

副线程工资到账(改变钱)

127.0.0.1:6379[2]> get money #查看当前的钱

"100"

127.0.0.1:6379[2]> set money 1000 #资金在转账过之后发生改动

OK

场景3(解锁 unwatch),放弃当前对象重新获取最新对象进行操作

如果修改失败,获取最新的值即可

步骤如下

1 如果发现事务执行失败,就先解锁

2 获取最新的值,再次监视

3 获取到最新的值之后再去执行事务

4 对监视的值有没有发生变化,如果没有变化,那么可以执行成功,如果发生变化就会执行失败,然后重新解锁再获取最新的锁

127.0.0.1:6379[2]> unwatch #解锁(放弃对象) 如果发现事务执行失败,就先解锁

OK

127.0.0.1:6379[2]> watch money #重新获取对象 获取最新的值,再次监视

OK 

127.0.0.1:6379[2]> multi #开启事务 获取到最新的值之后再去执行事务

OK

127.0.0.1:6379[2]> decrby money 20

QUEUED

127.0.0.1:6379[2]> incrby out 20

QUEUED

127.0.0.1:6379[2]> exec #执行事务 比对监视的值有没有发生变化,如果没有变化,那么key执行成功,如果发生变化就会执行失败

1) (integer) 960

2) (integer) 30

多线程操作乐观锁结论:多线程修改值,使用watch可以当做Redis的乐观锁操作