索引第二篇:MySQL索引加快查找速度的内部原理刨析

138 阅读26分钟

文章目录

一、前言

索引:建立一个索引就是建立一个B+树,通过建立一个B+树为代价得到加快查找的目的
唯一索引:不允许其中任何两行具有相同索引值的索引(仅允许一个为null),如果重复,插入报错
主键索引:不允许其中任何两行具有相同索引值的索引,且允许一个为null

主键使用主键索引,就是primary key,
主键是主键,自增是自增,任何一个int型字段都可以设置为自增,不光是主键。
物理主键一般使用递增的,不要使用uuid这样无序的,因为主键就是聚集索引,使用无序的造成B+树节点频繁的合并和分裂。

聚集索引:唯一键,表中的逻辑顺序和B+树的物理存储顺序完全相同,B+树中存放的叶子节点是实际的行记录,即数据即索引,索引即数据,每个表有且只有一个聚集索引,不能是零个和两个,只能是一个,必须是一个。

二级索引:非唯一键,表中的逻辑顺序和B+树的物理存储顺序不一致,B+树中存放的叶子节点是“索引字段值+聚集索引字段值”,每个表除了一个聚集索引,其他都是二级索引。

注意:二级索引叶子节点存放的是“索引字段值+聚集索引字段值”,而不是行记录的地址,因为地址是会改变。

单个索引:这个索引仅覆盖一个字段。
复合索引/组合索引:这个索引仅覆盖一个以上字段,遵循最左匹配原则,不允许跳过。

索引按照字段数量划分,分为单个索引和复合索引,按照性质不同划分,分为普通索引 唯一索引 主键索引,这两种划分方式都是表示具体的实际存在的索引,实际存在的B+树。按照叶子节点存储划分,分为聚集索引和二级索引,这个是不存在的,是将一个实际的索引作为表的聚集索引或者二级索引。

二、聚集索引和二级索引

2.1 聚簇索引

因为索引,所以逻辑上相邻的记录在磁盘上也并不是一定物理相邻的。
数据库索引 ,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用 B 树及其变种 B+树。
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
在这里插入图片描述
上图展示了一种可能的索引方式。左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址(金手指:注意逻辑上相邻的记录在磁盘上也并不是一定物理相邻的)。为了加快 Col2 的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在 O(log2n)的复杂度内获取到相应数据。

按照表中的逻辑顺序和B+树的物理存储顺序是否一致,一致则是聚集索引(唯一键充当),不一致则是二级索引(非唯一键)。试一下,执行建表语句:

CREATE TABLE `student` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT NOT NULL COMMENT '主键id',
  `student_no` VARCHAR(64) COMMENT '学号',
  `name` VARCHAR(64) COMMENT '学生姓名',
  `age` INT COMMENT '学生年龄',
  PRIMARY KEY (`id`)      // 主键id,无逻辑意义字段
) ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='学生信息表';

插入 5 条数据:

insert into student(student_no,name,age) values(101,"Alice",18);  
insert into student(student_no,name,age) values(102,"Bob",19);
insert into student(student_no,name,age) values(104,"Brandt",15);
insert into student(student_no,name,age) values(105,"David",19);
insert into student(student_no,name,age) values(109,"David",18);

在插入的过程中,MySQL 会用你指定的主键,在这里是递增主键,维护起一棵 B+树,我用了旧金山大学做的 BPlusTree Visualization 来模拟这棵树的样子,主键从 1 开始递增,插入五条,所以是 1 到 5:

在这里插入图片描述
注意:在你还没有执行 create index 语句的时候,MySQL 就已经创建索引了。

B+树在插入的过程中是怎么维护它的几个特性的:
1、B+树插入时保证有序(有序保证范围查找):左边节点比右边小;
2、B+树插入时保证自平衡(降低树高四措施之一):左右两边数量趋于相等;
3、B+树插入时保证节点分裂:节点在遇到元素数量超过节点容量时,是如何分裂成两个的,这个也是 MySQL 页分裂的原理。

B+树的叶子节点是带有行的全部数据的,如图:
在这里插入图片描述
对于B+树,非叶子节点起到检索作用,叶子节点存放表数据。
非叶子节点的检索中,保证三特性:
左边节点比右边小、
左右两边数量趋于相等、
节点中存放元素数量超过节点容量时,分裂成来给你个节点,因为一个节点大小一个也,所以这也是 MySQL 页分裂的原理
上图中,第一层非叶子节点为3,表示左边小于3,右边大于等于3;
第二层第一个非叶子节点为2,表示左边小于2,右边大于等于2;
第二层第二个非叶子节点为4,表示左边小于4,右边大于等于4

如果没有这棵 B+树,你要根据主键查询,比如

select * from student where id = 5;

对不起,数据是无序的,你只能全表扫描,犹如大浪淘沙。

问题:主键不是递增的吗,那不就可以用二分法来查找?
回答:不是的,主键虽然是递增的,但是如果你写入磁盘时,没有去维护有序数组这样一个数据结构(比如你删掉了 4,怎么把 5 往前面挪),那数据在磁盘里依旧是无序的,查找时只能随机查找,而如果你维护了有序数组这样的数据结构,其实也是建了索引,只是建了不一样的数据结构的索引罢了。
1、这颗B+树就是索引,就是索引数据结构,对于B+树,非叶子节点起到检索作用,叶子节点存放表数据。非叶子节点的检索中,保证左边节点比右边小、左右两边数量趋于相等、节点在遇到元素数量超过节点容量时,是如何分裂成两个的,这个也是 MySQL 页分裂的原理
上图中,第一层非叶子节点为3,表示左边小于3,右边大于等于3;
第二层第一个非叶子节点为2,表示左边小于2,右边大于等于2;
第二层第二个非叶子节点为4,表示左边小于4,右边大于等于4
2、使用B+树索引数据结构就直接在B+树上找,不用到整个磁盘上扫描,所以加快了检索效率。
为什么使用索引可以加快检索效率?
3、在create table新建表和insert into插入数据的时候,这颗B+树就已经出来了,B+树索引就已经好了。为什么说在你还没有执行 create index 语句的时候,MySQL 就已经创建了索引(这里指的是创建了聚集索引)?
一句话小结:对于聚集索引,作用于主键/唯一键,新建表和插入数据的时候就创建了,但是其他的,如二级索引,作用于非唯一键,一定要等到使用create index才被创建

现在有了这棵 B+树,数据被有规律的存储起来,查找 id=5,也不再大浪淘沙,而是变得很有章法:

1、从上到下,先找到 3,5 比它大,找右节点

2、接着找到 4,发现 5 还是比它大,继续找右节点

3、这次到达叶子节点了,叶子节点是一个递增的数组,那就用二分法,找到 id=5 的数据

小结:B+树检索+二分法检索递增数组,先用B+树检索直到叶子节点(一定要到叶子节点,因为具体表数据一定存放在叶子节点,非叶子节点只是检索之用),然后叶子节点是一个递增的数组,那就用二分法。
所以,你要访问磁盘的次数,是由这棵树的层数决定的,每一次具体检索访问磁盘的次数,就是叶子节点所在层数。

三种情况下确定一个索引为聚集索引
1、如果创建表的时候指定一个列为primary key,就用这个列做聚集索引;
2、如果创建表的时候没有使用primary key指定主键,表中有唯一键unique key也可以做聚集索引;
3、如果表没有主键primary key和唯一键unique key,mysql会给你建一个rowid字段做聚集索引,用它来组织这棵 B+树.
所以,mysql中任何一个表中有且仅有一个聚集索引(MySQL中任何一个表都一定存在一个聚集索引,也一个表也仅仅只有一个聚集索引,底层就是有且仅有一个聚集索引树,叶子节点是一个存储有完整行数据的索引)。

聚集索引局限:聚集索引只能加快主键检索/唯一键检索,对于非唯一键,无能为力,比如姓名,这就是二级索引的事情了。

问题:MySQL中的聚集索引为什么只允许有一个?
回答:
聚集索引结构:聚集索引就是一个B+树,非叶子节点用户检索,叶子节点中存放索引和数据,所以,聚集索引是索引和数据的组合体(这就是聚集索引的结构)。
聚集索引两个功能:1、非叶子节点快速检索,决定了数据的顺序;2、叶子节点中存储数据行
第一,从索引上说,非叶子节点快速检索,决定了数据的顺序:聚集索引cluster index 底层表示的是一个表的实际存储顺序,必然只有一个(即全班同学要集合排队了,不能同时按照个子高低和考试成绩排序,总得选一样,主键和唯一键都可以做聚集索引)。(2)如果你复制该表的数据再次按照另一种顺序(即 另一个cluster index)存储一遍,那就是另一个表了。
第二,从数据行上说,叶节点中存放数据行:如果有两个或者多个聚集索引,数据行会重复存储,浪费磁盘空间,也没那个必要。
在实际中,聚集索引的主键与业务无关,目的是便于顺序读写,减少随机读写的io开销。

2.2 二级索引

聚簇索引只能帮你加快主键查询,但是如果你想根据姓名查询呢?

对不起,看看上面这棵树你就知道,数据并没有按照姓名进行组织,所以,你还是只能全表扫描。

不想全表扫描,怎么办?那就给姓名字段也加个索引,让数据按照姓名有规律的进行组织:

create index idx_name on student(name);   // 执行create index这一句的时候,就创建一个在student表的name字段上的B+树索引,索引被命名为 idx_name 。

这时候 MySQL 又会建一棵新的 B+树(注意,和上面聚集索引的B+树不是同一个):

在这里插入图片描述

你会发现这棵树的叶子节点,只有姓名和主键ID两个字段,没有行的完整数据,这时候你执行:

select * from student where name = "David";

MySQL 到你刚刚创建的这棵 B+树 查询,快速查到有两条姓名是“David”的记录,并且拿到它们的主键,分别是 4 和 5,但是你要的是select *呀,怎么办?

别忘了,MySQL 在一开始就给你建了一棵 B+树 了,把这两棵树,放在一起,拿着从这棵树上查到的两个主键ID,去聚簇索引找,事情不就解决了?

在这里插入图片描述

小结,这种叶子节点不带行数据完整信息的索引,就叫二级索引(secondary index),也叫辅助索引。

key:判断聚集索引的和二级索引的依据:
1、聚集索引是根据唯一键/主键找到完整行信息,二级索引是根据非唯一键找到唯一键/主键。
2、聚集索引叶子节点存放完整行信息,二级索引叶子节点存放非完整行信息,即唯一键/主键。
3、聚集索引非叶子节点存放主键/唯一键作为检索,比较法则是int比较;二级索引非叶子节点存放非唯一键作为检索,比较法则是字母序,一般是字符串类型。
4、联系:聚集索引在where id=xxx;的时候单独使用,二级索引在where name = “David”;的时候先根据非唯一键找到唯一键/主键,然后根据唯一键/主键找到完整行信息。

2.3 回表

2.3.1 回表

定义:我们有个主键为ID的索引,和一个普通name字段的索引,我们在普通字段上搜索:
sql select * from table where name = ‘csdn’
执行的流程:
第一,先查询到name索引上的“csdn”,然后找到他的id是2;
第二,去主键索引,找到id为2对应的值。
其中,第二个过程,回到主键索引树搜索的过程,就是回表。不过也有方法避免回表,那就是覆盖索引。

2.3.2 回表的解决1:覆盖索引

回表是一个二级索引存在的问题,先找到主键,然后找到记录行信息。 覆盖索引是解决回表,覆盖索引可以减少树的搜索次数,提升性能,他也是我们在实际开发过程中经常用来优化查询效率的手段。很多联合索引的建立,就是为了支持覆盖索引,特定的业务能极大的提升效率。

解决方案:

(1)对于单个索引,建立一个 idx_name 索引,select id from student where idx_name = ?,就不回表了;
(2)对于联合/复合索引,建立一个 idx_name_age 索引,select id,age from student where idx_name = ?,就不回表了。

看上面的图二级索引和复合索引的图就知道。所以说,很多联合索引的建立,就是为了支持覆盖索引,避免回表,毕竟,mysql可能因为回表,不选择某一个索引,具体查看explain。

2.3.3 回表的解决2:索引下推

索引下推是一种mysql一种内置优化,可以减少回表次数。

假设有联合索引(a,b,c)。有以下SQL语句:

select * from itemcenter where a like ‘aa%’ and b=22 and c = 20;

根据左侧匹配规则,只能用到"a"检索数据,bc两个字段无法用到。在MySQL 5.6之前,只能一个个回表,到主键索引上找出数据行,再对比b、c字段值。而MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。

三、复合索引和前缀匹配原则

3.1 复合索引

需求:根据姓名和年龄同时查询呢?

select * from student where name = "David" and age = 18;

还是那个道理,数据虽然按照 name 有规律的组织了,但是没有按照 age 有规律组织,所以我们要给 name 和 age 同时建索引:

create index idx_name_age on student(name,age);   //创建一个同时在student表中的name字段和age字段上的索引,命名为idx_name_age,因为同时在两个字段上,所以被称为复合索引,其实就是一种两字段的二级索引,还是二级索引,所以还是要使用create index创建,还是一颗B+树。

只要是非聚集索引,就都要使用create index创建,二级索引和复合索引(不仅仅在一个字段上)都要使用create index创建。

金手指:聚集索引和二级索引的联系?
因为同时在两个字段上,所以被称为复合索引,其实就是一种两字段的二级索引,还是二级索引,所以还是要使用create index创建,还是一颗B+树。

这时候 MySQL 又会建一棵 B+树,这下 B+树 的节点里面,不只有 name,还有 age 了:

在这里插入图片描述
金手指:聚集索引和二级索引的联系?
上面说了,复合索引,其实就是一种两字段的二级索引,还是二级索引,所以还是先根据非唯一键找到唯一键/主键,然后根据唯一键/主键找到完整行信息。

注意观察我用红色虚线框出来的那两个节点,这是这棵树和上面那棵只给 name 建索引的树的唯一区别,两个元素换了个位,因为排序时,是先用 name 比较大小,如果 name 相同,则用 age 比较。

还是那句话,这里举的例子数据量很少,你可以想象下有一万个叫“David”的学生,年龄随机分布在 13 到 20 之间,这时候如果没有按照 age 进行有规律的存储,你还是得扫描一万行数据。

3.2 前缀匹配

问题1:最左匹配和前缀匹配是什么关系?
回答1:最左匹配是数据库底层已经存在的,无法改变的匹配原则,前缀匹配是对于长字符串字段的匹配方式,利用了最左匹配的原理。

问题2:为什么最左匹配适用于二级索引和复合索引,前缀匹配也适用于二级索引和复合索引?
回答2:最左匹配是数据库底层既有的匹配原则,所以自然适用于二级索引和复合索引,而前缀匹配是一个调优方式,利用了最左匹配原则,所以自然也适用于二级索引和复合索引。

3.2.1 底层原理:最左匹配

最左匹配原则:

适用范围:二级索引,包括单列索引和联合索引两种(集聚索引直接用主键/唯一键,不需要最左匹配了)。索引可以简单如一个列 (a),也可以复杂如多个列 (a,b,c,d),即无论是单列索引还是多列索引,最左匹配原则都适用。

如果是联合索引,那么key也由多个列组成,同时,索引只能用于查找key是否存在(相等),遇到范围查询 (、<、between、like左匹配)等就不能进一步匹配了,后续退化为线性查找。因此,联合索引中,列的排列顺序决定了可命中索引的列数。

例子(解释遇到范围查询就不能进一步匹配了,后续退化为线性查找):
如有索引 (a,b,c,d),查询条件 a=1 and b=2 and c<3 and d=4,则会在每个节点依次命中a、b、c,无法命中d。(c已经是范围查询了,d肯定是排不了序了)

问题:为什么复合索引中的列的顺序重要?
回答:一句话解释,索引的最左前缀匹配原则。

最左匹配在复合索引中的应用

只给 student 表建 idx_name_age 这个复合索引,这两个 sql 语句,会走索引吗?

select * from student where name = "David";    走idx_name_age复合索引
select * from student where age = 18;   不走idx_name_age复合索引,走全表扫描

3.2.2 前缀索引(适用:二级索引+复合索引)

1、二级索引长字符串:因为有了最左匹配原则,所以对于长字符串字段,既需要有好的检索效率,又需要小的存放空间,这两者的平衡点就是索引选择性,所以产生了前缀索引;前缀索引根据索引选择性来确定前缀索引应该取前面几个字符。

前缀索引优点:实现索引占用空间小且快的效果;
前缀索引缺点:
无法使用前缀索引做 ORDER BY 和 GROUP BY ;
无法使用前缀索引做覆盖扫描;
前缀索引也有可能增加扫描行数。

2、二级索引长字符串前缀相同:在长字符串字段的中,前缀相同怎么办(实际:身份证号码、学号、工号、邮箱等),前面三个,数据库中倒序存储这些前缀相同的号码,邮箱,去掉www开始检索;

3、联合索引联合索引中哪个索引放前面,索引选择性说了算,根本依据还是最左匹配原则(联合索引表示不仅仅一个字段,是二级索引的扩展)

3.2.2.1 根据索引选择性来确定前缀索引应该取前面几个字符

之前我们说过,如于长字符串的字段(如 url),我们可以用伪哈希索引的形式来创建索引,以避免索引变得既大又慢,除此之外其实还可以用前缀索引(字符串的部分字符)的形式来达到我们的目的,那么这个前缀索引应该如何选取呢,这叫涉及到一个叫索引选择性的概念

索引选择性定义:不重复的索引值(也称为基数,cardinality)占数据表的记录总数的比值,比值越高,代表索引的选择性越好,唯一索引的选择性是最好的,比值是 1。

我们可以通过 SHOW INDEXES FROM table 来查看每个索引 cardinality 的值以评估索引设计的合理性。

怎么选择这个比例呢,我们可以分别取前 3,4,5,6,7 的前缀索引,然后再比较下选择这几个前缀索引的选择性,执行以下语句

SELECT 
 COUNT(DISTINCT LEFT(city,3))/COUNT(*) as sel3,
 COUNT(DISTINCT LEFT(city,4))/COUNT(*) as sel4,
 COUNT(DISTINCT LEFT(city,5))/COUNT(*) as sel5,
 COUNT(DISTINCT LEFT(city,6))/COUNT(*) as sel6,
 COUNT(DISTINCT LEFT(city,7))/COUNT(*) as sel7
FROM city_demo

得结果如下

sel3	sel4	sel5	sel6	sel7
0.0239	0.0293	0.0305	0.0309	0.0310

可以看到当前缀长度为 7 时,索引选择性提升的比例已经很小了,也就是说应该选择 city 的前六个字符作为前缀索引,如下

 // 为city_demo表添加一个索引,用city前6个字段做索引
ALTER TABLE city_demo ADD KEY(city(6))   

我们当前是以平均选择性为指标的,有时候这样是不够的,还得考虑最坏情况下的选择性,以这个 demo 为例,可能一些人看到选择 4,5 的前缀索引与选择 6,7 的选择性相差不大,那就得看下选择 4,5 的前缀索引分布是否均匀了

SELECT COUNT(*) AS  cnt, LEFT(city, 4) AS pref FROM city_demo 
  GROUP BY pref 
  ORDER BY cnt DESC LIMIT 5

可能会出现以下结果

cnt	pref
305	Sant
200	Toul
90	Chic
20	Chan

可以看到分布极不均匀,以 Sant,Toul 为前缀索引的数量极多,这两者的选择性都不是很理想,所以要选择前缀索引时也要考虑最差的选择性的情况。

3.2.2.2 前缀索引可能增加扫描行数

前缀索引优点:实现索引占用空间小且快的效果;
前缀索引缺点:
无法使用前缀索引做 ORDER BY 和 GROUP BY ;
无法使用前缀索引做覆盖扫描;
前缀索引也有可能增加扫描行数。

前缀索引也有可能增加扫描行数,解释,假设有以下表数据及要执行的 SQL

id	email
1	zhangssxyz@163.com
2	zhangs1@163.com
3	zhangs1@163.com
4	zhangs1@163.com
SELECT id,email FROM user WHERE email='zhangssxyz@xxx.com';

如果我们针对 email 设置的是整个字段的索引,则上表中根据 「 zhangssxyz@163.com」查询到相关记记录后,再查询此记录的下一条记录,发现没有,停止扫描,此时可知只扫描一行记录,如果我们以前六个字符(即 email(6))「 zhangs」作为前缀索引,则显然要扫描四行记录,并且获得行记录后不得不回到主键索引再判断 email 字段的值,所以使用前缀索引要评估它带来的这些开销,前缀索引可能增加扫描行数。

3.2.2.3 前缀相同两种处理方式

问题:索引设计,前缀索引进阶使用:前缀相同怎么办,如身份证号码、学号、工号、邮箱、网址等?

回答:身份证号码、学号、工号:前缀相同后缀不同,数据库中倒序存储这些前缀相同的号码,邮箱、网址:前缀相同后缀相同,数据库中直接不存储这些前缀(www等)。

比如现在我们为某市的市民建立一个人口信息表,则这个市人口的身份证虽然不同,但身份证前面的几位数都是相同的,这种情况该怎么建立前缀索引呢。

可以将数据库中身份证倒序存储,查的时候可以按如下方式查询:

SELECT field_list FROM t WHERE id_card = reverse('input_id_card_string');

mysql中倒序存储身份证号码到t表中的id_card字段,所以,输入值input_id_card_string需要倒序,然后和数据库表中的id_card字段比较,这样就可以用身份证的后六位作前缀索引了。

3.2.2.4 按照索引选择性设置联合索引的顺序

联合索引中哪个索引放前面,由索引选择性说了算,根本依据还是最左匹配原则(联合索引表示不仅仅一个字段,是二级索引的扩展)

索引选择性同样适用于联合索引的设计,如果没有特殊情况,我们一般建议在建立联合索引时,把选择性最高的列放在最前面,比如,对于以下语句:

SELECT * FROM payment WHERE staff_id = xxx AND customer_id = xxx;

单就这个语句而言, (staff_id,customer_id) 和 (customer_id, staff_id) 这两个联合索引我们应该建哪一个呢,可以统计下这两者的选择性。

SELECT 
 COUNT(DISTINCT staff_id)/COUNT(*) as staff_id_selectivity,
 COUNT(DISTINCT customer_id)/COUNT(*) as customer_id_selectivity,
 COUNT(*)
FROM payment

结果为: ;

staff_id_selectivity: 0.0001
customer_id_selectivity: 0.0373
COUNT(*): 16049

从中可以看出 customer_id 的选择性更高,所以应该选择 customer_id 作为第一列。

create index idx_customer_staff  on student(customer_id,staff_id);  

四、索引相关知识

问题:如果使用的是ORM,是否还需要关心索引?
回答 : 是的,仍然需要关心索引,即使是使用对象美系映射(ORM)工具也要关心索引。
ORM工具能够生产符合逻辑的,合法的查询(多数时候),除非只是生成非常基本的查询(例如仅是根据主键查询),否则它很难生成适合索引的查询。无论是多么复杂的ORM工具,在精妙和复杂的索引面前都是"浮云"。很多时候,即使是查询优化技术专家也很难兼顾到各种情况,更别说ORM了。

问题:在 MySQL中,索引是在存储引擎层而不是服务层实现的(mysql架构分为两层:mysql服务层和存储引擎层)?
回答:第一,因为索引依赖存储引擎层的,所以不同从存储引擎提供不同的索引标准,并没有统一的索引标准:不同存储引擎的索引的工作方式并不一样,也不是所有的存储引擎都支持所有类型的索引。
第二,即使多个存储引擎支持同一种类型的索引,其底层数据结构的实现也可能不同。常用的索引有三种:B+树索引、哈希索引、全文索引。
即使是使用B+树索引,不同存储引擎的实现方式也有所不同:
(1)MyISAM 使用前缀压缩技术使得索引更小,但 InnoDB则按照原数据格式进行存储;
(2)MyISAM 索引通过数据的物理位置引用被索引的行,而 InnoDB则根据主键引用被索引的行。

问题:怎么看到为表格定义的所有索引?
回答:索引是通过以下方式为表格定义的:SHOW INDEX FROM

问题:可以使用多少列创建索引?
回答:任何标准表最多可以创建 16 个索引列。

长字符串字段的hash?

因为存在一个磁盘占用的问题,索引选取的越长,占用的磁盘空间就越大,相同的数据页能放下的索引值就越少,搜索的效率也就会越低。

方案是:hash,把字段hash为另外一个字段存起来,使用hash后的字段作为索引,每次校验hash就好了,hash的索引也不大

五、尾声

天天打码,天天进步!