什么是分布式锁
分布式锁,顾名思义就是分布式系统中的锁。锁是单体应用中解决共享资源访问问题的工具。而分布式锁,就是为了解决分布式系统中共享资源访问的问题,防止分布式系统中的多个进程之间相互干扰。
使用场景
在分布式系统里,我们有时需要执行某些定时任务。为了确保分布式系统中只有一个进程执行定时任务,所以需要分布式锁来进行控制。
通常,我们一般会使用Redis分布式锁、Zookeeper分布式锁的解决方案。但是由于定时任务的执行对性能要求不高且不希望引入新组件,我们有时需要基于现有数据库的解决方案,因此这里介绍的是基于MySQL实现的分布式锁方案。
实现方案
一种简单实现是基于MySQL数据库的唯一索引实现的,其核心思想是通过表的唯一索引进行互斥。首先在数据库中创建表,并创建唯一索引;想要执行某个定时任务,就需要向表中插入数据;成功插入则获取锁,执行完成后删除对应的行数据释放锁。
数据库表:
CREATE TABLE `task_lock`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`task_name` varchar(64) NOT NULL DEFAULT '' COMMENT '任务名',
`executor` varchar(64) NOT NULL DEFAULT '' COMMENT '任务执行者',
`description` varchar(64) NOT NULL DEFAULT '' COMMENT '任务描述',
`status` varchar(64) NOT NULL DEFAULT '' COMMENT '任务状态',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`exp_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '过期时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_task` (`task_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='分布式任务表';
获取锁:
@Transactional
public boolean acquireLock(String lockName, String owner, Long expTimeMin) {
LocalDateTime now = LocalDateTime.now();
TaskLock taskLock = new TaskLock(lockName, owner, now, getExpTime(now, expTimeMin));
int rows = 0;
try{
rows = taskLockMapper.insert(taskLock);
}catch (Exception e){
log.error("insert lock {} ex:{},",lockName,e.getMessage());
}
if (rows <= 0) {
TaskLock expLock = selectExpireLock(lockName);
if (null == expLock) return false;
rows = updateExpireLock(expLock, taskLock);
}
return rows > 0;
}
释放锁:
@Transactional
public void releaseLock(String lockName, String owner){
LambdaQueryWrapper<TaskLock> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(TaskLock::getTaskName, lockName);
wrapper.eq(TaskLock::getExecutor, owner);
taskLockMapper.delete(wrapper);
}
总结:
使用MySQL实现分布式锁的好处是项目不用增加额外的依赖,满足项目对于分布式锁的基本需要。