插入数据
insert优化
- 批量插入: 在需要插入多条数据时,尽量使用批量插入。在数据量比较大时才考虑使用多条insert语句插入
- 手动提交事务: 在存在多条插入语句时,手动对事务进行提交,避免在执行每条插入语句时频繁提交事务
- 主键顺序插入: 在插入的数据包含主键时,尽量按主键顺序进行插入。
- 大批量插入数据: 如果需要一次性插入大批量数据,可以使用load指令从文件中将数据导入某张表,操作如下:
# 客户端连接数据库时加上--local-infile参数
mysql --local-infile -u root -p
# 查看local_infile是否开启
select @@local_infile;
# 设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
set global local_infile = 1;
# 加载数据
load data local infile "文件路径" into table `表名` fields terminated by '字段分隔符' lines terminated by '行分割符';
主键优化
数据组织方式
在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(index organized table IOT)。
页分裂
页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据多大,会行溢出),根据主键排列。如果批量插入时主键是乱序的,就会经常触发页分裂。
页合并
当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged)为删除并且它的空间变得允许被其他记录声明使用。当页中删除的记录达到MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。
合并页的阈值,可以自己设置,在创建表或者创建索引时指定。
主键设计原则
- 满足业务需求的情况下,尽量降低主键的长度。
- 插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键。
- 尽量不要使用UUID做主键或者是其他自然主键,如身份证号,避免在批量插入时的主键乱序插入。
- 业务操作时,避免对主键的修改。
order by优化
排序分类
- Using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSort排序
- Using index:通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高。
这两种排序的主要区别就是在得到数据之后,是否需要在排序缓冲区进行排序操作。由于索引是按一定的顺序进行排列的(升序或降序),所以如果索引的排列结构符合排序要求,那么就直接可以返回而不需要将索引的数据读到排序缓冲区再进行排序
优化建议
- 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
- 尽量使用覆盖索引。
- 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)。创建索引时可按照以下语句定义排序规则:
CREATE INDEX 索引名 on 表名(字段名 [ASC | DESC], 字段名 [ASC | DESC]); - 如果不可避免的出现filesort,大数据量排序时,超出缓冲区大小的数据会在磁盘进行排序。所以为了避免磁盘io,可以适当增大排序缓冲区大小sort_buffer_size(默认256k)。
group by优化
- 在分组操作时,可以通过索引来提高效率
- 分组操作时,索引的使用也是满足最左前缀法则的
limit优化
对于limit 2000000,10 这种分页查询,MySQL 需排序前 2000010 条记录,仅返回第 2000000 - 2000010 条记录,丢弃其他记录,查询排序代价极大。
优化思路
一般分页查询时,可通过创建覆盖索引,采用覆盖索引加子查询形式优化。 使用
explain select * from tb_sku t, (select id from tb_sku order by id limit 2000000,10) a where t.id = a.id;
而非 select * from tb_sku limit 2000000, 10;
count优化
- MyISAM引擎 MyISAM引擎将表的总行数存储在磁盘上,执行
count(*)时可直接返回该数值,效率高。前提是没有where条件 - InnoDB引擎 InnoDB引擎执行
count(*)时,需逐行读取数据并累积计数,效率较低。
优化思路
自行计数来优化count(*)操作。使用一些比如内存型的kv数据库,在增删操作时动态记录count数量
count操作原理
- count (主键): 遍历整表取每行主键 id 值给服务层,服务层按行累加(主键非 null )。
- count (字段):
- 无 not null 约束时,遍历整表取字段值给服务层,判断非 null 后计数累加;
- 有 not null 约束时,遍历取字段值直接按行累加。
- count(1): 遍历整表但不取字段值,服务层对每行放 “1” 按行累加。
- count(*): 引擎做优化,不取字段值,服务层直接按行累加。
效率排序
在count计数时,取字段值会比不取字段值性能低,判断null比不判断null性能低,所以这几种计数方式的性能如下: count (字段) < count (主键 id) < count (1) ≈ count (*) ,推荐尽量使用 count (*) 。
update优化
InnoDB的行锁是基于索引加锁,并非针对记录。若索引失效或update的条件没有索引,行锁会升级为表锁,就会阻塞其他行的写操作,影响并发性能。