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

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

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

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

13.png

最新文章

死磕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版本链图

image

线程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指的是数据库的增删改查。

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容