高性能MySQL读书笔记--索引

本章主要介绍了索引的种类,各自的优缺点,如何设计和高效的使用索引,以及维护和修复索引使其保持正常工作状态。总的来说,就我的理解,分析一个较慢或给服务器造成较大压力的查询,可以从索引的设计是否正确(索引列的顺序,索引列的选择),索引的使用是否正确(避免单行和随机I/O,充分利用数据原生顺序避免排序,索引覆盖查询),以及查询语句设计是否正确,是否可以利用现有索引继续优化这三个方面来解决。

索引类型

*B-Tree索引:

B树索引的优势主要有两方面:1)身形较胖,从而树的深度大大较少,可以减少磁盘I/O次数,一次读取一个节点,在内存里用二分法对下个节点进行搜索。2)相对于红黑树等平衡二叉树相邻节点实际物理地址距离很远来说,B树的每个节点即为物理上的一整块存储块,相邻节点在物理地址上也相近,因而对于CPU读取一块数据时也会把相邻的数据顺便读入的这一特性十分相合,可以提高效率。

B树索引适用于全键值、键值范围和键前缀查询。注意匹配最左前缀时使用索引要与定义索引时顺序一致,在B树中查询时也是根据索引顺序,依次匹配键值是否相符。另外使用了范围查询之后的列都无法使用索引。

*Hash索引:

Hash索引与一般Hash表性质没什么太大差别,一个键值对应唯一一个hash值,因而等值查询,插入和删除基本上都是O(1)时间复杂度,但是Hash表最大缺点是内部无序,因而对于对索引范围查找的操作不支持。同时当负载因子较高时(已有节点/总的节点)查询等操作效率会大大降低,而解决负载因子较高的rehash操作会占用大量内寸和CPU资源,这是使用Hash索引需要考虑的地方。

Innodb引擎会对使用频繁的索引值增加一个hash索引,主要作用是将较长键值转换为hash值,从而提升查询效率,但实质上还是B树查找。

  *全文索引:

全文索引主要用来处理较长的文本数据列,它的键值是文本中出现次数较多的关键字,类似于搜索引擎,每个关键字对应出现过该关键字的文本数据,主要用于like模糊查询。

索引优缺点

*优点:1.减少需要扫描的数据行。2.索引本身是有序的,可以避免排序。

*缺点:1.索引占用空间。2.维护索引需要消耗资源,特别是聚集索引,每次插入等操作都占用比较多资源。3.对于特大型表,用索引单条匹配意义不大,通常使用元数据信息表指示一块目标信息对应的表。

高性能索引策略

*不可在索引上使用函数或计算式等。

*对于较大的文本列,除了加hash索引外,可以在满足足够索引选择性的条件下仅选取索引最左部分字符。前缀长度的选择可以根据统计不同前缀长度前几位前缀出现的次数来选择合适的前缀长度。

无法使用前缀索引做order by和group by,小弟猜测是因为排序需要根据全部值进行,而索引只包含一部分字符。

*尽量不要使用多个单列索引,虽然mysql已经对使用多个单列索引的语句进行了优化,但是这相对于多列索引导致了糟糕的性能:1.多个索引AND时,需要分别对每个索引进行查找,然后对各个结果AND,而多列索引仅需一次查找即可满足条件。2.多个索引OR时同理,会消耗大量缓存和CPU资源。3.优化器不能正确识别查询成本,导致选取成本更高的查询方式。

*多列索引的排列顺序一般来讲把选择性最高的列放在前面,因为选择性越高,对应的列越少,速度也越快,后续查询范围也越小。不过有时候也需要考虑列的使用频率、排序等问题,需要权衡这些因素来做出决定。

*聚集索引:聚集索引将数据和键值存储在一起(从而每个表只能有一个聚集索引),但更本质的特点是索引顺序和数据的物理地址顺序一致。聚集索引的优点主要在于数据和键值存储在一起,从而在范围查询时只需要将范围内的几页数据读取即可,而非聚集索引通过指针则需较多次磁盘I/O。但也由于聚集的特性,导致了更新、插入等操作的代价较大。全表扫描相对于非聚集索引通过数据在磁盘中的地址offset来遍历来说慢了很多。同时由于二级索引通过主键关联聚集索引,因而主键大小对二级索引性能影响较大。

Innodb引擎每一个叶子结点固定大小为16k,大致结构如下图:

每个record代表一行记录,通过链表顺序链接,每次磁盘I/O读取一页,然后根据page中的directory二分查找大致行的位置,每行数据除了各个列的数据以外还维护了事务ID、指向undo的rollback指针以及两个隐藏的版本控制号。

聚集索引主键应该设计成与数据完全无关,并且应该是顺序递增列。随机的键值将导致搜索插入点,稀疏页以及频繁分裂等问题(B树特性)。

*覆盖索引指存储索引列值得索引,在mysql中仅有B树索引做覆盖索引。如果仅查询索引列的值,则可以避免诸如myisam表系统调用读取数据、innodb表二级引擎的二次查询等问题,从而大大提高效率。

当索引无法覆盖查询语句中所有的查询字段,则无法使用覆盖索引。

为什么select * from products join(select proid from products where actor=’s’)as t1 on (t1.proid=products.proid);相对于select * from products where actor=‘s’;可以提高效率?虽然前一个语句的子查询可以使用覆盖索引,但是仍然需要根据proid在外部索引回表读取数据,似乎跟后一个语句没什么区别。

*order by使用索引也需满足最左前缀条件,多表联合查询时order by字段全为第一个

表时才能用索引排序。不过也有例外,比如在where或join等条件中的列指定了常量,那么order by中仅用了后续的索引列也可用来排序。总的来说,where等中的常量索引列和orderby里的索引列可以组合构成最左前缀,且指定的排序顺序一致(都为升序或者降序)才可用索引排序。

*冗余索引是已经有了多列索引,却仍有索引是该索引的最左前缀。大部分时候不应该有冗余索引,因为索引越多,空间和维护时间花费就越多。不过有些查询仅使用了部分多列索引,当这些查询速度很重要时,可以为这些索引使用的索引单独再创建冗余索引以提高速度。

索引案例学习

*1.对于使用频率最高的几列,应该在其上创建索引。2.为了避免重用索引,比如(sex,age)和(sex,region,age),可以将其合并成(sex,region,age),然后在查询时,对不需要用的索引列使用index IN(x1,x2…)来保证索引能够被使用。3.对于常用范围查询的列应该将其放在索引最后几列。4.不可大量用IN(),因为这会使优化器产生大量组合,从而在判断时进行大量比较。

*尽量不要使用多个范围查询。

*当查询存在limit且offset比较大时,如limit 10000,10;会扫描大量无用数据。这种情况可以通过延迟关联解决,即通过覆盖索引锁定所需数据的主键然后通过再join关联来减少非覆盖索引扫描的行数。

维护索引和表

*修复损坏的表:通过check table或者mysqlcheck来检查表是否损坏,通过repair table或者不做任何操作的alter table来重建表(alter table table_name engine=innodb;)。当repair table不支持或者不起作用时,可以使用1.repair table extend,不过要慢很多,需要慎重考虑。2.使用repair table use_frm,这会根据.frm重建表和索引,并根据.myd将数据填充。

一般来讲,innodb引擎的表不容易损坏,如果出现了损坏,可能是1.内存或者硬盘出现问题。2.在mysql外部操作了innodb的数据索引文件。一般来讲,innodb可以在启动时自动修复大部分错误,不过当碰见没有用户交互不能解决的严重错误时,可以设置启动项—innodb_force_recovery,并从低到高尝试其修复等级0~6,直到可以访问表,并通过select into outfile转存数据,重启服务器重建表并导入数据。

*更新索引统计信息:在解析sql语句时,查询优化器会通过两个API来了解索引值的分布信息,一个是records_in_range()可以知道两个边界值之间有多少行记录,一个是info()可以返回索引的基数等数据。

Mysql会在每次首次打开表或者analyze table时刷新统计信息,也会在show table status或者show index时更新统计信息,innodb由于统计信息存储在内存,因而在每次启动时也会重新生成统计信息,不过这在有大量数据时会导致性能问题,可以通过innodb_stats_on_metadata来关闭,不过这也会导致统计信息不准确。可以使用周期性analyze table来手动更新。

*B树可能会因为非顺序行插入、更新、页分裂导致索引碎片化(可能同时伴随数据碎片化),从而导致查询效率下降,可以通过删除重建索引来消除索引碎片化。对于数据碎片化,可以通过导出再导入数据或者不带操作的alter table重建表。

你可能感兴趣的:(mysql)