7.1说说为什么阿里数据库军规为什么要求员工尽量少使用多表的关联查询?
【Index Nested-Loop Join】
select * from t1 straight_join t2 on (t1.a=t2.a);
如果使用join语句,Mysql优化器可能会选择表t1或者表t2作为驱动表,这取决于Mysql的优化器选择;使用straight_join语句,可以使用t1表作为驱动表,t2作为被驱动表。
两张表都有主键id和索引a,字段b上无索引。t2表插入了1000行数据,t1表插入了100行数据。
执行流程:
(1)从t1中取出一行数据记录R;
(2)从数据记录R中,取出a字段到表t2里去查找;
(3)取出表t2满足条件的行,跟R组成一行,作为结果集的一部分;
(4)重复执行步骤1到3,直到表t1的末尾循环结束。
分析过程:
(1)对驱动表t1做了全表扫描,这个过程需要扫描100行;
(2)而对于每一行R,根据a字段去表t2查找,去表t2中搜索走的是树的搜索过程,所以每次搜索过程也只需要扫描一行,总共扫描100行;
(3)也就是说整个执行流程,只扫描了200行数据记录。
如果不使用join,走单表查询。
执行流程:
(1)执行select from t1,取出表t1的所有数据,这里有100行;
(2)循环遍历这100行数据;
select from t2 where a=$R.a;
把返回的结果和R构成结果集的一行。
可以看到,在这个过程中,是不如直接join的,客户端需要自己拼接sql语句和结果。
【怎么选择驱动表】?
在上述例子中,驱动表走全表扫描,而被驱动表走树搜索。
假设被驱动表的行数是 M。每次在被驱动表查一行数据,要先搜索索引 a,再搜索主键索引。每次搜索一棵树近似复杂度是以 2 为底的 M 的对数,记为 log2M,所以在被驱动表上查一行的时间复杂度是 2log2M。假设驱动表的行数是 N,执行过程就要扫描驱动表 N 行,然后对于每一行,到被驱动表上匹配一次。因此整个执行过程,近似复杂度是 N + N2log2M。(2是因为需要回表查询取出数据记录加入结果集)
结论:应该使用小表作为驱动表,使用join语句性能比执行单表sql语句要好,但前提是被驱动表能够走索引。
【Simple Nested-Loop Join】
如果t2被驱动表上where查询条件没有使用到索引,那被驱动表也是走的全表扫描的话,那总共扫描1001000=10W行数据。那效率就太低了。
【Block Nested-Loop Join】
针对Simple Nested-Loop Join算法的优化,
执行流程:
(1)把表t1的数据读入线程内存join_buffer中,由于语句是select ,所以是把整个t1表都放入了内存中;
(2)扫描表t2,把表t2中的每一行取出来,与join_buffer中的数据进行对比,满足join条件的,作为结果集的一部分。
分析:
这个过程表t1和t2表都做了一次全表扫描,因此扫描行数是100+1000=1100;但是在内存中执行判断的逻辑是100*1000万次;这是因此join_buffer是以无序数组的方式组织的;因此判断的逻辑是内存操作,速度会很快,性能也会更好。
7.2说说你对explain语句的了解
使用explain关键字,explain语句将产生附加信息。执行该语句,可以分析explain后面select的语句的执行情况,并且能够分析出所查询表的一些特性。
type:本次查询表联接类型,从这里可以看到本次查询的大概效率。
possiblekey:显示本次查询中,可能使用到的数据库表得索引。
key:本次查询,最终选择的索引,也有可能为Null。
ken_len:本次查询使用到的索引实际长度。
rows:预计需要扫描的记录数,越少越好。
Extra:额外信息,主要是确认是否出现Using filesort和Using temporary。
【type】:
const:针对主键或者唯一索引的扫描,结果仅仅往回一行数据记录;
ref:使用到了普通索引或者是组合索引;
range:在索引范围查询,且使用到了<、>、<=、>=、between等语句;
index:表示扫描整个索引文件,在索引树上可以找到所需要的数据;
all:全表扫描,扫描主键叶子节点的所有数据记录。
【Extra】:
Using index:表示使用了覆盖索引,即需要查询的字段在索引B+树的叶子节点上已经存在,不需要回表查询;
Using filesort:表示一些额外的排序工作,不能通过索引完成,需要占用cpu资源去完成;
Using temporary:表示使用了临时表,多出现在关联查询的情况;
【ken_len】:
一般使用到了组合索引的时候,可以使用explain语句来判断是否使用了整个组合索引,还是使用了索引下推。
比如这条查询语句,使用到了(name,age,address)的组合索引。
mysql的varchar字段与char字段的区别,考虑可以为Null的情况。varchar(30)与char(30)的区别:varchar占用的字节数是303+2+1,char字段占用的字节数是303+1。varchar中的两字节标识一个动态列类型,1字节标识允许null值
ken_len长度的计算方式:1个varchar字符占3字节,2字节标识动态列,1字节允许为null值,int类型占用4字节,另外1字节标识允许为null,所以计算结果是:
name(303+2+1)+age(4+1)+address(303+2+1)=191字节;
如果把name和address的varchar类型改成char类型,那ken_len计算方式就是:
name(303+1)+age(4+1)+address(303+1)=187字节;
如果查询条件改成:
explain select _from user where name=‘alice’ and age>18;
那么ken_len的计算方式就是303+1+5=96;使用到了index(name,age);