准备工作,创建数据表user_innodb表

DROP TABLE IF EXISTS `user_innodb`;
CREATE TABLE `user_innodb` (
  `id` bigint(64) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `gender` tinyint(1) NOT NULL,
  `phone` varchar(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

联合索引与最左前缀原则

在平时开发中,最常见的是单列索引(比如主键primary id),但是在需要多条件查询的时候,也会创建联合索引。单列索引也可以看成特殊的联合索引。比如在user表中,给name和phone建立一个联合索引。

ALTER TABLE 可以用来创建索引,包括普通索引、UNIQUE索引或PRIMARY KEY索引:

ALTER TABLE table_name ADD INDEX index_name (column_list)

ALTER TABLE table_name ADD UNIQUE (column_list)

ALTER TABLE table_name ADD PRIMARY KEY (column_list)

ALTER TABLE user_innodb add INDEX comidx_name_phone(name,phone); -- 创建联合索引

1.1 联合索引的组织

下图是B+树的索引结构,是一个单值索引:
在这里插入图片描述
可以看出,所有非叶子节点的构成都是由两部分组成,索引值+指针,而单值索引说的就是在索引值这里只有一个值(比如id),而联合索引在索引值可能会有多个值(比如name和phone)
在这里插入图片描述

与单值索引的不同

  1. 联合索引在B+树中是复合的数据结构
  2. 由于B+树本身是有序的,所以联合索引是从左到右的顺序来建立搜索树的(name在左边,phone在右边)。从上图可以看出,name是有序的,phone是无序的。当name相等时,phone才是有序的。
  3. 当存储引擎是innoDB时,叶子节点存储的是数据/主键

Q1:联合索引如何查找数据?
比如,使用where name = ‘zhangsan’ and phone='1320xx’去查询数据的时候

  1. B+树会优先比较name来确定下一步应该搜索的方向,往左还是往右
  2. 如果name相同的时候就比较phone

但是如果查询条件没有name,就不知道第一步应该查询哪个节点,因为建立搜索树的时候name是第一个比较因子,所以用不到索引。
Q2:联合索引与单值索引的关系?
假设项目里有两个查询很慢:

SELECT * FROM user_innodb WHERE name= ?; 
SELECT * FROM user_innodb WHERE name= ? AND phone=?;

按照我们的想法,一个查询创建一个索引,所以针对这两条SQL创建两个索引,这种做法觉得正确吗?

CREATE INDEX idx_name on user_innodb(name);  
CREATE INDEX idx_name_phoneonuser_innodb(name,phone);

当我们创建一个联索引的时候,用左边的字段name去查询的时候,也能用到索引,所以单独为name创建一个索引完全没必要,相当于建立了两个联合索引(name),(name,phone)
如果创建三个字段的索引index(a,b,c)相当于创建三个索引:index(a),index(a,b),index(a,b,c)。用where b=?和where b=? and c=? 和where a = ? and c = ?是不能使用到索引。以为不能不用第一个字段,也不能中断。

1.2 最左前缀原则

因为联合索引中包含了多个字段,所以不能像单值索引那样直接使用,需要遵守规则?
就是最左前缀原则:带头大哥不能死,中间兄弟不能断

1.建立索引时,一定要把常用的列放在最左边。比如下面三个语句,能用到联合索引吗?
使用两个字段,可以用到联合索引(注:两个字段的顺序颠倒并不影响,因为全值匹配时mysql会优化字段顺序)

EXPLAIN SELECT * FROM user_innodb WHERE name= '张三' AND phone='12345678910'

2.使用左边的name字段,可以用到联合索引:

EXPLAIN SELECT * FROM user_innodb WHERE name= '张三'

3.使用右边的phone字段,无法使用索引,全表扫描:

EXPLAIN SELECT * FROM user_innodb WHERE name= '12345678910'

不要在索引上做任何操作,因为可能会导致索引失效,转而全表扫描

1.3 索引失效的情况

  1. 使用函数,表达式,计算(加减乘除)。因为当前值改变后无法与索引存的值匹配
SELECT * FROM user_innodb where left(name, 3)='张三'-- left函数是一个字符串函数,它返回具有指定长度的字符串的左边部分
  1. 使用范围查询会导致右边列失效。因为二叉树的查找是=查找,若是一个范围的话无法继续下探
SELECT * FROM user_innodb where name='张三' and age > 22
  1. like以通配符开头,mysql索引失效会变成扫描操作。因为无法判断%代表多少字符。
SELECT * FROM user_innodb where name like '%三'
  1. 字符串不加’ '索引失效。因为会出现出现隐式转换,相当于给索引列做了操作
SELECT * FROM user_innodb where name = 007-- "007"从字符串变成了数字007
  1. 少用or,用它连接时很多情况下索引会失效
SELECT * FROM user_innodb where name = '张三' or name = '李四'
  1. is null,is not null 无法使用索引.
SELECT * FROM user_innodb where name is null

==> 对这一部分内容通过一首打油诗做个总结:

						全值匹配我最爱,最左前缀要遵守
						带头大哥不能死,中间兄弟不能断
						索引列上少计算,范围之后全失效
						like百分写最右,覆盖索引不写星
					    不等空值还有or,索引失效要少用

原文参考:https://blog.csdn.net/weixin_43935927/article/details/109361168

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐