SQL优化INNER JOIN子查询代替EXISTS

需求是将未分配的记录,按照指定排序选一条分配给一个人。
最原始的实现是,查询更新,先加锁,select出一条,再update,然后释放锁,由于select很慢,还提前生成了缓存,两分钟生成一次。

经过梳理,得到以下SQL,用的exists语法,直接update,利用MySQL的锁机制保证不会重复分配。

UPDATE com_info c SET infoOperatorName='wy-test', infoOperatorFlag=1 
WHERE deleteFlag=0 AND infoType='F道路封闭' AND infoOperatorFlag=0 AND infoOperatorName='' 
AND EXISTS (SELECT 1 FROM ext_info e WHERE e.infoCode=c.infoCode AND e.infoCheckResult='有效') 
ORDER BY DATE(infoCreateTime) DESC, catagory_priority, city_priority, infoLevel DESC, infoCreateTime DESC LIMIT 1 ;

实测耗时4s左右,很稳定。

一番优化后,舍弃了exists,改用inner join + 子查询,如下:

UPDATE com_info t1 INNER JOIN (
    SELECT c.infoCode FROM com_info c INNER JOIN ext_info e ON c.infoCode=e.infoCode  
    WHERE deleteFlag=0 AND infoType='F道路封闭' AND infoOperatorFlag=0 AND infoOperatorName='' 
    AND e.infoCheckResult='有效'
    ORDER BY DATE(infoCreateTime) DESC, catagory_priority, city_priority, infoLevel DESC, infoCreateTime DESC LIMIT 1
) t2 ON t1.infoCode=t2.infoCode SET infoOperatorName='wy-test', infoOperatorFlag=1;

实测耗时0.28s,很稳定。

以下是执行情况和执行计划:


执行耗时

执行计划

这里INNER JOINEXISTS快的原因,其实也看场景。
由于两张表的规模是一样的,并且主键相同,所以JOIN会走主键索引,非常快。EXISTS反而会去遍历了。

扫描方式 type,从慢到快:

  1. all : 全表扫描
  2. index : 全索引扫描,和全表扫描一样。只是扫描表的时候按照索引次序进行而不是行。主要优点就是避免了排序, 但是开销仍然非常大。如在Extra列看到Using index,说明正在使用覆盖索引,只扫描索引的数据,它比按索引次序全表扫描的开销要小很多。这个比all效率要好一点,主要有几种情况,一是当前的查询是覆盖索引的,即我们需要的数据在索引中就可以获取(Extra中有Using Index),或者是使用了索引进行排序,这样就避免数据的重排序(Extra中无 Using Index)。如果Extra中Using Index与Using Where同时出现的话,则是利用索引查找键值的意思
  3. range : 范围查询,避免了全索引扫描,限制的范围越小,效率越高
  4. index_subquery : 在 某 些 IN 查 询 中 使 用 此 种 类 型 , 与 unique_subquery 类似,但是查询的是非唯一 性索引
  5. unique_subquery : 在某些 IN 查询中使用此种类型,而不是常规的 ref
  6. index_merge : 说明索引合并优化被使用了
  7. ref_or_null : 如同 ref, 但是 MySQL 必须在初次查找的结果 里找出 null 条目,然后进行二次查找。
  8. ref : 使用了非唯一性索引进行数据的查找
  9. eq_ref : 使用的唯一性索引进行数据查找,例如主键索引之类的
  10. const : 通常情况下,将一个主键放置到where后面作为条件查询,mysql优化器就能把这次查询优化转化为一个常量,如何转化以及何时转化,这个取决于优化器。这个比eq_ref效率高一点。
  11. system : 表只有一行。不过这种情况下就没意义了。
  12. NULL : MySQL不用访问表或者索引就直接能到结果。

(关于覆盖索引:MySQL系列-优化之覆盖索引

测试时发现,由于com_info表的查询需求很多,因此针对不同的场景,对不同的字段建了各种索引,在测试数据库和线上数据库上测试发现,不同的数据分布下,MySQL对索引的选取差别很大,线上库采用了专门为这个场景建的联合索引(包含了WHERE中需要的字段),而测试库中则采用了index_merge的方式,挨个索引走一遍,取交集,实测index_merge还是要比ref慢一两个数量级。

另外index_merge在这个场景下会引发死锁。
因为这里用到了

UPDATE ... WHERE ... ORDER BY ... LIMIT ...;

由于有ORDER BY LIMIT存在,UPDARE的时候,会锁住命中的所有行。而index_merge在高并发的时候,如果索引行数有重叠,因为加锁的顺序可能不同,互相等待可能会导致死锁。加锁顺序的原因是,MySQL会先用索引1进行扫表,再用索引2进行扫表,然后求交集形成一个合并索引。这个使用索引扫表的过程和我们本身SQL使用索引的顺序可能存在互斥,造成了死锁。

这里有个场景可能引发死锁:
请求的WHERE条件不完全一样,但包含了相同的某两个字段,可能造成上述的问题。

index_merge引发的死锁排查

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

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,617评论 18 399
  • 写在开头 这里所说的劣质SQL限定在数据量未到分库分表和使用分布式缓存程度,指那些执行较差的查询、插入、更新、删除...
    alivs阅读 732评论 0 0
  • 有了孩子的人都会有这样一个感触,“不是你教育孩子,而是孩子在教导你“,他们是天生的哲学家,生来就通晓一切的真理。 ...
    果蛋皮阅读 759评论 3 2
  • 大脑如同肌肉,如果长期不用的话,真的是会退化的。人类一旦停止了稍微有深度的思考,面对问题的时候,就完全是凭着原始大...
    原同学阅读 16,724评论 4 3
  • 我一直以为自己心静如水 直到遇见他―― 一个逆着光行走的男孩 他蓦然转头 笑了 有那么一瞬间我看呆了 但我并没有以...
    仙女味的我阅读 104评论 0 0