来源:https://gitbook.cn/books/5b24922172342470d7d17ad6/index.html
使用数据库悲观锁来实现分布式锁
- 需要设置自动提交为
false
,即使用手动提交 - 悲观锁常用例子:
select * from tableName where id = ? for update
在事务没有被提交之前,这个锁是一直都存在的。所以可以在事务提交之前做业务处理的逻辑。
但是在高并发的情况下,只有一个线程获得了锁,其它的线程读被挂起。也就是说每一个被挂起的线程都持有了一个数据库连接,导致数据库连接被占用完,会导致正常的业务不能访问数据库。
使用 Redis 实现分布式锁
Redis 提供了 setnx
函数来保证原子性。通过这个函数来设置一个唯一的值来获取锁,在释放的时候通过 get
方法来获取这个唯一的值,判断是否跟之前设置的值相等。如果相等,则表明是当前线程获取的锁,则通过 del
方法删除,否则不做处理。
由于 get
与 del
不是原子性的操作,所以会导致误删。
但是 Redis 有一个叫做 eval
的函数,支持 Lua 脚本执行,并且能够保证脚本执行的原子性,也就是在执行脚本期间,其它执行 Redis 命令的线程都会被阻塞。
if redis.call('get', KEYS[1]) == ARGV[1]
then
return redis.call('del', KEYS[1])
else
return 0
end
使用 Zookeeper 实现分布式锁
- 创建临时节点
- 遍历根节点下的子节点,判断自己节点的编号是不是最小。如果是则表示获取到了锁;如果不是,则获取比自己小一号的节点,然后监听该节点,并挂起当前线程
- 当最小编号的线程获取锁,处理完业务之后就删除自己对应的节点,删除了之后,比自己大一号的节点会获取锁,线程从阻塞变成运行。