#让数据库服务器信任函数的创建,否则会报错 set global log_bin_trust_function_creators=1; # 不加global只是当前窗口有效
步骤3:创建函数 保证每条数据都不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#随机产生字符串 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 ; #假如要删除 #drop function rand_string;
随机产生班级编号
1 2 3 4 5 6 7 8 9 10 11
#用于随机产生多少到多少的编号 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 ; #假如要删除 #drop function rand_num;
步骤4:创建存储过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#创建往stu表中插入数据的存储过程 DELIMITER // CREATE PROCEDURE insert_stu( START INT , max_num INT ) BEGIN DECLARE i INT DEFAULT 0; SET autocommit = 0; #设置手动提交事务 REPEAT #循环 SET i = i + 1; #赋值 INSERT INTO student (stuno, name ,age ,classId ) VALUES ((START+i),rand_string(6),rand_num(1,50),rand_num(1,1000)); UNTIL i = max_num END REPEAT; COMMIT; #提交事务 END // DELIMITER ; #假如要删除 #drop PROCEDURE insert_stu;
创建往class表中插入数据的存储过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#执行存储过程,往class表添加随机数据 DELIMITER // CREATE PROCEDURE `insert_class`( max_num INT ) BEGIN DECLARE i INT DEFAULT 0; SET autocommit = 0; REPEAT SET i = i + 1; INSERT INTO class ( classname,address,monitor ) VALUES (rand_string(8),rand_string(10),rand_num(1,100000)); UNTIL i = max_num END REPEAT; COMMIT; END // DELIMITER ; #假如要删除 #drop PROCEDURE insert_class;
DELIMITER // CREATE PROCEDURE `proc_drop_index`(dbname VARCHAR(200),tablename VARCHAR(200)) BEGIN DECLARE done INT DEFAULT 0; DECLARE ct INT DEFAULT 0; DECLARE _index VARCHAR(200) DEFAULT ''; DECLARE _cur CURSOR FOR SELECT index_name FROM information_schema.STATISTICS WHERE table_schema=dbname AND table_name=tablename AND seq_in_index=1 AND index_name <>'PRIMARY' ; #每个游标必须使用不同的declare continue handler for not found set done=1来控制游标的结束 DECLARE CONTINUE HANDLER FOR NOT FOUND set done=2 ; #若没有数据返回,程序继续,并将变量done设为2 OPEN _cur; FETCH _cur INTO _index; WHILE _index<>'' DO SET @str = CONCAT("drop index " , _index , " on " , tablename ); PREPARE sql_str FROM @str ; EXECUTE sql_str; DEALLOCATE PREPARE sql_str; SET _index=''; FETCH _cur INTO _index; END WHILE; CLOSE _cur; END // DELIMITER ;
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) );
我们自定义的主键列 id 拥有AUTO_INCREMENT 属性,在插入记录时存储引擎会自动为我们填入自增的主键值。这样的主键占用空间小,顺序写入,减少页分裂。
#7)不等于(!= 或者<>)索引失效 #不等于时用不上B+树,只能一个一个查找 CREATE INDEX idx_name ON student(NAME);
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name <> 'abc' ; #或 EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name != 'abc' ; /*索引失效 +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+ | 1 | SIMPLE | student | NULL | ALL | idx_name | NULL | NULL | NULL | 498858 | 50.15 | Using where | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+ */
2.8 is null可以使用索引,is not null无法使用索引
1 2 3 4 5
#8)is null可以使用索引,is not null无法使用索引 #is null可以使用索引 EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NULL; #is not null无法使用索引 EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NOT NULL;
最好在设计数据表的时候就将字段设置为 NOT NULL约束,比如可以将INT类型的字段,默认值设置为0。将字符类型的默认值设置为空字符串’’’’。 拓展:同理,在查询中使用not like 也无法使用索引,导致全表扫描
CREATE TABLE IF NOT EXISTS `type` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `card` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`id`) ); #图书 CREATE TABLE IF NOT EXISTS `book` ( `bookid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `card` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`bookid`) );
#向分类表中添加20条记录 INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO TYPE(card) VALUES(FLOOR(1 + (RAND() * 20)));
#向图书表中添加20条记录 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
3.2 采用左外连接
下面开始 EXPLAIN 分析
1 2 3 4 5 6
# 情况1:左外连接 #连接的时候就和“嵌套循环”一样 #每次从驱动表里选取一条记录去被驱动表里整个遍历一遍 #将符合连接条件的放到结果集中 #驱动表和被驱动表-->EXPLAIN执行结果的记录中,上面的是驱动表,下面的是被驱动表 EXPLAIN SELECT SQL_NO_CACHE * FROM `type` LEFT JOIN book ON type.card = book.card;
结论:type 有All 添加索引优化
1 2 3
#给被驱动表加了索引可以避免全表扫描 ALTER TABLE book ADD INDEX Y ( card); #【被驱动表】,可以避免全表扫描 EXPLAIN SELECT SQL_NO_CACHE * FROM `type` LEFT JOIN book ON type.card = book.card;
可以看到第二行的 type 变为了 ref,rows 也变成了1,优化比较明显。这是由左连接特性决定的。LEFT JOIN条件用于确定如何从右表搜索行,左边一定都有,所以右边是我们的关键点,一定需要建立索引 。
1 2 3
#给驱动表加了索引也要全表扫描 ALTER TABLE `type` ADD INDEX X (card); #【驱动表】,无法避免全表扫描 EXPLAIN SELECT SQL_NO_CACHE * FROM `type` LEFT JOIN book ON type.card = book.card;
接着:
1 2
DROP INDEX Y ON book; EXPLAIN SELECT SQL_NO_CACHE * FROM `type` LEFT JOIN book ON type.card = book.card;
3.3 采用内连接
1 2
drop index X on type; drop index Y on book;#(如果已经删除了可以不用再执行该操作)
换成 inner join(MySQL自动选择驱动表)
1
EXPLAIN SELECT SQL_NO_CACHE * FROM type INNER JOIN book ON type.card=book.card;
添加索引优化
1 2
ALTER TABLE book ADD INDEX Y ( card); EXPLAIN SELECT SQL_NO_CACHE * FROM type INNER JOIN book ON type.card=book.card;
1 2
ALTER TABLE type ADD INDEX X (card); EXPLAIN SELECT SQL_NO_CACHE * FROM type INNER JOIN book ON type.card=book.card;
对于内连接来说,查询优化器可以决定谁作为驱动表,谁作为被驱动表出现的
接着:
1 2
DROP INDEX X ON `type`; EXPLAIN SELECT SQL_NO_CACHE * FROM TYPE INNER JOIN book ON type.card=book.card;
接着:
1 2
ALTER TABLE `type` ADD INDEX X (card); EXPLAIN SELECT SQL_NO_CACHE * FROM `type` INNER JOIN book ON type.card=book.card;
#创建班级表中班长的索引 CREATE INDEX idx_monitor ON class(monitor); #查询班长的信息 EXPLAIN SELECT * FROM student stu1 WHERE stu1.`stuno` IN ( SELECT monitor FROM class c WHERE monitor IS NOT NULL );
EXPLAIN SELECT stu1.* FROM student stu1 JOIN class c ON stu1.`stuno` = c.`monitor` WHERE c.`monitor` IS NOT NULL; /* +----+--------------+-------------+------------+--------+---------------------+---------------------+---------+-----------------------+--------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------+-------------+------------+--------+---------------------+---------------------+---------+-----------------------+--------+----------+--------------------------+ | 1 | SIMPLE | stu1 | NULL | ALL | NULL | NULL | NULL | NULL | 498858 | 100.00 | NULL | | 1 | SIMPLE | <subquery2> | NULL | eq_ref | <auto_distinct_key> | <auto_distinct_key> | 5 | atguigudb2.stu1.stuno | 1 | 100.00 | NULL | | 2 | MATERIALIZED | c | NULL | index | idx_monitor | idx_monitor | 5 | NULL | 9952 | 100.00 | Using where; Using index | +----+--------------+-------------+------------+--------+---------------------+---------------------+---------+-----------------------+--------+----------+--------------------------+ 3 rows in set, 1 warning (0.09 sec) */
#查询不为班长的学生信息 #方式一 EXPLAIN SELECT SQL_NO_CACHE a.* FROM student a WHERE a.stuno NOT IN ( SELECT monitor FROM class b WHERE monitor IS NOT NULL) /* +----+-------------+-------+------------+-------+---------------+-------------+---------+------+--------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+-------------+---------+------+--------+----------+--------------------------+ | 1 | PRIMARY | a | NULL | ALL | NULL | NULL | NULL | NULL | 498858 | 100.00 | Using where | | 2 | SUBQUERY | b | NULL | index | idx_monitor | idx_monitor | 5 | NULL | 9952 | 100.00 | Using where; Using index | +----+-------------+-------+------------+-------+---------------+-------------+---------+------+--------+----------+--------------------------+ +----+-------------+-------+------------+------+---------------+-------------+---------+--------------------+--------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+-------------+---------+--------------------+--------+----------+--------------------------+ | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 498858 | 100.00 | NULL | | 1 | SIMPLE | b | NULL | ref | idx_monitor | idx_monitor | 5 | atguigudb2.a.stuno | 1 | 100.00 | Using where; Using index | +----+-------------+-------+------------+------+---------------+-------------+---------+--------------------+--------+----------+--------------------------+ 2 rows in set, 2 warnings (0.09 sec) */ #方式二 EXPLAIN SELECT SQL_NO_CACHE a.* FROM student a LEFT OUTER JOIN class b ON a.stuno =b.monitor WHERE b.monitor IS NULL; /* +----+-------------+-------+------------+------+---------------+-------------+---------+--------------------+--------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+-------------+---------+--------------------+--------+----------+--------------------------+ | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 498858 | 100.00 | NULL | | 1 | SIMPLE | b | NULL | ref | idx_monitor | idx_monitor | 5 | atguigudb2.a.stuno | 1 | 100.00 | Using where; Using index | +----+-------------+-------+------------+------+---------------+-------------+---------+--------------------+--------+----------+--------------------------+ 2 rows in set, 2 warnings (0.02 sec) */
结论:尽量不要使用NOT IN 或者 NOT EXISTS,用LEFT JOIN xxx ON xx WHERE xx IS NULL替代
5.排序优化
5.1排序优化
问题:在WHERE条件字段上加索引,但是为什么在ORDER BY字段上还要加索引呢? 回答: 在MySQL中,支持两种排序方式,分别是 FileSort 和Index排序。 Using filesort: 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buiffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSot 排序。 using index: 通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高
SHOW INDEX FROM student; SHOW INDEX FROM class; #过程一: EXPLAIN SELECT SQL_NO_CACHE * FROM student ORDER BY age,classid; /* +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 498858 | 100.00 | Using filesort | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+----------------+ */ EXPLAIN SELECT SQL_NO_CACHE * FROM student ORDER BY age,classid LIMIT 10; /* +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 498858 | 100.00 | Using filesort | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+----------------+ */ #过程二:order by时不limit,索引失效 #创建索引 CREATE INDEX idx_age_classid_name ON student (age,classid,NAME);
#不限制,索引失效 EXPLAIN SELECT SQL_NO_CACHE * FROM student ORDER BY age,classid; /* +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 498858 | 100.00 | Using filesort | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+----------------+ */ #EXPLAIN SELECT SQL_NO_CACHE age,classid,name,id FROM student ORDER BY age,classid;
#增加limit过滤条件,使用上索引了。 EXPLAIN SELECT SQL_NO_CACHE * FROM student ORDER BY age,classid LIMIT 10; /* +----+-------------+---------+------------+-------+---------------+----------------------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+----------------------+---------+------+------+----------+-------+ | 1 | SIMPLE | student | NULL | index | NULL | idx_age_classid_name | 73 | NULL | 10 | 100.00 | NULL | +----+-------------+---------+------------+-------+---------------+----------------------+---------+------+------+----------+-------+ */
#过程三:order by时顺序错误,索引失效 #创建索引age,classid,stuno CREATE INDEX idx_age_classid_stuno ON student (age,classid,stuno);
#以下哪些索引失效? EXPLAIN SELECT * FROM student ORDER BY classid LIMIT 10;#失效
EXPLAIN SELECT * FROM student ORDER BY classid,NAME LIMIT 10; #失效
EXPLAIN SELECT * FROM student ORDER BY age,classid,stuno LIMIT 10;#使用索引,使用了三个字段
EXPLAIN SELECT * FROM student ORDER BY age,classid LIMIT 10;##使用索引,使用了三个字段
EXPLAIN SELECT * FROM student ORDER BY age LIMIT 10;#使用索引,使用了三个字段
#过程四:order by时规则不一致, 索引失效 (顺序错,不索引;方向反,不索引)
EXPLAIN SELECT * FROM student ORDER BY age DESC, classid ASC LIMIT 10;#失效,没用使用索引
EXPLAIN SELECT * FROM student ORDER BY classid DESC, NAME DESC LIMIT 10;#失效,没用使用索引
EXPLAIN SELECT * FROM student ORDER BY age ASC,classid DESC LIMIT 10; #失效,没用使用索引
EXPLAIN SELECT * FROM student ORDER BY age DESC, classid DESC LIMIT 10;#使用了索引 /* +----+-------------+---------+------------+-------+---------------+----------------------+---------+------+------+----------+---------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+----------------------+---------+------+------+----------+---------------------+ | 1 | SIMPLE | student | NULL | index | NULL | idx_age_classid_name | 73 | NULL | 10 | 100.00 | Backward index scan | +----+-------------+---------+------------+-------+---------------+----------------------+---------+------+------+----------+---------------------+ 1 row in set, 1 warning (0.01 sec) */ #过程五:无过滤,不索引
EXPLAIN SELECT * FROM student WHERE age=45 ORDER BY classid;#使用了索引,仅age字段 /* +----+-------------+---------+------------+------+--------------------------------------------+-----------------------+---------+-------+-------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+--------------------------------------------+-----------------------+---------+-------+-------+----------+-------+ | 1 | SIMPLE | student | NULL | ref | idx_age_classid_name,idx_age_classid_stuno | idx_age_classid_stuno | 5 | const | 19184 | 100.00 | NULL | +----+-------------+---------+------------+------+--------------------------------------------+-----------------------+---------+-------+-------+----------+-------+ */ EXPLAIN SELECT * FROM student WHERE age=45 ORDER BY classid,NAME; #使用了索引,仅age字段
EXPLAIN SELECT * FROM student WHERE classid=45 ORDER BY age;
EXPLAIN SELECT * FROM student WHERE classid=45 ORDER BY age LIMIT 10;#使用了索引,用了所有字段
CREATE INDEX idx_cid ON student(classid); EXPLAIN SELECT * FROM student WHERE classid=45 ORDER BY age;
小结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
INDEX a_b_c(a,b,c) order by 能使用索引最左前缀 - ORDER BY a - ORDER BY a,b - ORDER BY a,b,c - ORDER BY a DESC,b DESC,c DESC 如果WHERE使用索引的最左前缀定义为常量,则order by 能使用索引 - WHERE a = const ORDER BY b,c - WHERE a = const AND b = const ORDER BY c - WHERE a = const ORDER BY b,c - WHERE a = const AND b > const ORDER BY b,c 不能使用索引进行排序 - ORDER BY a ASC,b DESC,c DESC /* 排序不一致 */ - WHERE g = const ORDER BY b,c /*丢失a索引*/ - WHERE a = const ORDER BY c /*丢失b索引*/ - WHERE a = const ORDER BY a,d /*d不是索引的一部分*/ - WHERE a in (...) ORDER BY b,c /*对于排序来说,多个相等条件也是范围查询*/
5.3 案例实战
ORDER BY子句,尽量使用Index方式排序,避免使用FileSort方式排序 测试filesort和index排序: 执行案例前先清除student上的索引,只留主键:
1 2 3 4 5
DROP INDEX idx_age ON student; DROP INDEX idx_age_classid_stuno ON student; DROP INDEX idx_age_classid_name ON student; #或者 call proc_drop_index('atguigudb2','student');
场景:查询年龄为30岁的,且学生编号小于101000的学生,按用户名称排序
1 2 3 4 5 6 7 8
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND stuno <101000 ORDER BY NAME ; /* +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+ | 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 498858 | 3.33 | Using where; Using filesort | +----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+ */
type 是 ALL,即最坏的情况。Extra 里还出现了 Using filesort,也是最坏的情况。优化是必须 的 优化思路: 方案一: 为了去掉filesort我们可以把索引建成
1 2 3 4 5 6 7 8 9 10
#创建新索引 CREATE INDEX idx_age_name ON student(age,NAME); EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND stuno <101000 ORDER BY NAME ; /* +----+-------------+---------+------------+-------+---------------------------------+--------------------+---------+------+------+----------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------------------------+--------------------+---------+------+------+----------+---------------------------------------+ | 1 | SIMPLE | student | NULL | range | idx_age_stuno_name,idx_age_name | idx_age_stuno_name | 9 | NULL | 20 | 100.00 | Using index condition; Using filesort | +----+-------------+---------+------------+-------+---------------------------------+--------------------+---------+------+------+----------+---------------------------------------+ */
方案二: 尽量让where的过滤条件和排序使用上索引 建一个三个字段的组合索引:
1 2 3 4 5 6 7 8 9 10
DROP INDEX idx_age_name ON student; CREATE INDEX idx_age_stuno_name ON student (age,stuno,NAME); EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND stuno <101000 ORDER BY NAME ; /* +----+-------------+---------+------------+-------+--------------------+--------------------+---------+------+------+----------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+--------------------+--------------------+---------+------+------+----------+---------------------------------------+ | 1 | SIMPLE | student | NULL | range | idx_age_stuno_name | idx_age_stuno_name | 9 | NULL | 20 | 100.00 | Using index condition; Using filesort | +----+-------------+---------+------------+-------+--------------------+--------------------+---------+ */
SHOW VARIABLES LIKE '%max_length_for_sort_data%';#默认1024字节 /* SHOW VARIABLES LIKE '%max_length_for_sort_data%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | max_length_for_sort_data | 4096 | +--------------------------+-------+ */
Index Condition Pushdown(ICP)是MySQL 5.6中新特性,是一种在存储引擎层使用索引过滤数据的一种优化方式。ICP可以减少存储引擎访问基表的次数以及MySQL服务器访问存储引擎的次数 Index Condition Pushdown(ICP)是MySQL 5.6中新特性,是一种在存储引擎层使用索引过滤数据的优化方式
SELECT b.membername,c.goodsname,a.quantity,a.salesvalue,a.transdate FROM demo.trans AS a JOIN demo.membermaster AS b JOIN demo.goodsmaster AS c ON (a.cardno = b.cardno AND a.itemnumber=c.itemnumber); /* +------------+-----------+----------+------------+---------------------+ | membername | goodsname | quantity | salesvalue | transdate | +------------+-----------+----------+------------+---------------------+ | 张三 | 书 | 1.000 | 89.00 | 2020-12-01 00:00:00 | +------------+-----------+----------+------------+---------------------+ 1 row in set (0.00 sec) */
如果会员卡“10000001”又发给了王五,我们会更改会员信息表。导致查询时:
1 2 3 4 5 6 7 8 9 10 11 12 13
SELECT b.membername,c.goodsname,a.quantity,a.salesvalue,a.transdate FROM demo.trans AS a JOIN demo.membermaster AS b JOIN demo.goodsmaster AS c ON (a.cardno = b.cardno AND a.itemnumber=c.itemnumber); /* +------------+-----------+----------+------------+---------------------+ | membername | goodsname | quantity | salesvalue | transdate | +------------+-----------+----------+------------+---------------------+ | 王五 | 书 | 1.000 | 89.00 | 2020-12-01 00:00:00 | +------------+-----------+----------+------------+---------------------+ 1 row in set (0.01 sec */
改造UUID 若将时间高低位互换,则时间就是单调递增的了,也就变得单调递增了。MySQL 8.0可以更换时间低位和时间高位的存储方式,这样UUID就是有序的UUID了。 MySQL 8.0还解决了UUID存在的空间占用的问题,除去了UUID字符串中无意义的”-“字符串,并且将字符串用二进制类型保存,这样存储空间降低为了16字节可以通过MySQL8.0提供的uuid_to_bin函数实现上述功能,同样的,MySQL也提供了bin_to_uuid函数进行转化:
1 2
SET @uuid = UUID(); SELECT @uuid,uuid_to_bin(@uuid),uuid_to_bin(@uuid,TRUE);