美文网首页
高并发下的分布式锁-mysql篇

高并发下的分布式锁-mysql篇

作者: 知名乐天 | 来源:发表于2021-03-13 19:36 被阅读0次

前言

不管是在面试中,还是在平时的工作中,高并发永远是衡量一个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 在执行过程中可能会锁行,也可能会锁表 。 **

    1. 当查询中带有主键的时候,会锁行;
    2. 当明确带有(即是使用的不包括模糊查询之类的)主键,但是查询没有结果的,不会导致锁表;
    3. 当查询中无主键或者使用了like之类需要全表扫描的,会导致锁住整个表;(即使查询中没有任何数据的时候也会导致锁表)
  • 另外,select XX for update nowait 与 只有 for update 的区别是for update ,当另一个连接尝试获取到锁的时候,会阻塞在那里等待;如果加了 nowait 的话,当获取不到锁的时候会直接报错。

// 为了方便这里借用上上面的task表
// 起一个终端,连接上数据库,然后执行以下:
set autocommit = 0;
select * from task for update;
// 等待一段时间后再执行;
commit;

此时,另一个终端执行,可以发现,执行 select的话,可以直接返回,但是update的话会产生阻塞,直到之前的事务commit(看执行时间就可以知道)


image.png
// 如果使用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的性能确实在高并发环境下,不值得期待,但是多了解下这种知识,也算是扩宽下自己的解决问题的思路吧。
共勉

相关文章

  • 高并发下的分布式锁-mysql篇

    前言 不管是在面试中,还是在平时的工作中,高并发永远是衡量一个web工作者能力的重要场景。本篇幅我们主要是来讨论在...

  • 2019-03-17

    elasticsearch-head MySQL挑战:建立10万连接 取代 ZooKeeper!高并发下的分布式一...

  • 7.1-分布式核心技术-关于高并发下分布式锁你知道多少—小滴课堂

    分布式核心技术-关于高并发下分布式锁你知道多少? 简介:分布式锁核心知识介绍和注意事项 背景 就是保证同一时间只有...

  • 秒杀随笔

    方法: mysql悲观锁 mysql乐观锁 PHP+redis分布式锁 PHP+redis乐观锁(redis wa...

  • etcd:一款比Redis更骚的分布式锁的实现方式!用它

    分布式锁 关于为什么要有分布式****锁这个东西,欢迎阅读我的zk分布式锁的实现,介绍了单机高并发、分布式高并发的...

  • Mysql优化

    Mysql单进程多线程设计。 InnoDB优点5.5以后默认引擎行级锁支持事物更好的恢复性高并发下性能更好,对多核...

  • 知识合集

    开发工具篇 Maven入门指南 git指南 Java篇 大数据篇 分布式锁 分布式锁原理探究 分布式锁 基于Red...

  • 高性能无锁并发框架Disruptor,太强了

    Disruptor是一个开源框架,研发的初衷是为了解决高并发下队列锁的问题,最早由LMAX提出并使用,能够在无锁的...

  • 构建微服务之分布式锁

    测试题 为什么要用分布式锁?分布式锁的特点有哪些?用数据库怎么实现分布式锁?对于mysql的innodb能实现哪些...

  • 缓存击穿解决方案

    缓存击穿解决方案 解决方案分别有: 后台刷新 检查更新 mysql分布式锁 redis分布式锁 zookeeper...

网友评论

      本文标题:高并发下的分布式锁-mysql篇

      本文链接:https://www.haomeiwen.com/subject/opoxcltx.html