在开发中有遇到很简单的SQL却执行的非常慢,甚至只查询一行数据。

咔咔遇到的只有两种情况,一种是MySQL服务器CPU占用率很高,所有的SQL都执行的很慢直到超时,程序也直接502,另一种情况是行锁造成的锁等待。

接下来咔咔带领大家看看各种为难SQL执行的场景,本期文章带大家再熟悉一下MySQL中的锁

最新文章

死磕MySQL系列总目录

什么?还在用delete删除数据《死磕MySQL系列 九》

MySQL统计总数就用count(*),别花里胡哨的《死磕MySQL系列 十》

为什么MySQL字符串不加引号索引失效?《死磕MySQL系列 十一》

打开order by的大门,一探究竟《死磕MySQL系列 十二》

一、MDL锁

现在你应该知道要聊的是MDL,这个锁很少有开发人员去关注,在开发中并没有实际的语法来开启或关闭锁。

这个特性是在MySQL5.5引入的,目的是为了解决一张表同时在做查询和修改表结构,这种情况必定会造成查询结果跟表结构无法对应。

所以,当你访问一个表时会默认加上MDL锁,MDL锁的互斥关系跟共享锁、排它锁是一样的,读写互斥,写写互斥。

MDl锁是在事务提交后才会释放,执行期间一直持有。

同时你需要知道MDL锁的操作会形成一个队列,队列中写锁获取优先级高于读锁,一旦出现MDL写锁等待,会阻塞后续该表的所有CURL操作。

也就说,一旦你在一个未提交事务之后执行了DDL操作,那么等到的结果就是MySQL挂掉,客户端会有重试机制,DDL后所有CURD会在超时后重新发起请求,这个库的线程会很快爆满。

当线程A通过DDL时手里握着表的MDL写锁,而线程B的查询需要获取MDL读锁,所以线程B就一直处于锁等待状态。

在生产环境是坚决不可以直接修改表结构的,如果你的表非常大的话会很容易造成业务所有的CURD处于堵塞。

解决方案

大表DDL可以使用pt-online-schema-change这个工具来处理,具体怎么用后续文章会跟大家分享出来。

若不小心在线上执行了修改表结构,可以通过show processlist命令来查找,不过这个命令在查找上很不方便,可以使用performance_schema和sys系统库来进行查询。前提是你的MySQL参数performance_schema=on,在MySQL8.0.26版本中,这个参数是默认开启的,若你所在的版本没有开启时可以打开。

然后就可以执行select blocking_pid from sys.schema_table_lock_waits,就可以看到当前持有MDL锁的线程ID,直接使用kill命令即可。

二、全局锁

在MySQL强人“锁”难《死磕MySQL系列 三》的文章中给大家聊到了全局锁,使用语法flush table t with read lock 或者 flush table with read lock

指定表名时就锁定指定表,未指定时表示锁定所有表。

这两个语句执行是非常快的,一般不会造成SQL堵塞,但防火、防盗你也防不住有其它线程的语句把flush语句堵塞住。

线程A执行大事务,需要执行10s

线程B执行flush table t with read lock

线程C执行select * from evt_sms where id = 1

所以线程C哪怕是只查询一条数据在10s内也是返回不了结果的,线程B的flush 命令需要等线程A的事务执行完毕,而线程C此时却被未执行的线程B堵塞着。

解决方案

一般出现这种情况只需要执行show processlist就可以看到堵塞线程C的线程是那个,同样直接使用kill掉对应的线程即可。

三、行锁

这个场景是非常好模拟的,接下来让我们一起看看

线程A正常修改大批量数据执行语句为update evt_sms set code = 123 where id > 11089

线程B执行select * from evt_sms where id = 120365 lock in share mode

在文章开头就跟大家简单的说了一句,MySQL中读锁与写锁、写锁与写锁互斥,所以线程B会一直等待线程A的事务提交之后才能返回结果。

解决方案

分析一下,线程B执行的语句添加的是读锁,能被堵住的只有是写锁,所以可以直接在sys.innodb_lock_waits表中查到占着这个写锁的是谁。

执行语句select * from evt_sms sys.innodb_lock_waits where lock_table='kaka.evt_sms'\G

这个试验就不演示了,复现过程也十分简答可以自己看一下哈!输出结果的最后一行就是解决方案,带着你的答案来到评论区

四、快照读引发的问题

了解过MVCC实现原理的大概率都会看到过当前读、快照读这两个词,如果你还不知道它们是什么就好好记一下。

当前读

执行select语句时加上共享锁、排它锁的操作就是当前读。

例:select * from evt_sms where id = 1 lock in share mode

这里的共享锁、排它锁也就是常说的读锁、写锁

在MySQL的Innodb存储引擎中进行DML操作时会默认添加排它锁

上边这个例子,select语句一旦加上了共享锁其它线程是不能修改当前记录的,因此当前读读取的数据库就是最新的数据

快照读

快照读的前提是隔离级别不是串行级别,串行级别的快照读会退化为当前读,快照读的出现是为了提高事务并发性,其实现也是基于MVCC的

MVCC在某种情况下可以认为是行锁的一个变种,但要知道的是在很多情况是不会有加锁行为的

这时你应该记住快照读获取的数据不是最新的,有可能是之前版本的数据

实现MVCC的三大因素隐式字段、undo log、read-view,read-view就是通过快照读产生的,它是由查询的那一时间所有未提交事务ID组成的数组,和已经创建的最大事务ID组成的。然后通过本线程的事务ID在read-view中进行对比

为什么说快照读会引发查询迟迟不返回结果

上文给大家提了一个东西undo log,都知道undo log是回滚日志,查询慢的原因也在这里

线程A先开启一个事务

线程B开启对id为1的数据行进行更新

由于id = 1的数据很多所以会产生很多的版本链,这里就认为是5万个

线程A执行了select * from evt_sms where id = 1就会迟迟返回不了结果

此时线程B并没有提交事务,所以线程A的查询需要根据版本链一直回退到5W个undo log之前,也就是这里导致查询非常慢

下图是一个咔咔之前做的undo log版本链图

线程A的查询是快照读,执行查询时会产生read-view,read-view会把线程A、线程B的事务存放在一个数组中,然后用一定的规则进行判断线程A能看到的数据是什么。

比对规则是什么

trx_id为当前的事务ID,min_id、max_id为当前启动事务的最大事务ID和最小事务ID

如果落在trx_id<min_id,表示此版本是已经提交的事务生成的,由于事务已经提交所以数据是可见的

如果落在trx_id>max_id,表示此版本是由将来启动的事务生成的,是肯定不可见的

若在min_id<=trx_id<=max_id时

如果row的trx_id在数组中,表示此版本是由还没提交的事务生成的,不可见,但是当前自己的事务是可见的
如果row的trx_id不在数组中,表明是提交的事务生成了该版本,可见
在这里还有一个特殊情况那就是对于已经删除的数据,在之前的undo log日志讲述时说了update和delete是同一种类型的undo log,同样也可以认为delete就是update的特殊情况。

当删除一条数据时会将版本链上最新的数据复制一份,然后将trx_id修改为删除时的trx_id,同时在该记录的头信息中存在一个delete flag标记,将这个标记写上true,用来表示当前记录已经删除。

在查询时按照版本链的规则查询到对应的记录,如果delete flag标记位为true,意味着数据已经被删除,则不返回数据。

五、总结

本期文章通过MDL锁、全局锁、行锁、undo log说明查询一条数据页迟迟不返回的问题,可以看到大多数都是一些理论知识,有些东西看着看着也就理解其中的含义了。

这里需要注意的是不要把MDL和DML搞混淆了,这可是两个东西,MDL指的是锁、而DML指的是数据库的增删改查。

坚持学习、坚持写作、坚持分享是咔咔从业以来所秉持的信念。愿文章在偌大的互联网上能给你带来一点帮助,我是咔咔,下期见。

重重封锁,让你一条数据都拿不到《死磕MySQL系列 十三》相关推荐

  1. 什么?还在用delete删除数据《死磕MySQL系列 九》

    别再用delete删除数据 系列文章 一.表空间 二.数据删除流程 三.实践全表删除表文件大小不改变 四.如何正确的减少磁盘文件 五.实践是检验认识是否具有真理性的唯一标准 六.开发建议 七.总结 系 ...

  2. php 分表 实战,PHP实战:1亿条数据如何分表100张到Mysql数据库中(PHP)

    <PHP实战:1亿条数据如何分表100张到Mysql数据库中(PHP)>要点: 本文介绍了PHP实战:1亿条数据如何分表100张到Mysql数据库中(PHP),希望对您有用.如果有疑问,可 ...

  3. mysql 同一张表 某个字段更新到另一条数据上_面试基础:数据库MySQL基础入门(下)...

    本文是面试基础的第二篇.本篇偏理论,包括三节: 事务和并发 数据库设计 索引 所选的三个内容均是面试的高频考察点,需要细致地理解 No.1     事务和并发 事务:数据库操作的基本单元.对于数据库的 ...

  4. mysql 一对多 根据多条数据排序_优化的道路永无止境——Mysql的ICP及MRR

    在讲ICP和MRR之前,我们先了解一下MySQL的架构.于本文的重点并不在架构细节上,所以让我们直接看关键部分,存储引擎作为单独的一层,是连接底层存储系统和上层server其他部分的桥梁,而MySQL ...

  5. mysql 5000万条数据库_1亿条数据如何分表100张到Mysql数据库中(PHP)

    下面通过创建100张表来演示下1亿条数据的分表过程,具体请看下文代码. 当数据量猛增的时候,大家都会选择库表散列等等方式去优化数据读写速度.笔者做了一个简单的尝试,1亿条数据,分100张表.具体实现过 ...

  6. php mysql 插入多条数据_雷林鹏分享:PHP MySQL 插入多条数据

    使用 MySQLi 和 PDO 向 MySQL 插入多条数据 mysqli_multi_query() 函数可用来执行多条SQL语句. 以下实例向 "MyGuests" 表添加了三 ...

  7. 1亿条数据如何分表100张到Mysql数据库中(PHP)

    来源:http://www.jb51.net/article/70265.htm 这篇文章主要介绍了当数据量猛增的时候如何把一亿条数据分表100张到Mysql数据库中,需要的朋友可以参考下 下面通过创 ...

  8. MongoDB筛选数组中每一条数据都满足条件的文档

    假设存在如下的数据 date: Date users: [{ user: 1, group: 1 }{ user: 5, group: 2 } ]date: Date users: [{ user: ...

  9. 【clickhouse】阿里clickhouse 随便查询一条数据都报错 read time out

    本文为博主九师兄(QQ:541711153 欢迎来探讨技术)原创文章,未经允许博主不允许转载. 文章目录 1.概述 1.概述 这是kafka建表,抹去的是内网 ip 这是mysql建表 , 抹去的是 ...

最新文章

  1. 10万元奖金助力AI加速药物研发!小分子预测大赛来啦
  2. Linux第二周学习笔记(7)
  3. 实习小白::(转) Cocos2d-x 3.0开发(五)关联程序逻辑与cocoStudio导出文件
  4. 探秘!在阿里云做产品经理是怎样的体验?
  5. java 制作类似DOS功能
  6. facelets_不要在facelets中重复表情
  7. C++实现链栈的基本操作
  8. 流行的编程语言及其趋势
  9. python向量化和c哪个快_在python中向量化6 for循环累积和
  10. Group by 第二选择 OVER
  11. react的一些概念
  12. 100c之29:求具有abcd= ( ab + cd )^2 性质的四位数
  13. c#中的委托、事件、Func、Predicate、Observer设计模式以及其他
  14. ad20/ad21/ad22学习笔记(基本包含一套流程)Altium Designer
  15. UIAlertView、UIActionSheet兼容iOS8
  16. 第六章金融衍生工具市场
  17. Mac删除OBS的虚拟摄像头
  18. C语言void指针的用法
  19. 树莓派csi摄像头和usb摄像头_一、树莓派CSI摄像头
  20. PCB高速信号布线技巧

热门文章

  1. 20060303个人日志
  2. Line 5: Char 40: error: call to implicitly-deleted default constructor of ‘unordered_map<vector<int>
  3. C# beginInvoke
  4. xp计算机硬盘序列号,发表一个最简单的XP以上系统中获取CPUID、硬盘序列号、BIOS序列号等等的函数!(100分)...
  5. Netflix(奈飞)--SpringCloud
  6. 2022-2025年最新最全Java毕业设计选题Java/mysql/springboot/微信小程序
  7. 直流稳压模块电源DC-DC电压控制输出0-50v/0-100v/0-200v/0-150v/0-600v/0-1000v
  8. 基于51单片机的智能护眼台灯带闹钟功能proteus仿真原理图PCB
  9. 数学建模2013年B题——碎纸片拼接复原
  10. javafx和java swing_Java写GUI用swing还是JavaFX呢?