在结束性能知识分享,我们终于迎来了MySQL的 知识。
关于,MySQL的基本用法,小鱼也是整理的几篇文章,如:
《基本用法总括一》
《基本用法总括二》
接下来,小鱼会更详细的分享关于 MySQL的知识,包含这四大类:
①SQL优化
②MySQL索引
③MySQL锁
④事务
接下来的很长一段时间,
就让小鱼陪着你,
一起把 MySQL的姿(知)势(识) 搞起来!!!
按照惯例,先介绍一下 MySQL基本知识:
MySQL 是一种关联数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。
MySQL 的 SQL “结构化查询语言”,是用于访问数据库的最常用标准化语言。
MySQL 软件采用了 GPL(GNU 通用公共许可证),由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了降低网站总体拥有成本,而选择 MySQL 作为其网站数据库。
MySQL的优势有哪些,我们来瞧瞧。
・MySQL开源的,无需支付额外费用的;
・MySQL使用标准的SQL数据语言形式;
・MySQL可以运行多个系统上,并支持多个语言(如:C、C++、Python、Java、Perl、PHP、Eiffel、Ruby 等);
・MySQL可以定制,采用 GPL 协议,可修改源码来开发自己的 MySQL 系统。
所以,我们要搞好MySQL数据库,是不是非常有必要。
今天,
我们就先从order by和group by来说起。
为了更好,更容易理解相关知识点,咱先举个例子。
也是小鱼的一贯作风, 举例说话 !
先创建一张测试表,
/* auth:carl_DJ */
/* 使用ClassDJ这个database */
use ClassDJ;
/* 如果表t1存在则删除表t1 */
drop table if exists t1;
/* 创建表t1 */
CREATE TABLE 't1'(
'id' int(11) NOT NULL AUTO_INCREMENT,
'a' int(20) DEFAULT NULL ,
'b' int(20) DEFAULT NULL ,
'c' int(20) DEFAULT NULL ,
'd' datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY (`id`),
KEY `idx_a_b` (`a`,`b`),
KEY `idx_c` (`c`)
) ENGINE=InnoDB CHARSET=utf8mb4 ;
/* 如果存在存储过程insert_t1,则删除 */
drop procedure if exists insert_t1;
delimiter ;;
/* 创建存储过程insert_t1 */
create procedure insert_t1()
begin
/* 声明变量i */
declare i int;
/* 设置i的初始值为1 */
set i=1;
/* 对满足i<=10000的值进行while循环 */
while(i<=10000)do
/* 写入表d1中a、b两个字段,值都为i当前的值 */
insert into t1(a,b,c) values(i,i,i);
/* 将i加1 */
set i=i+1;
end while;
end ;;
delimiter ;
/* 运行存储过程insert_t1 */
call insert_t1();
/* 将id大于9000的行的a字段更新为1000 */
update t1 set a=1000 where id >9000;
创建完表,我们就来研究一下MySQL的排序原理
按照排序原理分,MySQL 排序方式分两种:
①通过有序索引直接返回有序数据
②通过Filesort进行的排序
那么,问题来了,
如何确定某条排序的SQL所使用的排序方式?
答:使用explain来查看该排序SQL的执行计划,重点关注****字段。
★如果Extra字段里显示是Using index,则表示是通过有序索引直接返回有序数据
例如;
explain select id,c from t1 order by c;
★如果Extra字段里显示是Using filesort,则表示该 SQL 是通过Filesort 进行的排序
例如:
explain select id,d from t1 order by d;
是不是,这就一目了然 了。
Nice
MySQL 中的 Filesort 并不一定是在磁盘文件中进行排序的,也有可能在内存中排序,
内存排序还是磁盘排序取决于排序的数据大小和sort_buffer_size 配置的大小。
・如果 “排序的数据大小” < sort_buffer_size:内存排序
・如果 “排序的数据大小” > sort_buffer_size:磁盘排序
同样,问了又来了,
如何确定使用 Filesort 排序的 SQL 是在内存还是在磁盘中进行的排序操作?
答:使用trace进行分析重点关注number_of_tmp_files,
★如果等于 0,则表示排序过程没使用临时文件,在内存中就能完成排序;
★如果大于0,则表示排序过程中使用了临时文件。
举例
一、未使用临时文件排序
上图中,因为number_of_tmp_files 等于 0,表示未使用临时文件进行排序,所以是内存排序。
参数解析:
・rows:预计扫描的行数
・examined_rows:参与排序的行
・number_of_tmp_files:使用临时文件的个数
・sort_buffer_size:sort_buffer 的大小
・sort_mode:排序模式
二、使用临时文件排序
上图中,因为number_of_tmp_files 等于 7,所以表示使用的是磁盘排序。
number_of_tmp_files 等于 7表示:
该SQL 将需要排序的数据分为 7 份,然后每份单独排序,再存放在 7 个临时文件中,最后把 7 个临时文件合并成一个大的有序文件。
一、排序模式
Filesort下的排序模式有三种:
・< sort_key, rowid >双路排序(又叫回表排序模式):是首先根据相应的条件取出相应的排序字段和可以直接定位行数据的行 ID,然后在 sort buffer 中进行排序,排序完后需要再次取回其它需要的字段;
・< sort_key, additional_fields >单路排序:是一次性取出满足条件行的所有字段,然后在sort buffer中进行排序;
・< sort_key, packed_additional_fields >打包数据排序模式:与单路排序相似,区别是将 char 和 varchar 字段存到 sort buffer 中时,更加紧缩。
MySQL 通过比较系统变量 max_length_for_sort_data 的大小和需要查询的字段总大小来判断使用哪种排序模式
二、判断排序模式
通过max_length_for_sort_data的大小和需要查询的字段总大小来判断使用哪种排序模式。
・如果max_length_for_sort_data 比查询字段的总长度大,那么使用 < **sort_key, additional_fields >**排序模式;
・如果max_length_for_sort_data 比查询字段的总长度小,那么使用<sort_key, rowid>排序模式。
三、实例举例
①
set session optimizer_trace="enabled=on",end_markers_in_json=on;
SET max_length_for_sort_data = 20;
/* 查询表d1的id、a、d三个字段的值,按照字段d进行排序 */
select a,d from t1 order by d;
SELECT * FROM information_schema.OPTIMIZER_TRACE\G
OPTIMIZER_TRACE 结果中排序信息,如下图:
可以看到,使用的是**<sort_key,additional_fields>**排序模式。
②
我们要是变成<sort_key, rowid>,怎么整呢?
思路:因为 a、d 两个字段的总长度为 12,可以尝试把 max_length_for_sort_data 改为小于 12 的值,看排序模式是否有改变。
操作:
set session optimizer_trace="enabled=on",end_markers_in_json=on;
SET max_length_for_sort_data = 4;
/* 查询表d1的id、a、d三个字段的值,按照字段d进行排序 */
select a,d from t1 order by d;
SELECT * FROM information_schema.OPTIMIZER_TRACE\G
OPTIMIZER_TRACE 结果中排序信息,如下图:
看看,是不是变成 <sort_key ,rowid>排序模式了。
非常Nice。
我们了解了order by的原理,
那么我们就来看看,优化order by 有什么技巧。
①首先我们看下对 d 字段(没有索引)进行排序的执行计划:
explain select d,id from t1 order by d;
执行结果如下:
发现使用的是filesort(关注Extra字段)。
②再看些对 c 字段(有索引)进行排序的执行计划:
explain select c,id from t1 order by c;
执行结果如下:
可以看到,根据有索引的字段排序,在Extra中显示的就为Using index,表示使用的是索引排序。
注:
如果数据量比较大,显然通过有序索引直接返回有序数据效率更高
问:有时面对的需求是要对多个字段进行排序,而这种情况应该怎么优化或者设计索引呢?
答:如果多个字段排序,可以在多个排序字段上添加联合索引来优化排序语句
举个例子
①
对 a、c 两个字段进行排序的执行计划
explain select id,a,c from t1 order by a,c;
执行结果如下:
观察 Extra 字段,发现使用的是filesort。
②
再看对 a、b(a、b 两个字段有联合索引)两个字段进行排序。
explain select id,a,b from t1 order by a,b;
执行结果如下:
发现使用的是索引排序。
注:
多个字段排序的情况,如果要通过添加索引优化,得注意排序字段的顺序与联合索引中列的顺序要一致
问:我们更多的情况是会先根据某个字段条件查出一部分数据,然后再排序,而这类 SQL 应该如果优化呢?
答:可以通过在条件字段和排序字段添加联合索引来优化此类排序语句。
举个例子
①
表 d1中,根据 a=1000 过滤数据再根据 d 字段排序的执行计划如下:
explain select id,a,d from t1 where a=1000 order by d;
执行结果如下:
可以在 Extra 字段中看到 “Using filesort”,说明使用的是filesort 排序。
②
再看下根据 a=1000 过滤数据在根据 b 字段排序的执行计划(a、b 两个字段有联合索引):
explain select id,a,b from t1 where a=1000 order by b;
执行结果如下:
可以在 Extra 字段中看到“Using index”,说明使用的是索引排序。
有时,我们其实并不需要查询出所有字段,但是,有的时候,我们习惯性的写出 “select * from table_name”。
举个例子
①
select * from t1 order by a,b; /* 根据a和b字段排序查出所有字段的值 */
执行结果如下:
②
select id,a,b from t1 order by a,b; /* 根据a和b字段排序查出id,a,b字段的值 */
执行结果如下:
通过这两个例子,我们可以看到,
查询所有字段的这条 SQL 是 filesort 排序,
而只查id、a、b 三个字段的 SQL 是 index 排序。
那么,
为什么查询所有字段会不走索引?
是因为:
扫描整个索引并查找到没索引的行的成本比扫描全表的成本更高,所以优化器放弃使用索引。
一、修改哪些参数?
这里就用到了在前面提到的两个参数:
max_length_for_sort_data。
sort_buffer_size。
二、如何修改这两个参数的值?
・max_length_for_sort_data:
如果觉得排序效率比较低,可以适当加大 max_length_for_sort_data 的值,让优化器优先选择全字段排序。当然不能设置过大,可能会导致 CPU 利用率过低或者磁盘 I/O 过高;
・sort_buffer_size:
适当加大 sort_buffer_size 的值,尽可能让排序在内存中完成。但不能设置过大,可能导致数据库服务器 SWAP。
默认情况,会对 group by 字段排序,
因此优化方式与 order by 基本一致,
如果目的只是分组而不用排序,可以指定order by null禁止排序
今天,整理的是 order by 和group by的 一些优化技巧,
因为在写sql中,这两个还是比较常用的,
所以,记住还是有必要的。
关于order by的优化,主要就这么几点知识:
・通过添加合适索引
・去掉不必要的返回字段
・调整参数:主要是max_length_for_sort_data和sort_buffer_size
回顾一下,是不是很简单,也很Nice。
嗯,细细品,慢慢品!