为啥相同的查询在从 MySQL 服务器上的解释和执行如此不同?
Posted
技术标签:
【中文标题】为啥相同的查询在从 MySQL 服务器上的解释和执行如此不同?【英文标题】:Why does the same query explain & perform so differently on a slave MySQL server than a master?为什么相同的查询在从 MySQL 服务器上的解释和执行如此不同? 【发布时间】:2016-02-12 12:02:55 【问题描述】:我有一个主 mysql 服务器和一个从服务器。数据在它们之间复制。
当我在主服务器上运行这个查询时,它需要几个小时;在奴隶上需要几秒钟。 EXPLAIN 计划支持这一点——从服务器检查的行数远少于主服务器。
但是,由于这两个数据库中的结构和数据完全相同(或者至少应该是),并且它们都运行相同版本的 MySQL(5.5.31 Enterprise),所以我不明白什么是造成这种情况。
这与this question(和其他人)的症状相似,但我认为这不是相同的根本原因,因为我的两台服务器通过 MySQL 复制同步,并且结构和数据内容是(或应该是)相同,并且两台服务器上的操作系统和硬件资源完全相同——它们是 VMWare,一个是另一个的映像。
我已经验证了每个表中的行数在两台服务器上完全相同,并且它们的配置相同(除了从站具有指向主站的指令)。没有通过数据本身来查看是否有任何差异,我不确定我还能检查什么,如果有任何建议,我将不胜感激。
查询是
SELECT COUNT(DISTINCT(cds.company_id))
FROM jobsmanager.companies c
, jobsmanager.company_jobsmanager_settings cjs
, jobsmanager.company_details_snapshot cds
, vacancies v
WHERE c.company_id = cjs.company_id
AND cds.company_id = c.company_id
AND cds.company_id = v.jobsmanager_company_id
AND cjs.is_post_a_job = 'Y'
AND cjs.can_access_jobsmanager = 'Y'
AND cjs.account_status != 'suspended'
AND v.last_live BETWEEN cds.record_date - INTERVAL 365 DAY AND cds.record_date
AND cds.record_date BETWEEN '2016-01-30' AND '2016-02-05';
高手是这样解释的,驱动表300万行,没有key使用,需要一个多小时才能返回结果:
+----+-------------+-------+--------+-------------------------+----------------+---------+---------------------------------+---------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-------------------------+----------------+---------+---------------------------------+---------+--------------------------+
| 1 | SIMPLE | v | ALL | job_owner,last_live_idx | NULL | NULL | NULL | 3465433 | |
| 1 | SIMPLE | c | eq_ref | PRIMARY | PRIMARY | 4 | s1jobs.v.jobsmanager_company_id | 1 | Using where; Using index |
| 1 | SIMPLE | cds | ref | PRIMARY,company_id_idx | company_id_idx | 4 | jobsmanager.c.company_id | 538 | Using where |
| 1 | SIMPLE | cjs | eq_ref | PRIMARY,qidx,qidx2 | PRIMARY | 4 | jobsmanager.c.company_id | 1 | Using where |
+----+-------------+-------+--------+-------------------------+----------------+---------+---------------------------------+---------+--------------------------+
slave 使用不同的驱动表,使用索引,预测大约 310,000 行检查,并在几秒钟内返回结果:
+----+-------------+-------+--------+-------------------------+-----------+---------+----------------------------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-------------------------+-----------+---------+----------------------------+--------+--------------------------+
| 1 | SIMPLE | cds | range | PRIMARY,company_id_idx | PRIMARY | 3 | NULL | 310381 | Using where; Using index |
| 1 | SIMPLE | c | eq_ref | PRIMARY | PRIMARY | 4 | jobsmanager.cds.company_id | 1 | Using index |
| 1 | SIMPLE | cjs | eq_ref | PRIMARY,qidx,qidx2 | PRIMARY | 4 | jobsmanager.c.company_id | 1 | Using where |
| 1 | SIMPLE | v | ref | job_owner,last_live_idx | job_owner | 2 | jobsmanager.cds.company_id | 32 | Using where |
+----+-------------+-------+--------+-------------------------+-----------+---------+----------------------------+--------+--------------------------+
我已经在两台服务器上运行了 ANALYZE TABLE、OPTIMIZE TABLE 和 REPAIR TABLE ... QUICK 以尝试使它们保持一致,但没有运气。
作为临时解决方案,我可以在从属服务器上运行查询,因为它们在 cron 脚本中,即使它们在从属服务器上花费很长时间,它们也不会像运行时那样增加主服务器的负载在主人身上。但是,我将不胜感激有关为什么这些不同的任何其他信息,或者我可以检查/修改的其他信息,这将解释两者之间如此巨大的差异。我唯一能找到的是奴隶有更多的 free 内存,因为它很少使用;仅此一项就可以解释吗?如果不是还有什么?
$ ssh s1-mysql-01 free # master
total used free shared buffers cached
Mem: 99018464 98204624 813840 0 160752 55060632
-/+ buffers/cache: 42983240 56035224
Swap: 4095992 4095992 0
$ ssh s1-mysql-02 free # slave
total used free shared buffers cached
Mem: 99018464 80866420 18152044 0 224772 72575168
-/+ buffers/cache: 8066480 90951984
Swap: 4095992 206056 3889936
$
非常感谢。
【问题讨论】:
这两个解释之间唯一真正大的区别是,在 master 上,空缺表上没有使用索引。您可以尝试在 master 上的 select 中放置一个索引提示(强制索引)以强制使用 job_owner 索引。您还可以尝试在 master 上对上述查询中涉及的所有表运行分析表,以确保更新表和索引统计信息。 好的,谢谢影子会做的。 您最近是否强制它在一台服务器上刷新索引?如果统计数据已过时,则可能会错误地忽略索引。我建议尝试在 master 上做一个优化表(dev.mysql.com/doc/refman/5.1/en/optimize-table.html),看看是否可以改善。 嗨,谢谢,我已经运行优化和分析,没有太大区别。我现在修改它以强制索引 (job_owner) 正如 Shadow 所建议的那样,这使 master 的 EXPLAIN 行数减少到 310k 行,现在它会在几秒钟内返回。感谢您的帮助。 @Shadow - 什么是job_owner
?这似乎无关紧要。 @JeremyJones - 请提供SHOW CREATE TABLE
。
【参考方案1】:
这两个解释之间唯一真正大的区别是,在 master 上,空缺表上没有使用索引。
您可以尝试将index hint(强制索引)放入 master 上的 select 以强制使用 job_owner 索引。
您也可以尝试在主节点上对上述查询中涉及的所有表运行analyze table,以确保更新表和索引统计信息。
【讨论】:
在查询中添加 FORCE INDEX job_owner 成功将 master 上的 EXPLAIN 计划更改为 310k 行,与从属类似。以上是关于为啥相同的查询在从 MySQL 服务器上的解释和执行如此不同?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我们不应该在生产服务器上的 mysql 查询中使用 Select *?
MySQL 专家:为啥 2 个查询给出不同的“解释”索引使用结果?