SQL优化涉及到多个方面,我们接下来主要从以下几个方面介绍:
- 插入数据时的优化。
- 主键优化。
- order by 排序的优化。
- group by 分组的优化。
- limit 分页的优化。
- count 统计的优化。
- update更新数据的优化。
- 插入数据的优化
即insert优化,我们可以从批量插入数据、手动事务提交,以及主键顺序插入等实现。
1)批量插入
# 注意此批量插入一次最多不超过1000条数据
insert into tb_user values (1, "Tom"), (2, "Cat"), (3, "Jerry"),...;
2)手动事务提交
start transaction;
insert into tb_user values (1, "Tom"), (2, "Cat"), (3, "Jerry");
insert into tb_user values (4, "Tom"), (5, "Cat"), (6, "Jerry");
insert into tb_user values (7, "Tom"), (8, "Cat"), (9, "Jerry");
commit;
3)主键顺序插入
# 主键乱序插入
8,1,9,10,22,81,2,4,15,89,5,7,3
# 主键顺序插入,基于MySQL的组织结构,顺序插入性能更高
1,2,3,4,5,7,8,9,10,15,22,81,89
4)大批量插入数据
如果一次性需要插入大批量数据,使用insert语句性能较低,此时可以使用MySQL数据库提供的load指令进行插入数据操作。操作如下:
例如,将文件a.text中的数据,通过load转为数据库存储:
# a.text
1, sdfgh, qwert, qwert, 2011-01-01, 0
2, sdfgh, qwert, qwert, 2011-11-01, 0
3, sdfgh, qwert, qwert, 2011-10-01, 1
4, sdfgh, qwert, qwert, 2011-01-11, 2
5, sdfgh, qwert, qwert, 2011-09-01, 0
6, sdfgh, qwert, qwert, 2011-01-01, 0
7, sdfgh, qwert, qwert, 2011-11-01, 0
8, sdfgh, qwert, qwert, 2011-10-01, 1
9, sdfgh, qwert, qwert, 2011-01-11, 2
10, sdfgh, qwert, qwert, 2011-09-01, 0
# 数据库存储结构
id, name, password, message, date, status
具体实现语法如下:
# 客户端连接服务器时,加上参数 --local-infile
mysql --local-infile -u root -p
# 创建一个数据库
create database ithero;
# 查看是否参加成功
show databases;
# 进入ithero数据库
use ithero;
# 查看当前数据库名称
select database();
# 查看参数 local_infile的值
select @@local_infile;
# 设置全局参数 local_infile = 1, 开启从本地加载文件导入数据的开关
set global local_infile=1;
# 创建测试表
# 注意,MySQL中,为了区分MySQL关键字与普通字符,MySQL引入了反引号
CREATE TABLE `tb_user` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT "主键",
`username` VARCHAR(50) NOT NULL COMMENT "用户名",
`password` VARCHAR(50) NOT NULL COMMENT "密码",
`name` VARCHAR(20) NOT NULL COMMENT "姓名",
`birthday` DATE DEFAULT NULL COMMENT "生日",
`sex` CHAR(1) DEFAULT NULL COMMENT "性别",
PRIMARY KEY (`id`),
UNIQUE KEY `unique_user_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT="测试表";
# 执行load指令将准备好的数据,加载到表结构中
load data local infile "/root/sql.log" into table "tb_user" fields terminated by ", " lines terminated by "\n";
# 注意,主键顺序插入的性能高于乱序插入
2. 主键的优化
1)数据的组织方式
在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(Index organized table, IOT)。
2)页分裂
页可以为空,也可以填充一半,也可以填充100%;每个页包含了2~N行数据(如果一行数据太大,会行溢出),根据主键排列。
当主键乱序插入时,会出现页分裂的现象。
即当已经存在2页(页a, 页b)数据填充满的情况下,又出现了一个中间主键(key);那么第一页(页a)将从中间断开,后1/2数据与该中间主键(key)组合成第3个页(c),然后指针变向:由a<->b,改成 a <-> c <-> b。
3)页合并
当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged)为删除,且它的空间变得允许被其他记录声明使用。
当页中删除的记录达到 MERGE_THRESHOLD(阈值,默认为50%),InnoDB会开始寻找最近的页(前或后),看看是否可以将两个页合并以优化空间使用。
注意:MERGE_THRESHOLD即合并页的阈值,可以自己设置,在创建表或者创建索引时指定。
4)主键的设计原则
- 满足业务需求的情况下,尽量降低主键的长度;
- 插入数据时,尽量选择顺序插入,选择使用 AUTO_INCREMENT 自增主键;
- 尽量不要使用UUID做主键,或者其他自然主键,如身份证号。(因为其大部分是无序的,且较长);
- 业务操作时,避免对主键的修改。(修改主键代价大)
3. order by 优化
1)Using filesort: 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作;所有不是通过索引直接返回排序结果的排序都叫 Filesort 排序。
2)Using index: 通过有序索引顺序扫描直接返回有序数据,这种情况叫 using index,不需要额外排序,操作效率高。
# 1. 没有创建索引时,根据age,phone进行排序
explain select id, age, phone from tb_user order by age, phone;
# 此时,由于没有索引,extra内展示的,即为Using filesort
# 2. 创建索引
create index idx_user_age_phone on tb_user(age, phone);
# 根据age, phone 升序排
explain select id, age, phone from tb_user order by age asc, phone asc;
# 根据age, phone 将序排
explain select id, age, phone from tb_user order by age desc, phone desc;
# 上述2种排序,都能使用索引完成,extra内展示的,即为Using index
# 3. 若根据age 升序, phone 将序排
explain select id, age, phone from tb_user order by age asc, phone desc;
# 此时,extra内展示的,既有Using index,也有Using filesort
# 表示即使用了索引,也使用了全表扫描
# 4. 我们创建age 升序 + phone 将序的索引
create index idx_user_age_pho_ad on tb_user(age asc, phone desc);
# 此时再次根据age 升序, phone 将序排
explain select id, age, phone from tb_user order by age asc, phone desc;
# 则extra内展示的,只有Using index,表示只使用了索引。
使用order by 优化的原则:
- 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则;
- 尽量使用覆盖索引
- 多字段排序,一个升序、一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)。
- 如果不可避免的出现 filesort,大数据量排序时,可以适当增大排序缓冲区大小 sort_buffer_size(默认256k)。
# 查看filesort排序时的,缓冲区大小
show variables like "sort_buffer_size";
后续的优化内容,我们在下一篇中继续学习。
原创文章,作者:guozi,如若转载,请注明出处:https://www.sudun.com/ask/90803.html