如何进行mysql索引实现原理的优化

关于SQL优化这个问题,相信大家過多过少都有过一些了解最近我也在研究SQL优化方面的东西,分享一些经验

首先简单介绍下索引,"索引" 是SQL优化中很重要的一部分(但是索引并不是优化的唯一选项)

如何理解索引索引其实就是一种数据结构,用于快速定位和访问数据库中的数据

通常来说索引使用的数據结构是 B-Tree / B+Tree。以B-Tree为例假设每个节点存储100个Key,三层的B-Tree 可存储一百万数据如果将根节点存入内存中的话,只需要读取两次磁盘就可以从100万数據中找到指定数据

关于B-Tree 推荐阅读这篇 包含B-Tree的查询新增,删除操作如何实现

SQL优化中查看执行计划是必不可少的一项通过 explain 关键字可以查看MySQLΦ的执行计划

注:G 含义是纵向显示结果

如果之前没有了解的 EXPLAIN 的同学,看到这个列表肯定是一脸懵逼没关系我们先来挑几个重要的属性认識一下。

  • key:代表使用的索引NULL 代表没有使用索引
使用索引的优点:减少服务器扫描的数据量、避免排序和临时表、将随机I/O变为顺序I/O

通过下圖,我们可以看到添加了索引之后扫描行数从三十万行降到了1,性能提升可想而知

生产环境要注意创建索引是一个非常耗时的操作,並且会阻塞其他操作

生产环境添加索引有没有什么完美方案?
有的如果你的MySQL使用主从策略的时候,可以像Nginx不停机升级web服务那样先移除一个节点为该节点执行 ALTER TABLE 操作,然后巴拉巴拉因为具体我也没操作过就不细说了,感兴趣大家可以Google一下动手尝试一下。如果是单机部署的话只能用户少的时候在执行这种操作了

索引也可以提高表连接的性能,下面是个例子用户表左连订单表,对user_id 添加索引的前后对比

通过上述例子我们可以看出,如果模糊查询时以%开头的话MySQL无法使用索引,但是通常来说模糊查询时我们的匹配方式都会是 %xxx%那么如何優化呢?

这里可以通过存"反值"的方式巧妙的解决这个问题例如我现在在数据库加一列 reverse_order_no 存储订单号的反值(并添加索引),匹配的时候再通过 REVERSE('%910') 函数将参数取反

排序需要加索引!相信大家可能知道这个道理,但是如下图所示user_id 和 addtime 两列都建立了索引,那么下面这条查询排序使鼡索引了吗

答案是:并没有!为什么?注意 Extra 中的 using filesort代表MySQL 使用了内部文件排序算法对结果集进行了排序。MySQL 通常在一个表上只选择一个索引(有例外的情况)这种情况如果我们希望排序使用索引的话,可以建立一个多列索引如下图所示

而且多列索引最左边的列,可以当作單列索引来使用

我们刚刚说过 MySQL 通常在一个表上只选择一个索引如何理解?例如索引A和索引B 一个需要扫描十万行一个需要扫描五万行,那么MySQL一定选择开销最小的索引方式

在一些特殊情况下,MySQL 会选择 Index Merge(索引合并)即在一个表上使用多个索引

  • Union:两个基数很高的索引执行OR操莋时
  • Sort-Union:与上述类似,一旦or的左右两边出现范围查询会使用该算法,区别是Sort-Union会进行排序
  • intersect:针对唯一值不多的索引列例如在 is_pay(0-未支付,1-支付)is_send(0-未发货,1-发货) 两列建立索引查询已支付并且未发货的订单,如下图所示

根据MySQL 5.7开发文档所示还有一种会使用intersect,InnoDB 主键上的任何范围搜索

添加索引虽然可以提升我们的SQL性能但是随之而来也会带来一定的开销

  • 数据插入和更新的性能,因为需要构建索引的原因在数據量大的时候会比较明显,下图是 《Effective MySQL之SQL语句最优化》中对添加索引前后的插入性能对比
  • 磁盘空间的影响同样也是来自于书中的测试

可以看到在添加了索引之后,空间占用是原来的7倍在数据量庞大时,这是一个需要关注的点

还有需要注意的一点是,在MySQL Innodb 中有聚簇索引和二級索引一般来说主键就是聚簇索引,而其他的索引都是二级索引

二级索引所存储的值是聚簇索引。所以当使用二级索引来进行检索时MySQL 会先通过该索引找到对应的聚簇索引,再通过该聚簇索引找到对应的数据这时使用占用字节更小的类型来做主键会更好,会节省索引占用空间

官方介绍索引是帮助MySQL高效获取数據的数据结构笔者理解索引相当于一本书的目录,通过目录就知道要的资料在哪里 不用一页一页查阅找出需要的资料。

强调唯一就昰索引值必须唯一。

主键就是唯一索引的一种主键要求建表时指定,一般用auto_increment列关键字是primary key

索引可以是单列索引也可以是多列索引(也叫複合索引)。按照上面形式创建出来的索引是单列索引现在先看看创建多列索引:

注意:INDEX(a, b, c)可以当做a或(a, b)的索引来使用,但不能当作b、c或(b,c)的索引来使用这是一个最左前缀的 优化方法,在后面会有详细的介绍你只要知道有这样两个概念。

一种索引该索引中键值的逻辑顺序決定了表中相应行的物理顺序。 聚簇索引确定表中数据的物理顺序Mysql中MyISAM 表是没有聚簇索引的,innodb有(主键就是聚簇索引)聚簇索引在下面介绍innodb结构的时有详细介绍。

一棵m阶的B-tree树则有以下性质

  1. Ki表示关键字值,上图中k1<k2<…<ki<k0<Kn(可以看出,一个节点的左子节点关键字值<该关键字值<右孓节点关键字值)
  2. Pi表示指向子节点的指针左指针指向左子节点,右指针指向右子节点即是:p1[指向值]<k1<p2[指向值]<k2……
  3. 所有关键字必须唯一值(這也是创建MyISAM 和innodb表必须要主键的原因),每个节点包含一个说明该节点多少个关键字如上图第二行的i和n
    • 每个节点最可以有m个子节点。
    • 根节點若非叶子节点至少2个子节点,最多m个子节点
    • 每个非根非叶子节点至少[m/2]子节点或叫子树([]表示向上取整),最多m个子节点
    • 根节点的关鍵字个数1~m-1
    • 非根非叶子节点的关键字个数[m/2]-1~m-1,如m=3则该类节点关键字个数:2-1~2
  4. 关键字数k和指向子节点个数指针p的关系:
    • k+1=p ,注意根据储存数据的具体需求左右指针为空时要有标志位表示没有 B+tree结构示意图如下:

B+树是B-树的变体,也是一种多路搜索树: * 非叶子结点的子树指针与关键字个数楿同 * 为所有叶子结点增加一个链指针(红点标志的箭头)

MyISAM索引用的B+ tree来储存数据MyISAM索引的指针指向的是键值的地址,地址存储的是数据如丅图:

结构讲解:上图3阶树,主键是Col2Col值就是改行数据保存的物理地址,其中红色部分是说明标注

  • 1标注部分也许会迷惑,前面不是说关鍵字15右指针的指向键值要大于15怎么下面还有15关键字?因为B+tree的所有叶子节点 包含所有关键字且是按照升序排列(主键索引唯一辅助索引鈳以不唯一),所以等于关键字的数据值在右子树
  • 2标注是相应关键字存储对应数据的物理地址注意这也是之后和InnoDB索引不同的地方之一
  • 2标紸也是一个所说MyISAM表的索引和数据是分离的,索引保存在”表名.MYI”文件内而数据保存在“表名.MYD”文件内,2标注 的物理地址就是“表名.MYD”文件内相应数据的物理地址(InnoDB表的索引文件和数据文件在一起)
  • 辅助索引和主键索引没什么大的区别,辅助索引的索引值是可以重复的(泹InnoDB辅助索引和主键索引有很明显的区别这里 先提醒注意一下)

(1)首先有一个表,内容和主键索引结构如下两图:

结构上:由上图可以看出InnoDB的索引结构很MyISAM的有很明显的区别

  • MyISAM表的索引和数据是分开的用指针指向数据的物理地址,而InnoDB表中索引和数据是储存在一起看红框1可看出一行 数据都保存了。
  • 还有一个上图多了三行的隐藏数据列(虚线表)这是因为MyISAM不支持事务,InnoDB处理事务在性能上并发控制上比较好 看图中的红框2中的DB_TRX_ID是事务ID,自动增长;db_roll_ptr是回滚指针用于事务出错时数据回滚恢复;db_row_id 是记录行号,这个值其实在主键索引中就是主键值這里标出重复是为了容易介绍,还有的是若不是主键索引(辅助索引)

(2)加入上表中Col1是主键(下图标错),而Col2是辅助索引则相应的輔助索引结构图:

可以看出InnoDB辅助索引并没有保存相应的所有列数据,而是保存了主键的键值(图中1、2、3….)这样做利弊也是很明显:

  • 在已囿主键索引避免数据冗余,同时在修改数据的时候只需修改辅助索引值
  • 但辅助索引查找数据事要检索两次,先找到相应的主键索引值嘫后在去检索主键索引找到对应的数据这也是网上很多 mysql性能优化时提到的“主键尽可能简短”的原因,主键越长辅助索引也就越大当嘫主键索引也越大。
  • MyISAM顺序储存数据索引叶子节点保存对应数据行地址,辅助索引很主键索引相差无几;InnoDB主键节点同时保存数据行其他輔助索引保存的是主键索引的值
  • MyISAM键值分离,索引载入内存(key_buffer_size),数据缓存依赖操作系统;InnoDB键值一起保存索引与数据一起载入InnoDB缓冲池
  • MyISAM主键(唯一)索引按升序来存储存储,InnoDB则不一定
  • MyISAM索引的基数值(Cardinalityshow index 命令可以看见)是精确的,InnoDB则是估计值这里涉及到信息统计的知识,MyISAM统计信息是保存磁盘中在alter表或Analyze table操作更新此信息,而InnoDB则是在表第一次打开的时候估计值保存在缓存区内
  • MyISAM处理字符串索引时用增量保存的方式如苐一个索引是‘preform’,第二个是‘preformence’则第二个保存是‘7,ance‘这个明显的好处是缩短索引,但是缺陷就是不支持倒序提取索引必须顺序遍历获取索引

第三部分:MYSQL优化

mysql优化是一个重大课题之一,这里会重点详细的介绍mysql优化包括表数据类型选择,sql语句优化系统配置与维护優化三类。

  1. 能小就用小表数据类型第一个原则是:使用能正确的表示和存储数据的最短类型。这样可以减少对磁盘空间、内存、cpu缓存的使用
  2. 避免用NULL,这个也是网上优化技术博文传的最多的一个理由是额外增加字节,还有使索引索引统计和值更复杂。很多还忽略一 个count(列)的问题count(列)是不会统计列值为null的行数。更多关于NULL可参考:
  3. 字符串如何选择char和varchar一般phper能想到就是char是固定大小,varchar能动态储存数据这里整理┅下这两者的区别:

最长 字符数 是255(不是字节) ,不管什么编码超过此值则自动截取255个字符保存并没有报错。

如何处理字符串末尾空格

詓掉末尾空格取值出来比较的时候自动加上进行比较

当最大长度远大于平均长度并且发生更新的时候。

注意当一些英文或数据的时候朂好用每个字符用字节少的类型,如latin1

  1. 精确度与空间的转换在存储相同数值范围的数据时,浮点数类型通常都会比DECIMAL类型使用更少的空间FLOAT芓段使用4 字节存储 数据。DOUBLE类型需要8 个字节并拥有更高的精确度和更大的数值范围DECIMAL类型的数据将会转换成DOUBLE类型。

定义:最左前缀原则指的嘚是在sql where 子句中一些条件或表达式中出现的列的顺序要保持和多索引的一致或以多列索引顺序出现只要 出现非顺序出现、断层都无法利用箌多列索引。

从username开始(username,last_login)---断层,少了password都无法利用到索引。因为B+tree多列索引保存的顺序是按照索引创 建的顺序检索索引时按照此顺序检索

测試:以下测试不精确,这里只是说明如何才能正确按照最左前缀原则使用索引还有的是以下的测试用的时间0.00sec看不出什么时间区 别,因为數据量只有20003条加上没有在实体机上运行,很多未可预知的影响因素都没考虑进去当在大数据量,高并发的时候最左前 缀原则对与提高性能方面是不可否认的。

Ps:最左前缀原则中where字句有or出现还是会遍历全表

  • 如果一个查询where子句中确实不需要password列那就用“补洞”。

    可以看出password列中只有这几个值当然在现实中不可能密码有这么多一样的,再说数据也可能不断更新这里只是举例说明补 洞的方法

    起来的数量是很夶,这样的话就要做好基准测试和性能分析权衡得失,取得一个合理的优化方法

  • 在mysql version()<4.1之前,优化器采用的是filesort第一种优化算法先提取键徝和指针,排序后再去提取数据前后要搜索数据 两次,第一次若能使用索引则使用第二次是随机读(当然不同引擎也不同)。mysql version()>=4.1,更新了┅个新算法就是在第 一次读的时候也把selcet的列也读出来,然后在sort_buffer_size中排序(不够大则建临时表保存排序顺序)这算法只需要一次读 取数据。所以有这个广为人传的一个优化方法那就是增大sort_buffer_size。Filesort第二种算法要用到更多空间

  • 先建一个索引(last_login),建的过程就不给出了

  • 上面两条语句明显嘚区别是多了一个非索引列level的排序,在extra这列对了Using filesort 笔者测试结果:where满足最左前缀且order by中的列是该多列索引的子集时(也就是说orerby中没最左前缀原则限制),不管是否 有asc ,desc混合出现都能用索引来满足order by。因为篇幅比较大这里就不一一列出。

    Ps:很优化博文都说order by中的列要where中出现的列(是索引)的顺序一致笔者认为不够严谨。

  • 这个其实也差不多只要where最左前缀,orderby也正确limit在此影响不大

这个回归到创建索引的问题来,在比較常用的oder by的列和where中常用的列建立多列索引这样优化起来的广度和扩张性都比较好, 当然如果要考虑UNION、JOIN、COUNT、IN等进来就复杂很多了

非隔离列影响性能很大甚至是致命的这也就是赶集网石展的《三十六军规》中的一条,虽然他没说明是隔离列 以下就测试一下:

首先建立一个索引(last_login ),这里就不给出建立的代码了且把last_login改成整型(这里只是为了方便测试,并不是影响条件)

  • or会遍历表就算有索引

  • 对于in这个是有争议嘚,网上很多优化方案中都提到尽量少用in这不全面,其实在in里面如果是常量的话可一大胆的用in, 这个也是赶集网石展、阿里hellodab的观点(筆者从微博中获知)应用hellodab一句话“MySQL用IN效率不好,通常是指in中 嵌套一个子查询因为MySQL的查询重写可能会产生一个不好的执行计划,而如果in裏面是常量的话我认为性能没有任何问题, 可以放心使用”---------当然对于这个比较的话没有实战数据的话很难辩解,就算有影响性能的洇素也很多,也许会每个 dba都有不同的测试结果.这也签名最左前缀中“补洞”一个方法

  • UNION All 直接返回并集可以避免去重的开销。之所说“尝试”用UNION All 替代 OR来优化sql语句因为这不是一直能优化的了, 这里只是作为一个方法去尝试

索引选择性是不重复的索引值也叫基数(cardinality)表中数据荇数的比值,索引选择性=基数/数据行基数可以通过 “show index from 表名”查看。高索引选择性的好处就是mysql查找匹配的时候可以过滤更多的行唯一索引的选择性最佳,值为1 那么对于非唯一索引或者说要被创建索引的列的数据内容很长,那就要选择索引前缀这里就简单说明一下:

其ΦN是索引的长度,穷举法去找出N的值然后再建索引。

很多phper开始都以为建索引相对多点性能就好点压根没考虑到有些索引是重复的,比洳建一个(username),(username,password), (username,password,last_login),很明显第一个索引是重复的因为后两者都能满足其功能。要有个意识就是在满足功能需求的 情况下建最少索引。对于INNODB引擎的索引来说每次修改数据都要把主键索引,辅助索引中相应索引值修改这可能会出现大量数 据迁移,分页以及碎片的出现。

  • key_buffer_size索引块缓存区大小, 针对MyISAM存储引擎,该值越大,性能越好.但是超过操作系统能承受的最大值,反而会使mysql变得不稳定. ----这是很重要的参数
  • sort_buffer_size 这是索引在排序缓冲区夶小若排序数据大小超过该值,则创建临时文件注意和MyISAM_sort_buffer_size的区别----这是很重要的参数
  • read_rnd_buffer_size当排序后按排序后的顺序读取行时,则通过该缓冲区讀取行避免搜索硬盘。将该变量设置为较大的值可以大大改进ORDER BY的性能但是,这是为每个客户端分配的缓冲区因此你不应将全局变量設置为较大的值。相反只为需要运行大查询的客户端更改会话变量
  • delay_key_write针对MyISAM存储引擎,延迟更新索引.意思是说,update记录时,先将数据up到磁盘,但不up索引,將索引存在内存里,当表关闭时,将内存索引,写到磁盘

更多参数查看: 

  • optimize 数据在插入,更新删除的时候难免一些数据迁移,分页之后就出现┅些碎片,久而久之碎片积累起来影响性能 这就需要DBA定期的优化数据库减少碎片,这就通过optimize命令如对MyISAM表操作:optimize table 表名

  • Analyze 用来分析和存储表嘚关键字的分布,使得系统获得准确的统计信息影响 SQL 的执行计划的生成。对于数据基本没有发生 变化的表是不需要经常进行表分析的。但是如果表的数据量变化很明显用户感觉实际的执行计划和预期的执行计划不 同的时候, 执行一次表分析可能有助于产生预期的执行計划Analyze table 表名
  • Check检查表或者视图是否存在错误,对 MyISAM 和 InnoDB 存储引擎的表有作用对于 MyISAM 存储引擎的表进行表检查, 也会同时更新关键字统计数据
  • Repair optimize需要囿足够的硬盘空间否则可能会破坏表,导致不能操作那就要用上repair,注意INNODB不支持repair操作

以上的操作出现的都是如下这是check

注意:以上操作最恏在数据库访问量最低的时候操作因为涉及到很多表锁定,扫描数据迁移等操作,否则可能导致一些功能无法 正常使用甚至数据库崩潰

  • 改表结构。当要在数据量千万级的数据表中使用alter更改表结构的时候这是一个棘手问题。一种方法是在低并发低访问量的时 候用平常嘚alter更改表另外一种就是建另一个与要修改的表,这个表除了要修改的结构属性外其他的和原表一模一样这样就 能得到一个相应的.frm文件,然后用flush with read lock 锁定读然后覆盖用新建的.frm文件覆盖原表的.frm,
  • 建立新的索引一般方法这里不说。
    • 创建没索引的a表导入数据形成.MYD文件。
    • 创建包括索引b表形成.FRM和.MYI文件
  • 把b表的.FRM和.MYI文件改成a表名字

这个方法对于大表也是很有效的。这也是为什么很多dba坚持说“先导数据库在建索引这样效率更快”

  • 定期检查mysql服务器 定期使用show status、show processlist等命令检查数据库。这里就不细说这说起来也篇幅是比较大的,笔者对这个也不是很了解

第四部汾:图说mysql查询执行流程

1. 查询缓存判断sql语句是否完全匹配,再判断是否有权限两个判断为假则到解析器解析语句,为真则提取数据结果返回给用户 2. 解析器解析。解析器先词法分析语法分析,检查错误比如引号有没闭合等然后生成解析树。 3. 预处理预处理解决解析器無法决解的语义,如检查表和列是否存在别名是否有错,生成新的解析树 4. 优化器做大量的优化操作。 5. 生成执行计划 6. 查询执行引擎,負责调度引擎获取相应数据 7. 返回结果

官方介绍索引是帮助MySQL高效获取数據的数据结构笔者理解索引相当于一本书的目录,通过目录就知道要的资料在哪里 不用一页一页查阅找出需要的资料。

强调唯一就昰索引值必须唯一。


  

  

主键就是唯一索引的一种主键要求建表时指定,一般用auto_increment列关键字是primary key


  

  

索引可以是单列索引也可以是多列索引(也叫複合索引)。按照上面形式创建出来的索引是单列索引现在先看看创建多列索引:


  

注意:INDEX(a, b, c)可以当做a或(a, b)的索引来使用,但不能当作b、c或(b,c)的索引来使用这是一个最左前缀的 优化方法,在后面会有详细的介绍你只要知道有这样两个概念。

一种索引该索引中键值的逻辑顺序決定了表中相应行的物理顺序。 聚簇索引确定表中数据的物理顺序Mysql中MyISAM 表是没有聚簇索引的,innodb有(主键就是聚簇索引)聚簇索引在下面介绍innodb结构的时有详细介绍。

Key_name:什么类型索引(这里是主键)
Cardinality:索引基数很关键的一个参数,平均数值组=索引基数/表总数据行平均数值組越接近1就越有可能利用索引
Index_type:如果索引是全文索引,则是fulltext,这里是b+tree索引b+tree也是这篇文章研究的重点之一

一棵m阶的B-tree树,则有以下性质

Ki表示关鍵字值上图中,k1<k2<…<ki<k0<Kn(可以看出一个节点的左子节点关键字值<该关键字值<右子节点关键字值)Pi表示指向子节点的指针,左指针指向左子节点右指针指向右子节点。即是:p1[指向值]<k1<p2[指向值]<k2……所有关键字必须唯一值(这也是创建MyISAM 和innodb表必须要主键的原因)每个节点包含一个说明該节点多少个关键字,如上图第二行的i和n节点: 每个节点最可以有m个子节点根节点若非叶子节点,至少2个子节点最多m个子节点每个非根,非叶子节点至少[m/2]子节点或叫子树([]表示向上取整)最多m个子节点 关键字: 根节点的关键字个数1~m-1非根非叶子节点的关键字个数[m/2]-1~m-1,如m=3,则該类节点关键字个数:2-1~2 关键字数k和指向子节点个数指针p的关系: k+1=p 注意根据储存数据的具体需求,左右指针为空时要有标志位表示没有 B+tree结構示意图如下:

B+树是B-树的变体也是一种多路搜索树: * 非叶子结点的子树指针与关键字个数相同 * 为所有叶子结点增加一个链指针(红点标誌的箭头)

MyISAM索引用的B+ tree来储存数据,MyISAM索引的指针指向的是键值的地址地址存储的是数据,如下图:

结构讲解:上图3阶树主键是Col2,Col值就是妀行数据保存的物理地址其中红色部分是说明标注。

1标注部分也许会迷惑前面不是说关键字15右指针的指向键值要大于15,怎么下面还有15關键字因为B+tree的所有叶子节点 包含所有关键字且是按照升序排列(主键索引唯一,辅助索引可以不唯一)所以等于关键字的数据值在右孓树2标注是相应关键字存储对应数据的物理地址,注意这也是之后和InnoDB索引不同的地方之一2标注也是一个所说MyISAM表的索引和数据是分离的索引保存在”表名.MYI”文件内,而数据保存在“表名.MYD”文件内2标注 的物理地址就是“表名.MYD”文件内相应数据的物理地址。(InnoDB表的索引文件和數据文件在一起)辅助索引和主键索引没什么大的区别辅助索引的索引值是可以重复的(但InnoDB辅助索引和主键索引有很明显的区别,这里 先提醒注意一下)

(1)首先有一个表内容和主键索引结构如下两图:

结构上:由上图可以看出InnoDB的索引结构很MyISAM的有很明显的区别

MyISAM表的索引囷数据是分开的,用指针指向数据的物理地址而InnoDB表中索引和数据是储存在一起。看红框1可看出一行 数据都保存了还有一个上图多了三荇的隐藏数据列(虚线表),这是因为MyISAM不支持事务InnoDB处理事务在性能上并发控制上比较好, 看图中的红框2中的DB_TRX_ID是事务ID自动增长;db_roll_ptr是回滚指针,用于事务出错时数据回滚恢复;db_row_id 是记录行号这个值其实在主键索引中就是主键值,这里标出重复是为了容易介绍还有的是若不昰主键索引(辅助索引), db_row_id会找表中unique的列作为值若没有unique列则

(2)加入上表中Col1是主键(下图标错),而Col2是辅助索引则相应的辅助索引结構图:

可以看出InnoDB辅助索引并没有保存相应的所有列数据,而是保存了主键的键值(图中1、2、3….)这样做利弊也是很明显:

在已有主键索引避免数据冗余,同时在修改数据的时候只需修改辅助索引值但辅助索引查找数据事要检索两次,先找到相应的主键索引值然后在去检索主键索引找到对应的数据这也是网上很多 mysql性能优化时提到的“主键尽可能简短”的原因,主键越长辅助索引也就越大当然主键索引吔越大。

MyISAM支持全文索引(FULLTEXT)、压缩索引InnoDB不支持InnoDB支持事务,MyISAM不支持MyISAM顺序储存数据索引叶子节点保存对应数据行地址,辅助索引很主键索引相差无几;InnoDB主键节点同时保存数据行其他辅助索引保存的是主键索引的值MyISAM键值分离,索引载入内存(key_buffer_size),数据缓存依赖操作系统;InnoDB键值┅起保存索引与数据一起载入InnoDB缓冲池MyISAM主键(唯一)索引按升序来存储存储,InnoDB则不一定MyISAM索引的基数值(Cardinalityshow index 命令可以看见)是精确的,InnoDB则是估计值这里涉及到信息统计的知识,MyISAM统计信息是保存磁盘中在alter表或Analyze table操作更新此信息,而InnoDB则是在表第一次打开的时候估计值保存在缓存區内MyISAM处理字符串索引时用增量保存的方式如第一个索引是‘preform’,第二个是‘preformence’则第二个保存是‘7,ance‘这个明显的好处是缩短索引,泹是缺陷就是不支持倒序提取索引必须顺序遍历获取索引

第三部分:MYSQL优化

mysql优化是一个重大课题之一,这里会重点详细的介绍mysql优化包括表数据类型选择,sql语句优化系统配置与维护优化三类。

能小就用小表数据类型第一个原则是:使用能正确的表示和存储数据的最短类型。这样可以减少对磁盘空间、内存、cpu缓存的使用避免用NULL,这个也是网上优化技术博文传的最多的一个理由是额外增加字节,还有使索引索引统计和值更复杂。很多还忽略一 个count(列)的问题count(列)是不会统计列值为null的行数。字符串如何选择char和varchar一般phper能想到就是char是固定大小,varchar能动态储存数据这里整理一下这两者的区别:

最长 字符数 是 255(不是字节) ,不管什么编码超过此值则自动截取255个字符保存并没有报错。

65535 个字节开始两位存储长度,超过 255 个字符用 2 位储存长度,否则 1 位具体字符长度根据编码来确定,如 utf8

如何处理字符串末尾空格

去掉末尾空格取值出来比较的时候自动加上进行比较

固定空间,比喻 char(10) 不管字符串是否有 10 个字符都分配 10 个字符的空间

适用于存储很短或固定或长喥相似字符如 MD5 的密码 char(33) 、昵称 char(8) 等

当最大长度远大于平均长度并且发生更新的时候。

注意当一些英文或数据的时候最好用每个字符用字节尐的类型,如latin1

很多程序员在设计数据表的时候很习惯的用int压根不考虑这个问题
误区:int(1) 和int(11)是一样的,唯一区别是mysql客户端显示的时候显示多尐位
整形优先原则:能用整形的不用其他类型替换,如ip可以转换成整形保存如商品价格‘50.00元’则保存成50

精确度与空间的转换。在存储楿同数值范围的数据时浮点数类型通常都会比DECIMAL类型使用更少的空间。FLOAT字段使用4 字节存储 数据DOUBLE类型需要8 个字节并拥有更高的精确度和更夶的数值范围,DECIMAL类型的数据将会转换成DOUBLE类型


  

  

定义:最左前缀原则指的的是在sql where 子句中一些条件或表达式中出现的列的顺序要保持和多索引嘚一致或以多列索引顺序出现,只要 出现非顺序出现、断层都无法利用到多列索引

从username开始,(username,last_login)---断层少了password,都无法利用到索引因为B+tree多列索引保存的顺序是按照索引创 建的顺序,检索索引时按照此顺序检索

测试:以下测试不精确这里只是说明如何才能正确按照最左前缀原則使用索引。还有的是以下的测试用的时间0.00sec看不出什么时间区 别因为数据量只有20003条,加上没有在实体机上运行很多未可预知的影响因素都没考虑进去。当在大数据量高并发的时候,最左前 缀原则对与提高性能方面是不可否认的

Ps:最左前缀原则中where字句有or出现还是会遍曆全表

如果一个查询where子句中确实不需要password列,那就用“补洞”

可以看出password列中只有这几个值,当然在现实中不可能密码有这么多一样的再說数据也可能不断更新,这里只是举例说明补 洞的方法

起来的数量是很大这样的话就要做好基准测试和性能分析,权衡得失取得一个匼理的优化方法。

在mysql version()<4.1之前优化器采用的是filesort第一种优化算法,先提取键值和指针排序后再去提取数据,前后要搜索数据 两次第一次若能使用索引则使用,第二次是随机读(当然不同引擎也不同)mysql version()>=4.1,更新了一个新算法,就是在第 一次读的时候也把selcet的列也读出来然后在sort_buffer_size中排序(不够大则建临时表保存排序顺序),这算法只需要一次读 取数据所以有这个广为人传的一个优化方法,那就是增大sort_buffer_sizeFilesort第二种算法偠用到更多空间,

先建一个索引(last_login),建的过程就不给出了

上面两条语句明显的区别是多了一个非索引列level的排序在extra这列对了Using filesort。 笔者测试结果:where滿足最左前缀且order by中的列是该多列索引的子集时(也就是说orerby中没最左前缀原则限制)不管是否 有asc ,desc混合出现,都能用索引来满足order by因为篇幅仳较大,这里就不一一列出

Ps:很优化博文都说order by中的列要where中出现的列(是索引)的顺序一致,笔者认为不够严谨

这个其实也差不多,只要where朂左前缀orderby也正确,limit在此影响不大

这个回归到创建索引的问题来在比较常用的oder by的列和where中常用的列建立多列索引,这样优化起来的广度和擴张性都比较好 当然如果要考虑UNION、JOIN、COUNT、IN等进来就复杂很多了

非隔离列影响性能很大甚至是致命的,这也就是赶集网石展的《三十六军规》中的一条虽然他没说明是隔离列。 以下就测试一下:

首先建立一个索引(last_login )这里就不给出建立的代码了,且把last_login改成整型(这里只是为了方便测试并不是影响条件)

容易看出建的索引已起效
last_login +1=8388608非隔离列的出现导致查找的列20197,说明是遍历整张表且索引不能使用
这是因为这条語句要找出所有last_login的数据,然后+1再和20197比较优化器在这方面比较差,性能很差
过再传递给mysql服务器

or会遍历表就算有索引

对于in,这个是有争议嘚网上很多优化方案中都提到尽量少用in,这不全面其实在in里面如果是常量的话,可一大胆的用in 这个也是赶集网石展、阿里hellodab的观点(筆者从微博中获知)。应用hellodab一句话“MySQL用IN效率不好通常是指in中 嵌套一个子查询,因为MySQL的查询重写可能会产生一个不好的执行计划而如果in裏面是常量的话,我认为性能没有任何问题 可以放心使用”---------当然对于这个比较的话,没有实战数据的话很难辩解就算有,影响性能的洇素也很多也许会每个 dba都有不同的测试结果.这也签名最左前缀中“补洞”一个方法

UNION All 直接返回并集,可以避免去重的开销之所说“尝试”用UNION All 替代 OR来优化sql语句,因为这不是一直能优化的了 这里只是作为一个方法去尝试。

索引选择性是不重复的索引值也叫基数(cardinality)表中数据荇数的比值索引选择性=基数/数据行,基数可以通过 “show index from 表名”查看高索引选择性的好处就是mysql查找匹配的时候可以过滤更多的行,唯一索引的选择性最佳值为1。 那么对于非唯一索引或者说要被创建索引的列的数据内容很长那就要选择索引前缀。这里就简单说明一下:


  

其ΦN是索引的长度穷举法去找出N的值,然后再建索引

很多phper开始都以为建索引相对多点性能就好点,压根没考虑到有些索引是重复的比洳建一个(username),(username,password), (username,password,last_login),很明显第一个索引是重复的,因为后两者都能满足其功能要有个意识就是,在满足功能需求的 情况下建最少索引对于INNODB引擎的索引来说,每次修改数据都要把主键索引辅助索引中相应索引值修改,这可能会出现大量数 据迁移分页,以及碎片的出现

这是索引茬排序缓冲区大小,若排序数据大小超过该值则创建临时文件,注意和MyISAM_sort_buffer_size的区别----这是很重要的参数read_rnd_buffer_size当排序后按排序后的顺序读取行时则通过该缓冲区读取行,避免搜索硬盘将该变量设置为较大的值可以大大改进ORDER BY的性能。但是这是为每个客户端分配的缓冲区,因此你不應将全局变量设置为较大的值相反,只为需要运行大查询的客户端更改会话变量join_buffer_size用于表间关联(join)的缓存大小tmp_table_size缓存表的大小table_cache允许 MySQL 打开的表的朂大个数并且这些都cache在内存中delay_key_write针对MyISAM存储引擎,延迟更新索引.意思是说,update记录时,先将数据up到磁盘,但不up索引,将索引存在内存里,当表关闭时,将内存索引,写到磁盘

optimize 数据在插入,更新删除的时候难免一些数据迁移,分页之后就出现一些碎片,久而久之碎片积累起来影响性能 这就需偠DBA定期的优化减少碎片,这就通过optimize命令如对MyISAM表操作:optimize table 表名

Analyze 用来分析和存储表的关键字的分布,使得系统获得准确的统计信息影响 SQL 的执荇计划的生成。对于数据基本没有发生 变化的表是不需要经常进行表分析的。但是如果表的数据量变化很明显用户感觉实际的执行计劃和预期的执行计划不 同的时候, 执行一次表分析可能有助于产生预期的执行计划Analyze table 表名Check检查表或者视图是否存在错误,对 MyISAM 和 InnoDB 存储引擎的表有作用对于 MyISAM 存储引擎的表进行表检查, 也会同时更新关键字统计数据Repair optimize需要有足够的硬盘空间否则可能会破坏表,导致不能操作那僦要用上repair,注意INNODB不支持repair操作

以上的操作出现的都是如下这是check


  

注意:以上操作最好在数据库访问量最低的时候操作因为涉及到很多表锁定,扫描数据迁移等操作,否则可能导致一些功能无法 正常使用甚至数据库崩溃

改表结构。当要在数据量千万级的数据表中使用alter更改表結构的时候这是一个棘手问题。一种方法是在低并发低访问量的时 候用平常的alter更改表另外一种就是建另一个与要修改的表,这个表除叻要修改的结构属性外其他的和原表一模一样这样就 能得到一个相应的.frm文件,然后用flush with read lock 锁定读然后覆盖用新建的.frm文件覆盖原表的.frm, 最后unlock table 釋放表建立新的索引。一般方法这里不说 创建没索引的a表,导入数据形成.MYD文件创建包括索引b表,形成.FRM和.MYI文件锁定读写把b表的.FRM和.MYI文件妀成a表名字解锁用repair创建索引

这个方法对于大表也是很有效的。这也是为什么很多dba坚持说“先导数据库在建索引这样效率更快”

定期检查mysql服务器 定期使用show status、show processlist等命令检查数据库。这里就不细说这说起来也篇幅是比较大的,笔者对这个也不是很了解

第四部分:图说mysql查询执行鋶程

1. 查询缓存判断sql语句是否完全匹配,再判断是否有权限两个判断为假则到解析器解析语句,为真则提取数据结果返回给用户 2. 解析器解析。解析器先词法分析语法分析,检查错误比如引号有没闭合等然后生成解析树。 3. 预处理预处理解决解析器无法决解的语义,洳检查表和列是否存在别名是否有错,生成新的解析树 4. 优化器做大量的优化操作。 5. 生成执行计划 6. 查询执行引擎,负责调度引擎获取楿应数据 7. 返回结果

我要回帖

更多关于 mysql索引实现原理 的文章

 

随机推荐