基于数据库表乐观锁 (基本废弃)
要实现分布式锁,最简单的⽅方式可能就是直接创建⼀一张锁表,然后通过操作该表中的数据来实现了了。
当我们要锁住某个⽅法或资源时,我们就在该表中增加一条记录,想要释放锁的时候就删除这条记录。
比如创建这样一张数据库表:
CREATE TABLE `methodLock` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`method_name` varchar(64) NOT NULL DEFAULT '' COMMENT '锁定的⽅方法名', `desc` varchar(1024) NOT NULL DEFAULT '备注信息',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存数据时间,⾃自动⽣生成',
PRIMARY KEY (`id`),
UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='锁定中的⽅方法';
当我们想要锁住某个方法时,执行以下SQL:
insert into methodLock(method_name,desc) values (‘method_name’,‘desc’)
因为我们对method_name做了唯一性约束,这里如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,可以执方法体内容。
当⽅法执行完毕之后,想要释放锁的话,需要执⾏行行以下sql:
delete from methodLock where method_name ='method_name'
上面说到这种方式基本废弃,那么这种简单的实现会存在哪些问题呢?
- 这把锁会强依赖数据库的可用性,数据库是一个单点,⼀旦数据库挂掉,会导致业务系统不可⽤。
- 这把锁并没有失效时间,⼀旦解锁操作失败,就会导致锁记录一直存在数据库中,其它线程无法再获得到锁。
- 这把锁只能是非阻塞的,因为数据的insert操作,⼀旦插⼊入失败就会直接报错。没有获得锁的线程并不会进入排队列,要想再次获得锁就要再次触发获得锁操作。
- 这把锁是非重⼊的,同⼀个线程在没有释放锁之前无法再次获得该锁。因为数据已经存在了。当然,我们也可以有其它方式解决上面的问题。
- 针对数据库是单点问题搞两个数据库,数据之前双向同步。⼀旦挂掉快速切换到备库上。
- 针对没有失效时间?我们可以做一个定时任务,每隔一定时间把数据库中的超时数据清理理一遍。
- 针对非阻塞的?搞⼀个自旋while循环,直到insert成功再返回成功。
- 针对⾮重入的?我们可以在数据库表中加个字段,记录当前获得锁的机器的主机信息和线程信息,那么下次再获取锁的时候先查询数据库,如果当前机器的主机信息和线程信息在数据库可以查到的话,直接把锁分配给他就可以了。
- 基于数据库排他锁 除了可以通过增删操作数据表中的记录以外,其实还可以借助数据中自带的锁来实现分布式的锁。我们⽤刚刚创建的那张数据库表。可以通过数据库的排他锁来实现分布式锁。 基于MySql的InnoDB 引擎,可以使用以下方法来实现加锁操作。
伪代码如下:
public boolean lock(){
connection.setAutoCommit(false)
while(true){
try{
result = select * from methodLock where method_name=xxx