LeftJoin 在 AWS RDS 上非常慢 MySQL,但在其他地方非常快
Very slow MySQL LeftJoin on AWS RDS, but very quick elsewhere
PROD 中的 MySQL 通过 AWS RDS (Aurora, db.t3.small) 是 运行。全面的资源利用率完全在令人满意的水平。
如果我执行以下 SELECT,它几乎是即时的:
SELECT SQL_CALC_FOUND_ROWS `submissions`.*
FROM `cmsb_submissions` as `submissions`
WHERE (status = '4')
LIMIT 15
如果我添加 LEFTJOIN,则需要 15 秒以上:
SELECT SQL_CALC_FOUND_ROWS `submissions`.*,
`explorer_points_earning`.`num` AS `explorer_points_earning.num`,
`explorer_points_earning`.`createdDate` AS `explorer_points_earning.createdDate`,
`explorer_points_earning`.`createdByUserNum` AS `explorer_points_earning.createdByUserNum`,
`explorer_points_earning`.`updatedDate` AS `explorer_points_earning.updatedDate`,
`explorer_points_earning`.`updatedByUserNum` AS `explorer_points_earning.updatedByUserNum`,
`explorer_points_earning`.`user` AS `explorer_points_earning.user`,
`explorer_points_earning`.`points_earned` AS `explorer_points_earning.points_earned`,
`explorer_points_earning`.`for_trail` AS `explorer_points_earning.for_trail`
FROM `cmsb_submissions` as `submissions`
LEFT JOIN `cmsb_explorer_points_earning` AS `explorer_points_earning`
ON submissions.num = explorer_points_earning.for_trail
AND explorer_points_earning.user = 7
WHERE (status = '4')
LIMIT 15
table 都不大:
explorer_points_earning = 17,000 records
submissions = 1,000 records
并且存在以下索引:
SHOW INDEX FROM cmsb_explorer_points_earning;
'cmsb_explorer_points_earning', '0', 'PRIMARY', '1', 'num', 'A', '17155', NULL, NULL, '', 'BTREE', '', ''
'cmsb_explorer_points_earning', '1', '_auto_for_trail', '1', 'for_trail', 'A', '2450', '16', NULL, 'YES', 'BTREE', '', ''
'cmsb_explorer_points_earning', '1', '_auto_user', '1', 'user', 'A', '3431', '16', NULL, 'YES', 'BTREE', '', ''
因此,无论如何这都不是一个大查询。
完全相同的查询(使用 LEFTJOIN),在我的本地开发服务器(更少的资源)上具有完全相同的数据库转储是即时的。
我确信这不是 RDS“资源”问题,因为我将实例加倍为 t3.medium,但没有任何区别。
这让我认为这是索引错误或某些 RDS 特定配置错误。
解释:
在 RDS 上:
'1', 'SIMPLE', 'submissions', 'ALL', NULL, NULL, NULL, NULL, '1100', 'Using where'
'1', 'SIMPLE', 'explorer_points_earning', 'ALL', '_auto_for_trail', NULL, NULL, NULL, '17155', 'Range checked for each record (index map: 0x2)'
开发中:
'1', 'SIMPLE', 'submissions', NULL, 'ALL', NULL, NULL, NULL, NULL, '1064', '100.00', NULL
'1', 'SIMPLE', 'explorer_points_earning', NULL, 'ALL', '_auto_user,_auto_for_trail', NULL, NULL, NULL, '17082', '100.00', 'Using where; Using join buffer (hash join)'
Notable 区别在于 RDS 只有“auto_for_trail”作为 'possible key' 而 DEV 有两个字段,_auto_user& _auto_for_trail。 RDS 使用嵌套循环,DEV 使用散列连接。
毫无疑问,我可以提供更多信息来帮助调试此问题,但我不确定是什么,所以请让我知道还有什么可以提供帮助。
任何帮助,非常感谢,
谢谢
-- 额外信息 --
SHOW CREATE TABLE cmsb_explorer_points_earning;
RDS
CREATE TABLE
`cmsb_explorer_points_earning` (
`num` int(10) unsigned NOT NULL AUTO_INCREMENT,
`createdDate` datetime NOT NULL,
`createdByUserNum` int(10) unsigned NOT NULL,
`updatedDate` datetime NOT NULL,
`updatedByUserNum` int(10) unsigned NOT NULL,
`user` mediumtext COLLATE utf8mb4_unicode_ci,
`points_earned` mediumtext COLLATE utf8mb4_unicode_ci,
`for_trail` mediumtext COLLATE utf8mb4_unicode_ci,
PRIMARY KEY (`num`),
KEY `_auto_for_trail` (`for_trail`(16)), KEY `_auto_user` (`user`(16))
) ENGINE=InnoDB AUTO_INCREMENT=17191 DEFAULT
CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
MySQL版本:5.6.10
DEV/LOCAL
CREATE TABLE
`cmsb_explorer_points_earning` (
`num` int unsigned NOT NULL AUTO_INCREMENT,
`createdDate` datetime NOT NULL,
`createdByUserNum` int unsigned NOT NULL,
`updatedDate` datetime NOT NULL,
`updatedByUserNum` int unsigned NOT NULL,
`user` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`points_earned` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`for_trail` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
PRIMARY KEY (`num`),
KEY `_auto_user` (`user`(16)), KEY `_auto_for_trail` (`for_trail`(16))
) ENGINE=InnoDB AUTO_INCREMENT=17181 DEFAULT
CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
MySQL版本:8.0.20-0ubuntu0.20.04.1
添加这个:
INDEX(user, for_trail)
此时,INDEX(user)
是多余的,应该删除。
如果“用户”是用户名,则不需要 MEDIUMTEXT
(最大 16M 字节)。您也不需要 TEXT
(最大 64K 字节)。把它改成像 VARCHAR(100)
.
这样文明的东西
其他 MEDIUMTEXTs
同上,如果可行的话。届时,我建议的索引将起作用。
另一种解决方法是像您尝试的那样使用“前缀”:
KEY `_auto_for_trail` (`for_trail`(16)),
KEY `_auto_user` (`user`(16))
但是,你已经发现那是没有用的
此外,没有 ORDER BY
的 LIMIT
会使 15 行的选择不可预测。
MySQL 通过 AWS RDS (Aurora, db.t3.small) 是 运行。全面的资源利用率完全在令人满意的水平。
如果我执行以下 SELECT,它几乎是即时的:
SELECT SQL_CALC_FOUND_ROWS `submissions`.*
FROM `cmsb_submissions` as `submissions`
WHERE (status = '4')
LIMIT 15
如果我添加 LEFTJOIN,则需要 15 秒以上:
SELECT SQL_CALC_FOUND_ROWS `submissions`.*,
`explorer_points_earning`.`num` AS `explorer_points_earning.num`,
`explorer_points_earning`.`createdDate` AS `explorer_points_earning.createdDate`,
`explorer_points_earning`.`createdByUserNum` AS `explorer_points_earning.createdByUserNum`,
`explorer_points_earning`.`updatedDate` AS `explorer_points_earning.updatedDate`,
`explorer_points_earning`.`updatedByUserNum` AS `explorer_points_earning.updatedByUserNum`,
`explorer_points_earning`.`user` AS `explorer_points_earning.user`,
`explorer_points_earning`.`points_earned` AS `explorer_points_earning.points_earned`,
`explorer_points_earning`.`for_trail` AS `explorer_points_earning.for_trail`
FROM `cmsb_submissions` as `submissions`
LEFT JOIN `cmsb_explorer_points_earning` AS `explorer_points_earning`
ON submissions.num = explorer_points_earning.for_trail
AND explorer_points_earning.user = 7
WHERE (status = '4')
LIMIT 15
table 都不大:
explorer_points_earning = 17,000 records
submissions = 1,000 records
并且存在以下索引:
SHOW INDEX FROM cmsb_explorer_points_earning;
'cmsb_explorer_points_earning', '0', 'PRIMARY', '1', 'num', 'A', '17155', NULL, NULL, '', 'BTREE', '', ''
'cmsb_explorer_points_earning', '1', '_auto_for_trail', '1', 'for_trail', 'A', '2450', '16', NULL, 'YES', 'BTREE', '', ''
'cmsb_explorer_points_earning', '1', '_auto_user', '1', 'user', 'A', '3431', '16', NULL, 'YES', 'BTREE', '', ''
因此,无论如何这都不是一个大查询。
完全相同的查询(使用 LEFTJOIN),在我的本地开发服务器(更少的资源)上具有完全相同的数据库转储是即时的。
我确信这不是 RDS“资源”问题,因为我将实例加倍为 t3.medium,但没有任何区别。
这让我认为这是索引错误或某些 RDS 特定配置错误。
解释:
在 RDS 上:
'1', 'SIMPLE', 'submissions', 'ALL', NULL, NULL, NULL, NULL, '1100', 'Using where'
'1', 'SIMPLE', 'explorer_points_earning', 'ALL', '_auto_for_trail', NULL, NULL, NULL, '17155', 'Range checked for each record (index map: 0x2)'
开发中:
'1', 'SIMPLE', 'submissions', NULL, 'ALL', NULL, NULL, NULL, NULL, '1064', '100.00', NULL
'1', 'SIMPLE', 'explorer_points_earning', NULL, 'ALL', '_auto_user,_auto_for_trail', NULL, NULL, NULL, '17082', '100.00', 'Using where; Using join buffer (hash join)'
Notable 区别在于 RDS 只有“auto_for_trail”作为 'possible key' 而 DEV 有两个字段,_auto_user& _auto_for_trail。 RDS 使用嵌套循环,DEV 使用散列连接。
毫无疑问,我可以提供更多信息来帮助调试此问题,但我不确定是什么,所以请让我知道还有什么可以提供帮助。
任何帮助,非常感谢,
谢谢
-- 额外信息 --
SHOW CREATE TABLE cmsb_explorer_points_earning;
RDS
CREATE TABLE
`cmsb_explorer_points_earning` (
`num` int(10) unsigned NOT NULL AUTO_INCREMENT,
`createdDate` datetime NOT NULL,
`createdByUserNum` int(10) unsigned NOT NULL,
`updatedDate` datetime NOT NULL,
`updatedByUserNum` int(10) unsigned NOT NULL,
`user` mediumtext COLLATE utf8mb4_unicode_ci,
`points_earned` mediumtext COLLATE utf8mb4_unicode_ci,
`for_trail` mediumtext COLLATE utf8mb4_unicode_ci,
PRIMARY KEY (`num`),
KEY `_auto_for_trail` (`for_trail`(16)), KEY `_auto_user` (`user`(16))
) ENGINE=InnoDB AUTO_INCREMENT=17191 DEFAULT
CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
MySQL版本:5.6.10
DEV/LOCAL
CREATE TABLE
`cmsb_explorer_points_earning` (
`num` int unsigned NOT NULL AUTO_INCREMENT,
`createdDate` datetime NOT NULL,
`createdByUserNum` int unsigned NOT NULL,
`updatedDate` datetime NOT NULL,
`updatedByUserNum` int unsigned NOT NULL,
`user` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`points_earned` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`for_trail` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
PRIMARY KEY (`num`),
KEY `_auto_user` (`user`(16)), KEY `_auto_for_trail` (`for_trail`(16))
) ENGINE=InnoDB AUTO_INCREMENT=17181 DEFAULT
CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
MySQL版本:8.0.20-0ubuntu0.20.04.1
添加这个:
INDEX(user, for_trail)
此时,INDEX(user)
是多余的,应该删除。
如果“用户”是用户名,则不需要 MEDIUMTEXT
(最大 16M 字节)。您也不需要 TEXT
(最大 64K 字节)。把它改成像 VARCHAR(100)
.
其他 MEDIUMTEXTs
同上,如果可行的话。届时,我建议的索引将起作用。
另一种解决方法是像您尝试的那样使用“前缀”:
KEY `_auto_for_trail` (`for_trail`(16)),
KEY `_auto_user` (`user`(16))
但是,你已经发现那是没有用的
此外,没有 ORDER BY
的 LIMIT
会使 15 行的选择不可预测。