1,基于数据库表实现
机制:在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。
1,首先创建一个 分布式锁表
DROP TABLE IF EXISTS `distributed_lock`; CREATE TABLE `distributed_lock` ( `distributed_lock_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', `method_name` VARCHAR(64) NOT NULL COMMENT '加锁的方法名', `distributed_lock_desc` VARCHAR(255) NOT NULL COMMENT '备注', `inserted_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`distributed_lock_id`), UNIQUE KEY `unique_key_method_name` (`method_name`) USING BTREE ) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='分布式锁';
2,将要执行的方法名作为参数,保存到分布式锁表中
INSERT INTO distributed_lock (distributed_lock_id,method_name, distributed_lock_desc) VALUES (816374617763712,'要加锁的方法名称', '描述信息');
如果可以插入成功,代表获得了锁,可以执行操作;逻辑执行完毕后,删除数据,释放锁
DELETE FROM distributed_lock WHERE method_name ='要释放锁的方法名称';
缺点:
1,需要考虑数据的性能以及保证数据库高可用
2,不具备可重入的特性
3,如果宕机,会造成死锁
4,获取不到锁,直接返回失败,需要代码做循环获取锁
2,基于Redis实现(redis,redission,redLock,lua脚本,看门狗机制)
机制:jedis.set(String key, String value, String nx, String px, int time);
参数说明:
key:锁,保证唯一
value:竞争者的ID,例如线程名,释放锁的时候需要判断释放者是否未该锁的持有者
nx:SET IF NOT EXIST,即当key不存在时进行set操作;当key已存在则不做任何操作。
px:表示要给当前的锁设置一个过期时间,防止死锁
time:key的过期时间
执行set()方法只有两种结果:
1.当key不存在(没有锁),进行加锁操作,并对锁设置一个过期时间,同时value为加锁的线程名。
2.已经有锁存在,不做任何操作。
解锁:
- 判断当前解锁竞争者的线程名是否为锁的持有者,如果不是直接返回失败,如果是则进入第2步。
- 删除key,如果删除成功,返回解锁成功,否则解锁失败。
缺点:
1,死锁问题
2,不可重入
3,因为设置了过期时间,如果业务执行时间超过过期时间,会自动释放锁
4,需要保证redis的高可用
3,基于Zookeeper实现
机制:利用Zookeeper创建临时有序节点来实现分布式锁
- 当客户端来请求时,在锁空间下面创建一个临时有序节点。
- 如果当前节点的排序是这个空间下面最小的,则代表加锁成功,否则加锁失败,加锁失败后设置Watcher,等待前面节点的通知。
- 当前节点监听其前面一个节点,如果前面一个节点删除了就通知当前节点。
- 当解锁时当前节点通知其后继节点,并删除当前节点。
缺点:
需要保证zk的高可用