我有一个300万条记录表叫做“交易”。
CREATE TABLE transactions(
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
lookupAId int(6) NOT NULL,
.....
updateDate TIMESTAMP
)
在最坏的情况下,用户将不指定过滤器,查询将如下所示:
select * from transactions
join lookupA on (well indexed columns)
.. ( 12 lookup table joins)
order by updateDate limit 500
如果没有order by子句,查询将以毫秒为单位运行,但按顺序运行大约需要一分钟。该表预计将增长到1200万至1500万条记录。
我在AWS中的xLarge内存优化RDS实例中运行MySql 5.7
UPDATE 1 updateDate有一个时间组件并被索引(B树,非唯一)
更新2这有效,虽然我不知道为什么
SELECT * FROM (select * from transactions order by updateDate) transactions
join lookupA on (well indexed columns)
.. ( 12 lookup table joins)
limit 500
在限制查询大小之前,MySQL可能在查询上做了大量工作。这似乎是MySQL的一个已知弱点。
尝试从子查询中的事务中进行选择,以在执行连接之前限制结果集大小。
SELECT * FROM (select * from transactions order by updateDate limit 500) transactions
join lookupA on (well indexed columns)
.. ( 12 lookup table joins)
如果你还没有它,那么ORDER BY
肯定会从索引中获益:
create index ix1 on transactions (updateDate);
解决此问题的常用技巧:
SELECT ... JOIN ...
LIMIT ...
是:
PRIMARY KEY
行的行的LIMIT
值。JOINs
以获取其余信息。正如您的查询所示,优化器会抛出所有JOIN
(尽可能优化每个),生成一个大(多行,多列)中间表,然后应用ORDER BY
(排序许多行的许多行)列)和LIMIT
(提供其中一些行)。
使用INDEX(OrderDate)
(并且该列在表中它选择启动JOINing
),优化器至少可以考虑使用索引。但这可能是最糟糕的情况 - 如果没有500行会怎么样;无论如何,它将完成所有的工作!
优化器不知道表是一个简单的“查找”表。它必须准备好找到0行或多于1行。
案例1:您知道每个查找(JOINed
)表中只有一行:
情况2:您知道每个查找表中最多有一行。
在这两种情况下,以下是重写查询的有效方法:
SELECT t.a, t.b, ...
( SELECT name FROM LU1 WHERE id = t.name_id ) AS name,
( SELECT foo FROM LU1 WHERE id = t.foo_id ) AS foo,
...
FROM transactions AS t
ORDER BY t.OrderDate
LIMIT ...
和
INDEX(OrderDate)
INDEX(id) -- for each LU table, unless there is already `PRIMARY KEY(id)`
这个查询的表述将集中在完成500行,由OrderDate
预先排序,查找每行12个事物。
它在语义上等同于案例2(LEFT JOIN
),因为它在没有映射时为NULL
(等)提供name
。
从技术上讲,案例1并不相同。如果查找失败,JOIN
将无法计算行,但我的重新制定将保持行,显示NULL
。