前言
不管是在面试中,还是在平时的工作中,高并发永远是衡量一个web工作者能力的重要场景。本篇幅我们主要是来讨论在高并发环境下我们应该如何实现分布式锁。
实现分布式锁的方式有比较多,这里主要考虑如何使用mysql实现分布式锁。
Mysql实现锁的方法
第一种,使用mysql唯一索引来实现:
针对这种实现,我们只需要新建一张表,专门用来处理分布式任务,比如新建一张 task的表,里面的唯一索引为 task_name 。
运行流程如下:当多个副本同时要抢占一个任务的锁的时候,就执行一个插入语句(这几个副本的task_name都是一致的),所以,当有一个副本之行插入成功后,后续的其他插入则会由于唯一索引的问题,导致插入失败。我们可以根据插入的影响条数为0 或者是1 ,判断是否抢锁成功。最后抢锁成功的副本,在任务执行结束之后,将该条记录删除,即把锁删除掉。
CREATE TABLE `tests`.`task` (
`task-name` varchar(255) NULL,
`id` int(0) NOT NULL AUTO_INCREMENT,
`ctime` timestamp(0) NULL,
`mtime` timestamp(0) NULL ON UPDATE CURRENT_TIMESTAMP(0),
PRIMARY KEY (`id`),
INDEX `uniq_idx_task_name`(`task-name`)
);
// 副本1执行:
INSERT INTO `tests`.`task`(`task-name`, `ctime`, `mtime`) VALUES ('demo1', 1, NULL, NULL);
// 副本2同时执行
INSERT INTO `tests`.`task`(`task-name`, `ctime`, `mtime`) VALUES ('demo1', 1, NULL, NULL);
// 以上只会有一个副本执行成功,另一个副本会直接失败
第二种,使用mysql的悲观锁
for update 和 for udpate no wait :
-
使用的前提:基于Innodb,并且在支持事务的情况下可以使用
-
当某用户执行了 for update 之后,该用户可以在提交或者放弃事务之前,对该事务进行查询和更新,其他用户只能查询但不能更新被加锁的数据行。
** select XXXX for update 在执行过程中可能会锁行,也可能会锁表 。 **
- 当查询中带有主键的时候,会锁行;
- 当明确带有(即是使用的不包括模糊查询之类的)主键,但是查询没有结果的,不会导致锁表;
- 当查询中无主键或者使用了like之类需要全表扫描的,会导致锁住整个表;(即使查询中没有任何数据的时候也会导致锁表)
-
另外,select XX for update nowait 与 只有 for update 的区别是for update ,当另一个连接尝试获取到锁的时候,会阻塞在那里等待;如果加了 nowait 的话,当获取不到锁的时候会直接报错。
// 为了方便这里借用上上面的task表
// 起一个终端,连接上数据库,然后执行以下:
set autocommit = 0;
select * from task for update;
// 等待一段时间后再执行;
commit;
此时,另一个终端执行,可以发现,执行 select的话,可以直接返回,但是update的话会产生阻塞,直到之前的事务commit(看执行时间就可以知道)

// 如果使用for update nowait;
// 副本1执行成功:
select * from task for update nowait;
// 副本2执行:
mysql> select * from task for update nowait;
ERROR 3572 (HY000): Statement aborted because lock(s) could not be acquired immediately and NOWAIT is set
//值得注意的是,for update nowait 的话,如果你执行 update 操作的话,仍然会阻塞在那里。
第三种,使用乐观锁
- (区分下第一种,第一种是针对单点定时任务的,这里延伸下成加锁)
- 使用乐观锁的话,可以给表中加一个 version 字段,用于表示该行记录的版本(如果你想要用 timestamp来做也可以,可以直接使用mysql的字段,那样你就不用考虑该值的更新)
- 因此,每次更新的流程是,先 select 获取到一个version(比如是3) 然后再进行 update ,查询中在原来的基础上多加一个 and version = 3, 如果在select 和 update 之间有个第三方操作了数据库并且操作成功了,此刻version变成了4,那么你进行这次update的时候 update XXX where XXX and version = 3的时候,更新就会失败,那么就不会影响。
结语
以上就是mysql锁的三种方式,不过说实话,mysql的性能确实在高并发环境下,不值得期待,但是多了解下这种知识,也算是扩宽下自己的解决问题的思路吧。
共勉
网友评论