SQL优化
1.插入多条数据
【1】合并SQL
批量插入数据原来是分开多条SQL插入,现在我们可以合并SQL为1条。
Insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
【2】合并事务
本来一条语句一个事务,现在我们可以手动控制合并成一个。
start transaction;
insert into tb_test1 values(1,'Tom'),(2,'Cat'),(3,'Jerry');
insert into tb_test2 values(4,'Tom'),(5,'Cat'),(6,'Jerry');
insert into tb_test3 values(7,'Tom'),(8,'Cat'),(9,'Jerry');
commit;
【3】通过数据库自带load命令大批量插入数据
如果一次性需要插入大批量数据(比如: 几百万的记录),使用insert语句插入性能较低,此时可以使 用MySQL数据库提供的load指令进行插入。操作如下:
可以执行如下指令,将数据脚本文件中的数据加载到表结构中:
-- 客户端连接服务端时,加上参数 -–local-infile
mysql –-local-infile -u root -p
-- 设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
set global local_infile = 1;
-- 执行load指令将准备好的数据,加载到表结构中
load data local infile '/root/sql1.log' into table tb_user fields terminated by ',' lines terminated by '\n' ;
load不能很好的控制事务,可以采用并发的方式进行,每个并发控制自己的事务,并且做好日志记录,如果哪个线程GC就知道了。
2.更新数据--条件语句是索引的为行锁,不是为表锁
update course set name = 'javaEE' where id = 1 ;
当我们在执行删除的SQL语句时,会锁定id【主键】为1这一行的数据,然后事务提交之后,行锁释放。
但是当我们在执行如下SQL时:
update course set name = 'SpringBoot' where name = 'PHP' ;
当我们开启多个事务,在执行上述的SQL时,我们发现行锁升级为了表锁。 导致该update语句的性能大大降低。
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁 ,并且该索引不能失效,否则会从行锁升级为表锁 。
3.查询数据
【1】count计数查询优化--建立变量保存数量。明确各种count的效率比较
count() 采用遍历的方式,如果数据量很大,在执行count操作时,是非常耗时的。
主要的优化思路:自己建立计数变量
(可以借助于mysql,redis这样的数据库进行,比如对一行存储点赞的总数 ,但是如果是带条件的count又比较麻烦了)。
用法:count(*)、count(主键)、count(字段)、count(数字)

按照效率排序的话,count(字段) < count(主键 id) < count(1) ≈ count(*),所以尽 量使用 count(*)。
【2】limit分页查询优化--往后查询效率低,使用覆盖索引的子查询优化
在数据量比较大时,如果进行limit分页查询,在查询时,越往后,分页查询效率越低。
因为,当在进行分页查询时,如果执行 limit 2000000,10 ,此时需要MySQL排序前2000010 记 录,仅仅返回 2000000 - 2000010 的记录,其他记录丢弃,查询排序的代价非常大 。
优化思路:覆盖索引提高查询效率。
一般分页查询时,可以通过覆盖索引的子查询形式进行优化。
子查询的目的是避免获取那么多无用的数据,另外在子查询中要尽量覆盖索引。
优化前:select * from tb_sku limit 2000000,10
优化后:select * from tb_sku t , (select id from tb_sku order by id limit 2000000,10) a where t.id = a.id;
#先用子查询筛选,回到原来的表中去查询其他信息
这个例子中其实覆盖索引不是重点,重点的是子查询只获取id,用时肯定快。
虽然都是走的全表扫描,但是之前的操作是全表扫200w全部行数据,现在是扫200wid数据+10条行数据,肯定后面快。
优化前:select * from tb_sku where name="fp" limit 2000000,10
优化后:select * from tb_sku t , (select id,name from tb_sku where name="fp" limit 2000000,10) a where t.id = a.id;
这个例子中覆盖索引就很重要了。
【3】order by优化--使用索引优化,不能优化的可以增大缓冲区
MySQL的排序,有两种方式:
Using filesort : 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
Using index : 通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高。
所以
A. 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
B. 尽量使用覆盖索引。
C. 多字段排序, 如果一个要求升序一个要求降序,此时需要注意联合索引在创建时的规则(ASC/DESC)。
D. 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小 sort_buffer_size(默认256k)。
【4】group by优化--使用索引优化
附:主键优化
主键顺序插入,性能要高于乱序插入,所以尽量选择自增主键。自增主键的删除速率也比较快。
主键乱序插入 : 8 1 9 21 88 2 4 15 89 5 7 3
主键顺序插入 : 1 2 3 4 5 7 8 9 15 21 88 89
为什么自增主键插入,删除快?
这和数据组织方式,还有页分裂(插入),页合并(删除)这些比较耗时的操作有关。
总结:批量插入的方式主要是拼接sql,多线程。而count可以建立计算数变量。主键尽量使用自增。
在更新方面的where语句尽量使用索引,不然会从行锁升级为表锁。
limit,order by,group by这些查询尽量使用索引【条件变量建立索引,查询变量覆盖索引,limit使用子查询,注意子查询要覆盖索引】