MySQL高级_第08章_索引的创建与设计原则

news2024/11/23 3:34:16

MySQL高级_第08章_索引的创建与设计原则

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 创建索引

1. 创建表的时候创建索引

举例:
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)
);
但是,如果显式创建表时创建索引的话,基本语法格式如下:
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 指定升序或者降序的索引值存储。
1. 创建普通索引
book 表中的 year_publication 字段上建立普通索引, 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)
);
2. 创建唯一索引
举例:
CREATE TABLE test1(
id INT NOT NULL ,
name varchar ( 30 ) NOT NULL ,
UNIQUE INDEX uk_idx_id(id)
);
该语句执行完毕之后,使用 SHOW CREATE TABLE 查看表结构:
SHOW INDEX FROM test1 \G
3. 主键索引
设定为主键后数据库会自动建立索引, innodb 为聚簇索引,语法:
  • 随表一起建索引:
CREATE TABLE student (
id INT ( 10 ) UNSIGNED AUTO_INCREMENT ,
student_no VARCHAR ( 200 ),
student_name VARCHAR ( 200 ),
PRIMARY KEY (id)
);
  • 删除主键索引:
ALTER TABLE student
drop PRIMARY KEY ;
修改主键索引:必须先删除掉 (drop) 原索引,再新建 (add) 索引
4. 创建单列索引
举例:
CREATE TABLE test2(
id INT NOT NULL ,
name CHAR ( 50 ) NULL ,
INDEX single_idx_name(name( 20 ))
);
该语句执行完毕之后,使用 SHOW CREATE TABLE 查看表结构:
SHOW INDEX FROM test2 \G
5. 创建组合索引
举例:创建表 test3 ,在表中的 id name age 字段上建立组合索引, 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)
);
该语句执行完毕之后,使用 SHOW INDEX 查看:
SHOW INDEX FROM test3 \G
6. 创建全文索引
举例 1 :创建表 test4 ,在表中的 info 字段上建立全文索引, 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 支持全文索引。
举例 2
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY ,
title VARCHAR ( 200 ),
body TEXT ,
FULLTEXT index (title, body)
) ENGINE = INNODB ;
创建了一个给 title body 字段添加全文索引的表。
举例 3
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 方式的的查询:
SELECT * FROM papers WHERE content LIKE ‘% 查询字符串 %’;
全文索引用 match+against 方式查询:
SELECT * FROM papers WHERE MATCH (title,content) AGAINST (‘ 查询字符串 ’);
注意点
1. 使用全文索引前,搞清楚版本支持情况;
2. 全文索引比 like + % N 倍,但是可能存在精度问题;
3. 如果需要全文索引的是大量数据,建议先添加数据,再创建索引。
7. 创建空间索引
空间索引创建中,要求空间类型的字段必须为 非空
举例:创建表 test5 ,在空间类型为 GEOMETRY 的字段上创建空间索引, 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 语句创建索引的基本语法如下:
ALTER TABLE table_name ADD [ UNIQUE | FULLTEXT | SPATIAL ] [ INDEX | KEY ]
[index_name] (col_name[length],...) [ ASC | DESC ]
2. 使用 CREATE INDEX 创建索引 CREATE INDEX 语句可以在已经存在的表上添加索引,在 MySQL 中,CREATE INDEX被映射到一个 ALTER TABLE 语句上,基本语法结构为:
CREATE [ UNIQUE | FULLTEXT | SPATIAL ] INDEX index_name
ON table_name (col_name[length],...) [ ASC | DESC ]

1.3 删除索引

1. 使用 ALTER TABLE 删除索引 ALTER TABLE 删除索引的基本语法格式如下:
ALTER TABLE table_name DROP INDEX index_name;
2. 使用 DROP INDEX 语句删除索引 DROP INDEX 删除索引的基本语法格式如下:
DROP INDEX index_name ON table_name ;
提示 删除表中的列时,如果要删除的列为索引的组成部分,则该列也会从索引中删除。如果组成
索引的所有列都被删除,则整个索引将被删除。

2. MySQL8.0索引新特性

2.1 支持降序索引

举例:分别在 MySQL 5.7 版本和 MySQL 8.0 版本中创建数据表 ts1 ,结果如下:
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 条随机数据,执行语句如下:
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 的执行计划,结果如下:
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 的执行计划,结果如下:
EXPLAIN SELECT * FROM ts1 ORDER BY a DESC ,b DESC LIMIT 5 ;
MySQL 8.0 版本中查看数据表 ts1 的执行计划。
从结果可以看出,修改后 MySQL 5.7 的执行计划要明显好于 MySQL 8.0

2.2 隐藏索引

MySQL 5.7 版本及之前,只能通过显式的方式删除索引。此时,如果发现删除索引后出现错误,又只能通过显式创建索引的方式将删除的索引创建回来。如果数据表中的数据量非常大,或者数据表本身比较大,这种操作就会消耗系统过多的资源,操作成本非常高。
MySQL 8.x 开始支持 隐藏索引( invisible indexes ,只需要将待删除的索引设置为隐藏索引,使查询优化器不再使用这个索引(即使使用force index (强制使用索引),优化器也不会使用该索引),确认将索引设置为隐藏索引后系统不受任何响应,就可以彻底删除索引。 这种通过先将索引设置为隐藏索引,再删除索引的方式就是软删除
1. 创建表时直接创建 MySQL 中创建隐藏索引通过 SQL 语句 INVISIBLE 来实现,其语法形式如下:
CREATE TABLE tablename(
propname1 type1[CONSTRAINT1],
propname2 type2[CONSTRAINT2],
……
propnamen typen,
INDEX [indexname](propname1 [(length)]) INVISIBLE
);
上述语句比普通索引多了一个关键字 INVISIBLE ,用来标记索引为不可见索引。
2. 在已经存在的表上创建
可以为已经存在的表设置隐藏索引,其语法形式如下:
CREATE INDEX indexname
ON tablename(propname[(length)]) INVISIBLE;
3. 通过 ALTER TABLE 语句创建
语法形式如下:
ALTER TABLE tablename
ADD INDEX indexname (propname [(length)]) INVISIBLE;
4. 切换索引可见状态 已存在的索引可通过如下语句切换可见状态:
ALTER TABLE tablename ALTER INDEX index_name INVISIBLE; # 切换成隐藏索引
ALTER TABLE tablename ALTER INDEX index_name VISIBLE; # 切换成非隐藏索引
如果将 index_cname 索引切换成可见状态,通过 explain 查看执行计划,发现优化器选择了 index_cname 索引。
注意 当索引被隐藏时,它的内容仍然是和正常索引一样实时更新的。如果一个索引需要长期被隐藏,那么可以将其删除,因为索引的存在会影响插入、更新和删除的性能。
通过设置隐藏索引的可见性可以查看索引对调优的帮助。
5. 使隐藏索引对查询优化器可见
MySQL 8.x 版本中,为索引提供了一种新的测试方式,可以通过查询优化器的一个开关
(use_invisible_indexes)来打开某个设置,使隐藏索引对查询优化器可见。如果 use_invisible_indexes 设置为off( 默认 ) ,优化器会忽略隐藏索引。如果设置为 on ,即使隐藏索引不可见,优化器在生成执行计划时仍会考虑使用隐藏索引。
(1)在 MySQL 命令行执行如下命令查看查询优化器的开关设置。
mysql> select @@optimizer_switch \G
在输出的结果信息中找到如下属性配置。
use_invisible_indexes = off
此属性配置值为 off ,说明隐藏索引默认对查询优化器不可见。
(2)使隐藏索引对查询优化器可见,需要在 MySQL 命令行执行如下命令:
mysql> set session optimizer_switch= "use_invisible_indexes=on" ;
Query OK, 0 rows affected ( 0.00 sec)
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)
此时,在输出结果中可以看到如下属性配置。
use_invisible_indexes = on
use_invisible_indexes 属性的值为 on ,说明此时隐藏索引对查询优化器可见。
(3)使用 EXPLAIN 查看以字段 invisible_column 作为查询条件时的索引使用情况。
explain select * from classes where cname = ' 高一 2 ' ;
查询优化器会使用隐藏索引来查询数据。
(4)如果需要使隐藏索引对查询优化器不可见,则只需要执行如下命令即可。
mysql> set session optimizer_switch= "use_invisible_indexes=off" ;
Query OK, 0 rows affected ( 0.00 sec)
再次查看查询优化器的开关设置。
mysql> select @@optimizer_switch \G
此时, use_invisible_indexes 属性的值已经被设置为 “off”

3. 索引的设计原则

3.1 数据准备

1 步:创建数据库、创建表
CREATE DATABASE atguigudb1;
USE atguigudb1;
#1. 创建学生表和课程表
CREATE TABLE `student_info` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT ,
`student_id` INT NOT NULL ,
`name` VARCHAR ( 20 ) DEFAULT NULL ,
`course_id` INT NOT NULL ,
`class_id` INT ( 11 ) DEFAULT NULL ,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
PRIMARY KEY ( `id` )
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET =utf8;
CREATE TABLE `course` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT ,
`course_id` INT NOT NULL ,
`course_name` VARCHAR ( 40 ) DEFAULT NULL ,
PRIMARY KEY ( `id` )
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET =utf8;
2 步:创建模拟数据必需的存储函数
# 函数 1 :创建随机产生字符串函数
DELIMITER //
CREATE FUNCTION rand_string(n INT )
RETURNS VARCHAR ( 255 ) # 该函数会返回一个字符串
BEGIN
DECLARE chars_str VARCHAR ( 100 ) DEFAULT
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ' ;
DECLARE return_str VARCHAR ( 255 ) DEFAULT '' ;
DECLARE i INT DEFAULT 0 ;
WHILE i < n DO
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR( 1 +RAND()* 52 ), 1 ));
SET i = i + 1 ;
END WHILE ;
RETURN return_str;
END //
DELIMITER ;
# 函数 2 :创建随机数函数
DELIMITER //
CREATE FUNCTION rand_num (from_num INT ,to_num INT ) RETURNS INT ( 11 )
BEGIN
DECLARE i INT DEFAULT 0 ;
SET i = FLOOR(from_num +RAND()*(to_num - from_num+ 1 )) ;
RETURN i;
END //
DELIMITER ;
创建函数,假如报错:
This function has none of DETERMINISTIC......
由于开启过慢查询日志 bin-log, 我们就必须为我们的 function 指定一个参数。
主从复制,主机会将写操作记录在 bin-log 日志中。从机读取 bin-log 日志,执行语句来同步数据。如果使用函数来操作数据,会导致从机和主键操作时间不一致。所以,默认情况下,mysql 不开启创建函数设置。
  • 查看mysql是否允许创建函数:
show variables like 'log_bin_trust_function_creators' ;
  • 命令开启:允许创建函数设置:
set global log_bin_trust_function_creators= 1 ; # 不加 global 只是当前窗口有效。
  • mysqld重启,上述参数又会消失。永久方法:
    • windows下:my.ini[mysqld]加上:

log_bin_trust_function_creators=1

    • linux下:/etc/my.cnfmy.cnf[mysqld]加上:
log_bin_trust_function_creators = 1
3 步:创建插入模拟数据的存储过程
# 存储过程 1 :创建插入课程表存储过程
DELIMITER //
CREATE PROCEDURE insert_course( max_num INT )
BEGIN
DECLARE i INT DEFAULT 0 ;
SET autocommit = 0 ; # 设置手动提交事务
REPEAT # 循环
SET i = i + 1 ; # 赋值
INSERT INTO course (course_id, course_name ) VALUES
(rand_num( 10000 , 10100 ),rand_string( 6 ));
UNTIL i = max_num
END REPEAT ;
COMMIT ; # 提交事务
END //
DELIMITER ;
# 存储过程 2 :创建插入学生信息表存储过程
DELIMITER //
CREATE PROCEDURE insert_stu( max_num INT )
BEGIN
DECLARE i INT DEFAULT 0 ;
SET autocommit = 0 ; # 设置手动提交事务
REPEAT # 循环
SET i = i + 1 ; # 赋值
INSERT INTO student_info (course_id, class_id ,student_id ,NAME ) VALUES
(rand_num( 10000 , 10100 ),rand_num( 10000 , 10200 ),rand_num( 1 , 200000 ),rand_string( 6 ));
UNTIL i = max_num
END REPEAT ;
COMMIT ; # 提交事务
END //
DELIMITER ;
4 步:调用存储过程
CALL insert_course( 100 );
CALL insert_stu( 1000000 );

3.2 哪些情况适合创建索引

1. 字段的数值有唯一性的限制

业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。(来源: Alibaba
说明:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的。

2. 频繁作为 WHERE 查询条件的字段

某个字段在 SELECT 语句的 WHERE 条件中经常被使用到,那么就需要给这个字段创建索引了。尤其是在数据量大的情况下,创建普通索引就可以大幅提升数据查询的效率。
比如 student_info 数据表(含 100 万条数据),假设我们想要查询 student_id=123110 的用户信息。

3. 经常 GROUP BY ORDER BY 的列

索引就是让数据按照某种顺序进行存储或检索,因此当我们使用 GROUP BY 对数据进行分组查询,或者使用 ORDER BY 对数据进行排序的时候,就需要 对分组或者排序的字段进行索引 。如果待排序的列有多个,那么可以在这些列上建立 组合索引

4. UPDATEDELETE WHERE 条件列

对数据按照某个条件进行查询后再进行 UPDATE DELETE 的操作,如果对 WHERE 字段创建了索引,就能大幅提升效率。原理是因为我们需要先根据 WHERE 条件列检索出来这条记录,然后再对它进行更新或删除。 如果进行更新的时候,更新的字段是非索引字段,提升的效率会更明显,这是因为非索引字段更 新不需要对索引进行维护。

5.DISTINCT 字段需要创建索引

有时候我们需要对某个字段进行去重,使用 DISTINCT ,那么对这个字段创建索引,也会提升查询效率。
比如,我们想要查询课程表中不同的 student_id 都有哪些,如果我们没有对 student_id 创建索引,执行SQL 语句:
SELECT DISTINCT (student_id) FROM `student_info` ;
运行结果(600637 条记录,运行时间 0.683s ):
如果我们对 student_id 创建索引,再执行 SQL 语句:
SELECT DISTINCT (student_id) FROM `student_info` ;
运行结果(600637 条记录,运行时间 0.010s ):
你能看到 SQL 查询效率有了提升,同时显示出来的 student_id 还是按照 递增的顺序 进行展示的。这是因为索引会对数据按照某种顺序进行排序,所以在去重的时候也会快很多。

6. 多表 JOIN 连接操作时,创建索引注意事项

首先, 连接表的数量尽量不要超过 3 ,因为每增加一张表就相当于增加了一次嵌套的循环,数量级增长会非常快,严重影响查询的效率。
其次, WHERE 条件创建索引 ,因为 WHERE 才是对数据条件的过滤。如果在数据量非常大的情况下,没有 WHERE 条件过滤是非常可怕的。
最后, 对用于连接的字段创建索引 ,并且该字段在多张表中的 类型必须一致 。比如 course_id
student_info 表和 course 表中都为 int(11) 类型,而不能一个为 int 另一个为 varchar 类型。
举个例子,如果我们只对 student_id 创建索引,执行 SQL 语句:
SELECT course_id, name, student_info .student_id , course_name
FROM student_info JOIN course
ON student_info .course_id = course .course_id
WHERE name = '462eed7ac6e791292a79' ;
运行结果(1 条数据,运行时间 0.189s ):
这里我们对 name 创建索引,再执行上面的 SQL 语句,运行时间为 0.002s

7. 使用列的类型小的创建索引

8. 使用字符串前缀创建索引

创建一张商户表,因为地址字段比较长,在地址字段上建立前缀索引
create table shop(address varchar ( 120 ) not null );
alter table shop add index (address( 12 ));
问题是,截取多少呢?截取得多了,达不到节省索引存储空间的目的;截取得少了,重复内容太多,字段的散列度( 选择性 ) 会降低。 怎么计算不同的长度的选择性呢?
先看一下字段在全部数据中的选择度:
select count ( distinct address) / count (*) from shop;
通过不同长度去计算,与全表的选择性对比:
公式:
count ( distinct left ( 列名 , 索引长度 ))/count(*)
例如:
select count ( distinct left (address, 10 )) / count (*) as sub10, -- 截取前 10 个字符的选择度
count ( distinct left (address, 15 )) / count (*) as sub11, -- 截取前 15 个字符的选择度
count ( distinct left (address, 20 )) / count (*) as sub12, -- 截取前 20 个字符的选择度
count ( distinct left (address, 25 )) / count (*) as sub13 -- 截取前 25 个字符的选择度
from shop;
引申另一个问题:索引列前缀对排序的影响
拓展: Alibaba Java 开发手册》
强制 】在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。
说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会 高达90% 以上 ,可以使用 count(distinct left( 列名 , 索引长度 ))/count(*) 的区分度来确定。

9. 区分度高(散列性高)的列适合作为索引

10. 使用最频繁的列放到联合索引的左侧

这样也可以较少的建立一些索引。同时,由于 " 最左前缀原则 " ,可以增加联合索引的使用率。

11. 在多个字段都要创建索引的情况下,联合索引优于单值索引

3.3 限制索引的数目

3.4 哪些情况不适合创建索引

1. where中使用不到的字段,不要设置索引

2. 数据量小的表最好不要使用索引

举例:创建表 1
CREATE TABLE t_without_index(
a INT PRIMARY KEY AUTO_INCREMENT ,
b INT
);
提供存储过程 1
# 创建存储过程
DELIMITER //
CREATE PROCEDURE t_wout_insert()
BEGIN
DECLARE i INT DEFAULT 1 ;
WHILE i <= 900
DO
INSERT INTO t_without_index(b) SELECT RAND()* 10000 ;
SET i = i + 1 ;
END WHILE ;
COMMIT ;
END //
DELIMITER ;
# 调用
CALL t_wout_insert();

创建表2 

CREATE TABLE t_with_index(
a INT PRIMARY KEY AUTO_INCREMENT ,
b INT ,
INDEX idx_b(b)
);
创建存储过程 2
# 创建存储过程
DELIMITER //
CREATE PROCEDURE t_with_insert()
BEGIN
DECLARE i INT DEFAULT 1 ;
WHILE i <= 900
DO
INSERT INTO t_with_index(b) SELECT RAND()* 10000 ;
SET i = i + 1 ;
END WHILE ;
COMMIT ;
END //
DELIMITER ;
# 调用
CALL t_with_insert();
查询对比:
mysql> select * from t_without_index where b = 9879 ;
+------+------+
| a | b |
+------+------+
| 1242 | 9879 |
+------+------+
1 row in set ( 0.00 sec)
mysql> select * from t_with_index where b = 9879 ;
+-----+------+
| a | b |
+-----+------+
| 112 | 9879 |
+-----+------+
1 row in set ( 0.00 sec)
你能看到运行结果相同,但是在数据量不大的情况下,索引就发挥不出作用了。
结论:在数据表中的数据行数比较少的情况下,比如不到 1000 行,是不需要创建索引的。

3. 有大量重复数据的列上不要建立索引

举例 1 :要在 100 万行数据中查找其中的 50 万行(比如性别为男的数据),一旦创建了索引,你需要先访问 50 万次索引,然后再访问 50 万次数据表,这样加起来的开销比不使用索引可能还要大。
举例 2 :假设有一个学生表,学生总数为 100 万人,男性只有 10 个人,也就是占总人口的 10 万分之 1
学生表 student_gender 结构如下。其中数据表中的 student_gender 字段取值为 0 1 0 代表女性, 1 代表男性。
CREATE TABLE student_gender(
student_id INT ( 11 ) NOT NULL ,
student_name VARCHAR ( 50 ) NOT NULL ,
student_gender TINYINT ( 1 ) NOT NULL ,
PRIMARY KEY (student_id)
) ENGINE = INNODB ;
如果我们要筛选出这个学生表中的男性,可以使用:
SELECT * FROM student_gender WHERE student_gender = 1
运行结果(10 条数据,运行时间 0.696s ):
 
结论:当数据重复度大,比如 高于 10% 的时候,也不需要对这个字段使用索引。

4. 避免对经常更新的表创建过多的索引

5. 不建议用无序的值作为索引

例如身份证、 UUID( 在索引比较时需要转为 ASCII ,并且插入时可能造成页分裂 ) MD5 HASH 、无序长字符串等。

6. 删除不再使用或者很少使用的索引

7. 不要定义冗余或重复的索引

① 冗余索引
举例:建表语句如下
CREATE TABLE person_info(
id INT UNSIGNED NOT NULL AUTO_INCREMENT ,
name VARCHAR ( 100 ) NOT NULL ,
birthday DATE NOT NULL ,
phone_number CHAR ( 11 ) NOT NULL ,
country varchar ( 100 ) NOT NULL ,
PRIMARY KEY (id),
KEY idx_name_birthday_phone_number (name( 10 ), birthday, phone_number),
KEY idx_name (name( 10 ))
);
我们知道,通过 idx_name_birthday_phone_number 索引就可以对 name 列进行快速搜索,再创建一个专门针对 name 列的索引就算是一个 冗余索引 ,维护这个索引只会增加维护的成本,并不会对搜索有什么好处。
② 重复索引
另一种情况,我们可能会对某个列 重复建立索引 ,比方说这样:
CREATE TABLE repeat_index_demo (
col1 INT PRIMARY KEY ,
col2 INT ,
UNIQUE uk_idx_c1 (col1),
INDEX idx_c1 (col1)
);
我们看到, col1 既是主键、又给它定义为一个唯一索引,还给它定义了一个普通索引,可是主键本身就会生成聚簇索引,所以定义的唯一索引和普通索引是重复的,这种情况要避免。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/540701.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新手如何重装Win10系统 新手重装Win10系统的方法

电脑系统是电脑运行的核心&#xff0c;如果出现问题就需要重装系统。对于新手来说&#xff0c;重装电脑系统可能会显得比较困难和陌生。本文将介绍新手如何重装电脑系统Win10&#xff0c;让电脑新手也能轻松搞定。 新手重装Win10系统的方法 一、准备工作 1、下载极客狗电脑重…

canvas、svg的基本使用【数据可视化】

什么是数据可视化&#xff1f; 基本概念&#xff1a;是关于数据视觉表现形式的科学技术研究 这个概念向我们传达了两个信息&#xff1a; &#xff08;1&#xff09;数据可视化是一门学科 &#xff08;2&#xff09;数据可视化与数据和视觉有关 数据可视化简单理解&#xff0c;…

veth网卡的多队列及RPS

背景&#xff1a; 3.10内核下容器使用的veth网卡&#xff0c;默认开启的是一个队列&#xff0c;导致在某些单线程多TCP链接的应用场景下&#xff0c;出现某个CPU软中断高的情况。之前处理的方案一直是开启这个veth网卡的RPS&#xff0c;让其在多流场景下可以去分散到其它CPU上…

DSSM - 双塔经典模型(微软)

《Learning Deep Structured Semantic Models for Web Search using Clickthrough Data》论文由微软发表于 CIKM-2013。DSSM被广泛用于工业界的 召回/粗排 阶段。 模型结构 模型结构一目了然&#xff0c;非常简单&#xff0c;双塔结构&#xff1a;user侧一个塔&#xff0c;ite…

ChatGPT的兴起的时代,国内chatgpt产品大盘点

在人工智能技术的不断发展和应用下&#xff0c;自然语言处理技术成为了研究的热点之一。而其中最受关注的就是“聊天机器人”技术&#xff0c;而GPT&#xff08;Generative Pre-trained Transformer&#xff09;模型则是目前最流行的聊天机器人生成模型之一。 随着 ChatGPT 技…

蓝牙RSSI/BLE AOA/UWB室内定位技术哪个好?

蓝牙AOA定位技术的出现&#xff0c;弥补了蓝牙RSSI值定位精度不高的缺陷。从理论上来说&#xff0c;可以对目前的蓝牙RSSI定位方案进行一定程度的替代。当然了&#xff0c;在高精度定位应用领域中&#xff0c;UWB定位已经在批量的成熟商用了。蓝牙AOA也具有很高的定位精度&…

单位网站被黑被下达整改进行行政处罚

最近这几年&#xff0c;由于信息系统安全等级保护法的普及&#xff0c;越来越多公司收到当地公安网监部门打来的电话&#xff0c;说你们公司网站有漏洞&#xff0c;需要限期在2-3内进行漏洞整改和加固&#xff0c;遇到这种情况&#xff0c;不要着急&#xff0c;下面来分享一下该…

JavaEE(系列8) -- 多线程案例(单例模式)

目录 1. 设计模式 2. 单例模式 -- 饿汉模式 3. 单例模式 -- 懒汉模式 4. 单例模式(懒汉模式-多线程) 1. 设计模式 什么是设计模式? 设计模式好比象棋中的 "棋谱". 红方当头炮, 黑方马来跳. 针对红方的一些走法, 黑方应招的时候有一些固定的套路. 按照套路…

【融合感知】激光雷达和相机融合感知-BEVFusion

BEVFusion有两篇文章&#xff0c;这里在一起分析下不同&#xff0c;分别是&#xff1a; 【1】BEVFusion: A Simple and Robust LiDAR-Camera Fusion Framework. 【2】BEVFusion: Multi-Task Multi-Sensor Fusion with Unified Bird’s-Eye View Representation 先说结论&…

品牌联名又出圈了!小红书数据揭示,引爆流量三部曲

这几天&#xff0c;你们的朋友圈是不是被喜茶FENDI黄刷屏啦&#xff1f;近日&#xff0c;茶饮品牌牵手意大利奢侈品牌联名上新&#xff0c;一跃成为各平台热门。 品牌新联名&#xff0c;这次又出圈了&#xff01; 喜茶可谓是联名界的老玩家了&#xff0c;曾与藤原浩、《梦华录》…

HTML5新增标签

前言 HTML5的新特性主要是针对以前的不足&#xff0c;增加了一些新标签&#xff0c;新表单和新表单属性 但是呢&#xff01;这些标签大多都存在兼容性的问题&#xff0c;基本是IE9以上的版本才支持 之前我们做布局&#xff0c;都用的是div&#xff0c;但是没有语义性 HTM…

Java开发的多商户商城系统源码小程序app

Java开发的多商户商城系统源码小程序app主要是为商家提供一个线上销售平台&#xff0c;实现商品展示、订单管理、支付等功能&#xff0c;并且支持多商户注册&#xff0c;以下是它的功能介绍&#xff1a; 一、 用户端&#xff1a;1. 用户注册与登录&#xff1a;用户可以使用手机…

关于LimeReport导出报告在Qt下的使用问题

关于这个第三方库的使用网上有很多资料了 具体说一下关于文本和图片控件的使用 1、文本使用 如果需要自定义文本内容&#xff0c;需要的格式为上图$V{}; 2、图片使用 如果需要自定义图片内容&#xff0c;需要的格式为上图$V{}&#xff0c;写法和文本内容一样&#xff1b; 3、…

博士毕业答辩流程 注意事项

前言&#xff1a;2023年5月17日14:00-17:00&#xff0c;与实验室其他同学一起旁听了本实验室的博士论文答辩。接下来&#xff0c;我对博士毕业答辩的大致流程进行简要介绍&#xff0c;并对个环节的注意事项进行总结归纳&#xff0c;供毕业生参考。 目录 1. 准备阶段2. 汇报期间…

Python 框架学习 Django篇 (一) 安装及基本使用

环境说明 python 3.11.3 Django 4.2.1 idea 2023.1 一、安装调试 我这里默认idea和python环境都是装好的&#xff0c;直接从建项目开始 新建项目 项目名称: demo 安装Django //配置清华镜像源 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simp…

AMBER分子动力学模拟之结果分析(突变型的能量计算,丙氨酸扫描)-- HIV蛋白酶-抑制剂复合物(5)

AMBER分子动力学模拟之结果分析(突变型的能量计算,丙氨酸扫描)-- HIV蛋白酶-抑制剂复合物(5) 丙氨酸扫描 在带电残基上引入一个或几个丙氨酸&#xff0c;观察这些改变对蛋白功能的影响。置换成丙氨酸&#xff0c;去除了侧链上的活性基团&#xff0c;换成了体积小、无其他官能…

【AGC】新版鸿蒙崩溃SDK集成使用方法

【背景】 我们知道AGC的Crash SDK都是需要强制集成华为分析SDK的&#xff0c;在使用时的崩溃数据上报都要依靠分析服务来完成&#xff0c;这就容易受到限制&#xff0c;有时出现无数据的情况就要依次排查崩溃SDK与分析SDK&#xff0c;比较麻烦。而就在不久前&#xff0c;鸿蒙崩…

【Linux】内存映射

目录 内存映射相关函数解析代码示例1&#xff08;使用内存映射实现没有关系的进程间的通信&#xff09;&#xff1a;代码示例2&#xff08;使用内存映射实现文件间拷贝&#xff09;&#xff1a;匿名内存映射 橙色 内存映射 内存映射(Memory-mapped I/o)是将磁盘文件的数据映射…

GitLabRunner安装部署

实战&#xff1a;GitLabRunner安装部署-2023.4.26(安装成功) 目录 实验环境 gitlab/gitlab-ce:15.0.3-ce.0 gitlab-runner-15.0.1-1.x86_64.rpm实验软件 链接&#xff1a;https://pan.baidu.com/s/10aL_kWzDKnZXBXG3-hS2tA 提取码&#xff1a;0820 2023.4.26-实战&#xff1a…

HTTP第16讲——HTTP的重定向和跳转

背景 “超文本”里含有“超链接”&#xff0c;可以从一个“超文本”跳跃到另一个“超文本”&#xff0c;对线性结构的传统文档是一个根本性的变革。 能够使用“超链接”在网络上任意地跳转也是万维网的一个关键特性。它把分散在世界各地的文档连接在一起&#xff0c;形成了复杂…