我有一个有几百万行数据的表。我在 id 上有一个主键,在 col2、col3、col4 和 my_date(称为 comp_indx)上有一个复合唯一键。示例数据显示在这里...
id col2 col3 col4 my_date col5 col6 col7
1 1 1 1 2020-01-03 02:00:00 a 1 a
2 1 2 1 2020-01-03 01:00:00 b 2 1
3 1 3 1 2020-01-03 03:00:00 c 3 b
4 2 1 1 2020-02-03 01:00:00 d 4 2
5 2 2 1 2020-02-03 02:00:00 e 5 c
6 2 3 1 2020-02-03 03:00:00 f 6 3
7 3 1 1 2020-03-03 03:00:00 g 7 d
8 3 2 1 2020-03-03 02:00:00 h 8 4
9 3 3 1 2020-03-03 01:00:00 i 9 e
如果我执行以下查询...
SELECT col2, col3, max(my_date)
FROM table
where col4=1 and my_date <= '2001-01-27'
group by col2, col3
...查询非常高效,运行解释命令显示...
select_type type key key_len rows Extra
----------- ----- --------- ------- ---- -------------------------------------
SIMPLE range comp_indx 11 669 Using where; Using index for group-by
但是,如果我运行类似的命令(只请求更多的列——没有一个是索引的一部分),例如……
SELECT col2, col3, max(my_date), col5, col7
FROM table
where col4=1 and my_date <= '2001-01-27'
group by col2, col3
...然后性能立即下降,如果我再次运行 explain 命令,我会...
select_type type key key_len rows Extra
----------- ----- --------- ------- ------- -----------
SIMPLE index comp_indx 11 5004953 Using where
我可以看到类型已经从范围变为索引,并且我可以看到索引不再用于分组依据。
我试图理解为什么会这样,更重要的是,我该如何解决这个问题?
顺便说一句,表定义是...
CREATE TABLE `my_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`col2` smallint(6) NOT NULL,
`col3` smallint(6) NOT NULL,
`col4` smallint(6) NOT NULL,
`my_date` datetime NOT NULL,
`col5` char(1) NOT NULL,
`col6` char(1) NOT NULL,
`col7` char(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `comp_indx` (`col2`,`col3`,`col4`,`my_date`)
) ENGINE=InnoDB;
添加以下索引
alter table my_table add key cl4_dt_cl2_cl3 (col4,my_date,col2,col3);
此外,如果启用了sql_mode only_full_group_by,则以下查询无效
SELECT col2, col3, max(my_date), col5, col7
FROM table
where col4=1 and my_date <= '2001-01-27'
group by col2, col3
我现在已经通过在同一张表上使用 2 个选择和一个连接来解决我的性能问题,例如...
SELECT *
FROM (
SELECT col2, col3, max(my_date) as max_date
FROM table
where col4=1 and my_date <= '2001-01-27'
group by col2, col3
) aaa
join
(
SELECT col2, col3, my_date, col5, col6, col7
FROM table
where col4=1
) bbb
on (aaa.col2=bbb.col2 and aaa.col3=bbb.col3 and aaa.max_date=bbb.my_date);
您可能需要添加此覆盖索引以使第二个查询更快:
create index comp2_index on my_table(col2, col3, col4, my_date, col5, col7);
您的原始查询:
SELECT col2, col3, max(my_date), col5, col7
FROM table
where col4=1 and my_date <= '2001-01-27'
group by col2, col3
col5, col7 也应该添加到 group by 子句中,对吧?
如果您不需要
id
任何东西,那么这将加快查询速度,无论您需要获取额外的列(col5/6/7)。
CREATE TABLE `my_table` (
`col2` smallint(6) NOT NULL,
`col3` smallint(6) NOT NULL,
`col4` smallint(6) NOT NULL,
`my_date` datetime NOT NULL,
`col5` char(1) NOT NULL,
`col6` char(1) NOT NULL,
`col7` char(1) NOT NULL,
PRIMARY KEY (col4,my_date,col2,col3) -- in this order
) ENGINE=InnoDB;
如果你确实需要
id
因为它被其他表引用,那么添加
`id` int(11) NOT NULL AUTO_INCREMENT,
INDEX(id) -- This is sufficient to keep auto_inc happy
我建议的 PK 是 11 字节(对比 4 字节 INT)。任何次级都将包含这 11 个字节。但是,不会重复 PK 和二级索引之间的任何公共列。例如
INDEX(col2, col7)
将有效INDEX(col2, col7, col4, my_date, col3)
.
请记住,PK 确定行的“参考位置”。任何以
col4
开头的二级索引几乎都是无用的,因为 PK 以此开头。 (当然,这取决于基数等)