[英]How can i improve the query performance for varchar(400) when trying to find one record in 5.4 million
[英]How can I improve query performance for 200+ million records
背景
我有一个MySQL测试环境,其中的表包含2亿多行。 在此表上必须执行两种类型的查询;
client_id
和sgtin
的列表( sgtin
可容纳50.000个项目),我需要知道表中存在哪些sgtin
。 client_id
和sgtin
的列表( sgtin
可容纳50.000个项目),我需要获取整行。 (商店,gtin ...) 对于单个“ client_id”,该表可以增长到200+百万条记录。
测试环境
至强E3-1545M / 32GB RAM / SSD。 InnoDB缓冲池24 GB。 (生产将是具有192GB RAM的更大服务器)
表
CREATE TABLE `sgtins` (
`client_id` INT UNSIGNED NOT NULL,
`sgtin` varchar(255) NOT NULL,
`store` varchar(255) NOT NULL,
`gtin` varchar(255) NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX (`client_id`, `store`, `sgtin`),
INDEX (`client_id`),
PRIMARY KEY (`client_id`,`sgtin`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
测验
首先,我生成了随机的sgtin值,这些值分布在10个“ client_id”上,以用2亿行填充表格。
我创建了一个基准工具,该工具可以执行尝试的各种查询。 另外,我还使用了解释计划来找出最佳性能。 对于每次测试,该工具都会从我用来填充数据库的数据中读取新的随机数据。 确保每个查询都是不同的。
在这篇文章中,我将使用28 sgtin
。 临时表
CREATE TEMPORARY TABLE sgtins_tmp_table (`sgtin` varchar(255) primary key)
engine=MEMORY;
存在查询
我使用此查询来查找sgtin
是否存在。 这也是我找到的最快的查询。 对于50K sgtin
此查询将花费3到9秒。
-- cost = 17 for 28 sgtins loaded in the temp table.
SELECT sgtin
FROM sgtins_tmp_table
WHERE EXISTS
(SELECT sgtin FROM sgtins
WHERE sgtins.client_id = 4
AND sgtins.sgtin = sgtins_tmp_table.sgtin);
选择查询
-- cost = 50.60 for 28 sgtins loaded in the temp table. 50K not usable.
SELECT sgtins.sgtin, sgtins.store, sgtins.timestamp
FROM sgtins_tmp_table, sgtins
WHERE sgtins.client_id = 4
AND sgtins_tmp_table.sgtin = sgtins.sgtin;
-- cost = 64 for 28 sgtins loaded in the temp table.
SELECT sgtins.sgtin, sgtins.store, sgtins.timestamp
FROM sgtins
WHERE sgtins.client_id = 4
AND sgtins.sgtin IN ( SELECT sgtins_tmp_table.sgtin
FROM sgtins_tmp_table);
-- cost = 50.60 for 28 sgtins loaded in the temp table.
SELECT sgtins_tmp_table.epc, sgtins.store
FROM sgtins_tmp_table, sgtins
WHERE exists (SELECT organization_id, sgtin FROM sgtins WHERE client_id = 4 AND sgtins.sgtin = sgtins_tmp_table.sgtin)
AND sgtins.client_id = 4
AND sgtins_tmp_table.sgtin = sgtins.sgtin;
摘要
存在查询是可用的,但选择速度很慢。 我该怎么办? 并欢迎任何建议:)
我会这样写你的exists
查询:
SELECT stt.sgtin
FROM sgtins_tmp_table stt
WHERE EXISTS (SELECT 1
FROM sgtins s
WHERE s.client_id = 4 AND
s.sgtin = stt.sgtin
);
对于此查询,您需要在sgtins(sgtin, client_id)
上sgtins(sgtin, client_id)
索引。
我建议重提您的EXISTS
SQL,因为相关的子查询往往会在大多数时间进行最差的优化。
建议的查询将改为使用INNER JOIN
。
SELECT filter.sgtin
FROM (SELECT '<value>' AS sgtin UNION ALL SELECT '<value>' ..) AS filter
INNER JOIN sgtins ON filter.sgtin = sgtins.sgtin WHERE sgtins.client_id = 4
最有可能的是,这比使用临时表要快。
但是您正在处理50K值,因此我有理由直接从临时表中使用动态SQL生成所需的派生表SQL。
也像我在聊天中建议的那样。
根据(sgtins, client_id)
的数据选择性,建立索引(sgtins, client_id)
很可能更有意义。
由于该索引可能会使您的相关子查询更快。
询问
# Maybe also needed to be changed with 50 K
# SET SESSION max_allowed_packet = ??;
# needed for GROUP_CONCAT as if defualts to only 1024
SET SESSION group_concat_max_len = @@max_allowed_packet;
SET @UNION_SQL = NULL;
SELECT
CONCAT(
'SELECT '
, GROUP_CONCAT(
CONCAT("'", sgtins_tmp_table.sgtin,"'", ' AS sgtin')
SEPARATOR ' UNION ALL SELECT '
)
)
FROM
sgtins_tmp_table
INTO
@UNION_SQL;
SET @SQL = CONCAT("
SELECT filter.sgtin
FROM (",@UNION_SQL,") AS filter
INNER JOIN sgtins ON filter.sgtin = sgtins.sgtin WHERE sgtins.client_id = 4
");
PREPARE q FROM @SQL;
EXECUTE q;
观看演示
由于评论而被编辑
一种更理想的方法是使用固定表,该表将为您建立索引并使用CONNECTION_ID()
来分隔搜索值。
CREATE TABLE sgtins_filter (
connection_id INT
, sgtin varchar(255) NOT NULL
, INDEX(connection_id, sgtin)
);
然后,您可以简单地在两个表之间进行联接
SELECT sgtins_filter.sgtin
FROM sgtins_filter
INNER JOIN sgtins
ON
sgtins_filter.sgtin = sgtins.sgtin
AND
sgtins_filter.connection_id = CONNECTION_ID()
AND
sgtins.client_id = 4;
观看演示
假设每个客户端有200M行,并且每个客户端不超过5万个sgtins,那么必须有超过4K的客户端吗?
仅对10个客户进行基准测试是有风险的。 在某些情况下,优化器会在使用索引和执行表扫描之间进行切换。 可能是这种情况。
因此,请说明最终目标; 我不想建议您如何更快地运行基准测试,而只是为了避免“实际”情况与建议配合使用。
另外,桩号列表是静态的吗? 您通过建议预先建立一个MEMORY
表来暗示这一点。 但这似乎并不常见。 也许“真实”情况每次都被赋予了一组不同的sgtins。
因此,我将回答这个问题:
client_id
值。 (只有10个时,Optimizer倾向于忽略索引并进行表扫描。) stgin
值 (client_id, stgin)
是唯一的 SELECT stgin FROM t WHERE client_id = 1234 AND stgin IN (..long list..)
SELECT * FROM t WHERE client_id = 1234 AND stgin IN (..long list..)
无论EXPLAIN
提供EXPLAIN
数字,以下都是这两个查询的最佳解决方案:
WHERE client_id = 1234 AND stgin IN (..long list..)`
PRIMARY KEY(client_id, stgin) -- in this order.
为什么?
client_id = constant
并跳转到stgin列表。 client_id
, SELECT
所有活动将集中在表的一小部分。 这很重要,因为它将接触的块数限制为小于buffer_pool_size。 SELECT stgin...
,独立的INDEX(client_id, stgin)
会更快,但是我不建议这样做,因为它是如此冗余,并且不会节省很多性能。 成本分析评论:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.