mysql笔记02 建立高性能的索引

建立高性能的索引mysql

1. 索引(在MySQL中也叫作"键(key)")是存储引擎用于快速找到记录的一种数据结构。算法

2. 索引能够包含一个或多个列的值。若是索引包含多个列,那么列的顺序也十分重要,由于MySQL只能高效低使用索引的最左前缀列。sql

3. B-Tree索引:当人们谈论索引的时候,若是没有特别执行索引类型,那多半说是B-Tree索引,它使用B-Tree数据结构来存储数据。缓存

    1). 可使用B-Tree索引的查询类型:服务器

         a. 全值匹配:全值匹配指的是和索引中的全部列进行匹配。数据结构

         b. 匹配最左前缀:即只使用索引的第一列。并发

         c. 匹配列前缀:也能够值匹配某一列的值的开头部分。函数

         d. 匹配范围值高并发

         e. 精确匹配某一列并范围匹配另一列工具

         f. 只访问索引的查询:覆盖索引

    2). 由于索引树中的节点是有序的,因此除了按值查找以外,索引还能够用于查询中的ORDER BY操做(按顺序查找)。通常来讲,若是B-Tree能够按照某种方式查找到值,那么也能够按照这种方式用于排序。

    3). B-Tree索引的限制

          a. 若是不是按索引的最左列开始查找,则没法使用索引。

          b. 不能跳过索引中的列。

          c. 若是查询中有某个列的范围查询,则最右边的全部列都没法使用索引优化查询。若是范围查询列值的数量有限,那么能够经过使用多个等于条件来代替范围查找。

4. 哈希索引(hash index):哈希索引基于哈希表实现,只有精确匹配索引全部列的查询才有效。结构十分紧凑,查询速度很是快。

    InnoDB引擎有一个特殊的功能叫作"自适应哈希索引"。当InnoDB注意到某个索引值被使用得很是频繁时,它会在内存中基于B-Tree索引之上再建立一个哈希索引,这样B-Tree索引也就有哈希索引的一些优势。

5. 空间数据索引,全文索引,其余索引类别

6. 索引的优势:索引可让服务器快速定位到表的指定位置。最多见的B-Tree索引,按照顺序存储数据,因此MySQL能够用来作ORDER BY 和 GROUP BY操做。总结下来,索引有以下三个优势:

    1). 索引大大减小了服务器须要扫描的数据量

    2). 索引能够帮助服务器避免排序和临时表

    3). 索引能够将随机IO变为顺序IO。

7. 三星系统:索引将相关的记录放到一块儿则得到一星;若是索引中数据顺序和查找中的排序顺序一致则得到二星;若是索引中的列包含了查询中须要的所有列则得到三星。

8. 索引是最好的解决方案吗?对于很是小的表,大部分状况下简单的全表扫描更高效。对于中到大型的表,索引就很是有效。但对于特大型的表,创建和使用索引的代价将随之增加。这种状况下,则须要一种技术

    能够直接区分出查询须要的一组数据,而不是一条记录一条记录的匹配。

    若是表的数量特别多,能够创建一个元数据信息表,用来查询须要用到的某些特性。例如:记录"哪一个用户信息存储在哪一个表里面"。

9. 高性能的索引策略:

    1). 独立的列:独立的列是指索引列不能是表达式的一部分,也不能是函数的参数。若是查询中的列不是独立的,则MySQL就不会使用索引。

    2). 前缀索引和索引选择性:有时候须要索引很长的字符列,这会让索引变得很大且慢。一般能够索引开始部分的字符,这样能够大大节约索引空间,从而提升索引效率。但这样会下降索引的选择性。

         a. 索引的选择性是指,不重复的索引值(也称为基数)和数据表的记录总数(#T)的比值,范围从1/#T到1之间。索引的选择性越高则查询效率越高,由于选择性高的索引可让MySQL在查找时过滤掉更多的行。

             惟一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

         b. 通常状况下某个列前缀的选择性也是足够高的,足以知足查询性能。对于BLOB、TEXT或者很长的VARCHAR类型的列,必须使用前缀索引,由于MySQL不容许索引这些列的完整长度。

         c. 诀窍在于要选择足够长的前缀以保证较高的选择性,同时又不能太长(以便节省空间)。计算合适的前缀长度的一个方法是计算完整列的选择性,并使前缀的选择性接近于完整列的选择性。

            下面是如何计算完整列的选择性:SELECT COUNT(DISTINCT city)/COUNT(*) FROM sakila.city_demo;        查询结构值为:0.031

            一般来讲,这个例子中若是前缀的选择性可以接近于0.031,基本上就能够用了。能够在一个查询中针对不一样前缀长度进行计算,这对于大表很是有用。

            下面给出了如何在同一个查询中计算不一样前缀长度的选择性:

             SELECT COUNT(DISTINCT LEFT(city,3)) AS sel3,COUNT(DISTINCT LEFT(city,4)) AS sel4,COUNT(DISTINCT LEFT(city,5)) AS sel5,

                         COUNT(DISTINCT LEFT(city,6)) AS sel6,COUNT(DISTINCT LEFT(city,7)) AS sel7 FROM sakila.city_demo;

             查询结果值,按顺序为:0.0238 ,  0.0293, 0.0305,0.0309,0.0310

             查询显示当前前缀长度到达7的时候,再增长前缀长度,选择性提高的幅度已经很小了。

         d. 只看平均选择性是不够的,也有例外的状况,须要考虑最坏状况下的选择性。

         e. 前缀索引是一种能使索引更小、更快的有效办法,但另外一方面也有其缺点:MySQL没法使用前缀索引作ORDER BY和GROUP BY , 也没法使用前缀索引作覆盖扫描。

         f. 有时候后缀索引也有用途(例如,找到某个域名的全部电子邮件地址)。MySQL原生并不支持反向索引,可是能够把字符串反转后存储,并基于此创建前缀索引。能够经过触发器来维护这种索引。

    3). 多列索引:在MySQL或更新的版本中,会使用"索引合并"策略,查询能同时使用两个单列索引进行扫描,并将结果进行合并。这种算法有三个变种:OR条件的联合(union),AND条件的相交,

         组合前两种状况的联合及相交。

         索引合并策略有时候是一种优化的结果,但实际上更多的时候说明表上的索引建的很糟糕:

         a. 当出现服务器对多个索引作相交操做时(一般多个AND条件),一般意味着须要一个包含全部相关列的多列索引,而不是多个独立的单列索引。

         b. 当服务器须要对多个索引作联合操做时(一般由多个OR条件),一般须要耗费大量CPU和内存资源在算法的缓存、排序和合并操做上。特别是当其中有些索引的选择性不高,须要合并扫描返回的大量数据时。

         c. 优化器不会把这些计算到"查询成本"(cost)中,优化器值关系随机页面读取。这样不只消耗更多的CPU和内存资源,还会影响查询的并发性。

         d. 若是在EXPLAIN中看到有索引合并,应该好好检查一下查询和表的结构,看是否是已经最优的。也能够哦太难过参数optimizer_switch来关闭索引合并功能。也可使用IGNORE INDEX提示让优化器

             忽略掉某些索引。

    4). 选择合适的索引列顺序:正确的索引顺序依赖于使用该索引的查询,并同时知足须要考虑如何更好地知足排序和分组的须要。

         a. 在一个多列的B-Tree索引中,索引列的顺序意味着索引首先按照最左列进行排序,其次是第二列,等等。因此,索引能够按照升序或者降序进行全表扫描,以知足符合列顺序的ORDER BY,GROUP BY和

             DISTINCT等字句的查询需求。

         b. 当不须要考虑排序和分组时,将选择性最高的列放在前面一般是很好的。这时候索引的做用只是用于优化WHERE条件查询。

         c. 更具运行频率最高的查询来调整索引的顺序,让这种状况下索引的选择性最高。

         d. 若是是从诸如pt-query-digest这样的工具的报告中提起"最差"查询,那么再按上面办法选定索引顺序每每是很是高效的。若是没有相似的具体查询来运行,那么最好仍是按经验法则来作,由于全局法则考

             滤的是全局基数和选择性,而不是某个具体查询。

             SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity, count(*) from payment

             查询结果值为:staff_id_selectivity  0.0001,customer_id_selectivity  0.0373, count(*)  16049 

             customer_id 的选择性更高,因此答案是将其做为索引列的第一列

         e. 尽管关于选择性和基数的经验法则值得去研究和分析,但必定要记住别忘了WHERE字句中的排序、分组和范围条件等其余因素,这些因素可能对查询的性能形成很是大的影响。

    5). 聚簇索引:InnoDB的聚簇索引实际上在同一个结构中保存了B-Tree索引和数据行。它的数据实际上存储在索引的叶子页中。"聚簇"表示把数据行和相邻的键值紧凑地存储在一块儿。由于没法同时把数据行存放在

          两个不一样的地方,因此一个表只能有一个聚簇索引。

         5.1). InnoDB将经过主键汇集数据。若是没有定义主键,InnoDB会选择一个惟一的非空索引代替。若是没有这样的索引,InnoDB会隐式定义一个主键做为聚簇索引。InnoDB只汇集在同一个页面中的记录。包

                  含相邻键值的页面可能会相距甚远。

         5.2). 聚簇索引的优势:

                 a. 能够把相关的数据保存在一块儿。

                 b. 数据访问更快。

                 c. 使用覆盖索引扫描的查询能够直接使用叶节点中的主键值。

         5.3). 聚簇索引的缺点:

                 a. 更新聚簇索引列的代价很高,由于会强制将每一个被更新的行移动到新的位置。

                 b. 可能会致使页分裂。

                 c. 致使全表扫描变慢,尤为是行比较稀疏。

                 e. 二级索引(非聚簇索引)可能比想象的要更大,由于在二级索引的叶子节点包含了引用行的主键列。

                 f. 二级索引访问须要两次索引查询,而不是一次。这是由于二级索引叶子节点保存的不是指向行的物理位置的指针,而是行的主键值。这意味着经过二级索引查找行,存储引擎须要找到二级索引的叶子

                    节点得到对应的主键值,而后根据这个值去聚簇索引中查找到对应的行。这里作了重复工做:两次B-Tree查找而不是一次。

         5.4). 最好避免随机的(不连续且值的分布范围很是大)聚簇索引,特别是对于IO密集型的应用。例如:从性能角度考虑,使用UUID做为聚簇索引会很糟糕:它使得聚簇索引的插入变得彻底随机,这是最坏的状况,

                  是的数据没有任何汇集。向UUID主键插入行不只花费更长的时间,并且索引占用的空间也更大。这一方面是因为主键字段更长,另外一方面毫无疑问是因为页分裂碎片致使的。

         5.5). 使用InnoDB时应该尽量地按主键顺序插入数据,而且尽量地使用单调增长的聚簇键的值插入新行。这样能够顺序地写入数据,减小随机IO,减小碎片和减小分页。

                 但对于高并发工做负载,按主键顺序插入可能形成明显的争用。

     6). 覆盖索引:若是一个索引包含(或者说覆盖)全部须要查询的字段的值,咱们就称之为"覆盖索引"

          6.1 覆盖索引的好处:

                a. 索引条目一般远小于数据行大小,因此若是只须要读取索引,那么MySQL就会极大地减小数据访问量。

                b. 由于索引是按照值顺序存储的(至少在单个页内如此),因此对于IO密集型的范围查询会比随机从磁盘读取每一行数据的IO要少得多。

                c. 因为InnoDB的聚簇索引,覆盖索引对InnoDB表特别有用。InnoDB的二级索引在叶子节点中保存了行的主键值,因此若是二级主键可以覆盖查询,则能够避免对主键索引的二次查询。

          6.2 不是全部类型的索引均可以做为覆盖索引。覆盖索引必需要存储索引列的值,而哈希索引、空间索引和全文索引等都不存储索引列的值,因此MySQL只能使用B-Tree索引作覆盖索引。不是全部

                的索引都支持覆盖索引。

          6.3 当发起一个索引覆盖的查询时,在EXPLAIN的Extra列能够看到"Using index"的信息。

          6.4 MySQL不能在索引中执行LIKE操做。由于该操做可能转换为简单的比较操做,可是若是是通配符开头的LIKE查询,存储引擎就没法比较匹配。MySQL服务器只能提取数据行的值而不是索引值来做比较。

          6.5 延迟关联:延迟对列的访问。在查询第一阶段MySQL能够作覆盖索引,在FROM子句的子查询中使用索引。

     7). 使用索引扫描来作排序:MySQL有两种方式能够生成有序的结果:经过排序操做;或者按索引顺序扫描;若是EXPLAIN出来的type列的值为"index",则说明MySQL使用了索引来作排序(不要和Extra列的

          "Using index"搞混淆了)。

           a. 索引自己是很快的,由于只须要从一条索引记录移动到紧接着的下一条记录。若是索引不能覆盖查询所需的所有列,那就不得不每扫描一条索引记录就回表查询一次对应的行。这基本上都是随机IO,所以

               按索引顺序读取数据的速度一般要比顺序地全表扫描慢,尤为是在IO密集型的工做负载时。

           b. 只有当索引的列顺序和ORDER BY子句顺序彻底一致,而且全部列的顺序方向(倒序或正序)都同样时,MySQL才可以使用索引来对结果作排序。若是查询须要关联多张表,则只有ORDER BY子句引用的

               字段所有为第一个表时,才能使用索引作排序。

           c. 有一种状况下ORDER BY子句能够不知足索引的最左前缀的要求,就是前导列为常量的时候。

      8). 压缩(前缀索引):MyISAM使用前缀索引压缩来减小索引的大小,从而让更多的索引能够放入内存中,这在某些状况下能极大地提升性能。默认值压缩字符串,到哪经过参数设置能够对整数压缩。

      9). 冗余和重复索引:MySQL容许在相同列上建立多个索引,不管是有意仍是无心的。MySQL须要单独维护重复的索引,而且优化器在优化查询的是时候也须要逐个地进行考虑,这会影响性能。

           a. 重复索引:

              例如以下代码:CREATE TABLE test (ID INT NOT NULL PRIMARY KEY, A INT NOT NULL,B INT NOT NULL,UNIQUE(ID),INDEX(ID)) ENGINE = InnoDB

              一个经验不足的用户多是想建立一个主键,先加上惟一限制,而后再加上索引以供查询使用。事实上,MySQL的惟一限制和主键限制都是经过索引实现的,所以,上面的写法实际上在相同的列上建立了三个

              重复的索引。一般并无理由这样作,除非是在同一列上建立不一样类型的索引来知足不一样的查询需求。

           b. 冗余索引:

               若是建立了索引(A,B),在建立索引(A)就是冗余索引,由于这只是前一个索引的前缀索引。

               冗余索引一般发生在为表添加新索引的时候。例如,有人可能会增长一个新的索引(A,B)而不是扩展已有的索引(A)。还有一种状况是将一个索引扩展为(A,ID),其中ID是主键,对于InnoDB来讲主键已经包含

               在二级索引中了,因此这也是冗余的。

           c. 解决冗余索引和重复索引的方法很简单,删除这些索引就能够了,但首先要找出这样的索引。

      10). 未使用的索引:对于服务器上一些永远不用的索引,彻底是累赘,建议考虑删除。

      11). 索引和锁:

             a. InnoDB只有在访问行的时候才会对其进行加锁,而索引可以减小InnoDB访问的行数,从而减小锁的数量。

10. 索引案例学习:

      1). 过滤条件:

           a. 能够经过使用IN语句让MySQL使用索引,避免使用范围查询致使SQL语句没法使用索引。可是这种技巧也不能滥用,由于每额外增长一个IN()条件,优化器就须要作的组合(每一个in的一个元素

               至关于一条查询)都将以指数形式增长。

           b. 在有更多不一样值的列上建立索引的选择性会更好。通常来讲这样作都是对的,由于可让MySQL更有效的过滤掉不须要的行。

           c. 尽量将须要作范围查询的列放到索引的后面,以便优化器能使用尽量多的索引列。

           d. 考虑表上的全部选项。当设计索引时,不要只为现有的查询考虑须要哪些索引,还须要考虑对查询进行优化。

      2). 避免多个范围条件:

           a. MySQL没法再使用范围列后面的其余索引列了,可是对于"多个等值条件查询"则没有这个限制。

      3). 优化排序:

           a. 对于那些选择性很是低的列,能够增长一些特殊的索引来锁排序。例如:能够建立(sex,rating)索引:SELECT <cols> FROM profiles WHERE sex='M' order by rating LIMIT 10;

               这个查询同时使用了ORDER BY 和 LIMIT ,若是没有索引的话会很慢。

           b. 优化索引另外一个比较好的策略是使用延迟关联,经过使用覆盖索引查询返回须要的主键,再根据这些主键关联原表得到须要的行。这能够减小MySQL扫描那些须要丢弃的行数。

11. 维护索引和表:

      1). 找到并修复损坏的表:若是遇到数据损坏,最重要的是找出是什么致使了损坏,而不仅是简单地修复,不然可能还会不断的损坏。能够经过设置innodb_force_recovery参数进入InnoDB的强制回复模式来

            修复数据。另外还能够经过使用开源的InnoDB数据恢复工具箱直接从InnoDB数据文件恢复数据。

      2). 更新索引统计信息:InnoDB不在磁盘存储索引统计信息,而是经过随机的索引访问进行评估并将其存储在内存中。InnoDB引擎经过抽样的方式计算统计信息,首先随机地读取少许的索引页面,而后以此

           为样本计算索引的统计信息。

      3). 减小索引和数据的碎片:

            数据碎片分类:

            行碎片:这种碎片指的是数据行被存储为多个地方的多个片断中。即便查询值从索引中访问一行记录,行碎片也会致使性能降低。

            行间碎片:行间碎片是指逻辑上顺序的夜,或者行在磁盘上不是顺序存储的。行间碎片对诸如全表扫描和聚簇索引扫描之类的操做有很大的影响,由于这些操做本来能从磁盘上顺序存储的数据中获益。

            剩余空间碎片:剩余空间碎片是指数据页中有大量的空余空间。这会致使服务器读取大量不须要的数据,从而形成浪费。

            InnoDB不会出现短小的行碎片,InnoDB会移动短小的行并重写到一个片断中。

            新版的InnoDB新增了"在线添加"和删除索引功能,能够先删除,而后再从新建立索引的方式来消除索引的碎片化。

12. mysql中每一个表都有一个聚簇索引(clustered index),除此以外的表上的每一个非聚簇索引都是二级索引,又叫辅助索引(secondary indexes)。

 

 

索引的优化:

1. 选择合适的索引列顺序

2. 若是查询中有某个列的范围查询,则最右边的全部列都没法使用索引优化查询。若是范围查询列值的数量有限,那么能够经过使用多个等于条件来代替范围查找。

3. 前缀索引

4. 使用索引扫描来作排序

5. 覆盖索引

 

 

6. 索引的优势:索引可让服务器快速定位到表的指定位置。最多见的B-Tree索引,按照顺序存储数据,因此MySQL能够用来作ORDER BY 和 GROUP BY操做。总结下来,索引有以下三个优势:

    1). 索引大大减小了服务器须要扫描的数据量

    2). 索引能够帮助服务器避免排序和临时表

    3). 索引能够将随机IO变为顺序IO。

相关文章
相关标签/搜索