1、什么是分布式锁
-
在不同程序对资源发生竞争的情况下,保证资源的安全
-
一个商品的剩余数量为 1,当多个线程下单时,可能都读到还剩 1 个,显然如果有一个订单下单成功,剩余数量就应该是 0 了。所以为了数据的准确性在处理下单操作时要给数据加锁。
-
多个线程读取到相同的数据时,一个线程修改数据时通过 store 触发一次性内存【MSI】,这个 MSI 修改数据的同时触发嗅探机制,数据修改成功后会让修改前获取得到的数据失效,其他线程必须要重新从主内存(redis) load 新的数据才能生效。
-
嗅探机制通过封装就变成一个分布式锁
-
lock 代码块只适用于同一个进程中多个线程对资源的竞争,不是分布式锁。
2、分布式锁(在多个进程中操作共享资源出现竞争,需要保证数据的安全)
-
阻塞锁:没有拿到锁,等待锁
-
非阻塞锁:没有拿到就返回异常,不等待。
3、常见的分布式锁实现方式
-
悲观锁,只有更新完,锁才会被释放
-
基于数据库的悲观锁(并发量 1000)
select xxx from for update
-
基于数据库的乐观锁(并发量 2000)
数据库中有一个字段记录数据的版本(时间),每次更新数据都会更新版本
-
redis 分布式锁,独占模式,并发 80W以内
客户端1 创建(使用完释放)
客户端2 创建 redis锁(有值的话就阻塞等待锁释放) 使用共享资源
客户端3 创建
- 当有一个客户端创建锁后程序在释放锁前程序出错,redis锁就释放不掉了,出现了死锁问题。 - 如果因为可能出现的 redis分布式锁 死锁问题而去设置超时删除锁的话,可能会出现误解锁的问题(key 相同可能出现一个进程删除了另一个进程的分布式锁)。 - 为了解决 redis分布式锁 误删问题,要在锁中添加唯一标识,只有该进程自己能删除自己的锁。
-
redis分布式重入机制(外部申请了锁,内部就不需要重新申请了)
-
解决 redis 原子性问题 lua脚本(里面可以包含多个 redis 操作,要么全成功,要么全失败)