一个慢查询引发的思考 selectcount(*)fromtaskwherestatus=2 and operator_id=20839 and operate_time>1371169729 and operate_time<1371174603 and type=2; 系统使用者反应有一个功能越来越慢,于是工程师找到了上面的SQL。 并且兴致冲冲的找到了我,“这个SQL需要优化,给我把每个字段都加上索引” 我很惊讶,问道“为什么需要每个字段都加上索引?” “把查询的字段都加上索引会更快”工程师信心满满 “这种情况完全可以建一个联合索引,因为是最左前缀匹配,所以operate_time需要放到最后,而且还需要把其他相关的查询都拿来,需要做一个综合评估。” “联合索引?最左前缀匹配?综合评估?”工程师不禁陷入了沉思。 多数情况下,我们知道索引能够提高查询效率,但应该如何建立索引?索引的顺序如何?许多人却只知道大概。其实理解这些概念并不难,而且索引的原理远没有想象的那么复杂。
参数介绍 id 如果是子查询,id的序号会递增,id的值越大优先级越高,越先被执行 select_type 查询的类型,主要用于区别普通查询、联合查询、子查询等的复杂查询 SIMPLE:简单的select查询,查询中不包含子查询或者UNION PRIMARY:查询中若包含任何复杂的子部分,最外层查询则被标记为PRIMARY(最后加载的那一个 ) SUBQUERY:在SELECT或WHERE列表中包含了子查询 DERIVED:在FROM列表中包含的子查询被标记为DERIVED(衍生)Mysql会递归执行这些子查询,把结果放在临时表里。 UNION:若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM字句的查询中,外层SELECT将被标记为:DERIVED UNION RESULT:从UNION表获取结果的SELECT type 显示查询使用了何种类型 从最好到最差依次是System>const>eq_ref>range>index>All(**全表扫描**) 一般来说**至少达到range级别,最好达到ref**System:表只有一行记录,这是const类型的特例,平时不会出现(忽略不计)const:表示通过索引一次就找到了,const用于比较primary key或者unique索引,因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL就能将该查询转换为一个常量。eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描。ref:非唯一索引扫描,返回匹配某个单独值的行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而它可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引,一般就是在你的where语句中出现了between、<、>、in等的查询。这种范围扫描索引比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引。index:FULL INDEX SCAN,index与all区别为index类型只遍历索引树。这通常比all快,因为索引文件通常比数据文件小。 extra 包含不适合在其他列中显示但十分重要的额外信息 包含的信息: **(危险!)**Using filesort:说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取,MYSQL中无法利用索引完成的排序操作称为“文件排序” **(特别危险!)**Using temporary:使用了临时表保存中间结果,MYSQL在对查询结果排序时使用临时表。常见于排序order by 和分组查询 group by Using index:表示相应的select操作中使用了覆盖索引,避免访问了表的数据行,效率不错。如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找操作。
建索引的几大原则 1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。 2.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式 3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录 4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’); 5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可
select * from task where status = 0 and type = 12 limit 10; select count(*) from task where status = 0 ; 那么索引建立成(status,type,operator_id,operate_time)就是非常正确的,因为可以覆盖到所有情况。这个就是利用了索引的最左匹配的原则