本文基本基于Explain做分析,如果不太了解可以先移步下文(。・ω・。)ノ
了解MySQL的Explain,读这一篇够了( ̄∇ ̄)/
开始✊✊✊
建表
我们先创建一个表
CREATE TABLE `aqin_index` (
`id` BIGINT(20) UnSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`card_no` VARCHAR(50) NOT NULL,
`time_created` DATETIME DEFAULT CURRENT_TIMESTAMP,
`note` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`),
INDEX `index_unit` (`name`,`time_created`,`card_no`) USING BTREE)
ENGINE=InnoDB DEFAULT CHARSET=utf8;
Message字段显示OK则表示创建成功
这里创建的时候直接添加了一个联合索引
INDEX 索引名称 (字段1,字段2,字段3) USING 索引方法
- 索引名称:自己起个就成
- 字段1,字段2,字段3:想要添加的联合索引的所有字段(需要注意顺序)
- 索引方法:一般选择BTREE(BTREE/HASH)
也可以在现有的表中添加
ALTER TABLE `aqinindex` ADD INDEX `index_unit` (`name`,`time_created`,`card_no`)
插入数据
创建函数
-- 插入百万数据
DELIMITER $$
CREATE FUNCTION mock_data_4_aqin_index()
RETURNS INT
BEGIN
DECLARE num INT DEFAULT 1000000;
DECLARE i INT DEFAULT 0;
WHILE i < num DO
-- 插入语句
INSERT INTO `aqin_index`(`name`,`card_no`,`note`)VALUES
(CONCAT('name-',i), CONCAT('18', FLOOR(RAND()*((999999999-100000000)+100000000))),
CONCAT('note-',FLOOR(RAND()*2), UUID(), FLOOR(RAND()*100)));
SET i = i+1;
END WHILE;
RETURN i;
END;
Message字段显示如上图即为执行成功
调用函数
SELECT mock_data_4_aqin_index()
Message字段显示如上图即为执行成功,瞅一眼咱们生成的数据
删除函数
DROP FUNCTION 函数名
- 注意⚠️此处的函数名是不加括号的
我们浅浅的测试下
执行测试语句
EXPLAIN select * from aqin_index where name = 'name-9999' and time_created="2023-01-09 16:37:56" and `card_no` = '18432153441'
可以看出type类型是ref,key_len值为310(三个字段长度和),走了联合索引并且3个字段都用上了
计算方式可以参考了解MySQL的Explain,读这一篇够了( ̄∇ ̄)/
各个参数无变化
结论:字段都有,换顺序对结果无影响
type类型是ALL,即走了全表查询
结论:第一个字段缺失则不走索引
type类型是ref,key_len值为158,用到了联合索引,但是只用了两个字段(name和time_created)
结论:第一个字段存在,就会使用联合索引,其他字段缺少只会影响用到索引的字段数
特殊情况
分析下上面两张图,他们的type类型是index,使用了全索引扫描,相比较ref挺慢的其实
分析下上面两张图,他们的区别在于查询的字段是否被联合索引覆盖,card_no在联合索引中,type类型是index,note不在type类型是ALL,index的性能虽然不如ref但优于全表扫描ALL
总结
联合索引的各个字段可以不按顺序出现(原因是WHERE后面字段的顺序会被优化器优化为符合最左前缀的顺序),但是第一个字段必须出现,否则不走索引(最左前缀匹配,即WHERE条件必须有联合索引的第一个字段,以最左边的为起点任何连续的索引都能匹配上),当SELECT后需要查询的字段被联合索引覆盖,也会走索引,但方式是进行全索引扫描,效率并不高。