如下图即为索引的左前缀,简单来说,我们创建的一个索引,这个索引中有多个字段,那么我们在使用这个索引时应遵循从左到右顺序使用,如上图的a,b,c字段,我们在使用是应为先a到b再到c的顺序,如果我们跳过了a,则整个索引将失效,如果我们想从a一步飞到c,那c将失效,因为我们跳过了b,并且在使用索引时应避免出现大于或小于等的范围,这会使后面的索引失效,在索引中也是可以使用like关键字的,但前提是%不能出现在查找字的前面,否则,此字段将变为范围,导致后面的索引失效
数据库表
CREATE TABLE `dept`(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`deptName` VARCHAR(30) DEFAULT NULL,
`address` VARCHAR(40) DEFAULT NULL,
ceo INT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB AUTO_INCREMENT=1;
CREATE TABLE `emp`(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`empno` INT NOT NULL,
`name` VARCHAR(20) DEFAULT NULL,
`age` INT(3) DEFAULT NULL,
`deptId` INT(11) DEFAULT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB AUTO_INCREMENT=1;
创建函数
查看mysq1是否允许创建函数
SHOW VARIABLES LIKE 'log_bin_function_creators';
命令开启,允许创建函数设置: (global-所有session都生效)
SET GLOBAL log_bin_trust_function_creators=1;
随机产生字符串
DELIMITER $$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
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 $$
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 $$
CREATE PROCEDURE insert_emp(START INT,max_num INT)
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i+1;
INSERT INTO emp(empno,NAME,age,deptId) VALUES((START+i),rand_string(6),rand_num(30,50),rand_num(1,10000));
UNTIL i = max_num
END REPEAT;
COMMIT;
END $$
插入部门数据
DELIMITER $$
CREATE PROCEDURE insert_dept(max_num INT)
BEGIN
DECLARE i INT DEFAULT 0;
REPEAT
SET i = i+1;
INSERT INTO dept(deptName,address,ceo) VALUES(rand_string(8),rand_string(10),rand_num(1,500000));
UNTIL i = max_num
END REPEAT;
COMMIT;
END $$
调用存储过程
执行存储过程,在emp表添加50万条数据,编号从100000开始
call insert_emp(100000,500000);
执行存储过程,往dept表添加1万条数据
call insert_dept(100000);
因为我们要测索引,所以批量删除无用的索引
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=2;
OPEN _cur;
FETCH _cur INTO _index;
WHILE _index<>''DO
SET @str = CONCAT("drop index ",_index," on ",tablename );
PREPARE sq1_str FROM @str ;
EXECUTE sq1_str;
DEALLOCATE PREPARE sql_str;
SET _index='';
FETCH _cur INTO _index;
END WHILE;
CLOSE _cur;
END $$
interview是库名,dept是表名,记得改
CALL proc_drop_index("interview","dept");
我们接下来就对这个emp这个表进行操作,给他创建一个索引
CREATE INDEX idx_age_deptid_name ON emp(age,deptid,NAME);
查看是否有索引
SHOW INDEX FROM emp;
有下图为前面的成功了
我们接下来就调用索引测试一下吧,通过前面最左前缀所说的,下面的这一个所以应该就会成功
EXPLAIN SELECT * FROM emp ORDER BY age,deptid;
但运行后发现他竟然是全查找,这代表我们的索引失效了,为什么呢?
这就引出了个很重要的关键点,即为无过滤不索引,因为我们没有where条件,所以它所以自然就失效了,所以当我们在下图加上where后索引就成功使用了
那我们把这个age改成范围呢?结果发现,他虽然用到了索引,但是他竟然是全扫描,这就代表了他不仅仅使用了索引,因为我们使用了*号,所以他会通过需要回表去查询所需的数据,所以在使用缩阴的时候,尽量避免出现范围
我们把*号改成我们对应的字段后类型就变成了range,这个是连接类型使用索引返回一个范围中的行,比如使用>或<查找东西时发生的情况,所以会比ref差,所以在使用索引的时候,尽量避免出现范围
根据我们前面所说的最左前缀法则,我们试着把age删掉,让它不符合条件,接下来我们就发现它会出现一个Using filesort,这是个很严重的错误,代表我们的索引要优化了,这就是order by非最左 filesort
下图这两个分别为顺序错误和中间跳过,这些都会报这个错误,所以使用时要遵循自己定义索引的顺序来排
接下来我们来看看对这个字段进行排序会怎么样,我现在是全都是为倒序来排序,发现他索引是能够正常使用的
那我们反骨的对其中一个的顺序进行更改呢?结果发现报filesort了,所以我们在进行排序的时候,也要保持一致的方向,方向反就会报filesort
本人知识有限,有错误的请跟我反馈,非常感谢