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 <> ''
如果在表上使用任何索引,则没有索引
可以在这个查询中使用,因为
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 - 如何优化此查询?的主要内容,如果未能解决你的问题,请参考以下文章