用MySQL数据库实现分布式锁
对于分布式锁的实现,现在绝大多数都是用redis来实现的,就是大名鼎鼎的setnx命令。这次我们用MySQL数据库来实现一下。
思路有很多种,这里我介绍一个非常简单的实现。建立一张表,比如lock_table,这个表中有个字段叫 lock_name,并且向lock_name 字段建立
唯一索引。 每次获取锁的时候向这张表插入一条记录,lock_name字段值任意。如果插入成功,那么说明加锁成功了,因为lock_name建立了
唯一索引,插入失败说明有人在你之前插入成功了,也就意味着别人获取到了锁。那么在业务执行完毕之后,删除这条记录,就是解锁操作。
直接上代码:
SQL语句
DROP TABLE IF EXISTS `lock_table`; CREATE TABLE `lock_table` ( `lock_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '分布式锁字段', UNIQUE INDEX `lock_name_index`(`lock_name`) USING BTREE COMMENT '分布式锁字段' ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
package cn.hongwei.lock;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* Description:
* 基于MySQL唯一索引来实现分布式锁
*
* @author shihongwei
* @email 1224028994@qq.com
*/
public class MysqlUniqueIndexLock {
private DataSource dataSource;
public MysqlUniqueIndexLock(DataSource dataSource) {
this.dataSource = dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public boolean lock() {
// 1. 插入数据
Connection connection = null;
PreparedStatement statement = null;
try {
connection = dataSource.getConnection();
connection.setAutoCommit(false);
statement = connection.prepareStatement("insert into lock_table values ('myLock')");
statement.execute();
connection.commit();
return true;
} catch (SQLException e) {
// 回滚
try {
connection.rollback();
} catch (SQLException ex) {
return false;
}
// 说明锁被占用,开启重试机制,如果时间太短,会导致连接不够用,所以连接池的最大线程要设置的大一点
//避免出现递归导致连接被用完或者导致栈内存溢出
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return lock();
} finally {
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
public void unlock() {
// 1. 删除数据
Connection connection = null;
PreparedStatement statement = null;
try {
connection = dataSource.getConnection();
connection.setAutoCommit(false);
statement = connection.prepareStatement("delete from lock_table where lock_name = 'myLock'");
statement.execute();
connection.commit();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
throw new RuntimeException(e);
} finally {
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
}
这种方式实现分布式锁非常暴力简洁,但是弊端很明显。性能较低,每次加锁和解锁都需要进行数据库的读写操作;没有锁超时机制,若持有锁的进程崩溃,可能会导致死锁。
不推荐使用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~