乐观锁
开启事务前,设置对数据的监听(watch),EXEC时,如果发生数据发生过修改,作用于改数据的事务会自动取消(DISCARD),事务EXEC后,无论成败,监听会被移除
悲观锁
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。
场景:如果项目中使用了缓存且对缓存设置了超时时间。
当并发量比较大的时候,如果没有锁机制,如果有大量请求访问过期的数据,那么大量并发请求会穿透缓存直接查询数据库,造成雪崩效应。
用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上,加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间key是锁着的,这是过来1000个请求999个都在阻塞的。同样会导致用户等待超时,这是个治标不治本的方法!
应用场景一:
多个客户端可能同时操作同一组数据,并且该数据一旦被操作修改后,便不适合在原基础上继续操作,如四个业务员对一个物品进行入库操作,一名业务员操作完成之后,其他的业务员将不可以再原先基础的数量上进行入库操作
解决方案:
加锁(乐观锁)
# 对key添加监视锁,在执行exec前如果key发生了变化,终止事务执行
watch key1 [key2...]
#取消对所有key的监视
unwatch
应用基于状态控制的批量任务执行,此应用场景有弊端,如果是很多用户购买同一件商品,如果用它来监控数据库的数量的,每次购买都将用户的订单消除掉?这是不现实的,有100个用户同时购买一件物品,一个用户购买成功之后,就将99人的订单消除掉,再让99人重新下订单,以此类推。
应用场景二
超卖:
如何避免最后一件商品不被多人同时购买?避免用户在没有库存的时候不能下单
业务分析:
- 使用watch监控一个key有没有改变已经不能解决问题,此处要监控的时具体的数据
- 虽然redis是单线程的,但是多个客户端对同一数据及逆行操作时,如果避免不被同时修改
解决方案:
分布式锁(悲观锁)
#使用setnx设置一个公共锁,这个lock-key必须相同,如果都不规范,那就乱套了,vlue随便设置
setnx lock-key valule
大量用户要购买一件商品,首先要做的是看看有没有人用(是否上锁),如果没有拿到执行权,进行购物(数据库数量减1操作),如果有锁,那就排队等待
利用setnx命令的返回值特征,有值则返回设置失败,无值则返回设置成功
- 对于返回设置成功,拥有控制权,进行下一步的具体业务操作
- 对于返回设置失败的,不具有控制权,排队或等待
操作完毕通过del操作释放锁
应用于基于分布式锁对应的场景控制
应用场景三
问题描述:
符合上面以上两种情形,用户获得锁之后,宕机了,如何解决?
业务分析:
- 由于锁操作由用户控制加锁解锁,必定会存在加锁未解锁的风险
- 需要解锁操作不能仅依赖用户控制,系统级别也要给出保底方案:定时解锁
解决方案:
#使用expire为key添加事件限定,到时不释放,放弃锁
expire lock-key second
pexpire lock-key milliseconds
由于操作通常都是微妙或毫秒级,因此改锁定事件不宜设置过大,具体事件需要业务测试后确认
- 持有锁的操作最长执行事件127ms,最短执行事件7ms
- 锁事件设定推荐:最大耗时120%+平均网络延迟110%