使用 MySQL 数据库实现分布式锁可以确保在多实例环境中定时任务不重复执行。
创建锁表
CREATE TABLE distributed_lock (lock_name VARCHAR(64) PRIMARY KEY,locked_by VARCHAR(64),locked_at DATETIME,timeout_at DATETIME
);
获取锁
在获取锁时,你需要尝试插入一条记录到 distributed_lock
表中。如果插入成功,表示获得锁;如果失败,表示锁已经被其他实例占用。以下是一个获取锁的示例 SQL 语句:
INSERT INTO distributed_lock (lock_name, locked_by, locked_at, timeout_at)
VALUES ('your_lock_name', 'instance_id', NOW(), DATE_ADD(NOW(), INTERVAL 10 SECOND))
ON DUPLICATE KEY UPDATE locked_by = VALUES(locked_by), locked_at = VALUES(locked_at), timeout_at = VALUES(timeout_at)
WHERE timeout_at < NOW();
释放锁
释放锁时,你可以简单地删除该锁的记录,或者更新 timeout_at
字段,以表示锁的释放。以下是释放锁的示例 SQL 语句:
DELETE FROM distributed_lock WHERE lock_name = 'your_lock_name';
定时任务逻辑
在你的定时任务中,首先调用获取锁的逻辑,如果成功获得锁,则执行任务逻辑,并在任务结束后释放锁。
public void executeScheduledTask() {boolean locked = acquireLock("your_lock_name", "instance_id");if (locked) {try {// 执行定时任务逻辑} finally {releaseLock("your_lock_name");}} else {// 锁未获得,跳过执行}
}
实现逻辑
下面是一个简单的获取锁和释放锁的方法示例:
public boolean acquireLock(String lockName, String instanceId) {String sql = "INSERT INTO distributed_lock (lock_name, locked_by, locked_at, timeout_at) " +"VALUES (?, ?, NOW(), DATE_ADD(NOW(), INTERVAL 10 SECOND)) " +"ON DUPLICATE KEY UPDATE locked_by = VALUES(locked_by), " +"locked_at = VALUES(locked_at), timeout_at = VALUES(timeout_at) " +"WHERE timeout_at < NOW()";// 执行 SQL 语句并返回结果
}public void releaseLock(String lockName) {String sql = "DELETE FROM distributed_lock WHERE lock_name = ?";// 执行 SQL 语句
}
锁的过期机制
在获取锁时,可以设置一个过期时间(如 10 秒),这样即使某个实例在执行任务时崩溃,其他实例仍然能够在锁超时后重新获取锁。
注意
- 确保定时任务执行时间不超过锁的过期时间。
- 定期清理超时未释放的锁记录,防止数据库表数据膨胀。