赞
踩
2.2 方式二:ALTER TABLE ... ADD INDEX ...
2.3 方式三:CREATE INDEX ... ON ...
4.1.3 经常 GROUP BY 和 ORDER BY 的列
4.1.4 UPDATE、DELETE 的 WHERE 条件列
4.1.11 在多个字段都要创建索引的情况下,联合索引优于单值索引
MySQL 的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。从 功能逻辑 上说,索引主要有 4 种,分别是普通索引、唯一索引、主键索引、全文索引。按照 物理实现方式 ,索引可以分为 2 种:聚簇索引和非聚簇索引。按照 作用字段个数 进行划分,分成单列索引和联合索引。
- CREATE DATABASE dbtest2;
-
- USE dbtest2;
-
- CREATE TABLE dept (
- dept_id INT PRIMARY KEY AUTO_INCREMENT,
- dept_name VARCHAR(20)
- );
-
- SHOW INDEX FROM dept;
- CREATE TABLE emp (
- emp_id INT PRIMARY KEY AUTO_INCREMENT,
- emp_name VARCHAR(20) UNIQUE,
- dept_id INT,
- CONSTRAINT fk_emp_dept_id FOREIGN KEY(dept_id) REFERENCES dept(dept_id)
- );
-
- SHOW INDEX FROM emp;
- CREATE TABLE book (
- book_id INT,
- book_name VARCHAR(100),
- `authors` VARCHAR(100),
- info VARCHAR(100),
- `comment` VARCHAR(100),
- year_publication YEAR,
- INDEX idx_bname(book_name)
- );
-
- SHOW INDEX FROM book;
如果我们写一个简单的sql语句,在where后面用 book_name 来筛选,可以通过 explain 性能分析工具来看看是什么样的?
- EXPLAIN
- SELECT *
- FROM book
- WHERE book_name = 'mysql';
- CREATE TABLE book1 (
- book_id INT,
- book_name VARCHAR(100),
- `authors` VARCHAR(100),
- info VARCHAR(100),
- `comment` VARCHAR(100),
- year_publication YEAR,
- UNIQUE INDEX uk_idx_cmt(`comment`)
- );
-
- SHOW INDEX FROM book1;
接下来,依次向表中插入三条记录,在插入第二条记录的时候,就会报错。而第三条记录是正常执行的。
- INSERT INTO book1(book_id, book_name, `comment`)
- VALUES(1, 'MySQL高级', '适合有数据库开发经验的人员学习');
-
- INSERT INTO book1(book_id, book_name, `comment`)
- VALUES(1, 'MySQL高级', '适合有数据库开发经验的人员学习');
-
- INSERT INTO book1(book_id, book_name, `comment`)
- VALUES(1, 'MySQL高级', NULL);
最终,在book1这张表中,将存在两条记录。
- CREATE TABLE book2 (
- book_id INT PRIMARY KEY,
- book_name VARCHAR(100),
- `authors` VARCHAR(100),
- info VARCHAR(100),
- `comment` VARCHAR(100),
- year_publication YEAR
- );
-
- SHOW INDEX FROM book2;
其实我们上面创建的那些都是单列索引。
- CREATE TABLE book3 (
- book_id INT ,
- book_name VARCHAR(100),
- AUTHORS VARCHAR(100),
- info VARCHAR(100) ,
- COMMENT VARCHAR(100),
- year_publication YEAR,
- UNIQUE INDEX idx_bname(book_name)
- );
-
- SHOW INDEX FROM book3;
- CREATE TABLE book4 (
- book_id INT ,
- book_name VARCHAR(100),
- AUTHORS VARCHAR(100),
- info VARCHAR(100) ,
- COMMENT VARCHAR(100),
- year_publication YEAR,
- INDEX mul_bid_bname_info(book_id, book_name, info)
- );
-
- SHOW INDEX FROM book4;
下面我们通过两条sql来分析一下,联合索引在查找过程中是怎么走的?
- EXPLAIN
- SELECT *
- FROM book4
- 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的。
- EXPLAIN
- SELECT *
- FROM book4
- 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是不会走索引的。
- 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(50))
- );
-
- SHOW INDEX FROM test4;
- DROP TABLE IF EXISTS book5;
- CREATE TABLE book5 (
- book_id INT ,
- book_name VARCHAR(100),
- `authors` VARCHAR(100),
- info VARCHAR(100) ,
- `comment` VARCHAR(100),
- year_publication YEAR
- );
-
- ALTER TABLE book5 ADD INDEX idx_cmt(`comment`);
- ALTER TABLE book5 ADD UNIQUE INDEX uk_idx_bname(book_name);
- ALTER TABLE book5 ADD INDEX mul_bid_bname_info(book_id, book_name, info);
-
- SHOW INDEX FROM book5;
- DROP TABLE IF EXISTS book6;
- CREATE TABLE book6 (
- book_id INT ,
- book_name VARCHAR(100),
- `authors` VARCHAR(100),
- info VARCHAR(100) ,
- `comment` VARCHAR(100),
- year_publication YEAR
- );
-
- CREATE INDEX idx_cmt ON book6(`comment`);
- CREATE UNIQUE INDEX uk_idx_bname ON book6(book_name);
- CREATE INDEX mul_bid_bname_info ON book6(book_id, book_name, info);
-
- SHOW INDEX FROM book6;
提示 删除表中的列时,如果要删除的列为索引的组成部分,则该列也会从索引中删除。如果组成索引的所有列都被删除,则整个索引将被删除。
- ALTER TABLE book6 DROP INDEX idx_cmt;
-
- DROP INDEX uk_idx_bname ON book6;
-
- ALTER TABLE book6 DROP COLUMN book_name;
- ALTER TABLE book6 DROP COLUMN book_id;
- ALTER TABLE book6 DROP COLUMN info;
-
- SHOW INDEX FROM book6;
我们先准备一些数据,用作下面创建索引及测试过程。
- #1. 数据的准备
- CREATE DATABASE atguigudb1;
-
- USE atguigudb1;
-
- #1.创建学生表和课程表
- CREATE TABLE `student_info` (
- `id` INT(11) 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;
-
-
- #函数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 ;
-
- SELECT @@log_bin_trust_function_creators;
-
- SET GLOBAL log_bin_trust_function_creators = 1;
-
-
- #函数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 ;
-
- # 存储过程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 ;
-
- #调用存储过程:
- CALL insert_course(100);
-
- SELECT COUNT(*) FROM course;
-
- CALL insert_stu(1000000);
-
- SELECT COUNT(*) FROM student_info;
业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。(来源: Alibaba )说明:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的。
某个字段在 SELECT 语句的 WHERE 条件中经常被使用到,那么就需要给这个字段创建索引了。尤其是在 数据量大的情况下,创建普通索引就可以大幅提升数据查询的效率。 比如 student_info 数据表(含 100 万条数据),假设我们想要查询 student_id=123110 的用户信息。下面的sql代码首先是查看student_info表中都有哪些索引?然后在不使用索引的情况下查询效率如何,对where查询条件的字段student_id添加索引之后查询效率又如何? 最后是执行耗时。
- SHOW INDEX FROM student_info;
-
- SELECT course_id, class_id, NAME, create_time, student_id
- FROM student_info
- WHERE student_id = 123110; #240ms
-
- ALTER TABLE student_info ADD INDEX idx_sid(student_id);
-
- SELECT course_id, class_id, NAME, create_time, student_id
- FROM student_info
- WHERE student_id = 123110; #17ms
索引就是让数据按照某种顺序进行存储或检索,因此当我们使用 GROUP BY 对数据进行分组查询,或者使用 ORDER BY 对数据进行排序的时候,就需要 对分组或者排序的字段进行索引 。如果待排序的列有多 个,那么可以在这些列上建立 组合索引 。这里先将GROUP BY中用到的student_id字段相关的索引删除,看下查询效率。之后再对这个字段加索引,再看查询效率。
- ALTER TABLE student_info DROP INDEX idx_sid;
- SHOW INDEX FROM student_info;
-
- SELECT student_id, COUNT(*) AS num
- FROM student_info
- GROUP BY student_id LIMIT 100; #514ms
-
- ALTER TABLE student_info ADD INDEX idx_sid(student_id);
-
- SELECT student_id, COUNT(*) AS num
- FROM student_info
- GROUP BY student_id LIMIT 100; #17ms
下面是针对查询中同时包含GROUP BY 和ORDER BY的sql,在为student_id和create_time分别创建索引的情况下,它的执行时间花了2.845s,挺长的,我们可以使用explain查看一下它是只走了 student_id 索引?还是只走了create_time索引?还是两个都走了?
- ALTER TABLE student_info ADD INDEX idx_sid(student_id);
-
- ALTER TABLE student_info ADD INDEX idx_cre_time(create_time);
-
- SHOW INDEX FROM student_info;
-
- #修改sql_mode
-
- SELECT @@sql_mode;
-
- SET @@sql_mode ='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
-
-
- SELECT student_id, COUNT(*) AS num FROM student_info
- GROUP BY student_id
- ORDER BY create_time DESC
- LIMIT 100; #2845ms
-
- EXPLAIN SELECT student_id, COUNT(*) AS num FROM student_info
- GROUP BY student_id
- ORDER BY create_time DESC
- LIMIT 100;
上面的explain执行结果告诉我们,它只走了student_id对应的单列索引,原因就是sql语句的执行流程中:是 FROM
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/662906
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。