上一篇:《第8章-7 explain》,接着学习常用的优化技巧
索引做为排序时。
使用order by排序时,如果没有按照索引顶序(要完全一样),会出现Using filesort
EXPLAIN SELECT name, dep_id, age
from employee ORDER BY name, dep_id, age;
当索引引字段为常量时可以当作是存在索引的。
EXPLAIN SELECT name, dep_id, age
from employee where name = '李四' ORDER BY dep_id, age;
当使用*时order by即使使用了全部索引,也会也filesort
EXPLAIN SELECT *
from employee ORDER BY name, dep_id, age;
使用排序一升一降会造成filesort
EXPLAIN SELECT name, dep_id, age
from employee ORDER BY name, dep_id desc, age asc;
当排序出现了索引左侧列,则允许使用索引排序
左侧字段单字段排序时,索引支持升降序
explain select * from rtx_resource
where menu_id < 14206986 order by menu_id desc;
explain select * from rtx_resource
where menu_id < 14206986 order by source_id;
在多字段情况下,左侧字段必须是升序,且顺序不允许打乱
explain select * from rtx_resource
where menu_id < 14206986 order by menu_id,source_id;
这个规则 跟 order by相似。
EXPLAIN SELECT NAME, dep_id, age
FROM employee GROUP BY NAME, dep_id DESC, age ASC;
改成一样排序
EXPLAIN SELECT NAME, dep_id, age
FROM employee GROUP BY NAME DESC, dep_id DESC, age DESC;
少了中间的dep_id
EXPLAIN SELECT NAME, dep_id, age
FROM employee GROUP BY NAME, age;
EXPLAIN SELECT NAME, dep_id, age
FROM employee where GROUP BY NAME, age;
说出以下语法使用索引的情况
假设建立复合索引(a,b,c),请说出以下条件是否使用到了索引及使用情况
where a = 4;
where a = 4 and b = 6;
where a = 4 and c = 5 and b = 6;
where b = 4 or b=5;
where a = 4 and c = 6;
where a = 4 and b > 5 and c=6;
where a = 4 and b like 'test%' and c=4;
where a = 4 order by b, c;
where b = 5 order a;
where b = 5 order c;
where a = 5 group by c, b;
解答:
where a = 4; 使用到索引a
where a = 4 and b = 6; 使用到索引a,b
where a = 4 and c = 5 and b = 6; 使用到索引a,b,c
where b = 4 or b=5; 有or索引失效
where a = 4 and c = 6; 使用到索引a
where a = 4 and b > 5 and c=6; 使用到索引a,b 范围右边的失效了。
where a = 4 and b like 'test%' and c=4; 使用到索引a,b, %相当于范围
where a = 4 order by b, c; 使用到a,不会有filesort
where b = 5 order by a; 用不到,order by 不会用上a,b也用不上,会有filesort
where b = 5 order by c; 用不到索引,会有filesort
where a = 5 group by c, b; 用到索引a,会生成temporary,
因为group by c, b 顺序变换了
大数据分页查询瓶颈
limit offset,N 的时候,随着offset增加,语句耗时也在增加。因为MySQL是取 offset + N行记录,然后放弃前 offset 行,只返回N行记录。当offset特别大的时候,效率就非常低下。如何进行优化?
直接复制employee
CREATE TABLE staff LIKE employee;
-- 或者:
DROP TABLE IF EXISTS staff;
CREATE TABLE staff (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(20) DEFAULT NULL,
dep_id int(11) DEFAULT NULL,
age int(11) DEFAULT NULL,
salary decimal(10,2) DEFAULT NULL,
cus_id int(11) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
#随机生成一个指定个数的字符串
DELIMITER //
CREATE FUNCTION rand_str(n INT) RETURNS VARCHAR(255)
BEGIN
# 声明一个str 包含52个字母
DECLARE str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
# 记录当前是第几个
DECLARE i INT DEFAULT 0;
# 生成的结果
DECLARE res_str VARCHAR(255) DEFAULT '';
WHILE i < n DO
SET res_str = CONCAT(res_str, SUBSTR(str,FLOOR(1 +RAND()*52),1));
SET i=i+ 1;
END WHILE;
RETURN res_str;
END //
DELIMITER ;
报错:
This function has none of DETERMINISTIC, NO SQL,
or READS SQL DATA in its declaration and binary logging
is enabled (you *might* want to use the less safe
log_bin_trust_function_creators variable)
设置:
SET GLOBAL log_bin_trust_function_creators = TRUE;
DELIMITER //
CREATE PROCEDURE insert_staff(IN max_num INT)
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i=i+ 1;
INSERT INTO staff(NAME, dep_id, age, salary, cus_id)
VALUES(rand_str(5), FLOOR(1 + RAND()*10), FLOOR(20 + RAND()*10),
FLOOR(6000 + RAND()*10), FLOOR(1+RAND()*10));
UNTIL i = max_num
END REPEAT;
COMMIT;
END //
DELIMITER ;
-- 插入1千万的数据
CALL insert_staff(10000000);
-- 执行耗时 : 0.018 sec
SELECT * FROM staff LIMIT 100000, 10;
-- 执行耗时 : 0.319 sec
SELECT * FROM staff LIMIT 1000000, 10;
-- 执行耗时 : 1.161 sec
SELECT * FROM staff LIMIT 3000000, 10;
-- 执行耗时 : 3.236 sec
SELECT * FROM staff LIMIT 8000000, 10;
优化后分页查询
通过使用索引覆盖获取需要的主键,再根据主键关联原表获得需要的数据。
set profiling=1
select * from employees limit 100000,10; --0.17s
select * from employees limit 600000,10; --0.46s
select * from employees limit 1200000,10; --1.31s
基数(offset)越大,语句耗时越多。
使用了索引覆盖,但employees是聚簇索引表,其叶子节点就是行记录,即使走索引覆盖,其效果也不是最理想的,因为叶子节点比较大
select emp_no from employees limit 100000,10;
使用二级索引,走索引覆盖,其叶子节点为:emp_no + emp_no,这个叶子节点比较小,也就是最理想的索引覆盖
create index idx_emp_no on employees(emp_no);
select emp_no from employees limit 100000,10;
select * from employees m inner join
(select emp_no from employees limit 100000,10) s
on s.emp_no=m.emp_no; -- 0.08
select * from employees m inner join
(select emp_no from employees limit 600000,10) s
on s.emp_no=m.emp_no; -- 0.21
select * from employees m inner join
(select emp_no from employees limit 1200000,10) s
on s.emp_no=m.emp_no; -- 0.46
记录上一页最大的id号使用范围查询。限制是只能使用于明确知道id的情况,不过一般建立表的时候,都会添加基本的id字段,而且id必须是自增的,这为分页查询带来很多便利
-- 执行耗时 : 0.001 sec
SELECT * FROM staff WHERE id > 8000000 LIMIT 10;
另外一种写法:
SELECT * FROM staff WHERE id BETWEEN 8000000 AND 8000010 LIMIT 10;
记录上一页的的id值
设计的时候,限制到100页。其它通过搜索的方式!
max的时候,对于其使用索引
create index idx_age on testemployee ( age ) ;
EXPLAIN SELCT max ( age) from testemployee;
-- count (*) 统计所有包含了null
-- count (字段) 统计所有不包含了null
SELECT count (name or name is null) from employee;
小表驱动大表,即小的数据集驱动大得数据集
for(int i=5;....)
小表驱动大表·
for(int j-1000....)
类似嵌套循环
如果小的循环在外层,对于数据库连接来说就只连接5次,进行1000次操作
小表驱动大表
如果1000在外,则需要进行1000次数据库连接,从而浪费资源,增加消耗。这就是为什么要小表驱动大表
-- SELECT * from employee whereid in(SELECT id from department) ;
for SELECT id from department d
for SELECT * from employee e where e.id = d.id;
小表驱动大表
SELECT * FROM employee e
WHERE EXISTS (SELECT 1 FROM department d WHERE d.id = e.dep_id) ;
-- exist可以替代 in
for SELECT * FROM employee e
for SELECT * FROM department d WHERE d.id = e.dep_id;
大表驱动小表
当A表中教据多于B表中的款据时,这时我门更利IN 优于EXISTS。
当B表中数据多于A表中的教据时,这时我们使用EXISTS 优于IN
因此是使用IN还是使用EXSTS就需要根据我们的需求决定了。但是如果两张表中的数据差不多时那么是使用IN还是使用EXISTS差别不大。EXISTS子查询只返回TRUJE或FALSE,因此子查询中的SELECT *可以是SELECT 1或者其他。
要确定小表驱动大表的顺序,可以遵循以下步骤:
SHOW TABLE STATUS LIKE '表名';
查询语句获取每个表的行数信息。平时使用的时候,注意排序和分页的处理,尤其是大数据分页的,最直接的就说限制返回的数量,而不是全部数据。 小表驱动大表这个使用的时候根据实际的数据量去判断,一搬字典表,区域表,这些是小表,业务表是大表。
上一篇:《第8章-7 explain》
下一篇: