概述
所谓的SQL优化其实大多数时候都是指对查询SQL的优化,因为查询SQL在应用中SQL占比为绝大部分
INSERT SQL优化
非大批量数据
(1) 批量插入 而非逐个插入
(2) 手动提交事务 而非自动提交事务
(3) 主键顺序插入 而非乱序插入
大批量数据(例如100万)
如果一次性需要插入大批量的数据,使用insert语句插入性能较低,此时可以使用MySQL数据库提供的
load
指令进行插入。操作如下:
#客户端连接服务端时,加上参数 --local-infile
mysql --local-infile -u root -p
#设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
select @@local-infile; #查询导入数据的开关是否开启
set global local-infile = 1; #开启从本地加载文件导入数据的开关
#执行load指令将准备好的数据加载到表结构中
load data local infile '/export/sql_files/sql_insert.log'
into table `tb_user`
fields terminated by ','
lines terminated by '\n';
使用load指令插入100万条数据的时间为16.83s,而使用insert语句进行插入大概需要10min,可见load指令对于插入大批量的数据效率很高。
主键优化
InnoDB的数据组织方式
在InnDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表被称为索引组织表(index organized table - IOT)
Tablespace - Segment - Extent(1M 64x16KB) - Page(16KB) - Row(大小?)
页分裂
页合并:当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged)为删除的状态,且它的空间变得允许被其他记录声明使用。当页中删除的记录达到MERGE_THRESHOLD(默认为页的50%),InnDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间的使用。
主键设计原则
(1) 满足业务要求的情况下,尽量降低主键的长度
(2) 插入数据的时候,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键
(3) 尽量不要使用UUID做主键或者是其他自然主键,如身份证
(4) 业务操作时,避免对主键的修改
GROUP BY 分组优化
(1)在分组的操作时,可以通过索引来提高效率
(2)分组操作时,索引的使用也是满足最左前缀法则的
explain select profession, count(*) from tb_user group by profession;
#创建索引
create index idx_user_pro_age_sta on tb_user(profession, age, status);
#执行分组操作,根据profession字段分组
explain select profession, count(*) from tb_user group by profession;
explain select profession, count(*) from tb_user group by profession, age;
ORDER BY 排序优化
(1) Using fileSort: 通过表的索引或者全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫做FileSort排序
(2) Using index: 通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高
创建表
create table user (
id int AUTO_INCREMENT primary key,
name varchar(32),
age int,
phone varchar(11)
);
插入测试数据
insert into user (name, age, phone) values ('zhangsan', 21, '15611112222'),('lisi', 19, '13311112222'),('wangwu', 23, '17811112222'),('zhaoliu', 20, '15611116666');
优化历程1
#无索引 -> Using filesort
explain select id,age,phone from user order by age,phone;
#创建索引
create index idx_age_phone_aa on user(age, phone);
#有索引 再次查询
explain select id,age,phone from user order by age,phone;
explain select id,age,phone from user order by age DESC, phone DESC;
explain select id,age,phone from user order by age DESC, phone ASC;
优化历程2 (Using index; Using filesort)
针对上方的 (Using index; Using filesort) 再次进行优化,可以做的优化操作
create index idx_age_phone_da on user(age DESC, phone ASC);
LIMIT 优化
当
limit
2000000,10的OFFSET很大的时候,查询的效率会很慢,优化的思路一般是通过创建覆盖索引 + 子查询
explain select *
from tb_sku t, (select id from tb_sku order by limit 2000000,10) sub_ids
where t.id = sub_ids.id;
COUNT 优化
count(主键)
InnDB引擎会遍历整张表,把每一行的主键id值都取出来,返回给服务层。服务层拿到数据后,直接按行进行累加(主键不可能为null)
count(其他字段)
(1)如果没有
not null
的约束,InnDB会遍历整张表把每一行的字段都取出来,返回给服务层,服务层判断是否为null,不为null,则直接按照行进行累加;(2)如果有
not null
的约束,InnDB引擎会遍历整张表把每一行的字段都取出来,返回给服务器,直接按照行进行累加
count(1)
InnDB会遍历整张表,但不取值。服务层对于返回的每一行,放一个数字1进去,直接进行累加
count(*)
InnDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接进行累加
count效率总结
count(字段) < count(id) < count(1) < count(*)