什么是分布式锁:
有分布式锁就有单机锁,按照我的理解单机锁就是服务器在一个单一节点上,当处理高并发的问题时我们可以利用可重入锁(ReentrantLock类和synchronized),但是分布式的同步问题非常突出,不同的服务是分布在不同的节点上,也就是不同的服务器,节点和节点之前的JVM无法解决同步问题
分布式锁的三种实现
基于数据库的分布式锁:一般有悲观锁和乐观锁的两种实现机制
乐观锁的实现是这样的(基于我比较懒就直接写文字了):
乐观锁的特点是认为数据不会遭到修改,也就是在操作的最后一步才会进行同步的处理。乐观锁就是在数据库增加version的字段,操作的线程每次操作前读取version,更新数据之前再次查看version,如果一样就更新数据,否则更新失败
想象一下不同的两个人同时对一个账户进行取款操作,同时会读取到数据中的余额,version假设是1,然后其中一个操作进行了取款,再次查看version还是1,然后把更新后的version+1和余额又写入数据库,这是version为2。那么另一个操作虽然一开始读到version是1,但是当更新完数据后查看version变成2,因为这时前一操作已经更改完毕,那么这次取款就操作失败,也就是说需要重新读取账户余额。
乐观锁操作条件:具备递增的version字段、每次更新前查看version是否正确,再进行更新
悲观锁的实现其实是用到了for update语句,一般用在JDBC链接时,当我们执行的查询语句最后包含for update的时候执行此语句的线程就获得了排它锁,我们认为这个时候此线程获得了悲观锁
当我们在语句后用了for update时,多个线程执行这条语句的情况下,只有一个线程会获取到锁,其它线程被阻塞挂起,获取锁的线程执行传递的callback 的业务逻辑,执行完毕后 执行commit 提交事务,这意味着当前线程释放了获取的锁,这时候被阻塞的线程会竞争获取该锁。
基于Redis的分布式锁:setx命令
简单说一个线程对Redis进行加锁,原理是设置一个不存在的key和value,设置成功后返回1,这时其他线程无法再次设置这个不存在的key因为此key已存在就会返回0
以下命令是Redis从2.6.12版本开始支持的:
set key value NX PX n
其中set key value NX
相当于以前的setnx key value
,px
是设置过期时间
为什么这个命令可以帮我们实现锁机制呢?
因为这个命令是只有在某个key不存在的时候,才会执行成功。那么当多个进程同时并发的去设置同一个key的时候,就永远只会有一个进程成功。
当某个进程设置成功之后,就可以去执行业务逻辑了,等业务逻辑执行完毕之后,再去进行解锁。
解锁很简单,只需要删除这个key就可以了,不过删除之前需要判断,这个key对应的value是当初自己设置的那个。
基于zookeeper的分布式锁
zookeeper提供了根节点和子节点这样一种实现方式,当客户机连接到zookeeper时,在lock/节点下创建子节点也就是临时节点,并同时设置监听器,当客户机监听到自己的节点属于最小的节点时就可以认为是获得了锁,这时候可以执行业务逻辑,执行完毕后zookeeper删除这个临时节点
参考博文:
https://blog.51cto.com/13732225/2165988