优化订单,限制在 MySql

Optimizing order by with a limit in MySql

我有一个名为 "transactions" 的 300 万条记录 table 。

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 子句查询 运行s 以毫秒为单位,但是使用 order by 大约需要一个分钟。 table 预计将增长到 12-15 百万条记录。

  1. 我的 SLA 是在一秒钟内得到结果,在 MySql 内可能吗?
  2. 如何优化 order by 子句以实现此效果。

I 运行 MySql AWS 中 xLarge 内存优化 RDS 实例中的 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

如果您还没有它,ORDER BY 肯定会从索引中受益:

create index ix1 on transactions (updateDate);

MySQL 可能在使用 limit 限制查询大小之前对查询做了很多工作。这似乎是 MySQL.

的一个已知弱点

尝试从子查询中的事务中执行 select 以在执行连接之前限制结果集的大小。

SELECT * FROM (select * from transactions order by updateDate limit 500) transactions
   join lookupA on (well indexed columns) 
   .. ( 12 lookup table joins) 

解决这个问题的常用技巧:

SELECT ... JOIN ...
    LIMIT ...

是:

  1. 做最少的工作来找到影响 LIMIT 行的行的 PRIMARY KEY 值。
  2. 将这些 ID 填入 JOINs 以获取其余信息。

当你的查询出现时,优化器会举起手来简单地做所有的JOIN(尽可能地优化每一个),生成一个大的(很多行,很多列)中间table,然后应用 ORDER BY(对许多列的许多行进行排序)和 LIMIT(提供其中一些行)。

使用 INDEX(OrderDate)(并且该列在 table 中,它选择以 JOINing 开始)优化器至少可以考虑使用索引。但这可能是最坏的情况——如果没有 500 行怎么办?它无论如何都会完成所有工作!

优化器不知道 table 是一个简单的 "lookup" table。必须准备查找0行或多于1行。

情况 1:您知道每个查找中正好有 1 行 (JOINed)tables:

情况 2:您知道每次查找中最多有 1 行 table。

在这两种情况下,以下是重写查询的有效方法:

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),因为它在没有映射时为 name(等)给出 NULL

从技术上讲,案例 1 并不相同。如果查找失败,JOIN 将无法计算该行,但我的重新制定将保留该行,显示 NULL.