《MySQL技术内幕 InnoDB存储引擎》笔记

278 阅读17分钟

第1章 MySQL体系结构和存储引擎

1.1 定义数据库和实例

1.2 MySQL体系结构

1.3 MySQL存储引擎

1.3.1 InnoDB存储引擎

InnoDB存储引擎支持事务,其设计目标主要面向在线事务处理(OLTP)的应用。其特点是行锁设计、支持外键,并支持类似于Oracle的非锁定读,即默认读取操作不会产生锁。从MySQL数据库5.5.8版本开始,InnoDB存储引擎是默认的存储引擎

InnoDB存储引擎将数据放在一个逻辑的表空间中,这个表空间就像黑盒一样由InnoDB存储引擎自身进行管理。从MySQL4.1(包括4.1)版本开始,它可以将每个InnoDB存储引擎的表单独存放到一个独立的ibd文件中。此外,InnoDB存储引擎支持用裸设备(row disk)用来建立其表空间。

InnoDB通过使用多版本并发控制(MVCC)来获得高并发性,并且实现了SQL 标准的4种隔离级别,默认为REPEATABLE级别。同时,使用一种被称为next-key locking的策略来避免幻读(phantom)现象的产生。除此之外,InnoDB储存引擎还提 供了插入缓冲(insert buffer)、二次写(double write)、自适应哈希索引(adaptive hash index)、预读(read ahead)等高性能和高可用的功能。

1.3.2 MYISAM存储引擎

MyISAM存储引擎不支持事务、表锁设计(??它应该是支持表锁的呀,不支持行锁),支持全文索引,主要面向一些OLAP数据库应用。在MySQL5.5.8版本之前MyISAM存储引擎是默认的存储引擎。 MyISAM存储引擎的缓冲池只缓存(cache)索引文件而不缓冲数据文件,这点和大多数的数据库都非常不同(MySQL数据库只缓存其索引文件,数据文件的缓存交由操作系统本身来完成)。

MyISAM存储引擎表由MYD(用来存放数据文件)和MYI(用来存放索引文件)组成。

1.4 各存储引擎之间的比较

image.png

1.5 连接MySQL

第2章 InnoDB存储引擎

第3章 文件

第4章 表

第5章 索引与算法

5.1 InnoDB存储引擎概述

InnoDB存储引擎支持以下几种常见索引:

  • B+树索引
  • 全文索引
  • 哈希索引

InnoDB存储引擎支持的哈希索引是自适应的(它会根据表的使用情况自动为表生成哈希索引,不能人工干预)

B+树索引并不能找到一个给定键值的具体行,B+树索引能找到的只是被查找数据行所在的页。然后数据库通过把页读入到内存,再在内存中进行查找,最后得到要查找的数据。

5.2 数据结构与算法

5.3 B+树

B+树由B树索引顺序访问方法演化而来。

B+树是为磁盘或其他直接存取辅助设备设计的一种平衡查找树。在B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶子节点上,由各叶子节点指针进行连接。

1. B+树的插入操作

image.png

2. B+树的删除操作

image.png

5.4 B+树索引

B+树索引的本质就是B+树在数据库中的实现。B+索引在数据库中有一个特点是高扇出性,因此在数据库中,B+树的高度一般在2 ~ 4层,这也就是说查找某一键值的行记录时最多只需要2到4次IO,这倒不错。因为当前一般的机械磁盘每秒至少可以做100次IO,2 ~ 4次的IO意味着查询时间只需0.02 ~ 0.04秒。

B+树索引可以分为聚集索引(clustered inex)和辅助索引(secondary index),但是不管是聚集还是辅助的索引,其内部都是B+树的,即高度平衡的,叶子节点存放着所有的数据。聚集索引与辅助索引不同的是,叶子节点存放的是否是一整行的信息

1. 聚集索引

InnoDB存储引擎表是索引组织表即表中数据按照主键顺序存放。而聚集索引就是按照每张表的主键构造一棵B+树,同时叶子节点中存放的即为整张表的行记录数据,也将聚集索引的叶子节点称为数据页。聚集索引的这个特性决定了索引组织表中数据也是索引的一部分。同B+树数据结构一样,每个数据页都通过一个双向链表来进行链接。

由于实际的数据页只能按照一棵B+树进行排序,因此每张表只能拥有一个聚集索引。在多数情况下,查询优化器倾向于采用聚集索引。因为聚集索引能够在B+树索引的叶子节点上直接找到数据。此外,由于定义了数据的逻辑顺序,聚集索引能够特别快地访问针对范围值的查询。查询优化器能够快速发现某一段范围的数据页需要扫描。

许多数据库的文档会这样告诉读者:聚集索引按照顺序物理地存储数据。但是试想一下,如果聚集索引必须按照特定顺序存放物理记录,则维护成本显得非常之高。所以,聚集索引的存储并不是物理上连续的,而是逻辑上连续的。这其中有两点:一是前面说过的页通过双向链表链接,页按照主键的顺序排序;另一点是每个页中的记录也是通过双向链表进行维护的,物理存储上可以同样不按照主键存储。

聚集索引的另一个好处是,它对于主键的排序查找和范围查找速度非常快。叶子节点的数据就是用户所要查询的数据。如用户需要查询一张注册用户的表,查询最后注册的10位用户,由于B+树索引是双向链表的,用户可以快速找到最后一个数据页,并取出10条记录。

另一个是范围查询(range query),即如果要查找主键某一范围内的数据,通过叶子节点的上层中间节点就可以得到页的范围,之后直接读取数据页即可。

执行explain可得到MySQL数据库的执行计划(execute plan),mysql > explain

2. 辅助索引

对于辅助索引(Secondary Index,也称非聚集索引/二级索引),叶子节点并不包含行记录的全部数据。叶子节点除了包含键值以外,每个叶子节点中的索引行中还包含了一个书签(bookmark)。该书签用来告诉InnoDB存储引擎哪里可以找到与索引相对应的行数据。由于InnoDB存储引擎表是索引组织表,因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键。图5-15显示了InnoDB存储引擎中辅助索引与聚集索引的关系。

image.png

辅助索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引。当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶级别的指针获得指向主键索引的主键,然后再通过主键索引来找到一个完整的行记录。举例来说,如果在一棵高度为3的辅助索引树中查找数据,那需要对这棵辅助索引树遍历3次找到指定主键,如果聚集索引树的高度同样为3,那么还需要对聚集索引树进行3次查找,最终找到一个完整的行数据所在的页,因此一共需要6次逻辑IO访问以得到最终的一个数据页。

3. B+树索引的分裂

4. B+树索引的管理

  1. 索引管理

索引的创建和删除可以通过两种方法,一种是ALTER TABLE,另一种是CREATE/DROP INDEX。

5.5 Cardinality值

5.6 B+树索引的使用

1. 不同应用中B+树索引的使用

2. 联合索引

联合索引是指对表上的多个列进行索引。前面讨论的情况都是只对表上的一个列进行索引。联合索引的创建方法与单个索引创建的方法一样,不同之处仅在于有多个索引列。

例如,以下代码创建了一张t表,并且索引idx_a_b是联合索引,联合的列为(a,b)。

CREATE TABLE t (
a INT,
b INT,
PRIMARY KEY (a),
KEY idx a b ( a,b)
) ENGINE = INNODB

从本质上来说,联合索引也是一棵B+树,不同的是联合索引的键值的数量不是1,而是大于等于2。两个整数列组成的联合索引如图:

image.png

从图5-22得,其实和之前讨论的单个键值的B+树并没有什么不同,键值都是排序的,通过叶子节点可以逻辑上顺序地读出所有数据,就上面的例子来说,即(1,1)、(1,2)、(2,1)、(2,4)、(3,1)、(3,2)。数据按(a,b)的顺序进行了存放。

因此,对于查询SELECT * FROM TABLE WHERE a=xxx and b=xxx,显然是可以使用(a,b)这个联合索引的。对于单个的a列查询SELECT * FROM TABLE WHERE a=xxx,也可以使用这个(a,b)索引。但对于b列的查询SELECT * FROM TABLE WHERE b=xxx,则不可以使用这棵B+树索引。可以发现叶子节点上的b值为1、2、1、4、1、2,显然不是排序的,因此对于b列的查询使用不到(a,b)的索引。

联合索引的第二个好处是已经对第二个键值进行了排序处理。例如,在很多情况下应用程序都需要查询某个用户的购物情况,并按照时间进行排序,最后取出最近三次的购买记录,这时使用联合索引可以避免多一次的排序操作,因为索引本身在叶子节点已经排序了。

3. 覆盖索引

InnoDB存储引擎支持覆盖索引(covering index,或称索引覆盖),即从辅助索引中就可以得到查询的记录,而不需要查询聚集索引中的记录。使用覆盖索引的一个好处是辅助索引不包含整行记录的所有信息,故其大小要远小于聚集索引,因此可以减少大量的IO操作。

注意:对于InnoDB版本小于1.0的,或者MySQL数据库版本为5.0或以下的,InnoDB存储引擎不支持覆盖索引特性。

对于InnoDB存储引擎的辅助索引而言,由于其包含了主键信息,因此其叶子节点存放的数据为(primary key1,primary key2,…,key1,key2,…)。例如,下列语句都可仅使用一次辅助联合索引来完成查询:

SELECT key2 FROM table WHERE key1=xxx;
SELECT primary key2,key2 FROM table WHERE key1=xxx;
SELECT primary key1,key2 FROM table WHERE key1=xxx;
SELECT primary key1,primary key2, key2 FROM table WHERE key1=xxx;

覆盖索引的另一个好处是对某些统计问题而言的。具体内容详见书本。

4. 优化器选择不使用索引的情况

在某些情况下,当执行EXPLAIN命令进行SQL语句的分析时,会发现优化器并没有选择索引去查找数据,而是通过扫描聚集索引,也就是直接进行全表的扫描来得到数据。这种情况多发生于范围查找JOIN链接操作等情况下。

☃...

5.7 哈希算法

哈希算法不只存在于索引中,每个数据库应用中都存在该数据库结构。

☃1. 哈希表

哈希表(Hash Table)也称散列表,由直接寻址表改进而来。

☃2. InnoDB存储引擎中的哈希算法

InnoDB存储引擎使用哈希算法来对字典进行查找,其冲突机制采用链表方式,哈希函数采用除法散列方式。对于缓冲池页的哈希表来说,在缓冲池中的Page页都有一个chain指针,它指向相同哈希函数值的页。而对于除法散列,m的取值为略大于2倍的缓冲池页数量的质数。例如:当前参数innodb_buffer_pool_size的大小为10M,则共有640个16KB的页。对于缓冲池页内存的哈希表来说,需要分配640×2=1280个槽,但是由于1280不是质数,需要取比1280略大的一个质数,应该是1399,所以在启动时会分配1399个槽的哈希表,用来哈希查询所在缓冲池中的页。

☃3. 自适应哈希索引

自适应哈希索引采用之前讨论的哈希表的方式实现。不同的是,这仅是数据库自身创建并使用的,DBA本身并不能对其进行干预。自适应哈希索引经哈希函数映射到一个哈希表中,因此对于字典类型的查找非常快速,如SELECT * FROM TABLE WHERE index_col='xxx'。但是对于范围查找就无能为力了。通过命令SHOW ENGINE INNODB STATUS可以看到当前自适应哈希索引的使用状况。

哈希索引只能用来搜索等值的查询,如:SELECT * FROM table WHERE index_col='xxx'。而对于其他查找类型,如范围查找,是不能使用哈希索引的。因此,这里出现了non-hash searches/s的情况。通过 hash searches:non-hash searches 可以大概了解使用哈希索引后的效率。由于自适应哈希索引是由InnoDB存储引擎自己控制的,因此这里的这些信息只供参考。不过可以通过参数innodb_adaptive_hash_index来禁用或启动此特性,默认为开启。

5.8 全文检索

☃1. 概述

全文检索(Full-Text Search)是将存储于数据库中的整本书或整篇文章中的任意内容信息查找出来的技术。它可以根据需要获得全文中有关章、节、段、句、词等信息,也可以进行各种统计和分析。

在之前的MySQL数据库中,InnoDB存储引擎并不支持全文检索技术。大多数的用户转向MyISAM存储引擎,这可能需要进行表的拆分,并将需要进行全文检索的数据存储为MyISAM表。这样的确能够解决逻辑业务的需求,但是却丧失了InnoDB存储引擎的事务性,而这在生产环境应用中同样是非常关键的。

从InnoDB 1.2.x版本开始,InnoDB存储引擎开始支持全文检索,其支持MyISAM存储引擎的全部功能,并且还支持其他的一些特性。

☃2. 倒排索引

☃3. InnoDB全文检索

InnoDB存储引擎从1.2.x版本开始支持全文检索的技术,其采用full inverted index的方式。

☃4. 全文检索

MySQL数据库支持全文检索(Full-Text Search)的查询,其语法为:

MATCH (col1,col2,...) AGAINST (expr [search_modifier])
search_modifier:
    {
        IN NATURAL LANGUAGE MODE
       | IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION
       | IN BOOLEAN MODE
       | WITH QUERY EXPANSION
}

MySQL数据库通过MATCH()...AGAINST()语法支持全文检索的查询,MATCH指定了需要被查询的列,AGAINST指定了使用何种方法去进行查询。

第6章 锁

6.1 什么是锁

6.2 lock与latch

latch 一般称为闩锁(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差。在InnoDB存储引擎中,latch又可以分为mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。

lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。并且一般lock的对象仅在事务commit或rollback后进行释放(不同事务隔离级别释放的时间可能不同)。此外,lock,正如在大多数数据库中一样,是有死锁机制的。表6-1显示了lock与latch 的不同。

lock 与 latch 比较:

image.png

6.3 InnoDB存储引擎中的锁

锁的类型

一致性非锁定读

一致性锁定读

自增长与锁

外键和锁

6.4 锁的算法

6.5 锁问题

6.6 阻塞

6.7 死锁

6.8 锁升级

第7章 事务

7.1 认识事务

7.2 事务的实现

原子性、一致性、持久性通过数据库的 redo log和 undo log来完成。redo log称为重做日志,用来保证事务的原子性和持久性。undo log用来保证事务的一致性。

有的DBA或许会认为 undo是 redo的逆过程,其实不然。redo和 undo的作用都可以视为是一种恢复操作,redo恢复提交事务修改的页操作,而 undo回滚行记录到某个特定版本。因此两者记录的内容不同,redo通常是物理日志,记录的是页的物理修改操作undo是逻辑日志,根据每行记录进行记录

redo

重做日志用来实现事务的持久性。其由两部分组成:一是内存中的重做日志缓冲(redo log buffer),其是易失的;二是重做日志文件(redo log file),其是持久的。

redo log用来保证事务的持久性,undo log用来帮助事务回滚及MVCC的功能。redo log基本上都是顺序写的,在数据库运行时不需要对redo log的文件进行读取操作。而 undo log是需要进行随机读写的。

在MySQL数据库中还有一种二进制日志(binlog),其用来进行POINT-IN-TIME(PIT)的恢复及主从复制环境的建立

undo

重做日志记录了事务的行为,可以很好地通过其对页进行“重做”操作。但是事务有时还需要回滚操作,这时就需要undo。

undo log 是逻辑日志,因此只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了,但是数据结构和页本身在回滚之后可能大不相同。

除了回滚操作,undo的另一个作用是 MVCC,即在 InnoDB存储引擎中 MVCC的实现是通过 undo来完成。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过 undo读取之前的行版本信息,以此实现非锁定读取。

undo log会产生redo log,也就是 undo log的产生会伴随着redo log的产生,这是因为undo log 也需要持久性的保护。

7.3 事务控制语句

7.4 隐式提交的SQL语句

7.5 对于事务操作的统计

7.6 事务的隔离级别

7.7 分布式事务

7.8 不好的事务习惯

7.9 长事务

第8章 备份与恢复

8.1 备份与恢复概述

8.2 冷备

8.3 逻辑备份

8.4 二进制日志备份与恢复

8.5 热备

8.6 快照备份

8.7 复制

复制的工作原理

复制(replication)是MySQL数据库提供的一种高可用高性能的解决方案,一般用来建立大型的应用。复制的工作原理分为以下3个步骤:

  1. 主服务器(master)把数据更改记录到二进制日志(binlog)中。
  2. 从服务器(slave)把主服务器的二进制日志复制到自己的中继日志(relay log)中。
  3. 从服务器重做中继日志中的日志,把更改应用到自己的数据库上,以达到数据的最终一致性。

快照+复制的备份架构

第9章 性能调优

第10章 InnoDB存储引擎源代码的编译与调试