如何优化这个基于范围的查询?
How can I optimize this range-based query?
我有一个包含四列的 MySQL 数据库:library_name
、一个字符串、function_name
、一个字符串、low_num
、一个数字和 high_num
, 一个号码。 low_num
和high_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_num
和 high_num
进行过滤,因为两者都是不等式条件。通常,索引只有助于在相等条件下减少 N 列的检查行,然后在不等条件下最多减少一列。你的条件就像一个相等,然后是两个不等:
where library_name = $name and low_num <= $num and high_num >= $num
(equality) (inequality) (inequality)
因此索引只能优化搜索的前两个词。第三个搜索词必须“艰难地”应用,方法是测试与前两个词匹配的每一行。
将搜索应用于 high_num
而不是 low_num
可能会更好地减少检查的行数,但这取决于数据。也就是说,如果平均而言 $num
和 high_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 的回答快得多。让我们知道。)
我有一个包含四列的 MySQL 数据库:library_name
、一个字符串、function_name
、一个字符串、low_num
、一个数字和 high_num
, 一个号码。 low_num
和high_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_num
和 high_num
进行过滤,因为两者都是不等式条件。通常,索引只有助于在相等条件下减少 N 列的检查行,然后在不等条件下最多减少一列。你的条件就像一个相等,然后是两个不等:
where library_name = $name and low_num <= $num and high_num >= $num
(equality) (inequality) (inequality)
因此索引只能优化搜索的前两个词。第三个搜索词必须“艰难地”应用,方法是测试与前两个词匹配的每一行。
将搜索应用于 high_num
而不是 low_num
可能会更好地减少检查的行数,但这取决于数据。也就是说,如果平均而言 $num
和 high_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 的回答快得多。让我们知道。)