方式一 for update悲观锁方式
-
select * from xxx for update 悲观锁方式。
- 首先要开启事务
- 在for update语句中where条件字段上创建索引,因为是通过索引来加锁的,否则会锁整个表。
- 这个在微服务中,使用不多,因为事务中很可能嵌套了第三方调用,不符合规范。
缺点:会导致数据库锁占用很长时间,同时会长时间占用数据库连接。
方式二 单独lock表乐观锁方式
锁示例
@Component
public class Example {
@Autowired
private Lock lock;
public void test() {
// 尝试获取锁
LLock llock = lock.acquire(key, Duration.ofSeconds(10));
if (Objects.isNull(llock)) {
throw new BusinessException("其他人正在处理中,请稍后重试");
}
try {
// 业务代码
} finally {
// 解除锁
lock.release(llock);
}
}
}
数据表结构
CREATE TABLE `distribute_lock` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
`lock_key` varchar(50) NOT NULL COMMENT '锁记录key',
`token` varchar(50) NOT NULL COMMENT '锁的token,防止误删其他人的锁',
`thread_id` varchar(50) NOT NULL COMMENT '获取锁的线程id',
`expire` bigint(20) NOT NULL COMMENT '锁的失效时间,时间戳',
PRIMARY KEY (`id`),
UNIQUE KEY `distribute_lock_UN` (`lock_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分布式锁';
tips:表名切勿使用lock,使用关键字语法会报错。在oracle里,resource也是关键字,mysql里不是。
整体
后边会再实现个redis版本的。
public interface Lock {
/**
* 尝试获取锁,
*
* @param key 锁定的key
* @param expiration 锁定的时间
* @return 锁定失败返回null,成功返回LLock锁对象
*/
LLock acquire(String key, Duration expiration);
/**
* 释放锁
*
* @param lock 锁对象
* @return 是否成功
*/
boolean release(LLock lock);
}
加锁
String token = fastSimpleUUID();
int row = INSERT ignore INTO distribute_lock (lock_key, token, expire, thread_id) VALUES (?, ?, ?, ?);
if (row == 0) {
return null;
}
// 续约线程
ScheduledFuture scheduledFuture = scheduleLockRefresh(refresh)
return new LLock(key,token,scheduledFuture);
解锁
客户端设置的锁,必须由自己解开。即会check key和token。
cancelSchedule(lock.getScheduledFuture());
DELETE FROM distribute_lock WHERE lock_key = ? AND token = ?;
续租
当客户端发现在锁的租期内无法完成操作时,就需要延长锁的持有时间,进行续租(renew)。同解锁一样,客户端应该只能续租自己持有的锁。续租需要每个锁后台定时任务时间建议是锁的1/3去续租。
UPDATE distribute_lock SET expire = ? WHERE lock_key = ? AND token = ?;
整体代码在:https://gitee.com/lakernote/easy-admin/tree/master/src/main/java/com/laker/admin/framework/lock
总结
优点:
- 实现简单,没有redis的集群和主从问题。
缺点:
- 性能方面来说没有redis好。
参考:
- https://github.com/alturkovic/distributed-lock