对于小表定义的明确

在两个表按照各自的条件过滤后,计算join的各个字段的总数据量,数据量小的那个表,就是“小表”,也就是驱动表。

关联sql的优化

  • 关联字段加索引, t1 inner join t2 on t1.a= t2.a ,在t1和t2的a字段上都做索引。这样mysql做join操作时会选择NLJ算法。驱动表因为需要全部查询出来,所以过滤的条件也尽量要走索引,避免全表扫描,总之,能走索引的过滤条件尽量都走索引。
  • 小表驱动大表,写多表连接sql时如果明确知道哪张表是小表可以用straight_join写法固定连接驱动方式,省去mysql优化器自己判断的时间。
    • straight_join能改变连表查询的执行顺序,能指定让左边的表来驱动右边的表。
    • select * from t2 straight_join t1 on t2.a = t1.a; 代表指定mysql选着 t2 表作为驱动表
    • straight_join只适用于inner join,不能用在left join和right join。一般不建议使用,让优化器去判断

in和exsits优化原则,小表驱动大表

in:当A表的数据集大于B表的数据集时,in优于exists

  1. select * from A where id in (select id from B)
  2. /*
  3. 从B表读取一条记录,然后去a表匹配
  4. */
  5. for(select id from B){
  6. select * from A where A.id = B.id
  7. }

exists:当A表的数据集小于B表的数据集时,exists优于in

select * from A where exists (select 1 from B where B.id = A.id)
/*
从a表读取一条记录,然后去b表匹配
*/
for(select * from A){
      select * from B where B.id = A.id
}

EXISTS (subquery)只返回TRUE或FALSE,因此子查询中的SELECT * 也可以用SELECT 1替换,官方说法是实际执行时会忽略SELECT清单,因此没有区别。

count(*)查询优化

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

截屏2022-04-28 下午5.25.28.png
上面的4种写法执行计划一样,都能走索引(ken_len=140)。

字段有索引:count(*)≈count(1)>count(字段)>count(主键 id)

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

字段无索引:count(*)≈count(1)>count(主键 id)>count(字段)

字段没有索引count(字段)统计走不了索引,count(主键 id)还可以走主键索引,所以count(主键 id)>count(字段)

  • count(*) ,mysql专门做了优化,效率很高。
  • count(1),不需要取出字段统计,就用常量1做统计,所以理论上count(1)比count(字段)会快一点。
  • count(字段)走二级索引,取出字段去统计。
  • count(id),是走mysql辅助索引,因为二级索引相对主键索引存储数据更少,检索性能应该更高

常见count优化方法

查询mysql自己维护的总行数

  • myisam存储引擎的表的总行数会被mysql存储在磁盘上,查询不需要计算
  • innodb存储引擎的表mysql不会存储表的总记录行数,查询count需要实时计算

show table status

如果只需要知道表总行数的估计值(只是个估算值)可以用如下sql查询,性能很高
截屏2022-04-28 下午5.45.09.png

将总数维护到Redis里

插入或删除表数据行的时候同时维护redis里的表总行数key的计数值(用incr或decr命令),但是这种方式可能不准,很难保证表操作和redis操作的事务一致性

增加数据库计数表

建一张计数表,插入或删除表数据行的时候同时维护计数表,让他们在同一个事务里操作。