索引合并/index merge。此文的所有SQL例子都基于以下表结构讲解:
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'primary key',
`username` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'user name',
`age` int(4) NOT NULL DEFAULT 20 COMMENT 'user age',
`birthday_date_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'user birthday',
`address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`remark` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'remark something',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create time',
`version` int(4) NOT NULL DEFAULT 0 COMMENT 'update version',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `idx_name`(`username`) USING BTREE,
INDEX `idx_age_remark`(`age`, `remark`) USING BTREE,
INDEX `idx_create_time`(`create_time`) USING BTREE,
INDEX `idx_address`(`address`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10003 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC;
一、Intersection合并:交集合并
EXPLAIN SELECT * FROM t_user WHERE address = 'shanghaishi' and create_time = '2021-06-22 09:58:07'; 同时使用了address和create_time两个索引列,先从这两个索引列的二级索引树上找到值,然后把相应的ID合并起来回表,这样比一般情况下只使用单个二级索引再回表再用Using Where要快。而即使肉眼可见可能使用交集合并,mysql也不一定会用,因为还涉及查询成本计算,所以要判断一定会走交集合并的,会有这几种情况:
1)等值匹配:
注意:在上面这个等值匹配的例子中,两个二级索引都不是联合索引,如果任何其中一个二级索引是联合索引比如age列,是二级索引范围匹配,那么还是不会走并集索引,因为没有用到remark这个联合索引的第二个,也就是可能范围会太大。
2)主键列范围匹配
因为主键列索引树按照主键排序了,所以范围匹配是有序的。有序的主键 ROR
二、Union合并:并集合并
二级索引列等值查询。这个查询查出来的ID自然而然是个有序的,直接合并就完了。
三、Sort-Union合并:并集合并后再排序
这个sql并不能使主键有序查询,但这里确实用到了两个二级索引,那么从各自的二级索引树里拿到ID后,把ID排好序,做一次排序,再来合并。所以比Union多了一次ID排序的过程。
四、解决方案:联合索引代替Intersection索引合并
这里的解决方案,可以是两个列做一个联合索引即可。
end.