SQL优化
insert优化
常规优化思路
- 每次插入数据时都需要和数据库建立连接、关闭连接,因此批量插入会大量节省IO避免浪费。(每次500-1000条)
- 开启手动提交事务(start transaction … commit)
- 主键顺序插入:主键插入顺序为1 2 3 4 5 6 … 顺序插入而非乱序插入
大批量插入数据
-
如果一次性需要插入大量批量数据,使用MySQL数据库提供的load指令进行插入。每个字段的格式要和表中字段格式对应。
# 客户端连接服务器时,加上参数 --loacal-infile mysql --local-infile -u root -p # 设置全局参数local_infile为1,开启从本地加载文件导入数据的开关。(查看select @@local_infile) set global local_infile=1; # 执行load指令将准备好的数据,加载到表结构中 load data local infile '/root/sql1.log' into table `t_user` fileds terminated by ',' lines terminated by '\n'; # 每一列使用,分隔。每一列使用\n分隔
主键优化
在innodb存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表。
页分裂 & 页合并:
-
在插入数据到页时,页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行(至少两行)的数据。
-
在删除数据时,页中的数据并没有被实际物理删除,只是记录被标记(flaged)为删除并且它的空间变得允许被其他记录声明使用。当页中删除的记录达到MERGE_THRESHOLD(默认为页的50%),Innodb会开始寻找最靠近的页并判断是否能将两个页合并为一个页以优化空间。
主键设计原则:
- 尽量降低主键长度
- 插入数据时尽量选择顺序插入,选择使用AUTO_INCREMENT自增
- 尽量不要使用UUID做主键,或是身份证
- 业务操作时避免对主键的修改
order by优化
Using filesort:通过全表扫面,读取满足条件的行,然后在排序缓冲区sort buffer完成排序操作。所有不是通过索引直接返回排序结果的排序都叫Using filesort
Using index:通过有序索引顺序扫描直接返回有序数据,这种排序叫做Using index。操作效率高于Using filesort
group by优化
主要研究索引对于分组操作的影响。
Using temporary:使用额外临时表进行分组,性能低、开销大。
Using index:使用索引字段进行分组,性能高。(使用索引时必须符合最左前缀法则否则依然Using temporary)
limit优化
在大数据量的分页查询中,如果limit 2000000 10,此时MySQL排序前2000010 的记录,仅仅返回2000000~2000010 的记录,其他记录丢弃,查询排序的代价非常大。
优化思路:覆盖索引+子查询。通过创建覆盖索引能够比较好的提高性能,可以通过覆盖索引加子查询的方式优化。
# 先通过主键索引查出想要的数据id
select id from student order by id limit 9000000,10;
# 查询出的id值就是我们需要的9000000 ~ 9000010之间的值,再通过联表查询的方法将数据查出
select s.* # 查询s表中所有值
from student s,(select id from student order by id limit 9000000,10) a # 子查询出需要的id
wheres.id = a.id; # 联表查询条件
count优化
在MyISAM存储引擎中将表的总行数存在了磁盘上,因此如果是:select count(*) from t_user。不带条件查询,会直接返回总行数,效率非常高。
在InnoDB中,在执行count(*)时,需要把数据一行一行的从引擎里面读出来,然后累计计数
优化思路:自己计数。在Redis中统计如果执行insert则+1,delete则-1。count不计入NULL值
几种用法:
- count(主键):InnoDB会遍历整张表,把每一行的主键id都取出来,返回给服务层。服务层拿到数据后直接进行累加(主键不可能为NULL)。
- count(字段):
- 没有not null约束时InnoDB会遍历整张表把每一行的字段值都取出来,返回给服务层。服务层判断是否为null,不为null时则计数累加。
- 有not null时InnoDB将每行数据取出来,返回给服务层。服务层直接进行计数累加
- count(1):InnoDB会遍历整张表但是不取值。服务层对于返回的每一行都插入一个 “1” ,并直接进行累加。这里无论是count(1) 、 count(0) 、 count(-1)都是一样的。
- count(*):InnoDB不取值,服务层直接按行累加。
效率排行:count(字段) < count(主键) < count(1) ≈ count(*)
update优化
在InnoDB中行锁是针对索引字段单独设立的,如果在update中’索引失效’则行锁升级为表锁。这样会大大降低并发性能。
例:
# 用户1
begin; # 开启事务
update student set name = 'zhangsan' where id = 1;# 用户2
begin; # 开启事务
update student set name = 'lisi' where id = 3;# 用户1提交事务
commit;
# 用户2提交事务
commit;
# 如果where后的条件字段不是索引字段的话,InnoDB会自动将行锁升级为表锁。也就是说,如果where name = 'xxx'; name不是索引字段的话,用户1开启事务时,用户2就无法给student表中插入任何数据
小结
- 插入数据SQL优化:load、手动控制事务、顺序插入
- 主键优化:长度尽量短、有序插入、主键自增
- order by优化:Using firesort、Using index
- group by优化:Using temporary、Using index
- limit优化:覆盖查询+子查询的方式来优化分页查询
- count优化:使用count(*)、自己维护数据库行数
- update优化:where条件后的字段尽量为索引字段,否则InnoDB会将行锁升级为表锁。降低并发性能。
The end.