数据库(Mysql) 实现分布式锁

tech2023-10-09  106

数据库(Mysql) 实现分布式锁

​ 基于MySql的InnoDB引擎,只有通过索引进行检索的时候才会使用行级锁,否则会使用表级锁.这个索引一定要创建成唯一索引,否则会出现多个重载方法之间无法同时被访问的问题

以下方案无法实现公平锁. 求大佬指点思路!!!

库表(t_lock)设计

字段类型是否可为空描述idint否主键,自增relockint否锁定次数 为0时,表示未分配锁methodString否加锁方法ipString否获取锁服务iplock_timedate否锁定时间(设计超时时间)

每个事务锁在表中为一条数据。

服务启动,查询t_lock表。准备获取锁。

select * from t_lock;

若表中无数据(服务初次启动),则进行添加:

INSERT INTO `t_lock`(`id`, `relock`, `method`, `ip`, `lock_time`) VALUES (1, 1, 'test', '127.0.0.1', NOW());

若添加成功,则认为获取到锁。

若表中有数据,则relock是否为 0。 1) 若锁定次数为0, 则可以自由获取锁。获取锁执行sql获取锁。

UPDATE `t_lock` SET `relock` = 1, `method` = 'test', `ip` = '127.0.0.1', `lock_time` = NOW() WHERE `ip` = '127.0.0.1' AND method='test' AND relock = 0; 若锁定次数不为0, 则判断ip和method是否一致。若一致,则可以进行重入。执行sql重入锁。 UPDATE `t_lock` SET `relock` = 2, `method` = 'test', `ip` = '127.0.0.1', `lock_time` = NOW() WHERE `ip` = '127.0.0.1' AND method='test' AND relock = 1; --假定表中锁定次数为 1

​ 若不一致,则轮询获取锁。获取锁规则为:1. 锁超时 2. 其他人释放锁。 ​ 锁超时: 锁定时间和当前时间的比较。若时间偏差过大(业务系统自己配置),则可以强制获取锁。

​ 其他人释放锁: relock = 0

UPDATE `t_lock` SET `relock` = 1, `method` = 'test', `ip` = '127.0.0.1', `lock_time` = NOW() WHERE `ip` = '127.0.0.1' AND method = 'test' AND ( ( relock = 2 AND NOW() > DATE_ADD( '2001-09-11 07:35:25', INTERVAL 5 MINUTE )) OR relock = 0 ); --假定表中锁定次数为 2, 超时时间5分钟

​ 考虑到各个服务器时间可能和数据库时间不一致。 所以使用数据库时间。传入时间为首次查询得到的数据库时间。

以上update操作,若成功,则获取到相应锁。

锁释放。

UPDATE `t_lock` SET `relock` = 0, `method` = 'test', `ip` = '127.0.0.1', `lock_time` = NOW() WHERE `ip` = '127.0.0.1' AND method='test' AND relock = 1; --假定表中锁定次数为 1

若被重入,则需多次释放。

注: 此设计 无法体现公平。

异常情况

服务重启或宕机

重启或宕机后:两种选择 1) 检查锁表,若ip和method一致,则清空锁标记(relock = 0)。2)检查锁表,ip和method一致,则进行重入。(导致所标记无法释放至0),其他线程获取锁,会进行锁超时判断。

数据库宕机

数据库应设计为主主同步。防止单点。

Spring提供了@ScheduledLock 防止定时任务在分布式环境重复执行

支持多种数据源(jdbc, redis,zk,mongodb …),详情参照: https://blog.csdn.net/qq_36341209/article/details/87375794

最新回复(0)