通过示例提高查询性能
Improving query performance by example
我正在尝试想出一种方法来改进查询,所使用的架构如下所示:
CREATE TABLE `orders` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`store_id` INTEGER NOT NULL,
`billing_profile_id` INTEGER NOT NULL,
`billing_address_id` INTEGER NULL,
`total` DECIMAL(8, 2) NOT NULL
);
CREATE TABLE `billing_profiles` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`name` TEXT NOT NULL
);
CREATE TABLE `billing_addresses` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`address` TEXT NOT NULL
);
CREATE TABLE `stores` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`name` TEXT NOT NULL
);
我正在执行的查询:
SELECT bp.name,
ba.address,
s.name,
Sum(o.total) AS total
FROM billing_profiles bp,
stores s,
orders o
LEFT JOIN billing_addresses ba
ON o.billing_address_id = ba.id
WHERE o.billing_profile_id = bp.id
AND s.id = o.store_id
GROUP BY bp.name,
ba.address,
s.name;
这里是 EXPLAIN
:
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------------------+-------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------------------+-------+----------+--------------------------------------------+
| 1 | SIMPLE | bp | NULL | ALL | PRIMARY | NULL | NULL | NULL |155000 | 100.00 | Using temporary |
| 1 | SIMPLE | o | NULL | ALL | NULL | NULL | NULL | NULL |220000 | 33.33 | Using where; Using join buffer (hash join) |
| 1 | SIMPLE | ba | NULL | eq_ref | PRIMARY | PRIMARY | 4 | factory.o.billing_address_id | 1 | 100.00 | NULL |
| 1 | SIMPLE | s | NULL | eq_ref | PRIMARY | PRIMARY | 4 | factory.o.store_id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------------------+------+----------+--------------------------------------------+
我面临的问题是这个查询需要 30 多秒才能执行,我们有超过 200000 个订单,还有 150000+ billing_profiles/billing_addresses.
关于 index/constraints 我应该怎么做才能使此查询执行得更快?
编辑: 在评论中提出一些建议后,我将查询编辑为:
SELECT bp.name,
ba.address,
s.name,
Sum(o.total) AS total
FROM orders o
INNER JOIN billing_profiles bp
ON o.billing_profile_id = bp.id
INNER JOIN stores s
ON s.id = o.store_id
LEFT JOIN billing_addresses ba
ON o.billing_address_id = ba.id
GROUP BY bp.name,
ba.address,
s.name;
但是还是太费时间了。
我过去使用过并在许多情况下对 MySQL 有帮助的一件事是使用 STRAIGHT_JOIN
子句,它告诉引擎按照列出的顺序执行查询。
我已将您的查询清理到正确的 JOIN 上下文。由于 ORDER table 是数据的主要基础,而其他 3 个是对其各自 ID 的查找引用,因此我将 ORDER table 放在第一位。
SELECT STRAIGHT_JOIN
bp.name,
ba.address,
s.name,
Sum(o.total) AS total
FROM
orders o
JOIN stores s
ON o.store_id = s.id
JOIN billing_profiles bp
on o.billing_profile_id = bp.id
LEFT JOIN billing_addresses ba
ON o.billing_address_id = ba.id
GROUP BY
bp.name,
ba.address,
s.name
现在,您的数据 table 看起来没有那么大,但是如果您要按顺序 table 中的 3 列进行分组,我会在基础上创建一个索引它们的基础是链接到其他 table 的“ID”键。添加总计以帮助覆盖索引/聚合查询,我将在
上建立索引
( store_id, billing_profile_id, billing_address_id, total )
我敢肯定,在现实中,您有许多其他列与订单相关联并且只是显示此查询的上下文。然后,我会更改为预查询,以便通过 ID 键对订单 table 完成一次聚合,然后将结果加入查找 tables,你只需要申请最终输出的 ORDER BY 子句。有点像..
SELECT
bp.name,
ba.address,
s.name,
o.total
FROM
( select
store_id,
billing_profile_id,
billing_address_id,
sum( total ) total
from
orders
group by
store_id,
billing_profile_id,
billing_address_id ) o
JOIN stores s
ON o.store_id = s.id
JOIN billing_profiles bp
on o.billing_profile_id = bp.id
LEFT JOIN billing_addresses ba
ON o.billing_address_id = ba.id
ORDER BY
bp.name,
ba.address,
s.name
将此索引添加到o
,确保以billing_profile_id
:
开头
INDEX(billing_profile_id, store_id, billing_address_id, total)
解释的讨论:
- 优化器发现它需要对 一些 table.
进行全面扫描
bp
比 o
小,所以它选择 bp
作为“第一个”table。
- 然后它反复进入下一个table。
- 它没有看到 suitable 索引(一个以
billing_profile_id
开头的索引)并决定做“使用连接缓冲区(散列连接)”,这涉及加载整个 table 到 RAM 中的散列。
- “使用临时”,虽然在“第一个”table 中提到,但直到
GROUP BY
之前才真正出现。 (GROUP BY
引用了多个table,没办法优化。)
潜在错误 请检查 Sum(o.total) AS total
的结果。它在之后所有JOINing
和之前和GROUP BY
执行,所以它可能 膨胀。请注意 DRapp 的公式如何在 SUM
之前 JOIN。
我正在尝试想出一种方法来改进查询,所使用的架构如下所示:
CREATE TABLE `orders` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`store_id` INTEGER NOT NULL,
`billing_profile_id` INTEGER NOT NULL,
`billing_address_id` INTEGER NULL,
`total` DECIMAL(8, 2) NOT NULL
);
CREATE TABLE `billing_profiles` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`name` TEXT NOT NULL
);
CREATE TABLE `billing_addresses` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`address` TEXT NOT NULL
);
CREATE TABLE `stores` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`name` TEXT NOT NULL
);
我正在执行的查询:
SELECT bp.name,
ba.address,
s.name,
Sum(o.total) AS total
FROM billing_profiles bp,
stores s,
orders o
LEFT JOIN billing_addresses ba
ON o.billing_address_id = ba.id
WHERE o.billing_profile_id = bp.id
AND s.id = o.store_id
GROUP BY bp.name,
ba.address,
s.name;
这里是 EXPLAIN
:
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------------------+-------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------------------+-------+----------+--------------------------------------------+
| 1 | SIMPLE | bp | NULL | ALL | PRIMARY | NULL | NULL | NULL |155000 | 100.00 | Using temporary |
| 1 | SIMPLE | o | NULL | ALL | NULL | NULL | NULL | NULL |220000 | 33.33 | Using where; Using join buffer (hash join) |
| 1 | SIMPLE | ba | NULL | eq_ref | PRIMARY | PRIMARY | 4 | factory.o.billing_address_id | 1 | 100.00 | NULL |
| 1 | SIMPLE | s | NULL | eq_ref | PRIMARY | PRIMARY | 4 | factory.o.store_id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------------------+------+----------+--------------------------------------------+
我面临的问题是这个查询需要 30 多秒才能执行,我们有超过 200000 个订单,还有 150000+ billing_profiles/billing_addresses.
关于 index/constraints 我应该怎么做才能使此查询执行得更快?
编辑: 在评论中提出一些建议后,我将查询编辑为:
SELECT bp.name,
ba.address,
s.name,
Sum(o.total) AS total
FROM orders o
INNER JOIN billing_profiles bp
ON o.billing_profile_id = bp.id
INNER JOIN stores s
ON s.id = o.store_id
LEFT JOIN billing_addresses ba
ON o.billing_address_id = ba.id
GROUP BY bp.name,
ba.address,
s.name;
但是还是太费时间了。
我过去使用过并在许多情况下对 MySQL 有帮助的一件事是使用 STRAIGHT_JOIN
子句,它告诉引擎按照列出的顺序执行查询。
我已将您的查询清理到正确的 JOIN 上下文。由于 ORDER table 是数据的主要基础,而其他 3 个是对其各自 ID 的查找引用,因此我将 ORDER table 放在第一位。
SELECT STRAIGHT_JOIN
bp.name,
ba.address,
s.name,
Sum(o.total) AS total
FROM
orders o
JOIN stores s
ON o.store_id = s.id
JOIN billing_profiles bp
on o.billing_profile_id = bp.id
LEFT JOIN billing_addresses ba
ON o.billing_address_id = ba.id
GROUP BY
bp.name,
ba.address,
s.name
现在,您的数据 table 看起来没有那么大,但是如果您要按顺序 table 中的 3 列进行分组,我会在基础上创建一个索引它们的基础是链接到其他 table 的“ID”键。添加总计以帮助覆盖索引/聚合查询,我将在
上建立索引( store_id, billing_profile_id, billing_address_id, total )
我敢肯定,在现实中,您有许多其他列与订单相关联并且只是显示此查询的上下文。然后,我会更改为预查询,以便通过 ID 键对订单 table 完成一次聚合,然后将结果加入查找 tables,你只需要申请最终输出的 ORDER BY 子句。有点像..
SELECT
bp.name,
ba.address,
s.name,
o.total
FROM
( select
store_id,
billing_profile_id,
billing_address_id,
sum( total ) total
from
orders
group by
store_id,
billing_profile_id,
billing_address_id ) o
JOIN stores s
ON o.store_id = s.id
JOIN billing_profiles bp
on o.billing_profile_id = bp.id
LEFT JOIN billing_addresses ba
ON o.billing_address_id = ba.id
ORDER BY
bp.name,
ba.address,
s.name
将此索引添加到o
,确保以billing_profile_id
:
INDEX(billing_profile_id, store_id, billing_address_id, total)
解释的讨论:
- 优化器发现它需要对 一些 table. 进行全面扫描
bp
比o
小,所以它选择bp
作为“第一个”table。- 然后它反复进入下一个table。
- 它没有看到 suitable 索引(一个以
billing_profile_id
开头的索引)并决定做“使用连接缓冲区(散列连接)”,这涉及加载整个 table 到 RAM 中的散列。 - “使用临时”,虽然在“第一个”table 中提到,但直到
GROUP BY
之前才真正出现。 (GROUP BY
引用了多个table,没办法优化。)
潜在错误 请检查 Sum(o.total) AS total
的结果。它在之后所有JOINing
和之前和GROUP BY
执行,所以它可能 膨胀。请注意 DRapp 的公式如何在 SUM
之前 JOIN。