了解分布式锁
分布式锁
为什么需要分布式锁
应用中需要避免多个线程在同一时间对同一个共享变量做修改
在单机部署的项目中,为了避免上述现象,需要对变量或代码块做同步
在分布式部署的项目中,为了避免上述现象,用同步是解决不了的(因为相同的项目部署在了多台服务器,同步只能解决单台服务器的问题),
所以就需要分布式锁,保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行
分布式锁有几种实现方式
主流的实现方式有三种
1、利用数据库实现
2、利用缓存(redis)实现
3、利用zookeeper实现
简述各种方式及优缺点
1、利用数据库实现
优点:简单、易理解
缺点:高并发时,性能差,增加了数据库开销
方法一:
- 新建一张表,向该表中添加相同主键的一条记录, 哪个线程添加成功,即获得了分布式锁,
- 然后执行相关逻辑,逻辑执行完之后,删除数据库表中的该数据,删除即代表解锁
方法二、
- 使用for update,数据表会在查询的时候添加排它锁,
- 多个线程同时来执行时,只有一个会成功加锁,其余的线程都会阻塞,
- 加锁成功的线程即获得分布式锁的线程,该线程执行相关逻辑,
- 逻辑执行完之后,使用connection.commit解锁,此时阻塞的其他线程中会有一个加锁成功….
2、使用redis缓存实现
优点:效率高
缺点:设置过期时间过长过短都不合适,需要根据实际情况权衡
大概思路:
- 向redis中添加key,并设置过期时间
- 如果key已存在,则添加失败
- 如果key不存在,则添加成功
- 添加成功,即获得了分布式锁
- 获得锁的线程执行业务逻辑,执行完之后,删除redis中的key(即解锁)
3、通过zookeeper实现
优点:有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题。实现起来较为简单
缺点:因为每次在创建锁和释放锁的过程中,都要动态创建、销毁临时节点来实现锁功能。
ZK中创建和删除节点只能通过Leader服务器来执行,然后将数据同步到所有的 Follower 机器上。
大概思路:
- 每一个锁都是zookeeper上的一个znode(普通节点)
- 当多个线程来获取锁时,都会在该znode上创建有序临时子节点,每个有序临时子节点都有自己的序号
- 只有序号最小的可以拥有锁,然后执行相关逻辑,执行完成之后删除子节点(即解锁),触发监听
- 序号如果不是最小的,则没有获得锁,设置监听事件,监听序号比本身小的前一个节点,
- 等待事件触发再次比较是否是最小的