从一个开发的角度切入mysql索引,查询优化
前言:我曾经看到过一个多年的开发,sql写的稀烂,他在一个百万量级的表上写sql查询,由于要连多张表,他在其中子查询套子查询,让我大受震撼,结果到页面就是体验极差,查询时间已经到了半个小时往上,他表示已经没法优化了(摊手
———-来自一个小萌新的震撼
(后续过手优化,将查询时间优化到十分钟左右........)
文章结构(如果有什么说的不太好的,欢迎指出):
- 基本查询的过程以及是否需要做查询优化
- 查询优化怎么入手(索引方面)
- 基本概念:(回表,索引(弊端),联合索引,索引下推—本质是避免回表)
- 问题1:面对多大的数据量需要考虑索引,或者是索引优化
- 问题2:联合索引可以避开回表,那么索引下推的意义在哪里(或者说:联合索引是为了避开回表,索引下推也是避开回表,两者是否有同时存在的必要?)
基本查询的过程以及是否需要做查询优化
mysql中的一个sql语句的执行需要经过两层,一个serve层,一个引擎层,serve层做的是词义解析,优化sql执行,调用存储引擎层执行数据的存储和检索(图,偷的,侵删
基本的sql执行过程(不考虑覆盖联合索引的情况):
首先建立连接,如果使用长链接的时候,可能要注意一个问题(mysql在执行过程中临时使用的内存是管理在链接对象里面的,这些资源会在链接断开的时候做释放,所以长连接累计下来可能会导致内存占用过大,被系统强行kill,当然可以在长连接的链接过程中手动清理),链接建立以后,在执行sql的过程中,首先会查询sql在缓存中是否命中,如果命中则直接返回,查询结束,如果没有命中,则做词法分析,分析过后过优化器(选择索引...等),最后通过执行器操作下一层的存储引擎来返回结果,一般来说,索引块中不会有所需要的所有字段,所以就会有一个通过索引拿到的指针列表,由于此时调用存储引擎拿到的数据不全,所以需要再次准确的访问表中的数据(也就是再一次调用存储引擎进行查询,对查到的结果在serve层做进一步的筛选)这也就是回表的概念,当所有的数据筛选结束,则返回结果。
总结下:在普通查询的过程中,考虑索引块中不包含所有的需要字段,那么必然会出现(一次访问存储引擎的索引不足以得到sql查询的所有数据所进行的二次查询)回表的操作,那么必然在第一次调用存储引擎的时候会返回一个指向记录的指针列表到serve层,这个时候由于是指针那么会再次调用存储引擎来获取指针列表对应的记录,这样的话,那么其实可以认为回表其实就是再一次调用存储引擎做查询。
查询优化怎么入手(索引方面)
基本概念:(回表,索引(弊端),联合索引,索引下推—本质是避免回表)
回表:经过上面的查询过程应该会有一个比较清晰的认识,在我看来应该可以认为是一次访问存储引擎的索引不足以得到sql查询的所有数据所进行的二次查询
索引:在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标志这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的SQL语句执行得更快,可快速访问数据库表中的特定信息(百度百科)。其实索引分为三种,普通索引(数据可重复),唯一索引(数据不可重复),主键索引(特殊的唯一索引,不允许NULL值,是表的物理存储顺序依据),如果要优化,当新建索引的时候你需要考虑一个问题不是,如果要新建索引,你的数据量必然很大,这个时候你就需要考虑索引创建所产生的物理内存,假设表有 1000万 行记录。InnoDB二级索引条目 ≈ 索引列值大小 + 主键大小 + 额外开销(约 10-20字节) ≈ 20 + 4 + 16 ≈ 40字节。该索引总大小 ≈ 10,000,000 * 40 bytes ≈ 400,000,000 bytes ≈ 400 MB。所以如果需要创建索引,应该尽量避开值过大的字段(做索引的列会在索引中存一份数据),同时也应该考虑该列是否有必要做索引。当然引入索引以后效率提升是几十倍甚至更高。
联合索引:在多个列上创建的单个索引(例如 INDEX (col1, col2, col3))。数据按索引定义的列顺序排序存储(先按 col1 排序,col1 相同再按 col2 排序,以此类推),当使用覆盖联合索引的查询时,是只需要访问一次存储引擎的。因为所有的需要的字段都可以在索引中找到所以只需要一次访问即可,这样的效率比其他的是会快超级多的。
索引下推:其实就是指将部分上层(服务层)负责的事情,交给了下层(引擎层)去处理,这个我会在问题2进行详细的解释。
问题1:面对多大的数据量需要考虑索引,或者是索引优化
其实对于十万以下的数据量其实不太需要考虑索引优化,本身的速度基本可以满足要求,如果十万以上就可以考虑索引优化了。
问题2:联合索引可以避开回表,那么索引下推的意义在哪里(或者说:联合索引是为了避开回表,索引下推也是避开回表,两者是否有同时存在的必要?)
其实在查询过程中所有逻辑都讲的差不多了,这边深入下索引下推和联合索引的查询过程,这个问题就不再是问题了。
联合索引(所有需要的字段都可以在索引中找到):概念解释里面有写不赘述
索引下推(在sql语句中存在不在索引中的列):首先应该知道索引下推是默认开启的,可以显式关闭,但是不建议。当一个sql中有字段在索引的数据中找不到的时候
这个时候如果不开启索引下推的执行流程是这样的:
如果我这个时候有一个类似这样的sql(select a,b,c from user where a=1 and b=2)这里的a和b是联合索引,但是里面有一个c字段是在索引数据中找不到的,首先语句做词法分析过优化器然后到执行器调用存储引擎,这个时候在存储引擎中,会对优化器决定的索引(假如是a)进行搜索,但是这里会有一个问题,如果我是联合索引按道理来说条件中的两个字段都会在索引数据中找到,但是如果不开启索引下推,这里是会无脑返回根据a数据找到的列表回到serve层再做筛选,找到符合要求的数据形成一个指针列表,返回serve层,然后serve层再次调用存储引擎拿到对应的数据行,然后在serve层中根据b条件在对数据进行进一步筛选。
但是如果开启了索引下推,那么这个时候的执行流程是这样的:
如果我这个时候有一个类似这样的sql(select a,b,c from user where a=1 and b=2)这里的a和b是联合索引,但是里面有一个c字段是在索引数据中找不到的,首先语句做词法分析过优化器然后到执行器调用存储引擎,这个时候在存储引擎中,会对优化器决定的索引(假如是a)进行搜索,好 ,不一样的地方来了,这个时候由于是联合索引条件中的两个字段都会在索引数据中找到,由于开启了索引下推,这个时候会将b也在当前的存储引擎查询中进行过滤,这样的话由于多过滤了一个筛选条件,返回的指针列表数据在二次查询的时候查询数据量会大大减少(比如说,一级索引国家,二级索引省),找到符合要求的数据形成一个指针列表,返回serve层,然后serve层再次调用存储引擎拿到对应的数据行,然后在serve层中只需要将需要处理的c字段取出返回即可。
索引下推对联合索引的影响:
如果没有开启索引下推,举个例子,如果我这个时候有一个类似这样的sql(select a,b from user where a=1 and b=2)这里的a和b是联合索引,由于是覆盖联合索引查询,且没有开启索引下推,所以当语句执行到存储引擎层的时候会根据优化器决定的索引来查询数据返回到serve层中,由于是联合索引所以需要的数据都可以在索引中查到,所以serve层不会再次调用存储引擎层,b的筛选会在serve层进行,然后返回结果。
如果开启索引下推,举个例子,如果我这个时候有一个类似这样的sql(select a,b from user where a=1 and b=2)这里的a和b是联合索引,由于是覆盖联合索引查询,且开启索引下推,所以当语句执行到存储引擎层的时候,由于是联合索引所以需要的数据都可以在索引中查到,且开启了索引下推,这个时候两个筛选都会在第一次调用存储引擎中进行,然后就可以一次调用,直接返回结果,主要区别就是b的筛选的位置,但是都不会二次调用存储引擎,所以不存在回表的情况。
吐槽下,csdn的在线编辑器怎么这么难用。。。。。。