标签:日志文件 derived put 生成 对比 asc develop 图片 length
在上一篇文章:Mysql索引(一篇就够le) 中介绍了索引的基本使用,分类和原理,也强烈建议先读Mysql索引(一篇就够le),然后继续本文的阅读
我们也知道mysql的优化可以从很多的方面进行,比如硬件,换个ssd的磁盘也很爽,环境的搭建,比如读写分离等,参数的配置,表结构的设计,字段类型的合理选用,sql的正确书写方式,索引的构建等,这里我们主要从sql的查询做优化,主要是基于走索引的方式去学习。
优化能带来的肯定是爽!!!当你作为一个用户去体验某个网页正在加载中。。。内心肯定是崩溃的,秒出的网页肯定是爽。做开发测试的是每次查询几分钟甚至十几分钟,内心不会崩溃,只想砸电脑,所以速度快就是爽。速度快了,用户爽了,你公司爽了,你老板爽了,你也就爽了,薪资说不定也爽了。上面我们提到了优化可以从很多个方面进行:最常见的就是sql和索引的优化了,因为写CURD的小伙伴都免不了写个查询语句,然后语句走不走索引呢,这也会决定你爽不爽了,这种层次的优化相对成本低,效果好,主要还是根据业务场景来,不像msyql环境,配置啊等,因为会有很多个业务呀,不可能让环境是合适所有的业务场景吧,顶多有个合理居中值。(瞎扯,编不下去了。。。不过确实能带来爽和成就感)
既然要做优化,首先要知道哪些需要优化吧,然后做分析这个sql慢是因为什么,然后才是怎么解决呢,在实际的环境中可能先要定位慢的语句,然后观察一段时间是不是一直都慢呢,还是有时候慢有时候又很快呢,这些都和实际环境中的并发,msyql环境当时的健康程度有关吧,所以要先锁定目标然后观察,然后把那些都慢的sql都弄出来进行分析,最后做优化。这里会说一下怎么找到慢的sql查询和分析,优化交给下一节。
mysql中有很多的日志文件,binlog日志,慢日志,查询日志,错误日志。这里我们要说的是慢日志,默认情况下是没有开始慢日志查询日志的,因为会影响一些些性能。(惨遭打脸,自己玩的竟突开启了)
show variables like ‘%slow_query_log%‘;
虽然是开启了慢日志查询的功能,还得要有个阈值判断执行多久是慢日志查询吧(默认10s): show variables like ‘%long_query_time%‘;
太长了,我们改改,10s一点都不爽;set global long_query_time=3;改完配置后记得重启session。来测试:
上图是弄了一个超过3s的执行语句,查看慢日志的文件,确实被记录在里头了,还包括了很多其他的值,比如时间,用户,主机,执行时间等。
那如果这个慢日志的文件很大很大,很多的查询,不可能这么手动的去找吧。所有就要有慢日志的分析工具了:mysqldumpslow
[root@lgh mysql]# ./bin/mysqldumpslow --help
Usage: mysqldumpslow [ OPTS... ] [ LOGS... ]
Parse and summarize the MySQL slow query log. Options are
--verbose verbose
--debug debug
--help write this text to standard output
-v verbose
-d debug
-s ORDER what to sort by (al, at, ar, c, l, r, t), ‘at‘ is default #按照某种方式排序,默认at
al: average lock time #平均锁定时间
ar: average rows sent #平均返回记录数
at: average query time #平均查询时间
c: count #访问次数
l: lock time #锁定时间
r: rows sent #返回记录
t: query time #查询时间
-r reverse the sort order (largest last instead of first) #倒序
-t NUM just show the top n queries #topN
-a don‘t abstract all numbers to N and strings to ‘S‘
-n NUM abstract numbers with at least n digits within names
-g PATTERN grep: only consider stmts that include this string #正则
-h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard),
default is ‘*‘, i.e. match all
-i NAME name of server instance (if using mysql.server startup script)
-l don‘t subtract lock time from total time #总时间(包括锁定时间)
./bin/mysqldumpslow -s t -t 3 /apps/mysql8/logs/mysql/slow3306.log
./bin/mysqldumpslow -s t -t 2 -r /apps/mysql8/logs/mysql/slow3306.log
用起来不是很难,当然还有很多其他的慢日志的一些工具,比如:mysqlsla,pt-query-digest等,这些都需要自己去安装的。
当我们找到了我们的哪些慢查询后怎么去分析呢,当然是借助explain啦,当然还有个帅气的小伙伴,show profile,接下来我们就一一介绍。
1、explain可以用来干嘛呢?
看完上面可能一脸懵逼吧,那就再看一遍,然后继续往下看就懂了
2、explain怎么玩呢?
语法:explain + sql查询
explain select * from item_description td inner join (select * from item_general where item_id in (select item_id from item where item_id > 332604504321036693 and item_id < 332604504321036710)) tt on tt.item_id=td.item_id;
如上我们先看一个explain+sql;
下面看下explain的具体说明:
1、id:select查询的序号列,表示查询select语句中表的执行顺序
2、select_type:表示SELECT语句的类型。它可以是以下几种取值:
3、table:表示查询的表
4、partitions:查询将从中匹配记录的分区。该值适用NULL于未分区的表
5、type:表示表的连接类型
6、possible_keys:指出MySQL能使用哪个索引在该表中找到行。若该列是NULL,则没有相关的索引。在这种情况下,可以通过检查WHERE子句看它是否引用某些列或适合索引的列来提高查询性能。如果是这样,可以创建适合的索引来提高查询的性能。
7、kye:表示查询实际使用的索引,如果没有选择索引,该列的值是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX
8、key_len:表示MySQL选择的索引字段按字节计算的长度,若键是NULL,则长度为NULL。注意,通过key_len值可以确定MySQL将实际使用一个多列索引中的几个字段
9、ref:表示使用哪个列或常数与索引一起来查询记录。
10、rows:显示MySQL在表中进行查询时必须检查的行数。
11、filtered:按表条件筛选的行的百分比
12、Extra:表示MySQL在处理查询时的详细信息
更多详情见官网:https://dev.mysql.com/doc/refman/8.0/en/explain-output.html
Show Profile是mysql提供的可以用来分析当前会话中sql语句执行的资源消耗情况的工具,可用于sql调优的测量。默认情况下处于关闭状态,并保存最近15次的运行结果。
SHOW PROFILE [type [, type] ... ]
[FOR QUERY n]
[LIMIT row_count [OFFSET offset]]
type: {
ALL #显示所有的开销信息
| BLOCK IO #显示块IO开销
| CONTEXT SWITCHES #上下文切换开销
| CPU #显示CPU开销信息
| IPC #显示发送和接收开销信息
| MEMORY #显示内存开销信息
| PAGE FAULTS #显示页面错误开销信息
| SOURCE #显示和Source_function,Source_file,Source_line相关的开销信息
| SWAPS #显示交换次数开销信息
}
show variables like ‘%profiling%‘;
set global profiling =on;
然后我们随性的多执行一些查询语句,在通过show profiles查看:
上面有3列,一个是查询的id,一个是执行的时间,最后一个是执行语句。
然后我们对id=8的语句进行诊断:show profile cpu,block io for query Query_ID;/*Query_ID为#3步骤中show profiles列表中的Query_ID*/
上表中第一列为该sql查询的状态,也是sql的一个生命周期,从开始,到权限,打开表,初始化,上锁,优化,统计,执行,发送数据,查询结束,关闭表等。
在状态这一列中要注意一些点:
从上看来主要的时间花在了数据的传输上面,所以可以针对该项进行优化,更多详情见官网:https://dev.mysql.com/doc/refman/8.0/en/show-profile.html
在去写好sql和优化sql查询之前,知道sql的执行顺序尤为的重要(所以这里要多读几遍)
from ->on ->join ->where ->group by ->having ->select ->distinct ->order by ->limit
1、from 对查询指定的表计算笛卡尔积
2、on 按照 join_condition 过滤数据
3、join 添加关联外部表数据
4、where 按照where_condition过滤数据
5、group by 进行分组操作
6、having 按照having_condition过滤数据
7、select 选择指定的列
8、distinct 指定列去重
9、order by 按照order_by_condition排序
10、limit 取出指定记录量
应该创建索引
不应该创建索引
制造点实验数据:
CREATE TABLE IF NOT EXISTS department(
id INT(11) PRIMARY KEY AUTO_INCREMENT COMMENT‘部门编号(主键)‘,
d_name VARCHAR(50) COMMENT‘部门名称‘,
d_address VARCHAR(50) COMMENT‘部门地址‘);
CREATE TABLE user(
id INT(11) NOT NULL PRIMARY KEY COMMENT‘员工编号‘,
u_name VARCHAR(50) COMMENT‘员工姓名‘,
sex int(11) COMMENT ‘性别‘,
d_id int(11) COMMENT ‘部门id‘,
word VARCHAR(50) COMMENT‘员工工作‘,
entryTime datetime COMMENT‘员工入职时间‘,
wage INT(11) COMMENT‘员工工资‘,
bonus INT(11) COMMENT‘员工奖金‘
);
create table sex(
id int(11),
name char(1) not null
)
insert into sex values(‘1‘,‘男‘),(‘0‘,‘女‘);
INSERT INTO user VALUES
(‘1‘, ‘小王‘, ‘0‘, ‘2‘, ‘a写代码1‘, ‘2017-06-14 14:30:50‘, ‘4000‘, null),
(‘2‘, ‘小李‘, ‘1‘, ‘3‘, ‘b写代码2‘, ‘2016-08-16 14:32:08‘, ‘20800‘, ‘5000‘),
(‘3‘, ‘小张‘, ‘1‘, ‘3‘, ‘c写代码2‘, ‘2016-05-04 14:33:05‘, ‘22700‘, null),
(‘4‘, ‘小高‘, ‘1‘, ‘1‘, ‘a写代码2‘, ‘2015-07-08 14:33:54‘, ‘5000‘, null),
(‘5‘, ‘小刘‘, ‘0‘, ‘4‘, ‘写代码3‘, ‘2017-11-08 14:35:35‘, ‘10000‘, null),
(‘6‘, ‘王一‘, ‘1‘, ‘5‘, ‘d写代码1‘, ‘2016-11-01 14:36:28‘, ‘20000‘, ‘5000‘),
(‘7‘, ‘王二‘, ‘0‘, ‘2‘, ‘写代码5‘, ‘2018-03-22 14:38:44‘, ‘5000‘, null),
(‘8‘, ‘李四‘, ‘1‘, ‘7‘, ‘写代码6‘, ‘2017-04-01 14:39:53‘, ‘5000‘, null),
(‘9‘, ‘李一‘, ‘0‘, ‘4‘, ‘写代码8‘, ‘2018-08-01 14:40:43‘, ‘5000‘, null);
INSERT INTO department(id,d_name,d_address)
(1,‘销售部‘,‘销售部地址‘),
(2,‘学业部‘,‘学业部地址‘),
(3,‘董事部‘,‘董事部地址‘),
(4,‘人力资源部‘,‘人力资源部地址‘),
(5,‘产品部‘,‘产品部地址‘),
(20,‘研发部‘,‘三楼‘);
MySQL内部采用了一种叫做 nested loop join的算法。Nested Loop Join 实际上就是通过驱动表的结果集作为循环基础数据,然后一条一条的通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。如果还有第三个参与 Join,则再通过前两个表的 Join 结果集作为循环基础数据,再一次通过循环查询条件到第三个表中查询数据,如此往复,基本上MySQL采用的是最容易理解的算法来实现join。(一定要用小表驱动大表)
看看这些表的索引情况:
除了两个表的主键自动创建的主键索引外,没有其他的任何索引。
explain select * from department d left join user u on u.d_id = d.id;
两个表都是全表扫描呢,命名department表在id上有主键索引呢,但是作为左连接,department需要保留全部的数据,所以建立索引是没什么影响的,接下来我们在user表上建立d_id的索引idx_d_id: create index idx_d_id on user (d_id);
看结果可以得出user表使用了索引,减少了数据的读取,可以得出left join主要的优化在于右表的索引的创建,那right join也是一样在于左表的索引的情况,对于inner join呢?我们看看。
在user表没有建立idx_d_id索引前:
在user表没有建立idx_d_id索引后:
好像优化器会选择把小的表来驱动大的表,全表扫描小的表,大的表走索引。
这是两个表的lefter join和right join的情况,那三个表的呢?还是上面的原则,有一个表会全保留,其他的走索引就好了。所以就是小表用来做驱动,大表用来走索引,这样就可以提高left join和right join的速度了。当然索引也要合适。。
1、order by
order by:就是排序,我们都知道InnoDB存储引擎的存储是根据主键按照顺序存储的,所以这些都是已经排好序的,但是我们又不仅仅是根据主键排序,还要更加其他列进行排序,这样又怎么弄呢,当然我们可以在这些列上建立索引呀(单列,或者组合索引,推荐组合),索引就是有序的,这样就不用额外的排序了,但是不可能每个列都创建好索引吧,还有就是默认的是asc排序,那desc排序呢,又当如何呢,这就会造成filesort,虽然可以排序,但是效率真的低,所以尽量不要使用。既然order by有两种排序,一种是通过索引的默认排序这样的速度好,还有就是filesort,但是filesort如何去优化下呢?
在MySQL中filesort 的实现算法实际上是有两种:
在MySQL4.1版本之前只有第一种排序算法双路排序,第二种算法是从MySQL4.1开始的改进算法,主要目的是为了减少第一次算法中需要两次访问表数据的 IO 操作,将两次变成了一次,但相应也会耗用更多的sortbuffer 空间。当然,MySQL4.1开始的以后所有版本同时也支持第一种算法。
MySQL主要通过比较我们所设定的系统参数 max_length_for_sort_data的大小和Query 语句所取出的字段类型大小总和来判定需要使用哪一种排序算法。如果 max_length_for_sort_data更大,则使用第二种优化后的算法,反之使用第一种算法。所以如果希望 ORDER BY 操作的效率尽可能的高,一定要注意max_length_for_sort_data 参数的设置。
是有filesort的情况(说白了就是不走索引):
同样我们使用3.3节中的实验数据(看完下面的结果可以好好体会下上面说的filesort的情况):
在join条件下的order by情况:order by的子句只引用了联接中的第一个表,MySQL会先对第一个表进行排序,然后进行联接。也就是expain中的Extra的Using Filesort.否则MySQL先把结果保存到临时表(Temporary Table),然后再对临时表的数据进行排序.此时expain中的Extra的显示Using temporary Using Filesort(这里有个坑,就是使用inner join的情况下,默认会使用小表驱动大表,所以就算你把大表写在前面,然后排序只用了大表的字段,但是mysql优化器会把小表放在前面,然后使用了大表的排序,这样就会生成临时表)
图1:第一种方法用于第一个非常量表中存在ORDER BY所依赖的列的索引,那就可直接使用已经有序的索引来查找关联表的数据,这种方式是性能最优的,因为不需要额外的排序动作
图2:第二种方式用于ORDER BY所依赖的列全部属于第一张查询表且没有索引,那么我们可以先对第一张表的记录进行filesort(模式可能是模式1也可能是模式2),得到有序行索引,然后再做关联查询,filesort的结果可能是在内存中,也可能在硬盘上,这取决于系统变量sort_buffer_size
图3:第三种方法用于当ORDER BY的元素不属于第一张表时,需要把关联查询的结果放入临时表,最后对临时表进行filesort
mysql> explain select * from department d left join user u on u.d_id = d.id order by d.d_name; #在没有索引的情况下,除了全表扫描,还有临时表,filesort
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | d | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | Using temporary; Using filesort |
| 1 | SIMPLE | u | NULL | ALL | NULL | NULL | NULL | NULL | 9 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain select * from department d left join user u on u.d_id = d.id order by d.id;#在没有索引的情况下,除了全表扫描,还有临时表,filsort
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | d | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | Using temporary; Using filesort |
| 1 | SIMPLE | u | NULL | ALL | NULL | NULL | NULL | NULL | 9 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
mysql> create index idx_d_id on user(d_id); #创建索引
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from department d left join user u on u.d_id = d.id order by d.id; #创建索引后user表的rows为1,没有使用filesort和临时表
+----+-------------+-------+------------+-------+---------------+----------+---------+----------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+----------------------+------+----------+-------+
| 1 | SIMPLE | d | NULL | index | NULL | PRIMARY | 4 | NULL | 6 | 100.00 | NULL |
| 1 | SIMPLE | u | NULL | ref | idx_d_id | idx_d_id | 5 | foundation_item.d.id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+----------+---------+----------------------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain select * from department d left join user u on u.d_id = d.id order by u.d_id; #把排序条件换成user的字段后,使用了临时表和filesort
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+---------------------------------+
| 1 | SIMPLE | d | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | Using temporary; Using filesort |
| 1 | SIMPLE | u | NULL | ref | idx_d_id | idx_d_id | 5 | foundation_item.d.id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+---------------------------------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain select * from department d left join user u on u.d_id = d.id order by d.d_name; #把排序字段换成department表的d_name列(没有索引),使用了filesort
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+----------------+
| 1 | SIMPLE | d | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | Using filesort |
| 1 | SIMPLE | u | NULL | ref | idx_d_id | idx_d_id | 5 | foundation_item.d.id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+----------------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain select * from department d inner join user u on u.d_id = d.id order by d.d_name,u.id; #使用了user表的id排序,新增临时表
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+---------------------------------+
| 1 | SIMPLE | d | NULL | ALL | PRIMARY | NULL | NULL | NULL | 6 | 100.00 | Using temporary; Using filesort |
| 1 | SIMPLE | u | NULL | ref | idx_d_id | idx_d_id | 5 | foundation_item.d.id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+---------------------------------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain select d.* from department d inner join user u on u.d_id = d.id order by d.d_name,u.id; #这里只返回department表的全部列,还是使用了临时表和filesort
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+---------------------------------+
| 1 | SIMPLE | d | NULL | ALL | PRIMARY | NULL | NULL | NULL | 6 | 100.00 | Using temporary; Using filesort |
| 1 | SIMPLE | u | NULL | ref | idx_d_id | idx_d_id | 5 | foundation_item.d.id | 1 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+---------------------------------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain select d.* from department d inner join user u on u.d_id = d.id order by d.d_name; #去掉user的id列排序后少了临时表
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+----------------+
| 1 | SIMPLE | d | NULL | ALL | PRIMARY | NULL | NULL | NULL | 6 | 100.00 | Using filesort |
| 1 | SIMPLE | u | NULL | ref | idx_d_id | idx_d_id | 5 | foundation_item.d.id | 1 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+----------+---------+----------------------+------+----------+----------------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain select d.* from department d inner join user u on u.d_id = d.id order by d.id; #使用department的id排序,没了filesort
+----+-------------+-------+------------+-------+---------------+----------+---------+----------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+----------------------+------+----------+-------------+
| 1 | SIMPLE | d | NULL | index | PRIMARY | PRIMARY | 4 | NULL | 6 | 100.00 | NULL |
| 1 | SIMPLE | u | NULL | ref | idx_d_id | idx_d_id | 5 | foundation_item.d.id | 1 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+----------------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
inner join下的一个坑点
面对如上情况的问题,可以使用如下的方式试试(执行计划差不多,有点区别,数据量很大的情况下可以试试,我也不确定速度会怎样,可能会好些,可以过滤掉一些数据,在不同的业务场景下可以尝试对比)
有时候filesort是无法避免的,但是还是可以做一些优化的:
2、group by
由于group by实际上也同样会进行排序操作,而且与group by相比,group by 主要只是多了排序之后的分组操作。当然,如果在分组的时候还使用了其他的一些聚合函数,那么还需要一些聚合函数的计算。所以,在group by 的实现过程中,与 group by一样也可以利用到索引。
由于group by无非就是用到索引和用不到索引的情况,用到索引的时候走索引速度快,用不到的情况用临时文件,所以会慢一些,其实就是在排序后分组,既然不能走索引的话就可以根据order by不能走索引,使用filesort的优化策略一样就好了,走索引的情况就要符合索引的最左前缀原则,这里不再深入的讨论group by的原理,有兴趣的可以自行去理解,反正我认为回了order by的优化就基本根据套路来优化group by。
还有就是能在where中过滤掉的就不要等到hiving过滤。
很多时候明明创建了索引,就是使用的过程中不走索引,所以有时候也会很苦恼,我们就来看看哪些情况不走索引(注意:如下的任何优化方式都是通过explain做理论支撑的,没有在实际的生成环境中跑过,所以有时候速度快不快也要看表的设计,索引的设计,数据量的大小等)
1、使用 like ‘%%‘ #使用like的时候要注意%在最左边的时候是不会走索引的,其他的方式会走索引
如上方式可以修改成: explain select * from (select id from user where word like ‘%写代码1%‘) a inner join user u on a.id = u.id; 其中id为主键索引。源于《深入浅出mysql:数据开发、优化与管理维护(第二版)》,我也不知道这个会不会有速度提升,毕竟type是index,建立索引后的word列的type是range,接下来我们把word上的索引去掉看看。
可以细细体会下如上的过程,在没有建立索引的情况下使用join这个写法速度快没问题,但是在建立索引的情况下,估计没得快(这里有条件的话可以去测试一下),所以对于like ‘%%‘这种写法,在不能创建索引的情况下就使用join,可以创建索引的情况下就添加索引。
2、在where中使用函数,计算,类型转换等不会走索引。
mysql> create index d_word on user(word); #创建索引
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from user where word= ‘c写代码2‘; #使用等值查询,使用到了索引
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
| 1 | SIMPLE | user | NULL | ref | d_word | d_word | 203 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from user where left(word,1)= ‘c‘; #在where中使用了函数或者计算,全表扫描没有使用到索引
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | user | NULL | ALL | NULL | NULL | NULL | NULL | 9 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
3、在组合索引中不符合最左前缀原则的不会走索引,或者只走部分索引。
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 and b > const order by b,c
不能使用索引进行排序
- order by a , b desc ,c desc --排序不一致
- where d=const order by b,c --a丢失
- where a=const order by c --b丢失
- where a=const order by b,d --d不是索引的一部分
- where a in(...) order by b,c --a属于范围查询
4、字符串没有单引号索引也会失效
5、使用or连接时,如果只有一个字段有索引,则索引会失效 ,可以根据实际情况使用分别查询然后union all连接
6、limit分页的优化
create table test( id int(11) primary key auto_increment, name varchar(56), salary double ); delimiter $$ create function rand_string(n int) returns varchar(255) begin declare char_str varchar(100) default ‘abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ‘; declare result_str varchar(255) default ‘‘; declare i int default 0; while i<n do set result_str = concat(result_str,SUBSTRING(char_str,FLOOR(1+rand()*52),1 )); set i=i+1; end while; return result_str; end $$ delimiter $$ create procedure insert_tab(in start int(10),in max_num int(10)) begin declare i int default 0; set autocommit = 0; repeat set i=i+1; insert into test(name,salary) values(rand_string(6),rand()*5000); until i = max_num end repeat; commit; end $$ delimiter ; call insert_tab(1,1000000);
到这里为止介绍一部分的优化内容,mysql作为一个基础内容,有超级强大,稳定,要学的东西特别多,所以优化的东西还有很多,后面有时间会慢慢道来,希望各位童鞋能够一起学习,也希望自己能够再接再厉!!!
参考:
https://www.cnblogs.com/yumingzhao/p/10711825.html
https://www.cnblogs.com/developer_chan/p/9231761.html
https://zhuanlan.zhihu.com/p/120043388
https://blog.csdn.net/lijingkuan/article/details/70341176
https://www.jb51.net/article/161684.htm
标签:日志文件 derived put 生成 对比 asc develop 图片 length
原文地址:https://www.cnblogs.com/zsql/p/13854191.html