带IN子句的MySQL查询会降低性能

时间:2018-10-09 16:52:15

标签: mysql

我有一个表来存储csv文件中的数据。它是一个大表(超过4000万行)。这是它的结构:

CREATE TABLE `imported_lines` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `day` date NOT NULL,
  `name` varchar(256) NOT NULL,
  `origin_id` int(11) NOT NULL,
  `time` time(3) NOT NULL,
  `main_index` tinyint(4) NOT NULL DEFAULT 0,
  `transaction_index` tinyint(4) NOT NULL DEFAULT 0,
  `data` varchar(4096) NOT NULL,
  `error` bit(1) NOT NULL,
  `expressions_applied` bit(1) NOT NULL,
  `count_records` smallint(6) NOT NULL DEFAULT 0,
  `client_id` tinyint(4) NOT NULL DEFAULT 0,
  `receive_date` datetime(3) NOT NULL,
  PRIMARY KEY (`id`,`client_id`),
  UNIQUE KEY `uq` (`client_id`,`name`,`origin_id`,`receive_date`),
  KEY `dh` (`day`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 
/*!50100 PARTITION BY HASH (`client_id`) PARTITIONS 15 */

当我使用一天过滤条件执行SELECT时,它非常快(0.4 s)返回数据。但是,随着日期范围的增加,它会变慢,直到出现超时错误。

这是查询:

SELECT origin_id, error, main_index, transaction_index, 
expressions_applied, name, day,    
COUNT(id) AS total, SUM(count_records) AS sum_records
FROM imported_lines FORCE INDEX (dh)
WHERE client_id = 1
AND day >= '2017-07-02' AND day <= '2017-07-03'  
AND name IN ('name1', 'name2', 'name3', ...)  
GROUP BY origin_id, error, main_index, transaction_index, expressions_applied, name, day;

我认为IN子句可能会失去性能。我还尝试向该查询添加uq索引,这带来了一点收获(FORCE INDEX (dh, uq))。 另外,我尝试进行INNER JOIN (SELECT name FROM providers WHERE id = 2) prov ON prov.name = il.name,但也无法加快查询速度。

编辑 解释查询

id - 1
select_type - SIMPLE
table - imported_lines
type - range
possible_keys - uq, dh
key - dh
key_len - 261
ref - NULL
rows - 297988
extra - Using where; Using temporary; Using filesort

有什么建议应该做什么?

1 个答案:

答案 0 :(得分:0)

我做了一些更改,添加了一个具有多列的新索引(如@Uueerdo所建议),并根据另一个用户的建议重写了查询(但他删除了答案)。

我运行了一些EXPLAIN PARTITIONS的查询,并通过SQL_NO_CACHE进行了测试,以确保它不会使用缓存,并且在一个月的时间内搜索数据现在需要1.8秒

它快得多了! 这就是我所做的:

ALTER TABLE `imported_lines` DROP INDEX dh;
ALTER TABLE `imported_lines` ADD INDEX dhc (`day`, `name`, `client_id`);

查询:

SELECT origin_id, error, main_index, transaction_index, 
expressions_applied, name, day,    
COUNT(id) AS total, SUM(count_records) AS sum_records
FROM imported_lines il
INNER JOIN (
    SELECT id FROM imported_lines
    WHERE client_id = 1 
    AND day >= '2017-07-01' AND day <= '2017-07-31'  
    AND name IN ('name1', 'name2', 'name3', ...)  
) AS il_filter
ON il_filter.id = il.id
WHERE il.client_id = 1
GROUP BY origin_id, error, main_index, transaction_index, expressions_applied, name, day;

我意识到使用INNER JOINEXPLAIN PARTITIONS开始使用索引。同样使用WHERE il.client_id = 1,查询会减少要查找的分区数。

感谢您的帮助!