1.背景前提
当系统有一个时间较长的同步任务执行时,不能有其他线程重复去执行这个任务,因此需要用到锁,由于是集群应用,所以只能是分布式锁。
分布式锁比较常见的方式都是借助一个中间件实现的,比如zookeeper锁,redis锁。但是这样的话会额外映入一个组件,并不是很划算。一般系统会用到redis,所以使用redis锁的会比较常见,但是redis并非完全可靠,所以也不是特别好。
所以这里使用数据库(mysql)实现一种基于行锁的简单优雅的实现方式。
2.使用方式
假如id=1的这条数据需要进行一个同步的任务,且status=3表示此时有同步任务正在进行,其他任务应该被阻塞。可以使用下面的sql语句,只需要判断是否修改成功(返回的修改条数>=1即为成功)。
UPDATE users SET status = 3 WHERE id = 1 AND status!=3
由于该操作是原子性的,加上数据库本身的函所支持。此时只会有一个线程能够修改成功,当他修改成功之后,其他线程修改的时候发现where条件不满足,则不会修改成功,也就不会进入后面的逻辑了。
3.行锁、表锁验证
3.1建表
CREATE TABLE `users` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`sex` varchar(255) DEFAULT NULL,
`status` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `users` (`name`, `sex`, `status`) VALUES ( 'lk', '男', 0);
INSERT INTO `users` (`name`, `sex`, `status`) VALUES ('lk1', '男', 0);
INSERT INTO `users` (`name`, `sex`, `status`) VALUES ('zs', '女', 0);
INSERT INTO `users` (`name`, `sex`, `status`) VALUES ('ls', '男', 0);
事务开启与关闭
-- 查看是否开启自动提交
select @@autocommit;
-- 1开启 0关闭
set autocommit = 0
想要进行下面的验证,先将自动提交事务关闭。(注意验证完毕后开启自动提交哦)
3.2表锁
如下图,开始事务但是不提交,此时将全表数据锁住了,修改任意一条数据都不能成功。
在不使用到索引的情况下,直接使用where条件,会触发表锁(应为只能扫全表才知道where的是那条记录)
提交事务之后,立马就能修改了
3.3行锁
按照图中开启事务,但是不提交,此时会将id=1的记录锁住,无法进行修改,但是其他的没有被锁住的记录,比如图中的id=2的记录,还是可以修改的。
当使用了索引的时候,可以直接定位到那一条具体的记录,此时就会触发行锁了。