Mysql高级——索引创建和使用

索引的创建

1. 索引的声明与使用

1.1 索引的分类

MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。

从功能逻辑上说,索引主要有 4 种,分别是普通索引、唯一索引、主键索引、全文索引。

按照物理实现方式,索引可以分为 2 种:聚簇索引和非聚簇索引。

按照作用字段个数进行划分,分成单列索引和联合索引。

  1. 普通索引
  2. 唯一性索引
  3. 主键索引
  4. 单列索引
  5. 多列(组合、联合)索引
  6. 全文索引
  7. 补充:空间索引

小结:不同的存储引擎支持的索引类型也不一样 InnoDB :支持 B-tree、Full-text 等索引,不支持 Hash

索引; MyISAM : 支持 B-tree、Full-text 等索引,不支持 Hash 索引; Memory :支持 B-tree、Hash 等

索引,不支持 Full-text 索引; NDB :支持 Hash 索引,不支持 B-tree、Full-text 等索引; Archive :不支

持 B-tree、Hash、Full-text 等索引;

1.2 创建索引

sql 复制代码
CREATE TABLE dept(
dept_id INT PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(20)
);
CREATE TABLE emp(
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(20) UNIQUE,
dept_id INT,
CONSTRAINT emp_dept_id_fk FOREIGN KEY(dept_id) REFERENCES dept(dept_id)
);	

显式创建表时创建索引的话,基本语法格式如下:

sql 复制代码
CREATE TABLE table_name [col_name data_type]
[UNIQUE | FULLTEXT | SPATIAL] [INDEX | KEY] [index_name] (col_name [length]) [ASC |
DESC]
  • UNIQUE 、FULLTEXT 和SPATIAL 为可选参数,分别表示唯一索引、全文索引和空间索引;
  • INDEX 与KEY 为同义词,两者的作用相同,用来指定创建索引;
  • index_name 指定索引的名称,为可选参数,如果不指定,那么MySQL默认col_name为索引名;
  • col_name 为需要创建索引的字段列,该列必须从数据表中定义的多个列中选择;
  • length 为可选参数,表示索引的长度,只有字符串类型的字段才能指定索引长度;
  • ASC 或DESC 指定升序或者降序的索引值存储。

创建普通索引

在book表中的year_publication字段上建立普通索引,SQL语句如下:

sql 复制代码
CREATE TABLE book(
    book_id INT ,
    book_name VARCHAR(100),
    authors VARCHAR(100),
    info VARCHAR(100) ,
    comment VARCHAR(100),
    year_publication YEAR,
    INDEX(year_publication)
);

创建唯一索引

sql 复制代码
CREATE TABLE test1(
    id INT NOT NULL,
    name varchar(30) NOT NULL,
    UNIQUE INDEX uk_idx_id(id)
);

主键索引

设定为主键后数据库会自动建立索引,innodb为聚簇索引,语法:

  • 随表一起建索引:
sql 复制代码
CREATE TABLE student (
    id INT(10) UNSIGNED AUTO_INCREMENT ,
    student_no VARCHAR(200),
    student_name VARCHAR(200),
    PRIMARY KEY(id)
);
  • 删除主键索引:
sql 复制代码
ALTER TABLE student
drop PRIMARY KEY ;
  • 修改主键索引:必须先删除掉(drop)原索引,再新建(add)索引

创建单列索引

sql 复制代码
CREATE TABLE test2(
    id INT NOT NULL,
    name CHAR(50) NULL,
    INDEX single_idx_name(name(20))
);

创建组合索引

举例:创建表test3,在表中的id、name和age字段上建立组合索引,SQL语句如下:

sql 复制代码
CREATE TABLE test3(
    id INT(11) NOT NULL,
    name CHAR(30) NOT NULL,
    age INT(11) NOT NULL,
    info VARCHAR(255),
    INDEX multi_idx(id,name,age)
);

创建全文索引

举例1:创建表test4,在表中的info字段上建立全文索引,SQL语句如下:

sql 复制代码
CREATE TABLE test4(
    id INT NOT NULL,
    name CHAR(30) NOT NULL,
    age INT NOT NULL,
    info VARCHAR(255),
    FULLTEXT INDEX futxt_idx_info(info)
) ENGINE=MyISAM;

在MySQL5.7及之后版本中可以不指定最后的ENGINE了,因为在此版本中InnoDB支持全文索引。

创建了一个给title和body字段添加全文索引的表。

sql 复制代码
CREATE TABLE `papers` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `title` varchar(200) DEFAULT NULL,
    `content` text,
    PRIMARY KEY (`id`),
    FULLTEXT KEY `title` (`title`,`content`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

不同于like方式的的查询:

sql 复制代码
SELECT * FROM papers WHERE content LIKE '%查询字符串%';

全文索引用match+against方式查询:

sql 复制代码
SELECT * FROM papers WHERE MATCH(title,content) AGAINST ('查询字符串');

注意点

  1. 使用全文索引前,搞清楚版本支持情况;
  2. 全文索引比 like + % 快 N 倍,但是可能存在精度问题;
  3. 如果需要全文索引的是大量数据,建议先添加数据,再创建索引。

创建空间索引

空间索引创建中,要求空间类型的字段必须为非空。

举例:创建表test5,在空间类型为GEOMETRY的字段上创建空间索引,SQL语句如下:

sql 复制代码
CREATE TABLE test5(
    geo GEOMETRY NOT NULL,
    SPATIAL INDEX spa_idx_geo(geo)
) ENGINE=MyISAM;

2. 在已经存在的表上创建索引

在已经存在的表中创建索引可以使用ALTER TABLE语句或者CREATE INDEX语句。

  1. 使用ALTER TABLE语句创建索引 ALTER TABLE语句创建索引的基本语法如下:
sql 复制代码
ALTER TABLE table_name ADD [UNIQUE | FULLTEXT | SPATIAL] [INDEX | KEY]
[index_name] (col_name[length],...) [ASC | DESC]
  1. 使用CREATE INDEX创建索引 CREATE INDEX语句可以在已经存在的表上添加索引,在MySQL中,
    CREATE INDEX被映射到一个ALTER TABLE语句上,基本语法结构为:
sql 复制代码
CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name
ON table_name (col_name[length],...) [ASC | DESC]

2.1 删除索引

  1. 使用ALTER TABLE删除索引 ALTER TABLE删除索引的基本语法格式如下:
sql 复制代码
ALTER TABLE table_name DROP INDEX index_name;
  1. 使用DROP INDEX语句删除索引 DROP INDEX删除索引的基本语法格式如下:
sql 复制代码
DROP INDEX index_name ON table_name;

提示 删除表中的列时,如果要删除的列为索引的组成部分,则该列也会从索引中删除。如果组成

索引的所有列都被删除,则整个索引将被删除。

3.MySQL8.0索引新特性

支持降序索引

分别在MySQL 5.7版本和MySQL 8.0版本中创建数据表ts1,结果如下:

sql 复制代码
CREATE TABLE ts1(a int,b int,index idx_a_b(a,b desc));

在MySQL 5.7版本中查看数据表ts1的结构,结果如下:

在MySQL 8.0版本中查看数据表ts1的结构,结果如下:

从结果可以看出,索引已经是降序了。下面继续测试降序索引在执行计划中的表现。

分别在MySQL 5.7版本和MySQL 8.0版本的数据表ts1中插入800条随机数据,执行语句如下:

sql 复制代码
DELIMITER //
    CREATE PROCEDURE ts_insert()
    BEGIN
    DECLARE i INT DEFAULT 1;
    WHILE i < 800
    DO
    insert into ts1 select rand()*80000,rand()*80000;
    SET i = i + 1;
    END WHILE;
    commit;
    END //
    DELIMITER ;
#调用
CALL ts_insert();

在MySQL 5.7版本中查看数据表ts1的执行计划,结果如下:

sql 复制代码
EXPLAIN SELECT * FROM ts1 ORDER BY a,b DESC LIMIT 5;

从结果可以看出,执行计划中扫描数为799,而且使用了Using filesort。

提示 Using filesort是MySQL中一种速度比较慢的外部排序,能避免是最好的。多数情况下,管理员

可以通过优化索引来尽量避免出现Using filesort,从而提高数据库执行速度。

在MySQL 8.0版本中查看数据表ts1的执行计划。从结果可以看出,执行计划中扫描数为5,而且没有使用

Using filesort。

注意 降序索引只对查询中特定的排序顺序有效,如果使用不当,反而查询效率更低。例如,上述

查询排序条件改为order by a desc, b desc,MySQL 5.7的执行计划要明显好于MySQL 8.0。

将排序条件修改为order by a desc, b desc后,下面来对比不同版本中执行计划的效果。 在MySQL 5.7版本

中查看数据表ts1的执行计划,结果如下:

sql 复制代码
EXPLAIN SELECT * FROM ts1 ORDER BY a DESC,b DESC LIMIT 5;

在MySQL 8.0版本中查看数据表ts1的执行计划。

从结果可以看出,修改后MySQL 5.7的执行计划要明显好于MySQL 8.0。

隐藏索引

在MySQL 5.7版本及之前,只能通过显式的方式删除索引。此时,如果发现删除索引后出现错误,又只能通过显式创建索引的方式将删除的索引创建回来。如果数据表中的数据量非常大,或者数据表本身比较大,这种操作就会消耗系统过多的资源,操作成本非常高。

从MySQL 8.x开始支持隐藏索引(invisible indexes) ,只需要将待删除的索引设置为隐藏索引,使=查询优化器不再使用这个索引(即使使用force index(强制使用索引),优化器也不会使用该索引),确认将索引设置为隐藏索引后系统不受任何响应,就可以彻底删除索引。这种通过先将索引设置为隐藏索引,再删除索引的方式就是软删除。

创建表时直接创建 在MySQL中创建隐藏索引通过SQL语句INVISIBLE来实现,其语法形式如下:

sql 复制代码
CREATE TABLE tablename(
    propname1 type1[CONSTRAINT1],
    propname2 type2[CONSTRAINT2],
    ......
    propnamen typen,
    INDEX [indexname](propname1 [(length)]) INVISIBLE
);

上述语句比普通索引多了一个关键字INVISIBLE,用来标记索引为不可见索引。

在已经存在的表上创建

可以为已经存在的表设置隐藏索引,其语法形式如下:

sql 复制代码
CREATE INDEX indexname
ON tablename(propname[(length)]) INVISIBLE;

通过ALTER TABLE语句创建

语法形式如下:

sql 复制代码
ALTER TABLE tablename
ADD INDEX indexname (propname [(length)]) INVISIBLE;

切换索引可见状态 已存在的索引可通过如下语句切换可见状态:

sql 复制代码
ALTER TABLE tablename ALTER INDEX index_name INVISIBLE; #切换成隐藏索引
ALTER TABLE tablename ALTER INDEX index_name VISIBLE; #切换成非隐藏索引

如果将index_cname索引切换成可见状态,通过explain查看执行计划,发现优化器选择了index_cname索引。

注意 当索引被隐藏时,它的内容仍然是和正常索引一样实时更新的。如果一个索引需要长期被隐

藏,那么可以将其删除,因为索引的存在会影响插入、更新和删除的性能。

通过设置隐藏索引的可见性可以查看索引对调优的帮助。

在MySQL 8.x版本中,为索引提供了一种新的测试方式,可以通过查询优化器的一个开关(use_invisible_indexes)来打开某个设置,使隐藏索引对查询优化器可见。如果 use_invisible_indexes设置为off(默认),优化器会忽略隐藏索引。如果设置为on,即使隐藏索引不可见,优化器在生成执行计划时仍会考虑使用隐藏索引。

(1)在MySQL命令行执行如下命令查看查询优化器的开关设置。

sql 复制代码
mysql> select @@optimizer_switch \G

在输出的结果信息中找到如下属性配置。

sql 复制代码
use_invisible_indexes=off

此属性配置值为off,说明隐藏索引默认对查询优化器不可见。

(2)使隐藏索引对查询优化器可见,需要在MySQL命令行执行如下命令:

sql 复制代码
mysql> set session optimizer_switch="use_invisible_indexes=on";
Query OK, 0 rows affected (0.00 sec)

SQL语句执行成功,再次查看查询优化器的开关设置

sql 复制代码
mysql> select @@optimizer_switch \G
*************************** 1. row ***************************
@@optimizer_switch:
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_
intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_co
st_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on
,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on
,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_ind
exes=on,skip_scan=on,hash_join=on
1 row in set (0.00 sec)

此时,在输出结果中可以看到如下属性配置。

sql 复制代码
use_invisible_indexes=on

use_invisible_indexes属性的值为on,说明此时隐藏索引对查询优化器可见。

(3)使用EXPLAIN查看以字段invisible_column作为查询条件时的索引使用情况。

sql 复制代码
explain select * from classes where cname = '高一2班';

查询优化器会使用隐藏索引来查询数据。

(4)如果需要使隐藏索引对查询优化器不可见,则只需要执行如下命令即可。

sql 复制代码
mysql> set session optimizer_switch="use_invisible_indexes=off";
Query OK, 0 rows affected (0.00 sec)

再次查看查询优化器的开关设置。

sql 复制代码
mysql> select @@optimizer_switch \G

此时,use_invisible_indexes属性的值已经被设置为"off"。

相关推荐
CoderYanger1 小时前
C.滑动窗口-求子数组个数-越长越合法——2799. 统计完全子数组的数目
java·c语言·开发语言·数据结构·算法·leetcode·职场和发展
廋到被风吹走1 小时前
【数据库】【MySQL】InnoDB外键解析:约束机制、性能影响与最佳实践
android·数据库·mysql
C++业余爱好者1 小时前
Java 提供了8种基本数据类型及封装类型介绍
java·开发语言·python
想用offer打牌1 小时前
RocketMQ如何防止消息丢失?
java·后端·架构·开源·rocketmq
皮卡龙1 小时前
Java常用的JSON
java·开发语言·spring boot·json
hhhjjjj1 小时前
docker安装postgreSQL
docker·postgresql·容器
掘根1 小时前
【消息队列】交换机数据管理实现
网络·数据库
Logic1012 小时前
《Mysql数据库应用》 第2版 郭文明 实验6 数据库系统维护核心操作与思路解析
数据库·sql·mysql·学习笔记·计算机网络技术·形考作业·国家开放大学
利刃大大2 小时前
【JavaSE】十三、枚举类Enum && Lambda表达式 && 列表排序常见写法
java·开发语言·枚举·lambda·排序
float_六七2 小时前
Java反射:万能遥控器拆解编程
java·开发语言