【发布时间】:2021-08-25 01:34:29
【问题描述】:
我目前正在从事在线电子商务平台后台工作。 我目前有大约 70 000 种产品,我想加快数据的显示速度,以便员工可以更高效地工作。
我正在使用 MySQL“Ver 14.14 Distrib 5.7.28”。
基本上对于我的后台(我不会明确列出列的详细信息,因为我认为这并不重要),我有:
- 主表
node_node包含所有数据的基本信息,例如creation_date、last_modification_date(日期字段) - 主表
staff_node_staffnode包含员工创建的所有数据(如产品、品牌等)的基本信息。它主要包含字段owner_id(staff 表的外键,我在这里不详述)和is_verified(布尔字段)和指向node_node的外键staffnode_ptr_id - 像
product_merchandise、product_brand这样的数据结构表,它们包含自己的字段和指向staff_node_staffnode的外键staffnode_ptr_id
我首先运行一个查询来检索我想要显示的产品的所有 ID(考虑到大量数据,我更喜欢首先只检索我的列表中产品的 ID,每页限制为 30 个,并且然后在这个子集上检索更多数据,并在其他表上进行更多连接)
SELECT id from product_merchandise pm
INNER JOIN staff_node_staffnode sns ON sns.node_ptr_id = pm.staffnode_ptr_id
INNER JOIN node_node nn ON nn.id = sns.node_ptr_id
ORDER BY creation_date DESC LIMIT 30;
product_merchandise(staffnode_ptr_id) 和 staff_node_staffnode(node_ptr_id) 和 node_node(id) 上有一个索引。
运行此查询平均需要 2 到 3 秒,这太长了。
编辑:正如 cmets 中所建议的,这里是 EXPLAIN 查询的输出。 EXPLAIN ANALYZE 不适用于我的 Mysql 版本。
+----+-------------+-------+------------+--------+---------------+------------------------------+---------+------------------------+-------+----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+------------------------------+---------+------------------------+-------+----------+----------------------------------------------+
| 1 | SIMPLE | pm | NULL | index | PRIMARY | product_merchandise_447d3092 | 5 | NULL | 69623 | 100.00 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | sns | NULL | eq_ref | PRIMARY | PRIMARY | 4 | db.pm.staffnode_ptr_id | 1 | 100.00 | Using index |
| 1 | SIMPLE | nn | NULL | eq_ref | PRIMARY | PRIMARY | 4 | db.pm.staffnode_ptr_id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+------------------------------+---------+------------------------+-------+----------+----------------------------------------------+
我决定在node_node(creation_date) 上添加一个索引creation_date_idx,当我强制使用它时,我得到了0.10s 到0.15s 之间,这是完美的:
SELECT id from product_merchandise pm
INNER JOIN staff_node_staffnode sns ON sns.node_ptr_id = pm.staffnode_ptr_id
INNER JOIN node_node nn FORCE INDEX(creation_date_idx) ON nn.id = sns.node_ptr_id
ORDER BY creation_date DESC LIMIT 30;
现在的问题是,做产品的工作人员应该可以根据不同的参数进行过滤,比如owner_id。
SELECT id from product_merchandise pm
INNER JOIN staff_node_staffnode sns ON sns.node_ptr_id = pm.staffnode_ptr_id
INNER JOIN node_node nn FORCE INDEX(creation_date_idx) ON nn.id = sns.node_ptr_id
WHERE sns.owner_id = [NUMBER]
ORDER BY creation_date DESC LIMIT 30;
结果很糟糕(我在 30 秒左右停止了查询,但我认为这可能需要更多时间),这是有道理的,因为我强制使用与此处无关的索引 creation_date_index。
如果我不使用这个索引,我会得到更好的结果(1-2 秒),但我又回到了第一个问题:计算时间太长。
编辑:按照建议,这里是 EXPLAIN for 的输出
SELECT id from product_merchandise pm
INNER JOIN staff_node_staffnode sns ON sns.node_ptr_id = pm.staffnode_ptr_id
INNER JOIN node_node nn ON nn.id = sns.node_ptr_id
WHERE sns.owner_id = [NUMBER]
ORDER BY creation_date DESC LIMIT 30;
+----+-------------+-------+------------+--------+---------------------------------------+------------------------------+---------+------------------------+-------+----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------------------------------+------------------------------+---------+------------------------+-------+----------+----------------------------------------------+
| 1 | SIMPLE | pm | NULL | index | PRIMARY | product_merchandise_447d3092 | 5 | NULL | 69220 | 100.00 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | sns | NULL | eq_ref | PRIMARY,staff_node_staffnode_5e7b1936 | PRIMARY | 4 | db.pm.staffnode_ptr_id | 1 | 19.00 | Using where |
| 1 | SIMPLE | nn | NULL | eq_ref | PRIMARY | PRIMARY | 4 | db.pm.staffnode_ptr_id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------------------------------+------------------------------+---------+------------------------+-------+----------+----------------------------------------------+
我想我应该创建另一个索引,但我真的不知道在哪些列上。
此外,工作人员应该能够过滤 5 个不同的字段(假设它们都是 VARCHAR 或 FOREIGN KEY 或 BOOLEAN),并按这些不同的字段排序。这些字段可能来自表product_merchandise(例如product_name)或staff_node_staffnode(创建者或is_verified)或事件node_node(例如creation_date)。
我希望我说得够清楚。 感谢您的宝贵时间,我将不胜感激!
祝你有美好的一天。
【问题讨论】:
-
你必须为你的慢查询分享你的执行计划
-
感谢您的回答。执行计划是什么意思?你想让我使用 EXPLAIN sql 函数吗?
-
是的
EXPLAIN ANALYZE -
好的,我按照你的建议添加了 EXPLAIN。 EXPLAIN ANALYZE 不适用于我拥有的 MySQL 版本(Ver 14.14 Distrib 5.7.28)。此外,我只在第一个查询中使用了 EXPLAIN,而不是超过 30 秒的查询,因为我认为它不相关。
-
请为表格提供
SHOW CREATE TABLE。然后我们可以讨论您可能需要哪些额外的索引。
标签: mysql sql indexing sql-order-by query-optimization