MySql优化

161 阅读4分钟

分页

走的聚集索引直接全表查询900005条数据,然后取出最后5条数据,还会文件排序,效率低

会走二级索引(这里会索引覆盖),先查出来5个id排好序,再去聚集索引里查询,效率高

mysql的表关联常见有两种算法

1、 嵌套循环连接 Nested-Loop Join(NLJ) 算法

下图中t1和t2表结构一样,t2是100条数据,t1是10000条数据,a字段都有索引,那么他执行计划会先去t2表中查询全部的数据这个过程不会走索引,但是查出来的数据会去t1表继续查询,那这个时候t1表会走a的这个索引,最终查询的结果是t2表会查询100次,t1表也会查询100次,总共200次

   mysql> EXPLAIN select * from t1 inner join t2 on t1.a= t2.a;

2、 基于块的嵌套循环连接 Block Nested-Loop Join(BNL)算法

1.下图中t1和t2表结构一样,t2是100条数据t1是10000条数据,b字段都没有索引,那么他执行计划会先去t2表中查询全部的数据,把这些数据放入到join_buffe(内存),加载到内存中的数据是无序的,这个过程不会走索引,然后再去t1表全表查询,这个过程也不会走索引,每查询1条数据就会加入到join_buffe中跟t2表100条数据进行比对,那么整个过程就是t2表查询100次,t1表查询10000次,他们在内存中对比的次数就是10000X100次

2.这个例子里表 t2 才 100 行,要是表 t2 是一个大表,join_buffer 放不下怎么办呢?· join_buffer 的大小是由参数 join_buffer_size 设定的,默认值是 256k。如果放不下表 t2 的所有数据话,策略很简单,就是分段放。 比如 t2 表有1000行记录, join_buffer 一次只能放800行数据,那么执行过程就是先往 join_buffer 里放800行记录,然后从 t1 表里取数据跟 join_buffer 中数据对比得到部分结果,然后清空 join_buffer ,再放入 t2 表剩余200行记录,再次从 t1 表里取数据跟 join_buffer 中数据对比。所以就多扫了一次 t1 表

**3.被驱动表的关联字段没索引为什么要选择使用 BNL 算法而不使用 Nested-Loop Join 呢? 如果上面第二条sql使用 Nested-Loop Join,那么扫描行数为 100 * 10000 = 100万次,这个是磁盘扫描。 很显然,用BNL磁盘扫描次数少很多,相比于磁盘扫描,BNL的内存计算会快得多。 因此MySQL对于被驱动表的关联字段没索引的关联查询,一般都会使用 BNL 算法。如果有索引一般选择 NLJ 算法,有索引的情况下 NLJ 算法比 BNL算法性能更高 **

4.优化:对于关联sql的优化 关联字段加索引,让mysql做join操作时尽量选择NLJ算法,驱动表因为需要全部查询出来,所以过滤的条件也尽量要走索引,避免全表扫描,被驱动表一般数据量比驱动表大,也尽量走索引,总之,能走索引的过滤条件尽量都走索引 小表驱动大表,写多表连接sql时如果明确知道哪张表是小表可以用straight_join写法固定连接驱动方式,省去mysql优化器自己判断的时间

mysql>EXPLAIN select * from t1 inner join t2 on t1.b= t2.b;

count(*)查询优化

-- 临时关闭mysql查询缓存,为了查看sql多次执行的真实时间
mysql> set global query_cache_size=0;
mysql> set global query_cache_type=0;

mysql> EXPLAIN select count(1) from employees;
mysql> EXPLAIN select count(id) from employees;
mysql> EXPLAIN select count(name) from employees;
mysql> EXPLAIN select count(*) from employees;

注意:以上4条sql只有根据某个字段count不会统计字段为null值的数据行

四个sql的执行计划一样,说明这四个sql执行效率应该差不多

1.字段有索引:count(*)≈count(1)>count(字段)>count(主键 id) //字段有索引,count(字段)统计走二级索引,二级索引存储数据比主键索引少,所以count(字段)>count(主键 id)

2.字段无索引:count(*)≈count(1)>count(主键 id)>count(字段) //字段没有索引count(字段)统计走不了索引,count(主键 id)还可以走主键索引,所以count(主键 id)>count(字段)

3.count(1)跟count(字段)执行过程类似,不过count(1)不需要取出字段统计,就用常量1做统计,count(字段)还需要取出字段,所以理论上count(1)比count(字段)会快一点

4.count()是例外,mysql并不会把全部字段取出来,而是专门做了优化,不取值,按行累加,效率很高,所以不需要用count(列名)或count(常量)来替代 count()。 为什么对于count(id),mysql最终选择辅助索引而不是主键聚集索引?因为二级索引相对主键索引存储数据更少,检索性能应该更高,mysql内部做了点优化(应该是在5.7版本才优化)