如何优化这个基于范围的查询?

How can I optimize this range-based query?

我有一个包含四列的 MySQL 数据库:library_name、一个字符串、function_name、一个字符串、low_num、一个数字和 high_num, 一个号码。 low_numhigh_num一起代表一个范围,属于同一个库(具有相同library_name)的所有范围是互斥的。我需要 运行 支持一个查询,给定 ~14,000 str-num 对,对于每一对,它 returns 行 low_num <= num < high_num and library_name = str。我已经创建了我的主键 (library_name, low_num),我目前最快的查询是 (select * from table where library_name = $name and $num between low_num and high_num limit 1) union (select ...) ...。即,每一对都有自己的查询,然后它们全部联合在一起。但是,它仍然很慢(大约需要 20 秒)。当我对 14,000 对进行这样的 1 次查询时,我也 运行 遇到了内存问题,所以我不得不将它分成 ~14 个查询,每个查询找到 1,000 对(但即使是这 1,000 对查询中的一个它自己需要 4 秒)。关于如何加快此查询的任何想法?

SHOW CREATE TABLE: https://db-fiddle.com/f/bjB1zLez2suhdCzt6itge5/0

EXPLAIN SELECT(对于联合中的一个选择):https://db-fiddle.com/f/hyq7aKN89soLZDPyxyJu5T/0

好的,下面是解释:

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
#3 UNION sym_large NULL range PRIMARY PRIMARY 1026 NULL 14000 11.11 Using where

table的主键在(library_name, low_nm)上,分别是VARCHAR(255)和INT。 1026 的 key_len 表示它正在使用 PRIMARY 键的两列:4 个字节用于 INT,255*4 用于 utf8mb4 字符串加上 2 个字节用于字符串长度。

可能无法对 low_numhigh_num 进行过滤,因为两者都是不等式条件。通常,索引只有助于在相等条件下减少 N 列的检查行,然后在不等条件下最多减少一列。你的条件就像一个相等,然后是两个不等:

where library_name = $name and low_num <= $num and high_num >= $num
               (equality)          (inequality)         (inequality)

因此索引只能优化搜索的前两个词。第三个搜索词必须“艰难地”应用,方法是测试与前两个词匹配的每一行。

将搜索应用于 high_num 而不是 low_num 可能会更好地减少检查的行数,但这取决于数据。也就是说,如果平均而言 $numhigh_num 之间的行数比 low_num$num 之间的行数要少,它可能会更有效率。

如果这是真的,那么在 (library_name, high_num) 上使用不同的索引可能会有所帮助。但它仍然仅限于搜索三个条件中的两个。无法在同一个查询中过滤两个不等式。

如果将 14,000 个 name/number 对加载到临时 table 中并执行 JOIN 而不是数千个子查询,也可以避免 UNION。

CREATE TABLE tmp_search_pairs (
  library_name VARCHAR(255) NOT NULL,
  num INT UNSIGNED NOT NULL,
  KEY (library_name, num)
);

...insert your search data into the temp table...

SELECT s.*
FROM sym_large AS s
JOIN tmp_search_pairs AS p
  ON s.library_name = p.library_name 
  AND s.low_num <= p.num 
  AND s.high_num >= p.num;

我认为使用不等式语法与 BETWEEN 语法是个人偏好。他们应该优化相同。

SELECT *
    FROM ( SELECT * FROM sym_large
            WHERE library_name = ?
              AND low_num >= ?
            ORDER BY low_num
            LIMIT 1
         ) AS x
    WHERE high_num <= ?;

内查询因为PK很快,查到一行。外部验证 high_num 没有被违反。如果您确定总会有匹配项,则只需执行内部查询。

(我认为这 运行 比 Bill 的回答快得多。让我们知道。)