当前位置:   article > 正文

MySQL高级篇——索引的创建与设计原则_mysql索引语句

mysql索引语句

文章目录:

1.索引的分类

2.创建索引的三种方式

2.1 方式一:CREATE TABLE

2.1.1 小例子

2.1.2 普通索引

2.1.3 唯一性索引

2.1.4 主键索引

2.1.5 单列索引

2.1.6 联合索引

2.1.7 全文索引

2.2 方式二:ALTER TABLE ... ADD INDEX ...

2.3 方式三:CREATE INDEX ... ON ...

3.删除索引的两种方式

3.1 使用ALTER TABLE删除索引

3.2 使用DROP INDEX语句删除索引

4.索引的设计原则(未完待续,明天补全......)

4.1 哪些情况适合创建索引?

4.1.1 字段的数值有唯一性的限制

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

4.1.3 经常 GROUP BY 和 ORDER BY 的列

4.1.4 UPDATE、DELETE 的 WHERE 条件列

4.1.5 DISTINCT 字段需要创建索引

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

4.1.7 使用列的类型小的创建索引

4.1.8 使用字符串前缀创建索引

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

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

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

4.2 限制索引的数目

4.3 哪些情况不适合创建索引?

4.3.1 在where中使用不到的字段,不要设置索引

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

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

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

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

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

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


1.索引的分类

MySQL 的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。
功能逻辑 上说,索引主要有 4 种,分别是普通索引、唯一索引、主键索引、全文索引。
按照 物理实现方式 ,索引可以分为 2 种:聚簇索引和非聚簇索引。
按照 作用字段个数 进行划分,分成单列索引和联合索引。


2.创建索引的三种方式

2.1 方式一:CREATE TABLE

2.1.1 小例子

  1. CREATE DATABASE dbtest2;
  2. USE dbtest2;
  3. CREATE TABLE dept (
  4. dept_id INT PRIMARY KEY AUTO_INCREMENT,
  5. dept_name VARCHAR(20)
  6. );
  7. SHOW INDEX FROM dept;

  1. CREATE TABLE emp (
  2. emp_id INT PRIMARY KEY AUTO_INCREMENT,
  3. emp_name VARCHAR(20) UNIQUE,
  4. dept_id INT,
  5. CONSTRAINT fk_emp_dept_id FOREIGN KEY(dept_id) REFERENCES dept(dept_id)
  6. );
  7. SHOW INDEX FROM emp;

2.1.2 普通索引

  1. CREATE TABLE book (
  2. book_id INT,
  3. book_name VARCHAR(100),
  4. `authors` VARCHAR(100),
  5. info VARCHAR(100),
  6. `comment` VARCHAR(100),
  7. year_publication YEAR,
  8. INDEX idx_bname(book_name)
  9. );
  10. SHOW INDEX FROM book;

如果我们写一个简单的sql语句,在where后面用 book_name 来筛选,可以通过 explain 性能分析工具来看看是什么样的?

  1. EXPLAIN
  2. SELECT *
  3. FROM book
  4. WHERE book_name = 'mysql';

2.1.3 唯一性索引

  1. CREATE TABLE book1 (
  2. book_id INT,
  3. book_name VARCHAR(100),
  4. `authors` VARCHAR(100),
  5. info VARCHAR(100),
  6. `comment` VARCHAR(100),
  7. year_publication YEAR,
  8. UNIQUE INDEX uk_idx_cmt(`comment`)
  9. );
  10. SHOW INDEX FROM book1;

接下来,依次向表中插入三条记录,在插入第二条记录的时候,就会报错。而第三条记录是正常执行的。

  1. INSERT INTO book1(book_id, book_name, `comment`)
  2. VALUES(1, 'MySQL高级', '适合有数据库开发经验的人员学习');
  3. INSERT INTO book1(book_id, book_name, `comment`)
  4. VALUES(1, 'MySQL高级', '适合有数据库开发经验的人员学习');
  5. INSERT INTO book1(book_id, book_name, `comment`)
  6. VALUES(1, 'MySQL高级', NULL);

最终,在book1这张表中,将存在两条记录。

2.1.4 主键索引

  1. CREATE TABLE book2 (
  2. book_id INT PRIMARY KEY,
  3. book_name VARCHAR(100),
  4. `authors` VARCHAR(100),
  5. info VARCHAR(100),
  6. `comment` VARCHAR(100),
  7. year_publication YEAR
  8. );
  9. SHOW INDEX FROM book2;

2.1.5 单列索引

其实我们上面创建的那些都是单列索引。

  1. CREATE TABLE book3 (
  2. book_id INT ,
  3. book_name VARCHAR(100),
  4. AUTHORS VARCHAR(100),
  5. info VARCHAR(100) ,
  6. COMMENT VARCHAR(100),
  7. year_publication YEAR,
  8. UNIQUE INDEX idx_bname(book_name)
  9. );
  10. SHOW INDEX FROM book3;

2.1.6 联合索引

  1. CREATE TABLE book4 (
  2. book_id INT ,
  3. book_name VARCHAR(100),
  4. AUTHORS VARCHAR(100),
  5. info VARCHAR(100) ,
  6. COMMENT VARCHAR(100),
  7. year_publication YEAR,
  8. INDEX mul_bid_bname_info(book_id, book_name, info)
  9. );
  10. SHOW INDEX FROM book4;

下面我们通过两条sql来分析一下,联合索引在查找过程中是怎么走的?

  1. EXPLAIN
  2. SELECT *
  3. FROM book4
  4. WHERE book_id = 1001 AND book_name = 'mysql';

解释:因为我们建的联合索引是 book_id, book_name, info 这样的顺序,所以在构建B+树的时候,就是先按照 book_id 来进行排序,当book_id相同时,再按照 book_name来排序,当book_name一样时,最后按照info来排序。(在B+树中,book_name实际上是位于book_id下方的,查找一定是先经过book_id、后经过book_name的)

所以上面这条sql,会先走book_id,再走book_name的。

  1. EXPLAIN
  2. SELECT *
  3. FROM book4
  4. WHERE book_name = 'mysql';

经过上面的分析,那么这条sql为什么就没有走联合索引呢?(这book_name不是存在于联合索引中吗?),这里其实还是构建B+树的顺序问题,你要想在where筛选中走book_name索引,你就必须先要走book_id索引的,因为book_name在联合索引中位于book_id之后,所以在B+树中book_name就处于book_id下方,你连B+树的第二层(假设)都还没走到,又何谈到达B+树的第三层呢?    所以这条sql是不会走索引的。

2.1.7 全文索引

  1. CREATE TABLE test4 (
  2. id INT NOT NULL,
  3. NAME CHAR(30) NOT NULL,
  4. age INT NOT NULL,
  5. info VARCHAR(255),
  6. FULLTEXT INDEX futxt_idx_info(info(50))
  7. );
  8. SHOW INDEX FROM test4;

2.2 方式二:ALTER TABLE ... ADD INDEX ...

  1. DROP TABLE IF EXISTS book5;
  2. CREATE TABLE book5 (
  3. book_id INT ,
  4. book_name VARCHAR(100),
  5. `authors` VARCHAR(100),
  6. info VARCHAR(100) ,
  7. `comment` VARCHAR(100),
  8. year_publication YEAR
  9. );
  10. ALTER TABLE book5 ADD INDEX idx_cmt(`comment`);
  11. ALTER TABLE book5 ADD UNIQUE INDEX uk_idx_bname(book_name);
  12. ALTER TABLE book5 ADD INDEX mul_bid_bname_info(book_id, book_name, info);
  13. SHOW INDEX FROM book5;

2.3 方式三:CREATE INDEX ... ON ...

  1. DROP TABLE IF EXISTS book6;
  2. CREATE TABLE book6 (
  3. book_id INT ,
  4. book_name VARCHAR(100),
  5. `authors` VARCHAR(100),
  6. info VARCHAR(100) ,
  7. `comment` VARCHAR(100),
  8. year_publication YEAR
  9. );
  10. CREATE INDEX idx_cmt ON book6(`comment`);
  11. CREATE UNIQUE INDEX uk_idx_bname ON book6(book_name);
  12. CREATE INDEX mul_bid_bname_info ON book6(book_id, book_name, info);
  13. SHOW INDEX FROM book6;


3.删除索引的两种方式

3.1 使用ALTER TABLE删除索引

3.2 使用DROP INDEX语句删除索引

提示 删除表中的列时,如果要删除的列为索引的组成部分,则该列也会从索引中删除。如果组成索引的所有列都被删除,则整个索引将被删除。
  1. ALTER TABLE book6 DROP INDEX idx_cmt;
  2. DROP INDEX uk_idx_bname ON book6;
  3. ALTER TABLE book6 DROP COLUMN book_name;
  4. ALTER TABLE book6 DROP COLUMN book_id;
  5. ALTER TABLE book6 DROP COLUMN info;
  6. SHOW INDEX FROM book6;


4.索引的设计原则(未完待续,明天补全......)

4.1 哪些情况适合创建索引?

我们先准备一些数据,用作下面创建索引及测试过程。

  1. #1. 数据的准备
  2. CREATE DATABASE atguigudb1;
  3. USE atguigudb1;
  4. #1.创建学生表和课程表
  5. CREATE TABLE `student_info` (
  6. `id` INT(11) AUTO_INCREMENT,
  7. `student_id` INT NOT NULL ,
  8. `name` VARCHAR(20) DEFAULT NULL,
  9. `course_id` INT NOT NULL ,
  10. `class_id` INT(11) DEFAULT NULL,
  11. `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  12. PRIMARY KEY (`id`)
  13. ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  14. CREATE TABLE `course` (
  15. `id` INT(11) NOT NULL AUTO_INCREMENT,
  16. `course_id` INT NOT NULL ,
  17. `course_name` VARCHAR(40) DEFAULT NULL,
  18. PRIMARY KEY (`id`)
  19. ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  20. #函数1:创建随机产生字符串函数
  21. DELIMITER //
  22. CREATE FUNCTION rand_string(n INT)
  23. RETURNS VARCHAR(255) #该函数会返回一个字符串
  24. BEGIN
  25. DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
  26. DECLARE return_str VARCHAR(255) DEFAULT '';
  27. DECLARE i INT DEFAULT 0;
  28. WHILE i < n DO
  29. SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
  30. SET i = i + 1;
  31. END WHILE;
  32. RETURN return_str;
  33. END //
  34. DELIMITER ;
  35. SELECT @@log_bin_trust_function_creators;
  36. SET GLOBAL log_bin_trust_function_creators = 1;
  37. #函数2:创建随机数函数
  38. DELIMITER //
  39. CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11)
  40. BEGIN
  41. DECLARE i INT DEFAULT 0;
  42. SET i = FLOOR(from_num +RAND()*(to_num - from_num+1)) ;
  43. RETURN i;
  44. END //
  45. DELIMITER ;
  46. # 存储过程1:创建插入课程表存储过程
  47. DELIMITER //
  48. CREATE PROCEDURE insert_course( max_num INT )
  49. BEGIN
  50. DECLARE i INT DEFAULT 0;
  51. SET autocommit = 0; #设置手动提交事务
  52. REPEAT #循环
  53. SET i = i + 1; #赋值
  54. INSERT INTO course (course_id, course_name ) VALUES (rand_num(10000,10100),rand_string(6));
  55. UNTIL i = max_num
  56. END REPEAT;
  57. COMMIT; #提交事务
  58. END //
  59. DELIMITER ;
  60. # 存储过程2:创建插入学生信息表存储过程
  61. DELIMITER //
  62. CREATE PROCEDURE insert_stu( max_num INT )
  63. BEGIN
  64. DECLARE i INT DEFAULT 0;
  65. SET autocommit = 0; #设置手动提交事务
  66. REPEAT #循环
  67. SET i = i + 1; #赋值
  68. 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));
  69. UNTIL i = max_num
  70. END REPEAT;
  71. COMMIT; #提交事务
  72. END //
  73. DELIMITER ;
  74. #调用存储过程:
  75. CALL insert_course(100);
  76. SELECT COUNT(*) FROM course;
  77. CALL insert_stu(1000000);
  78. SELECT COUNT(*) FROM student_info;

4.1.1 字段的数值有唯一性的限制

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

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

某个字段在 SELECT 语句的 WHERE 条件中经常被使用到,那么就需要给这个字段创建索引了。尤其是在 数据量大的情况下,创建普通索引就可以大幅提升数据查询的效率。 比如 student_info 数据表(含 100 万条数据),假设我们想要查询 student_id=123110 的用户信息。
下面的sql代码首先是查看student_info表中都有哪些索引?然后在不使用索引的情况下查询效率如何,对where查询条件的字段student_id添加索引之后查询效率又如何?  最后是执行耗时。
  1. SHOW INDEX FROM student_info;
  2. SELECT course_id, class_id, NAME, create_time, student_id
  3. FROM student_info
  4. WHERE student_id = 123110; #240ms
  5. ALTER TABLE student_info ADD INDEX idx_sid(student_id);
  6. SELECT course_id, class_id, NAME, create_time, student_id
  7. FROM student_info
  8. WHERE student_id = 123110; #17ms

4.1.3 经常 GROUP BY ORDER BY 的列

索引就是让数据按照某种顺序进行存储或检索,因此当我们使用 GROUP BY 对数据进行分组查询,或者使用 ORDER BY 对数据进行排序的时候,就需要 对分组或者排序的字段进行索引 。如果待排序的列有多 个,那么可以在这些列上建立 组合索引
这里先将GROUP BY中用到的student_id字段相关的索引删除,看下查询效率。之后再对这个字段加索引,再看查询效率。
  1. ALTER TABLE student_info DROP INDEX idx_sid;
  2. SHOW INDEX FROM student_info;
  3. SELECT student_id, COUNT(*) AS num
  4. FROM student_info
  5. GROUP BY student_id LIMIT 100; #514ms
  6. ALTER TABLE student_info ADD INDEX idx_sid(student_id);
  7. SELECT student_id, COUNT(*) AS num
  8. FROM student_info
  9. GROUP BY student_id LIMIT 100; #17ms

下面是针对查询中同时包含GROUP BY 和ORDER BY的sql,在为student_id和create_time分别创建索引的情况下,它的执行时间花了2.845s,挺长的,我们可以使用explain查看一下它是只走了 student_id 索引?还是只走了create_time索引?还是两个都走了?

  1. ALTER TABLE student_info ADD INDEX idx_sid(student_id);
  2. ALTER TABLE student_info ADD INDEX idx_cre_time(create_time);
  3. SHOW INDEX FROM student_info;
  4. #修改sql_mode
  5. SELECT @@sql_mode;
  6. SET @@sql_mode ='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
  7. SELECT student_id, COUNT(*) AS num FROM student_info
  8. GROUP BY student_id
  9. ORDER BY create_time DESC
  10. LIMIT 100; #2845ms
  11. EXPLAIN SELECT student_id, COUNT(*) AS num FROM student_info
  12. GROUP BY student_id
  13. ORDER BY create_time DESC
  14. LIMIT 100;

上面的explain执行结果告诉我们,它只走了student_id对应的单列索引,原因就是sql语句的执行流程中:是 FROM

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/662906
推荐阅读
相关标签