大多状况下咱们都知道加索引能提升查询效率,可是应该如何加索引呢?索引的顺序如何呢?mysql
你们看一下下面的sql语句(在没有看下面的优化的方法以前)应该如何优化加索引以及优化sql语句:算法
一、select count(*) from task where status=2 and operator_id=20839 and operate_time>1371169729 and operate_time<1371174603 and type=2;sql
二、一、select count(*) from task where from_unixtime(create_time) = ’2014-05-29’;数据库
三、update test set name='zhangsan' where sex=1 and age>20;(注意这种状况加索引与不加索引的区别是什么)数据结构
四、select distinct l.emp_id from log l inner join (select l2.id as emp_id,l2.id as cert_id from log2 l2 left join log3 l3 on l2.id = l3.emp_id where l2.is_deleted=0 ) l4 on ( l.ref_table='Employee' and l.ref_oid= l4.emp_id函数
) or (l.ref_table='EmpCertificate' and l.ref_oid= l2.cert_id) where l.last_upd_date >='2013-11-07 15:03:00' and l.last_upd_date<='2013-11-08 16:00:00'性能
下面说一下MySql索引原理大数据
索引的目的在于提升查询效率,能够类比字典,若是要查“mysql”这个单词,咱们确定须要定位到m字母,而后从下往下找到y字母,再找到剩下的sql。若是没有索引,那么你可能须要把全部单词看一遍才能找到你想要的,若是我想找到m开头的单词呢?或者ze开头的单词呢?是否是以为若是没有索引,这个事情根本没法完成?优化
除了词典,生活中随处可见索引的例子,如火车站的车次表、图书的目录等。它们的原理都是同样的,经过不断的缩小想要得到数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是咱们老是经过同一种查找方式来锁定数据。spa
数据库也是同样,但显然要复杂许多,由于不只面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对全部的问题呢?咱们回想字典的例子,能不能把数据分红段,而后分段查询呢?最简单的若是1000条数据,1到100分红第一段,101到200分红第二段,201到300分红第三段……这样查第250条数据,只要找第三段就能够了,一会儿去除了90%的无效数据。但若是是1千万的记录呢,分红几段比较好?稍有算法基础的同窗会想到搜索树,其平均复杂度是lgN,具备不错的查询性能。但这里咱们忽略了一个关键的问题,复杂度模型是基于每次相同的操做成原本考虑的,数据库实现比较复杂,数据保存在磁盘上,而为了提升性能,每次又能够把部分数据读入内存来计算,由于咱们知道访问磁盘的成本大概是访问内存的十万倍左右,因此简单的搜索树难以知足复杂的应用场景。
前面讲了生活中索引的例子,索引的基本原理,数据库的复杂性,又讲了操做系统的相关知识,目的就是让你们了解,任何一种数据结构都不是凭空产生的,必定会有它的背景和使用场景,咱们如今总结一下,咱们须要这种数据结构可以作些什么,其实很简单,那就是:每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么咱们就想到若是一个高度可控的多路搜索树是否能知足需求呢?就这样,b+树应运而生。
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)的索引,那么只须要修改原来的索引便可
回到上面的给出的mysql
一、根据最左匹配原则,第一条sql语句的索引应该是status、operator_id、type、operate_time的联合索引;其中status、operator_id、type的顺序能够颠倒,因此我才会说,把这个表的全部相关查询都找到,会综合分析;
二、根据索引列不能参与计算,保持列“干净”,第二条sql语句应该写成create_time = unix_timestamp(’2014-05-29’);
三、update 语句不加索引是锁表,加索引是锁行 where后面的条件根据第一条同样优化便可。
四、第4条sql语句就复杂了在没有作如何优化以前结果是:53
rows
in
set
(
1.87
sec)
经过explain
简述一下执行计划,首先mysql根据idx_last_upd_date索引扫描log表得到379条记录;而后查表扫描了63727条记录,分为两部分,derived表示构造表,也就是不存在的表,能够简单理解成是一个语句造成的结果集,后面的数字表示语句的ID。derived2表示的是ID = 2的查询构造了虚拟表,而且返回了63727条记录。咱们再来看看ID = 2的语句究竟作了写什么返回了这么大量的数据,首先全表扫描log2表13317条记录,而后根据索引emp_certificate_empid关联log3表,rows = 1表示,每一个关联都只锁定了一条记录,效率比较高。得到后,再和log的379条记录根据规则关联。从执行过程上能够看出返回了太多的数据,返回的数据绝大部分log都用不到,由于log只锁定了379条记录。
如何优化呢?能够看到咱们在运行完后仍是要和log作join,那么咱们能不能以前和log作join呢?仔细分析语句不难发现,其基本思想是若是log的ref_table是EmpCertificate就关联log3表,若是ref_table是Employee就关联log2表,咱们彻底能够拆成两部分,并用union链接起来,注意这里用union,而不用union all是由于原语句有“distinct”来获得惟一的记录,而union刚好具有了这种功能。若是原语句中没有distinct不须要去重,咱们就能够直接使用union all了,由于使用union须要去重的动做,会影响SQL性能。
优化过的语句以下
select
l2.id
from
log l
inner join
log2 l2
on l.ref_table = 'Employee'
and l.ref_oid = emp.id
where
l.last_upd_date >='2013-11-07 15:03:00'
and l.last_upd_date<='2013-11-08 16:00:00'
and l2.is_deleted = 0
union
select
l2.id
from
log l
inner join
log3 l3
on l.ref_table = 'EmpCertificate'
and l.ref_oid = l3.id
inner join
log2 l2
on l2.id = l3.emp_id
where
l.last_upd_date >='2013-11-07 15:03:00'
and l.last_upd_date<='2013-11-08 16:00:00'
and l2.is_deleted = 0