cod*_*eak 6 mysql performance explain slow-log
我在慢查询日志中有这个条目:
# User@Host: user[host] @ [ip]
# Thread_id: 1514428 Schema: db Last_errno: 0 Killed: 0
# Query_time: 2.795454 Lock_time: 0.000116 Rows_sent: 15 Rows_examined: 65207 Rows_affected: 0 Rows_read: 65207
# Bytsent: 26618
SET timestamp=1407511874;
select off.*,translated_title,translated_description
from ephpb2b_products off USE INDEX(id_viewed)
INNER JOIN ephpb2b_members mem
ON off.uid = mem.id
Left Join ephpb2b_product_language_new pol
ON off.id = pol.offer_id
and pol.language='en'
where off.approved=1
order by off.viewed
LIMIT 15;
Run Code Online (Sandbox Code Playgroud)
当我解释这个查询时,它绝对没问题。
mysql> explain select off.*,translated_title,translated_description from ephpb2b_products off USE INDEX(id_viewed) INNER JOIN ephpb2b_members mem ON off.uid = mem.id Left Join ephpb2b_product_language_new pol ON off.id = pol.offer_id and pol.language='en' where off.approved=1 order by off.viewed LIMIT 15;
+----+-------------+-------+--------+-------------------------+-------------+---------+---------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-------------------------+-------------+---------+---------------------------+------+-------------+
| 1 | SIMPLE | off | index | NULL | id_viewed | 4 | NULL | 3 | Using where |
| 1 | SIMPLE | mem | eq_ref | PRIMARY | PRIMARY | 4 | db.off.uid | 1 | Using index |
| 1 | SIMPLE | pol | ref | offer_id,id_language | offer_id | 5 | db.off.id | 4 | |
+----+-------------+-------+--------+-------------------------+-------------+---------+---------------------------+------+-------------+
3 rows in set (0.17 sec)
Run Code Online (Sandbox Code Playgroud)
如何优化此查询?为什么解释显示 3 行,慢查询日志说它检查了 65207 行。
为了回答这个问题,您必须了解解释上的行列是什么意思,以及基于统计和执行后统计的计算之间的区别。
当您运行解释时,行列将告诉您,对于每个表访问,将使用预期过滤器检查多少行。有两种计算方法:索引潜水(通常应该为您提供准确的结果)或使用每个引擎独立存储的近似统计数据 -每个表最多 5.6。虽然第一种方法在可以使用时是首选(单个索引列上的简单过滤器),但在许多情况下,只能使用近似值 - 否则,查询优化器将花费与查询执行本身一样多的时间。
在任何情况下,这些行都是每个表访问要读取(不返回)的计算行。即使它是精确的(并且很多时候有几个数量级的差异,但对于优化器来说仍然足够好),它也不能预测整个连接访问的实际行数。例如,如果您连接表A(精确读取X行)和表B(精确读取Y行),在 order 中A -> B,读取的实际行数将为:X + # of rows returned by A (<=X) multiplied by Y,因为标准 mysql 仅支持嵌套循环连接。
慢日志,如处理程序统计信息或其他分析机制,告诉您处理和发送的实际行数,因为这些统计信息是在执行后收集的,因此是精确的。
关于您的特定情况,EXPLAIN应该归咎于它,因为它表明第一次访问只会扫描 3 行,而实际上它可能正在执行完整索引扫描(因为它仅使用键进行排序),然后会成倍增加对于执行的每个连接。不信解释。您可以使用:
FLUSH STATUS;
-- Execute your query here
SHOW STATUS like 'Hand%';
Run Code Online (Sandbox Code Playgroud)
检查行操作的实际数量和种类(PK 访问、引用、索引扫描、表扫描)。我会用它来单独测试每个表的访问。
为了获得更具体的帮助,我们需要每个表的表结构和每个过滤条件的近似选择性。
感谢大家的见解。我通过将此查询拆分为两个不同的查询来解决此问题。首先,我查询 id,然后将这些 id 传递到其他表以获取信息。
select id from ephpb2b_products off INNER JOIN ephpb2b_members mem
ON off.uid = mem.id
where off.approved=1
order by off.viewed
LIMIT 15;
Run Code Online (Sandbox Code Playgroud)
然后:
select * from ephpb2b_product_language_new where offer_id IN ({ids from lasts query})
Run Code Online (Sandbox Code Playgroud)
这表现得更好并且不会表现得很奇怪。
| 归档时间: |
|
| 查看次数: |
7703 次 |
| 最近记录: |