MySQL - 如何优化此查询?

Posted

技术标签:

【中文标题】MySQL - 如何优化此查询?【英文标题】:MySQL - How can this query be optimised? 【发布时间】:2010-06-28 05:04:26 【问题描述】:

以下查询有效,但 10 条记录(2 秒)的速度非常慢。分析说它创建了一个 tmp 表,但我不确定为什么。

基本上,我将当前用户加入 acl 组,获取他们所在的所有组,然后将组加入公司,获取他们所在的所有公司,然后将公司加入订单,得到所有的订单..

如果我删除这一行

ORDER BY orders.created_on DESC 

然后查询在 0.06 秒内执行(超过可接受的时间)..

帮助,关于如何优化的任何想法?非常感谢:)

SELECT
    orders.uuid,
    companies.name as company_name
FROM
    users u
JOIN    
    users_acl_groups g on   u.uuid = g.user_uuid
JOIN
    users_acl acl on (acl.user_uuid = u.uuid or acl.group_uuid = g.group_uuid) 
JOIN 
    companies on acl.item_uuid = companies.uuid
JOIN
    orders on companies.uuid = orders.company_uuid
WHERE
    u.uuid = 'DDEC8073-5056-C000-01ED583A51CBCA32' and orders.status <> ''
ORDER BY orders.created_on DESC 

limit 0, 10;

UPDATE,查询的解释..

1 SIMPLE 订单 ALL 9403 使用 暂时的;使用文件排序

1 SIMPLE acl ALL 1859 使用 where; 使用连接缓冲区

1 SIMPLE g ALL 2005 使用 where; 使用连接缓冲区

1 SIMPLE 公司 eq_ref PRIMARY PRIMARY 52 table.orders.company_uuid 1

1 SIMPLE u ALL 33595 使用 where; 清楚的;使用连接缓冲区

【问题讨论】:

修改了你的标题,因为当你的问题措辞得当时,猜测它的必要性和苛刻的语气会让你付出一票否决权。 尝试使用相同的查询,并将联接应用于除 UID 以外的任何列。尝试使用 int、float、string、UID 并注意时间。如果您发现任何变化,请告诉我们。 你有关于 orders.created_on 的索引吗?在您的查询中显示EXPLAIN 上的输出。也许您可以从其他索引中受益。 EXPLAIN 会告诉我们。 是的,EXPLAIN 会有所帮助。看起来,如果没有ORDER BY,则用户可以进行查询,并与订单进行小连接;并与ORDER BY 反向,从订单、公司、acl(可能是 tmp 表的内容)之间的大型笛卡尔连接,然后最终与过滤后的用户连接... users.uuid 是否已编入索引? (可能是唯一索引)奇怪的是查询并没有试图减少更胖的表...... 【参考方案1】:

您是否考虑过将事实表样式设计作为非规范化步骤?

基本上是一种多对多的交集表,例如:

CREATE TABLE user_order_fact (
  user_uuid ...
  order_uuid ...
  order_created_on ...
  order_status ...
  company_name ...,
  primary key (user_uuid, order_uuid),
  key (user_uuid, order_status, order_created_on, order_uuid, company_name)
);

... fill with data ...

SELECT
    order_uuid,
    company_name
FROM
    user_order_fact
WHERE
    user_uuid = 'DDEC8073-5056-C000-01ED583A51CBCA32' and order_status <> ''
ORDER BY order_created_on DESC 

limit 0, 10;

我猜的是复合索引。你必须进行试验,直到你做对为止。基本上,您试图让优化器计划报告它正在使用索引

当然,这是以非规范化形式冗余存储数据,因此您需要设置一些触发器以使其与规范化表保持同步。

【讨论】:

嗯,也许 PK 只是 order_uuid。我不保证这是最好的设计,只是想让您了解我的意思。【参考方案2】:

确保“orders.created_on”有一个索引...如果有,那么比尔在顶部的方法将是最好的,但需要一些工作。

【讨论】:

我认为可以?密钥created_on (created_on)【参考方案3】:

如果不了解现有索引或每个表的数量,很难回答。

此外,没有太多关于模型的信息……查询是否返回所有结果?

所有用户都属于一个组吗?似乎不是……而且查询不会返回组外的用户。

一个组可以属于一个组,调用递归查询吗?

【讨论】:

我试图解决递归查询的问题,但运气不佳 :( 表很小,10,000 条记录。目前没有索引..跨度> @Brett:在这种情况下如果存在索引将不起作用,因为 NOT 运算符和 Like 运算符不使用索引【参考方案4】:

我不确定它需要 2 秒的确切原因是什么。此查询无法获取 10 条记录,但这里看到的是

    acl.user_uuid = u.uuid or acl.group_uuid = g.group_uuid

    基于 UID 的连接,可能你也是 将其用作主键 上面已经回答了。

    ORDER BY orders.created_on。在date 上使用Order by 不如使用PK 或任何整数值最佳 比较合适。

    orders.status &lt;&gt; '' 如果在表上使用任何索引,则没有索引 可以在这个查询中使用,因为 NOT 运算符和 Like 运算符在任何查询中使用时都不使用索引。

    表中存在的记录量可能是另一个原因,但仅是由于上述因素。否则它也可以处理大容量。

我认为是 UID 用于联接的主要因素 因此,在您的查询中可以看到所有三个避免条件,这可能会使您的查询变得懒惰

【讨论】:

您好,感谢您的回复。.. 2号中最小化是什么意思?【参考方案5】:

几个想法:

您实际上没有在查询中选择orders.created_on。因此,没有必要对该列进行排序。也许,选择它 (SELECT orders.created_on ...) 会提高性能(只是胡乱猜测——我不知道我在说什么)。

您始终可以在您的应用程序中进行排序——如果您的查询返回的记录数量不多的话。

有时使用 N 个小查询而不是 1 个大 sql 查询更能提高性能。伪代码:

user_id = get_one("SELECT uuid FROM users WHERE ...");
group_ids = get_many("SELECT uuid FROM groups WHERE user_uuid = " + user_id);
comps_ids = get_many("SELECT DISTINCT item_uuid FROM acls WHERE user_uuid = " + user_id + " OR group_uuid IN " + groups_ids.to_q());
orders = get_many("SELECT * FROM orders WHERE company_uuid IN " + comps_ids.as_q() + " WHERE status <> '' ORDER BY created_on");

【讨论】:

以上是关于MySQL - 如何优化此查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 MySQL 中优化此查询

如何优化这个 MySql 查询 - 连接 3 个表?

优化 SQL:如何重写此查询以提高性能? (使用子查询,摆脱 GROUP BY?)

mysql慢查询

MySql 查询优化器

MySQL:优化查询