数据库索引设计与优化读书笔记

Posted dbLenis

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库索引设计与优化读书笔记相关的知识,希望对你有一定的参考价值。

高估理想,低看现实;错把长修的人生,当成短行的当下;想得太多,而做得太少。

    大家好!我是风一、这段时间针对于数据库的学习,一直在看一本《数据库索引设计与优化》,这是一位行业大佬推荐的一本蛮有年代的书,市面上基本快买不到正版了,但我想、针对于一些底层的技术研究、这些藏书的内容还是相当不错的,值得花大量的时间去学习、思考、总结;这里风一会结合看的几个章节的内容进行一个分享,如有不足之处、还望指正。

首先,书中的中心思路贯彻了一句话、那就是:所有"真正的"关系型系统的优化器都有一个相同的任务,它们都必须扫描索引和表。

这本书是从数据索引的设计开始写入的、并是像大多数关于索引的书籍或者文章、一开始就介绍索引是什么,可以做什么,文章是直接从索引设计切入、然后对服务器、数据库底层结构进行思考发散,来传输索引一系列关联的知识要点以及如何设计出三星索引和最佳索引。

以下开始进入正文:内容很肝、你若能坚持阅读,进行思考并实践、相信会带来不一样的收获。

第一章:概述

首先讲述的是关系型系统的工作原理,书中称为SQL优化器:关系型系统决定了如何以最高效的方式找到我们所需要的所有信息,然后通过优化器指挥表和索引进行扫描、来找到我们需要的数据。

然后介绍不合适的索引是性能低下的最常见的几个原因:

  • 表上没有足够多的索引

  • 一些查询语句可能没有有效的索引

  • 有时索引包含了正确的列,但列的顺序是不对的

其次是索引常见的误区理解:

  • 误区1:索引的层级不要超过5层,通常基于的假设前提是只有根页留在内层中的

  • 误区2:单表的索引数不要超过6个(保证所有的SQL语句都能够流畅运行是设计的底线)

  • 误区3:不应该索引不稳定的列

    • 索引行是按照索引键的顺序存储的,所以当索引键有一列被更新时,DBMS可能不得不把相应的行从旧的索引位置移动新的位置来保持这一顺序。

    • 当不稳定的列为复合索引的尾列,更新这个不稳定的列绝不会导致其迁移到新的叶子页。

    • 在当前的磁盘条件下,只有在更新频率多于10次/秒的情况下,不稳定的列才可能成为问题,但是这样的列并不常见。

最后是系统化的索引设计:

  • 找到由于索引不合适而导致运行太慢的查询语句(最差输入:导致执行时间最长的变量值)

  • 设计索引、使所有的查询语句都运行足够快(表的维护、插入、更新、删除)也必须足够快

第二章:表和索引结构

首先在索引设计之前,必须先理解索引和表是如何组织和使用的:

  • 索引页:

    • 表和索引行都被存储在索引页中。页的大小一般为4KB,这是一个可以满足大多数需求的大小,不过也可以使用其他大小;

    • 页的大小仅仅决定了一个页可以存储多少个索引行、表行以及一共需要多少个页来存储表或者索引。当表和索引被加载或重组时,每个页都会预留一定比例的空闲空间,以满足向其添加新的表行或索引行的需求;

    • 缓冲池的I/O活动都是基于页的,例如一次将一个完整的页读取到缓冲池。这意味着一次I/O会读入多条记录到缓冲池,而不仅是一条。当然一次I/O也可以读取多个页到缓冲池中。

  • 索引行:

    • 索引行是评估访问路径的时候是一个非常有用的概念;对于一个唯一索引、一个索引行等同于叶子页中的一个索引条目;对于一个非唯一索引、一个特定的索引值所对应的索引行应该被想象成独立的索引条目。每一个条目都含有相同的索引值(同一个索引),但是却有不同的指针;

    • 每一个索引行都指向表中的相对应的一行记录,指针通常标识了记录所存放的页及它在页中的位置。表中的每一行除了存储行之外,还包含了一些控制信息用于定义行并帮助DBMS处理插入或删除操作;

    • 当加载表或向表中插入记录的时候,表中记录的顺序可以被定义成和它的某一个索引记录相同的顺序。在这种情况下、当索引行被按照顺序处理时,对应的表行也将按顺序被逐个处理。索引和表都按照相同的顺序被访问,这是一个高效的处理过程;但表中记录的顺序只能按照表上某一个索引的顺序来组织,如果通过表上其他的索引来访问这张表,那么表中的相应记录将不会按照与索引条目相同的顺序存储。

  • 缓冲池和磁盘I/O:

    • 从DBMS缓冲池进行读取:如果一个索引或者表页在缓冲池中被找到,唯一的成本就是去处理这些索引或表记录。成本的记录高度依赖于这些记录是否是DBMS所需要的。

    • 直接从磁盘驱动器读取的成本是巨大的

    • 关系型数据库系统最重要的一个目标是确保表中或者索引中的数据是随时可用的。为了尽可能地实现这个目标,我们使用内存中的缓冲池来最小化磁盘活动;每一个DBMS都会根据对象类型(表或索引)以及页的大小拥有多个缓冲池,每一个缓冲池都足够大、大到可以存放许多页,可能是成千上万个页。缓冲池管理器将尽力确保经常使用的数据将保存在缓冲池中,以避免不必要的磁盘读写。

    • 索引或者表列在不在缓冲池中,访问的成本是不同的

然后是从磁盘驱动器进行的顺序读取:

  • 将多个页读取到缓冲池中,并按顺序处理他们。只有那些不在缓冲池中的页会从磁盘服务器中读取,因为那些已经在缓冲池中的页可能包含了尚未写入磁盘中的数据。

    • 全表扫描

    • 全索引扫描

    • 索引片扫描

    • 通过聚簇索引扫描表行

  • 顺序读取页有两个非常重要的优势:

    • 同时读取多个页、意味着平均读取每个月的时间将会减少;

    • 由于DBMS事先知道需要读取哪些页,所以可以在页被真正读取之前就将其读取出来,我们称为预读。

最后是DBMS的特性:

  • 页:表页的大小限定了表行的最大长度。通过一个表行必须能够存入一个页中,一个索引页必须能够存入一个叶子页中。如果一张表的平均行长大于表页的三分之一,那么空间利用率将会很糟糕。

  • 表聚簇:通常情况下、一张表页只包含一张表的数据。

第三章:SQL处理过程

首先、谓词(条件表达式或真值表达式)是索引设计的主要入手点:

  • WHERE 子句由一个或多个谓词(搜索参数)组成,如果一个索引可以满足SELECT查询语句的所有谓词表达式,那么优化器很有可能建立起一个高效的访问路径。

其次是优化器及访问路径:

  • 关系型数据库的一大优势就是、用户无需关系数据的访问方式,其访问方式是由DBMS的一个组件,即优化器来确认的:优化器是SQL处理过程的核心

  • 在SQL语句能够被真正执行之前,优化器必须首先确定如何访问数据。这包括:应该使用哪一个索引,索引的访问方式如何,是否要使用辅助式随机读等。

然后是索引过滤及过滤列:

  • 有些列,即可能存在于WHERE子句中,也存在于索引中,但是这个列却不能参与索引片的定义。并不是所有的索引列都能够定义索引片的大小。不过这些列仍然能够减少回表进行同步读的次数,所以这些列仍然扮演着很重要的角色,称为过滤列。

最后、过滤因子:

  • 过滤因子描述了谓词的选择性,即表中满足谓词条件的记录行数所占的比例,它主要依赖于列值的分布情况。

  • 过滤因子(FF) = 结果集的数量 / 表行的总数量。

  • 在评估一个索引是否合适时,最差的情况下的过滤因子比平均过滤因子更重要,因为最差情况与最差输入相关,即在该输入条件下,基于特定索引的查询将消耗最长的时间。

第四章:为SELECT语句创建理想的索引

从这一章开始、涉及的操作设计方面就逐渐偏多、而第四章也是我们对索引优化来说、最在意的部分篇章了。

首先明确一个很重要的概念:

  • 使用一个不合适的索引、可能比全表扫描的性能还差

其次引入三星索引的及构造:

        对于一个查询索引可能的最好索引、如果使用了三星索引的话,一次查询通常只需要进行一次磁盘随机读及一次窄索引片的扫描。

  • 星级的给定:

    • 如果一个查询相关的索引行是相邻的,或者至少相距足够靠近的话,那这个索引就可以标记上第一颗星。这最小化了必须扫描的索引片的厚度;

    • 如果索引行的顺序与查询语句的需求一致,则索引可以被标记上第二颗星;

    • 如果索引行包含了查询语句中的所有列,那么索引就可以被标记上第二颗星。这避免了访问表的操作:仅访问索引就可以了。

  • 三星索引的构造:

    • 满足第一颗星:取出所有等值谓词的列(where col = ...),把这些列作为索引最开头的列,这些列的顺序无所谓。

    • 满足第二颗星:将ORDER BY 列加入索引中。不要改变这些列的顺序,但是忽略那些在第一步中已经加入索引的列。

    • 满足第三颗星:将查询语句中剩余的列加入索引中去,列在索引中添加的顺序对查询语句没有影响,但是将易变的列放在最后能够降低更新的成本。

  • 范围谓词和三星索引:

    • ORDER BY 列能够使索引满足第二颗星,但是这个仅在将其放在BETWEEN谓词列之前的情况下才成立,否则就需要进行排序操作。因此、为了满足第二颗星、排序必须在BETWEEN谓词列的前面,这样就无需额外排序。

    • 第一颗星的索引扫描、取决于列的过滤因子,索引片越窄越好,因此会和第二颗星产生冲突。

    • 理想索引是三星索引都满足,但有时只能有第一颗星或第二颗星(范围谓词及列的排序情况),避免排序、拥有第二颗星;拥有可能的最窄的索引片,拥有第一颗星;一般而言、第一颗星比第二颗星重要(因为现如今的排序速度很快、但当一个程序只需要获取能够填充一个屏幕的数据量、第二颗星就比第一颗星好,因为数据库管理系统只要一次一次地读取数据行就能对结果集进行物化)。

  • 创建最佳索引(也许不是理想的三星索引)的过程公式化:

    • 首先设计一个索引片尽可能窄(第一颗星)的宽索引(第三颗星)。如果查询使用这个索引不需要排序(第二颗星),那这个索引就是三星索引。否则这个索引只能是二星索引,牺牲第二颗索引。或者采用另一种选择,避免排序,牺牲第一颗保留第二颗星。这两种二星索引中的一个将会是相应查询语句的最佳索引

最后、明确是否所有查询语句都需要设计理想索引:

  • 倘若、为所有语句都设计理想索引,那么随着表中新增的索引越来越多,这样表的插入、更新和删除操作就会变得很慢;当数据库管理系统向表中添加一行时,它必须在每一个索引上添加相应的行,都会消耗磁盘一定的时间。

  • 因此、即使在目前磁盘空间成本较低的情况下,机械性地为每一个查询设计最佳索引也是不明智的,因为索引的维护可能会使得一些程序速度太慢或者使磁盘负载超负荷(这会影响所有程序)。最佳索引(根据两个候选索引的方法设计或者使用索引工具设计)是一个好的开端,但是、在决定为一个新的查询创建理想索引前需要先考虑一下三种多余的索引(完全多余的索引、近乎多余的索引、可能多余的索引)。

  • 实际应用中、往往只对那些由于不合适的索引而导致速度太慢的查询语句进行索引设计

第六章:影响索引设计过程的因素

首先明确索引设计的概念:

  • 通常情况下,我们为一个SELECT语句设计一个索引(而不是反过来),但有时候为了让数据库管理系统能够以最高效的方式使用一个已经存在的索引,对一个SQL语句进行重写也是可取的。

最后、是影响索引的一些困难谓词:

  • 定义:如果一个谓词字段不能参与定义索引片,即它无法成为一个匹配谓词,那么我们就说它对优化器而言太困难了。用户友好型的数据库管理系统供应商会提供一个对自己的优化器而言的困难谓词列表。这些谓词有时被称为不可索引化;至于谓词对当前使用的优化器而言是否是困难的,无论自己是否清楚,用EXPLAIN检查一下总是明智的。

  • LIKE 谓词:如果数据库管理系统因为LIKE谓词不参与匹配过程而扫描整个索引,那么访问路径将会变得很慢。只有在已知优化器不会造成问题的前提下,才应该使用谓词LIKE。

  • OR操作符和布尔谓词:

    语句一:SELECT A,B,C FROM TABLE WHERE A>:A AND B>:B;

    语句二:SELECT A,B,C FROM TABLE WHERE A>:A OR B>:B

    语句一中(可独立过滤行数据),单个索引片将会被扫描,由谓词A>:A定义,因为它是个范围谓词,所以它是唯一的一个匹配字段。B字段因为索引筛选将会在此次扫描中被检查,如果谓词B>:B不满足条件,则索引条目将会被忽略。

    语句二中(不可独立过滤行数据),由于OR操作符的存在,数据库管理系统不能只读这一个索引片,因为即使不满足谓词A>:A条件,谓词B>:B的条件也可能会满足。

    • 假设在TABLE表上有一个索引(A,B):

假设一个谓词的判断结果为False,而这时如果不检查其他谓词就不能确定地将一行记录排除在外,那么这类谓词对优化器而言就是太过困难的。而"好"的谓词,因为只要任何一个简单谓词被判定为不满足条件,那么这一行就可以被排除;因此、理想索引的设计,只将那些对优化器而言足够简单的谓词添加至索引上。

第七章:被动式索引设计

首先、索引设计两种情况:

  • 产品应用前进行索引设计:理论上来说、在应用索引设计需要投入使用之前就被优化是最好的,但一些异常还是会在投入使用产生,因此需要使用一些性能工具和技术,以在用户觉察之前发现性能问题,或至少在问题变得令人无法忍受之前发现他们。在这种场景下、是属于主动式的索引设计的。

  • 产品应用后进行索引设计:在产品投入大规模使用后才对大部分索引进行调优工作,就像是将一个满载乘客的大型客机推下悬崖。这可能会有一个好结果,但是数据库员工必须做好快速响应的准备。如果没有人在应用开发阶段关注索引设计的话,一些程序可能会在应用头图使用后变得非常慢。

其次是、EXPLAIN的监测点:

  • 扫描整个索引或表时

  • 结果集的排序问题

  • 成本估算

EXPLAIN是分析优化器问题的一个不可或缺的工具。因为这正是他存在的理由,而不仅仅被用于索引设计。对于每个访问的表,优化器决策所基于的统计信息,连同当前的索引列表及其特征,都会在报告中提高。这是极其有用的。如果这些内容没有在报告中给出的话,及必须从系统表中分别获取这部分信息。最好的EXPLAIN还集成了另外一个有价值的步骤:提供索引优化建议。

相较EXPLAIN来说,测量是分析一个慢程序的更换的着手点,它除了包含访问路径问题外,也包含所有其他可能导致长运算时间的原因。然而、看监视报告可能会花费大量的时间,尤其是在没有实现一个好的监视策略的情况下。

然后介绍性能监视器的演进史:

最后、明确:有优化空间的问题制造和无优化空间的问题制造者

  • 有优化空间的问题制造者是指那些能够通过改进索引来获得大幅性能提升的事务。

  • 有优化空间的问题制造者有两个特征

    问题制造者:有优化空间(改进索引?) 、  无优化空间(更改程序?)

    • 磁盘服务时间长

    • 磁盘读大多是对表页的读取

截止到这、风一想分享的内容就基本结束了;这里再强调一下:以上所有的内容均来自于书籍:《数据库索引设计与优化》的读书笔记及总结、当然这里只挑选了部分章节的读书笔记进行分享、以及不足之处欢迎指正;另外、感兴趣的朋友、可以专门去找这本书籍进行详细阅读、相信会带来很大的收获。

最后:学而不思则罔、思而不学则殆;理论的知识的熟悉只是最基本的前提的、多次的不停实践才是核心之本、针对于数据库索引的这一大主题,还需要结合当前环境、不同服务器、数据库进行不同的多少实操,可能才能正在的运用于胸。

在生命的旅途中,即便我们不能成为一轮明月,也要努力成为一颗星星,在漫天中安静的发光、发亮。保持着内心的清宁与干净,温暖与明媚 — 风一

- END -

以上是关于数据库索引设计与优化读书笔记的主要内容,如果未能解决你的问题,请参考以下文章

《高性能MySQL》读书笔记之创建高性能的索引

《高性能MySQL》读书笔记

《Linux内核设计与实现》第18章读书笔记

读书笔记-《Redis设计与实现》数据结构与对象

读书笔记-《Redis设计与实现》数据结构与对象

读书笔记-《Redis设计与实现》数据结构与对象