1.插入数据
·insert优化:
例如要插入下面这些
insert into tb_test values(1,'tom');
insert into tb_test values(2,'cat');
insert into tb_test values(3,'jerry');
我们可以通过以下几个方面进行操作:
>批量插入(如果一次性要插入多条数据可以通过一条insert语句来完成)
像上面这样每次insert都要与数据库进行连接进行网络传输,这样性能相对是比较低的,所以可以考虑用批量插入。
insert into tb_test values(1,'tom'),(2,'cat'),(3,'jerry');
不过不能超过一千条,如果要一次插入一万条数据怎么办?可以可以分割为多条insert语句插入嘛
>手动提交事务
MySQL中事务提交方式默认是自动的,也就是说,当你执行完一条SQL语句后,它事务就提交了,再次执行一条insert,执行之前开启事务,执行完毕又关闭事务,这时候就涉及事务的频繁开启与提交,所以我们建议手动提交事务。
在insert之前开启事务,然后执行完多条SQL语句后再统一提交事务
例:
start transaction;
insert into tb_test values(1,'tom'),(2,'cat'),(3,'jerry');
insert into tb_test values(4,'tom1'),(5,'cat1'),(6,'jerry1');
insert into tb_test values(7,'tom2'),(8,'cat2'),(9,'jerry2');
commit;
>主键顺序插入
主键插入要么乱序插入要么顺序插入,建议主键顺序插入,因为顺序插入性能高于乱序插入。
·大批量插入数据
如果一次性需要插入大批量数据,比如要一次性将一百万的数据加载进表结构中,使用insert语句插入性能较低,此时可以使用MySQL数据库提供的load指令进行插入。
需要做3步操作:
#客户端连接服务器端时,加上参数--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指令时我们也建议按照主键顺序插入,主键顺序插入性能高于乱序插入。
2.主键优化
·数据组织方式:
在innoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(简称IOT)
回顾之前的逻辑存储结构:
表空间(里面存储一个个段)-->段(里面存储一个个区)-->区(大小固定为1M,里面存储一个个页)-->页(大小固定为16K,里面存储一个个行)-->行(当中存放的就是具体的字段值)
页是innoDB磁盘管理的最小单元,一个页大小默认为16K,也就意味着一个区当中可以包含64个页。接下来我们看一下当我们往数据库表结构中插入数据的时候它的流程是什么样的。
主要理解好页分裂和页合并的原理是什么
·页分裂
页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据太大,会行溢出),根据主键排列 。
可知如果主键乱序插入即可能会导致页分裂现象
·页合并
当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记为删除并且它的空间变得允许被其它记录声明使用。当页中删除的记录达到 MERGE_THRESHOLD(默认为页的50%),innoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。
·主键设计原则:
>满足业务需求的情况下,尽量降低主键的长度。
我们知道二级索引是可以有很多个嘛,而二级索引叶子节点存储的就是主键,如果你主键长度太长,那么就会占用大量的磁盘空间,而且在搜索的时候会耗费大量的磁盘io
>插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键。
>尽量不要使用UUID做主键或者是其它自然主键,如身份证号。
>业务操作时,避免对主键的修改。
3.order by优化
①using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫filesort排序,性能比较低。
②using index:通过有序索引顺序扫描直接返回有序数据,这种情况称为using index,不需要额外排序,操作效率高。
所以尽量优化为using index。
为什么会出现using filesort呢?因为没有用到索引,通过创建索引再执行就可以优化为using index,出现using index就说明我们的排序效率比较高,因为它直接通过索引给我们返回了有序数据。
例:
总结:
>根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则
>尽量使用覆盖索引
>多字段排序,一个升序一个降序,此时要注意联合索引在创建时的规矩(ASC/DESC)
>如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort_buffer_size(默认256K)
4. group by 优化
分组操作的时候,我们主要研究的就是索引对分组操作的影响。
>在分组操作时,可以通过索引来提高效率
>分组操作时,索引的使用也需要满足最左前缀法则的
5.limit优化
对于limit来说,对于大数据量的情况下你进行分页查询,越往后它的效率越低耗时越长。所以在大数据量的情况下我们就需要对limit分页操作进行优化。
一个常见又非常头疼的问题是limit2000000,10,此时需要MySQL排序前2000010记录,仅仅返回2000000-2000010的记录,其它记录丢弃,查询排序的代价非常大。
优化思路:一般分页查询时,通过创建覆盖索引能够比较好的提高性能,可以通过覆盖索引加子查询形式进行优化。
6.count优化
>MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)的时候会直接返回这个数,效率很高,不过前提是查询时后面没有where条件哈;
>InnoDB引擎就麻烦了,它执行count(*)的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数,所以这个就比较耗时了。
优化思路:自己计数
·count的几种用法:
>count()是一个聚合函数,对于返回的结果集,一行一行的判断,如果count函数的参数不是NULL,累计值就加1,否则不加,最后返回累计值。
>用法:count(*)、count(主键)、count(字段)、count(1)
接下来就学习这几种用法以及它的性能
>count(主键)
InnoDB引擎会遍历整张表,把每一行的主键id值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(不用判断是不是null,因为主键不可能为null)
>count(字段)
没有 not null 约束:InnoDB会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。
有not null约束:InnoDB引擎会遍历整张表,把每一行的主键id值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加。
>count(1)
InnoDB引擎遍历整张表,但是不取值。服务器对于返回的每一行,放一个数字"1"进去,直接按行进行累加。
>count(*)
InnoDB引擎并不会把全部字段取出来,因为数据库专门做了优化,不取值,服务层直接按行进行累加
哪哪个效率高呢?
就看它取不取值,你看count(*)count(1)都不取值,count(主键)取主键值,count(字段)看情况。
所以按照效率排序的话,count(字段)<count(主键id)<count(1)=count(*),所以用count求总数时尽量使用count(*)
7.update优化(避免行锁升级为表锁)
之前我们学InnoDB的时候提到了它的三大特性:事务,外键,行级锁。也就是说在InnoDB引擎中我们当前默认的事务隔离级别是行锁。
进行update语句的时候,如果后面条件没有索引,那么行锁就会升级为表锁。
如比如某表id有索引,name没有建立索引
然后你还update student set no=‘200103080020’ where name='陈小画';就会引起行锁升级为表锁
所以进行update语句的时候,我们一定要根据索引字段进行更新,否则就会造成行锁升级为表锁,就会锁住整张表,那一旦锁表了,我们的并发性能就会降低。
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁。