前言
对于锁大家肯定不会陌生,比如 synchronized 关键字 和 ReentrantLock 可重入锁,一般我们用其在多线程环境中控制对资源的并发访问。但是随着业务的发展,分布式的概念逐渐出现在我们系统中,我们在开发的过程中经常需要进行多个系统之间的交互,于是上面的加锁方法就会失去作用。于是在分布式锁就自然而然的诞生了,接下来我们来聊一聊分布式锁实现的几种方式。
分布式锁的使用场景
-
效率性:使用分布式锁可以避免不同节点重复相同的工作。
-
正确性:分布式锁可以避免破坏正确性的发生,如果两个节点在同一条数据上面操作,比如多个节点机器对同一个订单操作不同的流程有可能会导致该笔订单最后状态出现错误,造成损失。
分布式锁的几种特性
-
互斥性:和我们本地锁一样互斥性是最基本,但是分布式锁需要保证在不同节点的不同线程的互斥。
-
可重入性:同一个节点上的同一个线程如果获取了锁之后那么也可以再次获取这个锁。
-
锁超时:和本地锁一样支持锁超时,防止死锁。
-
高效,高可用:加锁和解锁需要高效,同时也需要保证高可用防止分布式锁失效,可以增加降级。
-
支持阻塞和非阻塞:和ReentrantLock一样支持lock和trylock以及tryLock(long timeOut)。
-
支持公平锁和非公平锁(可选):公平锁的意思是按照请求加锁的顺序获得锁,非公平锁就相反是无序的。
分布式锁的几种实现方式
分布式锁有以下几个方式:
-
MySql
-
Zk
-
Redis
-
一些自研的分布式锁(Chubby)
一、基于 Mysql 实现分布式锁
1、首先,我们需要创建一个锁表:
CREATE TABLE `resource_lock` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`resource_name` varchar(128) NOT NULL DEFAULT '' COMMENT '资源名称',
'node_info' varchar(128) DEFAULT '0' COMMENT '节点信息',
'count' int(11) NOT NULL DEFAULT '0' COMMENT '锁的次数,统计可重入锁',
'desc' varchar(128) DEFAULT NULL COMMENT '额外的描述信息',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY ('id'),
UNIQUE KEY 'un_resource_name' ('resource_name')
) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4;
2、lock
先进行查询,如果有值,那么需要比较 node_info 是否一致,这里的 node_info 可以用机器 IP 和线程名字来表示,如果一致那么就加可重入锁 count 的值,如果不一致那么就返回 false 。如果没有值那么直接插入一条数据。伪代码如下:
// 添加事务,原子性
@Transaction
public void lock() {
if (select * from resource_lock where resource_name = 'xxx' for update;) {