sql—将MySQL5.6升级到5.7,并使用where in子句执行极慢的查询

wgmfuz8q  于 2021-07-26  发布在  Java
关注(0)|答案(0)|浏览(440)

在使用amazonrds升级到mysql 5.7(从5.6升级到5.7)之后,一些在in子句中使用大量id(42k+)的查询开始变得慢得多。
查询

SELECT (`sales_contact_emails`.`contact_id`) AS `_prefetch_related_val_contact_id`,
       `sales_email`.`id`,
       `sales_email`.`type`,
       `sales_email`.`email`
FROM `sales_email`
         INNER JOIN `sales_contact_emails` ON (`sales_email`.`id` = `sales_contact_emails`.`email_id`)
WHERE `sales_contact_emails`.`contact_id` IN (/* 42000+ different IDs in here */);

显示创建表

CREATE TABLE `sales_contact_emails` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `contact_id` int(11) NOT NULL,
  `email_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `contact_id` (`contact_id`,`email_id`),
  KEY `email_id_refs_id_5cebc930` (`email_id`)
) ENGINE=InnoDB AUTO_INCREMENT=26433420 DEFAULT CHARSET=latin1;

CREATE TABLE `sales_email` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` varchar(1) NOT NULL,
  `email` varchar(75) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `sales_email_idx_email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=189951028 DEFAULT CHARSET=latin1;

解释MySQL5.6
idselect\ typetabletypepossible\ keyskeykey\ lenrefrowsextra1simplesales\ contact\ emailsrangecontact\ id,email\ id\ refs\ id\ 5cebc930 contact\ id4null41376使用where;使用index1simplesales\u emaileq\u refprimaryprimary4company.sales\u contact\u emails.email\u id1null
配置文件(重新启动后首次运行):
状态持续时间开始0.011993检查权限0.000010检查权限0.000006打开表0.006008init0.005540系统锁定0.000013优化0.001441统计0.042569准备0.001433执行0.000008发送数据0.051343结束0.000009查询结束0.000007关闭表0.000010释放项0.001068清理0.000417
解释MySQL5.7
idselect\u typetablepartitionstypepossible\u keyskeykey\u lenrefrowsfilteredexamplesales\u contact\u emailsnallindexcontact\u id,email\u refs\u id\u 5cebc930 contact\u id8null2497877750 using where;使用index1simplesales\u emailnulleq\u refprimaryprimary4company.sales\u contact\u emails.email\u id1100null
配置文件(启动后首次运行):
statusdurationstarting0.015114检查权限0.000014检查权限0.000004打开表0.000019初始化0.007370系统锁定0.000023优化0.001555统计0.024513准备0.001390执行0.000006发送数据148.873194结束0.000012查询结束0.000010关闭表0.000008释放项0.001544清理0.000711
配置文件(第二次运行)
statusdurationstarting0.014875检查权限0.000012检查权限0.000005打开表0.000019初始化0.006042系统锁定0.000015优化0.001538统计0.023283准备0.001399执行0.000007发送数据6.101950end0.000013查询end0.000009关闭表0.000008释放项0.000996清理0.000438
事实
世界上大约有2500万排 sales_contact_emails table。 contact_id 索引是一个具有极高基数的复合索引(contact\u id、email\u id)。它实际上是一个唯一的索引。 SHOW INDEXES 下图:
表格非唯一键\u名称索引\u索引列\u名称排序心意子项\u零件 Package 完整索引\u类型销售\u联系方式\u电子邮件0联系方式\u ID 1联系方式\u IDA24976228空零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零零目录
in子句的最小id值为 43890973 最大值是 43839266 (相差约51k)。对一个 range scan 我想是吧。这与MySQL5.6的估计相匹配。
mysql版本: 5.6.445.7.33 这两个计划的区别在于 type=range vs type=index 以及对 rows .
两个例子都使用rds。相同的快照(当然有一个迁移到MySQL5.7)。
使用两个引擎的默认参数/选项组。
0流量,只有我自己和我的查询基准。
实际的sql语句约为400kb。
迄今为止的研究
https://bugs.mysql.com/bug.php?id=87164:MySQL5.7上的in子句似乎有一个bug,但这可能与我的特定案例无关。我尝试了MySQL8的相同快照,从5.7获得了相同的性能
试图禁用/启用所有@@optimizer\u开关(完全关闭和完全打开)
试着玩 @@eq_range_index_dive_limit (50k改为200,无差异)
问题
MySQL5.7上的什么行为改变使得这些查询如此缓慢?
有没有办法将行为改回原来的方式,在查询结构中不做任何更改或只做最小更改的情况下获得类似的性能?
集中
我知道一个包含42k iten的in子句本身可能是一个问题,并且有解决方法(子查询、连接、临时表……),但这不是目前的问题。重点是mysql 5.7与5.6引擎/优化器/查询规划器的不同行为。
提前谢谢,:)

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题