《mysql技术内幕:innodb存储引擎》笔记

  • 任何时候Why都比What重要;
  • 不要相信任何的“神话”,学会自己思考;
  • 不要墨守成规,大部分人都知道的事情可能是错误的;
  • 不要相信网上的传言,去测试,根据自己的实践做出决定;
  • 花时间充分地思考,敢于提出质疑。

        1.MYSQL被设计为一个单进程多线程架构的数据库,MYSQL数据库实例在系统上的表现就是一个进程。

        2.Oracle中如果没有参数文件,在启动实例时会提示找不到该参数文件,数据库启动失败。而在 MYSQL数据库中,可以没有配置文件,在这种情况下, MYSQL会按照编译时的默认参数设置启动实例。

        3.从概念上来说,数据库是文件的集合,是依照某种数据模型组织起来并存放于二级存储器中的数据集合;数据库实例是程序,是位于用户与操作系统之间的一层数据管理软件,用户对数据库数据的任何操作,包括数据库定义、数据查询、数据维护、数据库运行控制等都是在数据库实例下进行的,应用程序只有通过数据库实例才能和数据库打交道。

        4.MYSQL由以下几部分组成:

  • 连接池组件
  • 管理服务和工具组件
  • SQL接口组件
  • 查询分析器组件
  • 优化器组件
  • 缓冲(Cache)组件
  • 插件式存储引擎
  • 物理文件

        5.MYSQL数据库区别于其他数据库的最重要的一个特点就是其插件式的表存储引擎。

        6.存储引擎是基于表的,而不是数据库。

        7.裸设备(raw device),也叫裸分区(原始分区),是一种没有经过格式化,不被Unix通过文件系统来读取的特殊字符设备文件。由应用程序负责对它进行读写操作。不经过文件系统的缓冲。它是不被操作系统直接管理的设备

        8.如果没有显式地在表定义时指定主键, Innodb存储引擎会为每一行生成一个6字节的ROWID,并以此作为主键。

        9.MyISAM存储引擎的另一个与众不同的地方是它的缓冲池只缓存(cache)索引文件,而不缓冲数据文件,这点和大多数的数据库都非常不同。

        10.MYISAM存储引擎表由MYD和MYI组成,MYD用来存放数据文件,MYI用来存放索引文件。

        11.可以通过使用myisampack工具来进一步压缩数据文件,因为myisampack工具使用赫夫曼(Huffman)编码静态算法来压缩数据,因此使用myisampack工具压缩后的表是只读的,当然用户也可以通过myisampack来解压数据文件。

        12.NDB存储引擎是一个集群存储引擎,其结构是share nothing的集群架构,因此能提供更高的可用性。NDB的特点是数据全部放在内存中(从MYSQL5.1版本开始,可以将非索引数据放在磁盘上),因此主键查找(primary key lookups)的速度极快,并且通过添加NDB数据存储节点(Data Node)可以线性地提高数据库性能,是高可用、高性能的集群系统。

        13.关于NDB存储引擎,有一个问题值得注意,那就是NDB存储引擎的连接操作(JOIN)是在MYSQL数据库层完成的,而不是在存储引擎层完成的。

        14.Memory存储引擎默认使用哈希索引,而不是我们熟悉的B+树索引。

        15.此外有一点容易被忽视, MYSQL数据库使用 Memory存储引擎作为临时表来存放查询的中间结果集( intermediate result)。如果中间结果集大于 Memory存储引擎表的容量设置,又或者中间结果含有TEXT或BLOB列类型字段,则 MYSQL数据库会把其转换到 MYISAM存储引擎表而存放到磁盘中。之前提到 MYISAM不缓存数据文件,因此这时产生的临时表的性能对于查询会有损失。

        16.Maria存储引擎是新开发的引擎,设计目标主要是用来取代原有MyISAM存储引擎,从而成为MYSQL的默认存储引擎。因此,它可以看做是MyISAM的后续版本。Maria存储引擎的特点是:支持缓存数据和索引文件,应用了行锁设计,提供了MVCC功能,支持事务和非事务安全的选项,以及更好的BLOB字符类型的处理性能。

        17.可以通过SHOW ENGINES语句查看当前使用的 MYSQL数据库所支持的存储引擎,也可以通过查找information_schema架构下的ENGINES表。

        18.MYSQL提供了一个非常好的用来演示MYSQL各项功能的示例数据库。

        19.连接MySQL操作是个连接进程和MySQL数据库实例进行通信,本质上是进程通信

        20.常用的进程通信方式有管道、命名管道、命名字、TCP/IP套接字、UNIX域套接字。

        21.TCP/IP套接字方式是MySQL数据库在任何平台下都提供的连接方式。

        22.在通过TCP/IP连接到MySQL实例时, MySQL数据库会先检查一张权限视图,用来判断发起请求的客户端IP是否允许连接到MySQL实例。该视图在mysql架构下,表名为user

        23.BDB是第一个支持事务的 MySQL存储引擎。

        24.Innodb存储引擎有多个内存块,可以认为这些内存块组成了一个大的内存池。

        25.后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据。此外将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常的情况下Innodb能恢复到正常运行状态。

        26.InnoDB存储引擎是多线程的模型,因此其后台有多个不同的后台线程,负责处理不同的任务。

  • Master Thread是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插人缓冲(INSERT BUFFER)、UNDO页的回收等。
  • 在Innodb存储引擎中大量使用了AIO( Async IO)来处理写IO请求,这样可以极大提高数据库的性能。而IO Thread的工作主要是负责这些IO请求的回调(call back)处理。分别是write、read、insert buffer和log IO thread。
  • 事务被提交后,其所使用的undolog可能不再需要,因此需要Purge Thread来回收已经使用并分配的undo页。
  • Page Cleaner Thread是在 Innodb1.2x版本中引入的。其作用是将之前版本中脏页的刷新操作都放入到单独的线程中来完成。

        27.在数据库中进行读取页的操作,首先将从磁盘读到的页存放在缓冲池中,这个过程称为将页“FIX”在缓冲池中。下一次再读相同的页时,若在缓冲池中,称该页在缓冲池中被命中,直接读取该页。

        28.对于数据库中页的修改操作,则首先修改在缓冲池中的页,然后再以一定的频率刷新到磁盘上。Checkpoint

        29.具体来看,缓冲池中缓存的数据页类型有:索引页、数据页、undo页、插入缓冲(insert buffer)、自适应哈希索引(adaptive hash index)、Innodb存储的锁信息(lock info)、数据字典信息(data dictionary)等。

        30.从InnoDB 1.0.x版本开始,允许有多个缓冲池实例。每个页根据哈希值平均分配到不同缓冲池实例中。

        31.从MySQL56版本开始,还可以通过information_schema架构下的表INNODB_BUFFER_POOL_STATS来观察缓冲的状态。

        32.在InnoDB存储引擎中,缓冲池中页的大小默认为16KB,同样使用LRU算法对缓冲池进行管理。LRU列表中还加入了midpoint位置。新读取到的页放入到LRU列表的midpoint位置。这个算法称为midpoint insertion strategy。在默认配置下,该位置在LRU列表长度的5/8处。

        33.在Innodb存储引擎中,把midpoint之后的列表称为old列表,之前的列表称为new列表。可以简单地理解为new列表中的页都是最为活跃的热点数据。

        34.InnoDB存储引擎引入了另一个参数来进一步管理LRU列表这个参数是innodb_old_blocks_time,用于表示页读取到mid位置后需要等待多久才会被加人到LRU列表的热端。

        35.Buffer pool hit rate,表示缓冲池的命中率,通常该值不应该小于95%。若发生Buffer pool hit rate的值小于95%这种情况,用户需要观察是否是由于全表扫描引起的LRU列表被污染的问题。

        36.InnoDB存储引擎从1.0.x版本开始支持压缩页的功能,即将原本16KB的页压缩为1KB、2KB、4KB和8KB。对于非16KB的页,是通过unzip_LRU列表进行管理的。

        37.首先,在unzip_LRU列表中对不同压缩页大小的页进行分别管理。其次,通过伙伴算法进行内存的分配。

        38.在LRU列表中的页被修改后,称该页为脏页(dirty page),即缓冲池中的页和磁盘上的页的数据产生了不一致。这时数据库会通过CHECKPOINT机制将脏页刷新回磁盘,而Flush列表中的页即为脏页列表。需要注意的是,脏页既存在于LRU列表中,也存在于Flush列表中。LRU列表用来管理缓冲池中页的可用性, Flush列表用来管理将页刷新回磁盘,二者互不影响。

        39.InnoDB存储引擎的内存区域除了有缓冲池外,还有重做日志缓冲(redo log buffer)。 Innodb存储引擎首先将重做日志信息先放入到这个缓冲区,然后按一定频率将其刷新到重做日志文件。

        40.下列三种情况下会将重做日志缓冲中的内容刷新到外部磁盘的重做日志文件中。

  • Master Thread每一秒将重做日志缓冲刷新到重做日志文件;
  • 每个事务提交时会将重做日志缓冲刷新到重做日志文件;
  • 当重做日志缓冲池剩余空间小于1/2时,重做日志缓冲刷新到重做日志文件。

        41.在InnoDB存储引擎中,对内存的管理是通过一种称为内存堆(heap)的方式进行的。在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行申请。

        42.为了避免发生数据丢失的问题,当前事务数据库系统普遍都采用了Write Ahead Log策略,即当事务提交时,先写重做日志,再修改页。当由于发生宕机而导致数据丢失时,通过重做日志来完成数据的恢复。这也是事务ACID中D(Durability持久性)的要求。

        43.因此Checkpoint(检査点)技术的目的是解决以下几个问题:

  • 缩短数据库的恢复时间;
  • 缓冲池不够用时,将脏页刷新到磁盘;
  • 重做日志不可用时,刷新脏页。

        44.对于InnoDB存储引擎而言,其是通过LSN(Log Sequence Number)来标记版本的。而LSN是8字节的数字,其单位是字节。每个页有LSN,重做日志中也有LSN,Checkpoint也有LSN。可以通过命令SHOW ENGINE INNODB STATUS来观察。

        45.在InnoDB存储引擎内部,有两种 Checkpoint,分别为:

  • Sharp Checkpoint 发生在数据库关闭时将所有的脏页都刷新回磁盘,这是默认的工作方式,即参数innodb_fast_shutdown=1。
  • Fuzzy Checkpoint 在Innodb存储引擎内部使用Fuzzy Checkpoint进行页的刷新,即只刷新部分脏页,而不是刷新所有的脏页回磁盘。

        46.在InnoDB存储引擎中可能发生如下几种情况的Fuzzy Checkpoint:

  • Master Thread Checkpoint:Master Thread 中发生的Checkpoint, 差不多以每秒或每十秒的速度从缓冲池的脏页列表中刷新一定比例的页回磁盘。
  • FLUSH LRU LIST Checkpoint:FLUSH_LRU_LIST Checkpoint是因为 Innodb存储引擎需要保证LRU列表中需要有差不多100个空闲页可供使用。
  • Async/Sync Flush Checkpoint:Async/Sync Flush Checkpoint指的是重做日志文件不可用的情况,这时需要强制将一些页刷新回磁盘,而此时脏页是从脏页列表中选取的。
  • Dirty Page too much Checkpoint:Checkpoint的情况是 Dirty Page too much,即脏页的数量太多,导致Innodb存储引擎强制进行Checkpoint。缓冲池中脏页数量占到75%。

        47.Master Thread具有最高的线程优先级别。其内部由多个循环(loop)组成:主循环(lop)、后台循环(backgroup loop)、刷新循环(flush loop)、暂停循环(suspend loop)。
Master Thread会根据数据库运行的状态在loop、background loop、flush loop和suspend loop中进行切换。

        48.每秒一次的操作包括:

  • 日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)
  • 合并插入缓冲(可能)--前1秒IO次数小于5次。
  • 至多刷新100个Innodb的缓冲池中的脏页到磁盘(可能)
  • 如果当前没有用户活动,则切换到background loop(可能)

        49.即使某个事务还没有提交, Innodb存储引擎仍然每秒会将重做日志缓冲中的内容刷新到重做日志文件。这一点是必须要知道的,因为这可以很好地解释为什么再大的事务提交(commit)的时间也是很短的。

        50.接着来看每10秒的操作,包括如下内容:

  • 刷新100个脏页到磁盘(可能的情况下)--10秒内IO次数小于200次。
  • 合并至多5个插人缓冲(总是);
  • 将日志缓冲刷新到磁盘(总是);
  • 删除无用的Undo页(总是);
  • 刷新100个或者10个脏页到磁盘(总是)。脏页超过70%刷新100个,否则刷新10%。

        51.对表进行update、delete这类操作时,原先的行被标记为删除,但是因为一致性读(consistent read)的关系,需要保留这些行版本的信息。

        52.InnoDB存储引擎在执行full purge操作时,每次最多尝试回收20个undo页。

        53.若当前没有用户活动(数据库空闲时)或者数据库关闭(shutdown),就会切换到这个循环。background loop会执行以下操作:

  • 删除无用的Undo页(总是)
  • 合并20个插入缓冲(总是)
  • 跳回到主循环(总是)
  • 不断刷新100个页直到符合条件(可能,跳转到fush loop中完成)。

        54.InnoDB Plugin(从 Innodb1.0x 版本开始)提供了参数innodb_io_capacity,用来表示磁盘IO的吞吐量,默认值为200。对于刷新到磁盘页的数量,会按照innodb_io_capacity 的百分比来进行控制。规则如下:

  • 在合并插入缓冲时,合并插入缓冲的数量为innodb_io_capacity值的5%;
  • 在从缓冲区刷新脏页时,刷新脏页的数量为innodb_io_ capacity。

        55.InnoDB对其内部进行了一些优化,当压力大时并不总是等待1秒。因此,并不能认为1_second和sleeps的值总是相等的。在某些情况下,可以通过两者之间差值的比较来反映当前数据库的负载压力。

        56.InnoDB存储引擎的关键特性包括:

  • 插入缓冲(Insert Buffer)
  • 两次写(Double Write)
  • 自适应哈希索引(Adaptive Hash Index)
  • 异步IO(Async IO)
  • 刷新邻接页(Flush Neighbor Page)

        57.B+树的特性决定了非聚集索引插入的离散性。

        58.对于非聚集索引的插入或更新操作,先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插人;若不在,则先放入到一个Insert Buffer对象中。然后再以定的频率和情况进行Insert Buffer和辅助索引页子节点的 merge(合并)操作,这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚集索引插入的性能。

        59.Insert Buffer的使用需要同时满足以下两个条件:

  • 索引是辅助索引(secondary index)
  • 索引不是唯一(unique)的。

        60.辅助索引不能是唯一的,因为在插入缓冲时,数据库并不去查找索引页来判断插入的记录的唯一性。如果去查找肯定又会有离散读取的情况发生,从而导致Insert Buffer失去了意义。

        61.MYSQL 4.1前的版本中每张表有一棵Insert Buffer B+树。而在现在的版本中,全局只有一棵Insert Buffer B+树,负责对所有的表的辅助索引进行Insert Buffer。而这棵B+树存放在共享表空间中,默认也就是ibdata1中。

        62.在Innodb存储引擎中,每个表有一个唯一的space id。

        63.因为启用Insert Buffer索引后,辅助索引页(spae,page_no)中的记录可能被插入到Insert Buffer B+树中,所以为了保证每次Merge Insert Buffer页必须成功,还需要有一个特殊的页用来标记每个辅助索引页(space, page_no)的可用空间。这个页的类型为Insert Buffer Bitmap。

        64.Merge Insert Buffer的操作可能发生在以下几种情况下:

  • 辅助索引页被读取到缓冲池时;
  • Insert Buffer Bitmap页追踪到该辅助索引页已无可用空间时;
  • Master Thread。

        65.重做日志中记录的是对页的物理操作,如果这个页本身已经发生了损坏,再对其进行重做是没有意义的。这就是说,在应用(apply)重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是doublewrite

        66.InnoDB存储引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。AHI是通过缓冲池的B+树页构造而来,因此建立的速度很快,而且不需要对整张表构建哈希索引。InnoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引。

        67.AHI有一个要求,即对这个页的连续访问模式必须是一样的。访问模式一样指的是查询的条件一样。哈希索引只能用来搜索等值的查询,而对于其他查找类型,如范围查找,是不能使用哈希索引的。

        68.为了提高磁盘操作性能,当前的数据库系统都采用异步IO(Asynchronous IO,AIO)的方式来处理磁盘操作。Innodb存储引擎亦是如此。AIO的另一个优势是可以进行IO Merge操作,也就是将多个IO合并为1个IO,这样可以提高IOPS的性能。

        69.InnoDB存储引擎还提供了Flush Neighbor Page(刷新邻接页)的特性。其工作原理为:当刷新一个脏页时, InnoDB存储引擎会检测该页所在区(extent)的所有页,如果是脏页,那么一起进行刷新。

        70.在关闭时,参数innodb_fast_shutdown影响着表的存储引擎为lnnoDB的行为该参数可取值为0、1、2,默认值为1。

  • 0表示在MySQL数据库关闭时, Innodb需要完成所有的full purge和merge insert buffer,并且将所有的脏页刷新回磁盘。如果在进行 InnoDB升级时,必须将这个参数调为0,然后再关闭数据库。
  • 1是参数innodb_fast_shutdown的默认值,表示不需要完成上述的full purge和merge insert buffer操作,但是在缓冲池中的一些数据脏页还是会刷新回磁盘。
  • 2表示不完成full purge和merge insert buffer操作,也不将缓冲池中的数据脏页写回磁盘,而是将日志都写入日志文件。这样不会有任何事务的丢失,但是下次MySQL数据库启动时,会进行恢复操作(recovery)。

        71.参数innodb_force_recovery还可以设置为6个非零值:1~6。大的数字表示包含了前面所有小数字表示的影响。
具体情况如下:

  • 1(SRV_FORCE_IGNORE_CORRUPT):忽略检查到的 corrupt页。
  • 2(SRV_FORCE_NO_BACKGROUND):阻止Master Thread线程的运行,如Master Thread线程需要进行full purge操作,而这会导致crash
  • 3(SRV FORCE NO TRX UNDO):不进行事务的回滚操作。
  • 4(SRV_FORCE_NO_IBUF_MERGE):不进行插入缓冲的合并操作。
  • 5(SRV_FORCE_NO_UNDO_LOG_SCAN):不查看撤销日志(Undo Log), InnoDB存储引擎会将未提交的事务视为已提交。
  • 6(SRV_FORCE_NO_LOG_REDO):不进行前滚的操作。

需要注意的是,在设置了参数innodb_force_recovery大于0后,用户可以对表进行select、create和drop操作,但Insert、update和delete这类DML操作是不允许的。

        72.MySQL数据库和InnoDB存储引擎表的各种类型文件。这些文件有以下这些。

  • 参数文件:告诉MySQL实例启动时在哪里可以找到数据库文件,并且指定某些初始化参数,这些参数定义了某种内存结构的大小等设置,还会介绍各种参数的类型。
  • 日志文件:用来记录MySQL实例对某种条件做出响应时写入的文件,如错误日志文件、二进制日志文件、慢査询日志文件、查询日志文件等。
  • socket文件:当用UNIX域套接字方式进行连接时需要的文件。
  • pid文件:MySQL实例的进程DI文件。
  • MySQL表结构文件:用来存放MySQL表结构定义文件。
  • 存储引擎文件:因为MySQL表存储引擎的关系,每个存储引擎都会有自己的文件来保存各种数据。这些存储引擎真正存储了记录和索引等数据。

        73.MySQL数据库中的参数可以分为两类:

  • 动态(dynamic)参数
  • 静态(static)参数

        74.MYSQL数据库中常见的日志文件有:

  • 错误日志(error log)
  • 二进制日志(binlog)
  • 慢查询日志(slow query log)
  • 查询日志(log)

        75.错误日志文件对MySQL的启动、运行、关闭过程进行了记录。该文件不仅记录了所有的错误信息,也记录一些警告信息或正确的信息。用户可以通过命令SHOW VARIABLES LIKE "log_error来定位该文件。当出现MySQL数据库不能正常启动时,第一个必须查找的文件应该就是错误日志文件。

        76.在默认情况下, MySQL数据库并不启动慢查询日志。另一个和慢查询日志有关的参数是log_queries_not_using_indexes,如果运行的SQL语句没有使用索引,则 MYSQL数据库同样会将这条SQL语句记录到慢査询日志文件。

        77.用户可以通过额外的参数long_query_ io将超过指定逻辑IO次数的SQL语句记录到slow log中。

        78.而为了兼容原MySQL数据库的运行方式,还添加了参数 slow_query_type,用来表示启用 slow log的方式,可选值为:

  • 0表示不将SQL语句记录到 slow log
  • 1表示根据运行时间将SQL语句记录到slow log
  • 2表示根据逻辑IO次数将SQL语句记录到slow log
  • 3表示根据运行时间及逻辑IO次数将SQL语句记录到slow log

        79.查询日志记录了所有对MySQL数据库请求的信息,无论这些请求是否得到了正确的执行。默认文件名为:主机名.log。

        80.二进制日志(binary log)记录了对MySQL数据库执行更改的所有操作,但是不包括SELECT和SHOW这类操作,因为这类操作对数据本身并没有修改。

        81.二进制日志主要有以下几种作用。

  • 恢复(recovery):某些数据的恢复需要二进制日志,例如,在一个数据库全备文件恢复后,用户可以通过二进制日志进行point-in-time的恢复。
  • 复制(replication):其原理与恢复类似,通过复制和执行二进制日志使一台远程的MySQL数据库(一般称为save或standby)与一台 MySQL数据库(一般称为master或primary)进行实时同步。
  • 审计(audit):用户可以通过二进制日志中的信息来进行审计,判断是否有对数据库进行注入的攻击。

        82.InnoDB存储引擎密切相关的文件:重做日志文件、表空间文件。

        83.每个Innodb存储引擎至少有1个重做日志文件组(group),每个文件组下至少有2个重做日志文件,如默认的ib_logfile0和ib_logfile。为了得到更高的可靠性,用户可以设置多个的镜像日志组(mirrored log groups),将不同的文件组放在不同的磁盘上,以此提高重做日志的高可用性。

        84.无论用户将二进制日志文件记录的格式设为STATEMENT还是ROW,又或者是MIXED,其记录的都是关于一个事务的具体操作内容,即该日志是逻辑日志。而 Innodb存储引擎的重做日志文件记录的是关于每个页(Page)的更改的物理情况

        85.重做日志条目是由4个部分组成:

  • redo_log_type占用1字节,表示重做日志的类型
  • space表示表空间的ID,但采用压缩的方式,因此占用的空间可能小于4字节
  • page_no表示页的偏移量,同样采用压缩的方式
  • redo_log_body表示每个重做日志的数据部分,恢复时需要调用相应的函数进行解析

        86.写入重做日志文件的操作不是直接写,而是先写入一个重做日志缓冲(redo log buffer)中,然后按照一定的条件顺序地写入日志文件。

        87.从重做日志缓冲往磁盘写人时,是按512个字节,也就是一个扇区的大小进行写入。因为扇区是写人的最小单位,因此可以保证写入必定是成功的。因此在重做日志的写入过程中不需要有doublewrite。

        88.从InnoDB存储引擎的逻辑存储结构看,所有数据都被逻辑地存放在一个空间中,称之为表空间(tablespace)。表空间又由段(segment)、区(extent)、页(page)组成。页在一些文档中有时也称为块(block), InnoDB存储引擎的逻辑存储结构大致如图4-1所示。

        89.InnoDB存储引擎不会在执行rollback时去收缩这个表空间。虽然InnoDB不会回收这些空间,但是会自动判断这些undo信息是否还需要,如果不需要,则会将这些空间标记为可用空间,供下次undo使用。

        90.常见的段有数据段、索引段、回滚段等。数据段即为B+树的叶子节点,索引段即为B+树的非索引节点。

        91.区是由连续页组成的空间,在任何情况下每个区的大小都为1MB。为了保证区中页的连续性,InnoDB存储引擎一次从磁盘申请4~5个区。在默认情况下,Innodb存储引擎页的大小为16KB,即一个区中一共有64个连续的页。

        92.在InnoDB存储引擎中,常见的页类型有:

  • 数据页(B-tree Node)
  • undo页(undo Log Page)
  • 系统页(System Page)
  • 事务数据页(Transaction system Page)
  • 插入缓冲位图页(Insert Buffer Bitmap)
  • 插入缓冲空闲列表页(Insert Buffer Free List)
  • 未压缩的二进制大对象页(Uncompressed BLOB Page)
  • 压缩的二进制大对象页(compressed BLOB Page)

        93.InnoDB存储引擎是面向列的(row-oriented),也就说数据是按行进行存放的。每个页存放的行记录也是有硬性定义的,最多允许存放16KB/2-200行的记录,即7992行记录。这里提到了 row-oriented的数据库,也就是说,存在有column-oriented的数据库。

        94.InnoDB存储引擎提供了Compact和Redundant两种格式来存放行记录数据。

        95.Compact行记录是在MySQL 5.0中引入的,其设计目标是高效地存储数据。简单来说,一个页中存放的行数据越多,其性能就越高。

         96.每行数据除了用户定义的列外,还有两个隐藏列,事务ID列和回滚指针列分别为6字节和7字节的大小。若InnoDB表没有定义主键,每行还会增加一个6字节的rowid列。

        97.不管是CHAR类型还是VARCHAR类型,在compact格式下NULL值都不占用任何存储空间。

        98.n_fields值代表一行中列的数量,占用10位。同时这也很好地解释了为什么MySQL数据库一行支持最多的列为1023。

        99.InnoDB存储引擎可以将一条记录中的某些数据存储在真正的数据页面之外。

        100.MYSQL官方手册中定义的65535长度是指所有VARCHAR列的长度总和,如果列的长度总和超出这个长度,依然无法创建。

        101.在一般情况下, InnoDB存储引擎的数据都是存放在页类型为B-tree node中。但是当发生行溢出时,数据存放在页类型为Uncompress BLOB页中。

        102.是放在数据页中还是BLOB页中,和前面讨论的VARCHAR一样,至少保证一个页能存放两条记录。

        103.MySQL4.1版本开始,CHAR(N)中的N指的是字符的长度,而不是之前版本的字节长度。也就说在不同的字符集下,CHAR类型列内部存储的可能不是定长的数据。

        104.在多字节字符集的情况下,CHAR和VARCHAR的实际行存储基本是没有区别的。

        105.Innodb数据页由以下7个部分组成:

  • File header(文件头)
  • Page Header(页头)
  • Infimum和 Supremum Records
  • User records(用户记录,即行记录)
  • Free Space(空闲空间)
  • Page Directory(页目录)
  • File trailer(文件结尾信息)

        106.在InnoDB存储引擎中,每个数据页中有两个虚拟的行记录,用来限定记录的边界。Infimum记录是比该页中任何主键值都要小的值, Supremum指比任何可能大的值还要大的值。

        107.B+树索引本身并不能找到具体的一条记录,能找到只是该记录所在的页。数据库把页载入到内存,然后通过Page Directory再进行二叉查找。

        108.数据完整性有以下三种形式:

  • 实体完整性保证表中有一个主键。
  • 域完整性保证数据每列的值满足特定的条件。
  • 参照完整性保证两张表之间的关系。

        109.对于InnoDB存储引擎本身而言,提供了以下几种约束:

  • Primary Key
  • Unique Key
  • Foreign Key
  • Default
  • NOT NULL

        110.约束更是一个逻辑的概念,用来保证数据的完整性,而索引是一个数据结构,既有逻辑上的概念,在数据库中还代表着物理存储的方式。

        111.如果用户想通过约東对于数据库非法数据的插入或更新,即MySQL数据库提示报错而不是警告,那么用户必须设置参数sql_mode,用来严格审核输入的参数。

        112.目前MySQL数据库的外键约束都是即时检查(immediate check)。

        113.通过触发器,在MySQL数据库中实现了类似物化视图的功能。但是MySQL数据库本身并不支持物化视图,因此对于物化视图支持的查询重写(Query Rewrite)功能就显得无能为力,用户只能在应用程序端做一些控制。

        114.MySQL数据库支持的分区类型为水平分区,并不支持垂直分区。此外, MySQL数据库的分区是局部分区索引,一个分区中既存放了数据又存放了索引。

        115.不论创建何种类型的分区,如果表中存在主键或唯一索引时,分区列必须是唯一索引的一个组成部分。

        116.分区类型:

  • RANGE分区;
  • LIST分区;
  • HASH分区;
  • KEY分区;
  • COLUMNS分区;

        116.RANGE、LIST、HASH和KEY这四种分区中,分区的条件是:数据必须是整型(interger),如果不是整型,那应该需要通过函数将其转化为整型。

        117.RANGE COLUMNS分区可以对多个列的值进行分区。

        118.子分区(subpartitioning)是在分区的基础上再进行分区,有时也称这种分区为复合分区(composite partitioning)。

        119.MySQL数据库的分区总是视NULL值视小于任何的一个非NULL值,这和MySQL数据库中处理NULL值的ORDER BY操作是一样的。

        120.数据库的应用分为两类:一类是OLTP(在线事务处理);另一类是OLAP(在线分析处理)。

        121.因此对于使用InnoDB存储引擎作为OLTP应用的表在使用分区时应该十分小心,设计时确认数据的访问模式,否则在OLTP应用下分区可能不仅不会带来查询速度的提高,反而可能会使你的应用执行得更慢。

        122.MySQL 5.6开始支持ALTER TABLE … EXCHANGE PARTITION语法。该语句允许分区或子分区中的数据与另一个非分区的表中的数据进行交换。

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

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

        124.B+树中的B不是代表二叉(binary),而是代表平衡(balance),因为B+树是从最早的平衡二叉树演化而来,但是B+树不是一个二叉树。

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

        126.平衡二叉树的定义如下:首先符合二叉查找树的定义其次必须满足任何节点的两个子树的高度最大差为1。

        127.平衡二叉树的查找性能是比较高的,但不是最高的,只是接近最高性能。最好的性能需要建立一棵最优二叉树,但是最优二叉树的建立和维护需要大量的操作,因此,用户一般只需建立一棵平衡二叉树即可。

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

        129.扇出(fan out):节点中指向子节点的指针数量。

        130.旋转发生在Leaf Page已经满,但是其的左右兄弟节点没有满的情况下。这时B+树并不会急于去做拆分页的操作,而是将记录移到所在页的兄弟节点上。

        131.B+树使用填充因子(fill factor)来控制树的删除变化,50%是填充因子可设的最小值。

        132.在多数情况下,查询优化器倾向于采用聚集索引。因为聚集索引能够在B+树索引的叶子节点上直接找到数据。此外,由于定义了数据的逻辑顺序,聚集索引能够特别快地访问针对范围值的查询。

        133.InnoDB中的槽是一个稀疏目录,即一个槽中可能属于多个记录,最少属于4个目录,最多属于8个目录。二叉查找的结果只是一个粗略的结果,所以InnoDB必须通过recorder。

        134.聚集索引的存储并不是物理上连续的,而是逻辑上连续的。这其中有两点:一是前面说过的页通过双向链表链接,页按照主键的顺序排序;另一点是每个页中的记录也是通过双向链表进行维护的,物理存储上可以同样不按照主键存储。

        135.聚集索引的另ー个好处是,它对于主键的排序查找和范围查找速度非常快。

        136.对于其他的一些数据库,如Microsoft SQL Server数据库,其有一种称为堆表的表类型,即行数据的存储按照插入的顺序存放。这与 MySQL数据库的MyISAM存储引擎有些类似。堆表的特性决定了堆表上的索引都是非聚集的,主键与非主键的区别只是是否唯一且非空(NOT NULL)。因此这时书签是一个行标识符(Row Identified,RID),可以用如“文件号:页号:槽号”的格式来定位实际的行数据。

        137.此外另一个不能忽视的是对于排序和范围査找,索引组织表通过B+树的中间节点就可以找到要査找的所有页,然后进行读取,而堆表的特性决定了这对其是不能实现的。

        138.若插入是随机的,则取页的中间记录作为分裂点的记录,这和之前介绍的相同。若往同一方向进行插入的记录数量为5,并且目前已经定位(cursor)到的记录(Innodb存储引擎插入时,首先需要进行定位,定位到的记录为待插人记录的前条记录)之后还有3条记录,则分裂点的记录为定位到的记录后的第三条记录,否则分裂点记录就是待插入的记录。

        139.用户可以设置对整个列的数据进行索引,也可以只索引一个列的开头部分数据。

        140.若用户想要查看表中索引的信息,可以使用命令SHOW INDEX。

  • Collation:列以什么方式存储在索引中。可以是A或NULL。B+树索引总是A,即排序的。如果使用了Heap存储引擎,并且建立了Hash索引,这里就会显示NULL了。因为Hash根据Hash桶存放索引数据,而不是对数据进行排序。
  • Cardinality:非常关键的值,表示索引中唯一值的数目的估计值。 Cardinality表的行数应尽可能接近1,如果非常小,那么用户需要考虑是否可以删除此索引。
  • Sub_part:是否是列的部分被索引。如果看idx_b这个索引,这里显示100,表示只对b列的前100字符进行索引。如果索引整个列,则该字段为NULL。
  • Packed:关键字如何被压缩。如果没有被压缩,则为NULL口Null:是否索引的列含有NULL值。可以看到idx_b这里为Yes,因为定义的列b允许NULL值。
  • Index_type:索引的类型。 Innodb存储引擎只支持B+树索引,所以这里显示的都是BTREE。

        141.Cardinality值非常关键,优化器会根据这个值来判断是否使用这个索引。

        142.Cardinality为NULL,在某些情况下可能会发生索引建立了却没有用到的情况。或者对两条基本一样的语句执行EXPLAIN,但是最终出来的结果不一样:一个使用索引,另外一个使用全表扫描。这时最好的解决办法就是做一次ANALYZE TABLE的操作。

        143.Innodb存储引擎从Innodb10x版本开始支持一种称为 Fast Index Creation(快速索引创建)的索引创建方式——简称FIC。

        144.由于FIC在索引的创建的过程中对表加上了S锁,因此在创建的过程中只能对该表进行读操作,若有大量的事务需要对目标表进行写操作,那么数据库的服务同样不可用。此外,FIC方式只限定于辅助索引,对于主键的创建和删除同样需要重建一张表。

        145.Online Schema Change(在线架构改变,简称OSC)最早是由Facebook实现的一种在线执行DDL的方式,并广泛地应用于Facebook的 MySQL数据库。所谓“在线”是指在事务的创建过程中,可以有读写事务对表进行操作,这提高了原有MySQL数据库在DDL操作时的并发性。

        146.MySQL 5.6版本开始支持Online DDL(在线数据定义)操作,其允许辅助索引创建的同时,还允许其他诸如INSERT、UPDATE、DELETE这类DML操作,这极大地提高了MySQL数据库在生产环境中的可用性。

        147.对于什么时候添加B+树索引,一般的经验是,在访问表中很少一部分时使用B+树索引才有意义。对子性别字段地区字段、类型字段,它们可取值的范围很小,称为低选择性。

        148.Cardinality值非常关键,表示索引中不重复记录数量的预估值。同时需要注意的是, Cardinality是一个预估值,而不是一个准确值,基本上用户也不可能得到一个准确的值。在实际应用中, Cardinality / n_rows_in_table应尽可能地接近1。如果非常小,那么用户需要考虑是否还有必要创建这个索引。

        149.默认InnoDB存储引擎对8个叶子节点(Leaf Page)进行采样。

        150.在OLTP应用中,查询操作只从数据库中取得一小部分数据,一般可能都在10条记录以下,甚至在很多时候只取1条记录,如根据主键值来取得用户信息,根据订单号取得订单的详细信息,这都是典型OLTP应用的查询语句。在OLAP应用中,都需要访问表中大量的数据,根据这些数据来产生查询的结果,这些查询多是面向分析的查询,目的是为决策者提供支持。

        151.possible_keys在这里有两个索引可供使用,分别是单个的userid索引和(userid, buy_date)的联合索引。但是优化器最终的选择是索引userid,因为该索引的叶子节点包含单个键值,所以理论上一个页能存放的记录应该更多。

        152.InnoDB存储引擎并不会选择通过查询聚集索引来进行统计。由于表上还有辅助索引,而辅助索引远小于聚集索引,选择辅助索引可以减少IO操作。

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

        154.原因在于用户要选取的数据是整行信息,而OrderId索引不能覆盖到我们要查询的信息,因此在对OrderId索引查询到指定数据后,还需要一次书签访问来査找整行数据的信息。虽然OrderId索引中数据是顺序存放的,但是再一次进行书签查找的数据则是无序的,因此变为了磁盘上的离散读操作。如果要求访问的数据量很小,则优化器还是会选择辅助索引,但是当访问的数据占整个表中数据的蛮大一部分时(一般是20%左右),优化器会选择通过聚集索引来查找数据。因为之前已经提到过,顺序读要远远快于离散读。

        155.MYSQL数据库支持索引提示(INDEX HINT),显式地告诉优化器使用哪个索引。

        156.某SQL语句可以选择的索引非常多,这时优化器选择执行计划时间的开销可能会大于SQL语句本身。例如,优化器分析Range查询本身就是比较耗时的操作。

        157.虽然我们指定使用a索引,但是优化器实际选择的是通过表扫描的方式。因此, USE INDEX只是告诉优化器可以选择该索引,实际上优化器还是会再根据自己的判断进行选择。

        158.MySQL5.6版本开始支持Multi-Range Read(MRR)优化。Multi-Range Read优化的目的就是为了减少磁盘的随机访问,并且将随机访问转化为较为顺序的数据访问,这对于IO-bound类型的SQL查询语句可带来性能极大的提升。Mutil-Range Read优化可适用于range,ref, eq_ref类型的查询。MRR优化有以下几个好处:

  • MRR使数据访问变得较为顺序。在查询辅助索引时,首先根据得到的查询结果,按照主键进行排序,并按照主键排序的顺序进行书签查找。
  • 减少缓冲池中页被替换的次数。
  • 批量处理对键值的查询操作。

        159.Multi-Range Read还可以将某些范围查询,拆分为键值对,以此来进行批量的数据查询。这样做的好处是可以在拆分过程中,直接过滤一些不符合查询条件的数据。

        160.在支持Index Condition Pushdown后, MySQL数据库会在取出索引的同时,判断是否可以进行WHERE条件的过滤,也就是将WHERE的部分过滤操作放在了存储引擎层。在某些査询下,可以大大减少上层SQL层对记录的索取(fetch),从而提高数据库的整体性能。

        161.在之前的 MySQL数据库中, InnoDB存储引擎并不支持全文检索技术。从InnoDB 1.2.x版本开始,InnoDB存储引擎开始支持全文检索,其支持MyISAM存储引擎的全部功能,并且还支持其他的一些特性。

        162.全文检索通常使用倒排索引(inverted index)来实现。它在辅助表(auxiliary table)中存储了单词与单词自身在一个或多个文档中所在位置之间的映射。这通常利用关联数组实现,其拥有两种表现形式:

  • inverted file index,其表现形式为{单词,单词所在文档的ID}
  • full inverted index,其表现形式为{单词,(单词所在文档的ID,在具体文档中的位置)}

        163.InnoDB存储引擎从1.2x版本开始支持全文检索的技术,其采用full inverted index的方式。而MyISAM存储引擎不支持该特性。倒排索引需要将word存放到一张表中,这个表称为Auxiliary Table(辅助表)。在InnoDB存储引擎中,为了提高全文检索的并行性能,共有6张Auxiliary Table,目前每张表根据word的Latin编码进行分区。

        164.Auxiliary Table是持久的表,存放于磁盘上。然而在 InnoDB存储引擎的全文索引中,还有另外一个重要的概念FTS Index Cache(全文检索索引缓存),其用来提高全文检索的性能。

        165.FTS Index Cache是一个红黑树结构,其根据(word,ilist)进行排序。

        166.对于InnoDB存储引擎而言,其总是在事务提交时将分词写人到FTS Index Cache,然后再通过批量更新写入到磁盘。

        167.FTS Document ID是另外一个重要的概念。在InnoDB存储引擎中,为了支持全文检索,必须有一个列与word进行映射,在InnoDB中这个列被命名为FTS_DOC_ID,其类型必须是BIGINT UNSIGNED NOT NULL,并且InnoDB存储引擎自动会在该列上加入一个名为FTS_DOC_ID_INDEX的Unique Index。

        168.stopword列表(stopword list)表示该列表中的word不需要对其进行索引分词操作。默认共有36个stopword。

        169.当前InnoDB存储引擎的全文检索还存在以下的限制:

  • 每张表只能有一个全文检索的索引。
  • 由多列组合而成的全文检索的索引列必须使用相同的字符集与排序规则。
  • 不支持没有单词界定符(delimiter)的语言,如中文、日语、韩语等。

        170.在WHERE条件中使用MATCH函数,查询返回的结果是根据相关性(Relevance)进行降序排序的,即相关性最高的结果放在第一位。相关性的值是一个非负的浮点数字,0表示没有任何的相关性。根据MySQL官方的文档可知,其相关性的计算依据以下四个条件:

  • word是否在文档中出现。
  • word在文档中出现的次数。
  • word在索引列中的数量。
  • 多少个文档包含该word。

        171.MySQL数据库允许使用IN BOOLEAN MODE修饰符来进行全文检索。

  • +表示该word必须存在。
  • -表示该word必须被排除。
  • (no operator)表示该word是可选的,但是如果出现,其相关性会更高
  • @distance表示查询的多个单词之间的距离是否在 distance之内, distance的单位是字节。
  • >表示出现该单词时增加相关性。
  • <表示出现该单词时降低相关性。
  • ~表示允许出现该单词,但是出现时相关性为负(全文检索查询允许负相关性)
  • *表示以该单词开头的单词。
  • "表示短语。

        172.MySQL数据库还支持全文检索的扩展査询。这种査询通常在査询的关键词太短,用户需要implied knowledge(隐含知识)时进行。而这时可以使用Query Expansion模式来开启全文检索的implied knowledge。

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

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

        174.InnoDB存储引擎实现了如下两种标准的行级锁:

  • 共享锁(S Lock),允许事务读一行数据。
  • 排他锁(X Lock),允许事务删除或更新一行数据。

        175.InnoDB存储引擎支持多粒度(granular)锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。为了支持在不同粒度上进行加锁操作, InnoDB存储引擎支持种额外的锁方式,称之为意向锁(Intention Lock)。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度(fine granularity)上进行加锁。

        176.InnoDB存储引擎支持意向锁设计比较简练,其意向锁即为表级别的锁。设计目的主要是为了在一个事务中揭示下一行将被请求的锁类型。其支持两种意向锁:

  • 1)意向共享锁(IS Lock),事务想要获得一张表中某几行的共享锁
  • 2)意向排他锁(IX Lock),事务想要获得一张表中某几行的排他锁

详解 MySql InnoDB 中意向锁的作用 - 掘金

        177.由于InnoDB存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫以外的任何请求。

        178.从InnoDB1.0开始,在INFORMATION_SCHEMA架构下添加了表INNODB_TRX、INNODB_LOCKS、 INNODB_LOCK_WAITS。

 

        179.一致性的非锁定读(consistent nonlocking read)是指 Innodb存储引擎通过行多版本控制(multi versioning)的方式来读取当前执行时间数据库中行的数据。如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此去等待行上锁的释放。相反地, InnoDB存储引擎会去读取行的一个快照数据。

        180.该实现是通过undo段来完成。而undo用来在事务中回滚数据,因此快照数据本身是没有额外的开销。

        181.在事务隔离级别READ COMMITTED和REPEATABLE READ(Innodb存储引擎的默认事务隔离级别)下,InnoDB存储引擎使用非锁定的一致性读。然而,对于快照数据的定义却不相同。在READ COMMITTED事务隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。而在REPEATABLE READ事务隔离级别下,对于快照数据,非一致性读总是读取事务开始时的行数据版本。

        182.对于READ COMMITTED的事务隔离级别而言,从数据库理论的角度来看,其违反了事务ACID中的I的特性,即隔离性。

        183.InnoDB存储引擎对于SELECT语句支持两种一致性的锁定读(locking read)操作:

  • SELECT… FOR UPDATE(加X锁)
  • SELECT… LOCK IN SHARE MODE(加S锁)

        184.插入操作会依据这个自增长的计数器值加1赋予自增长列。这个实现方式称做AUTO-INC Locking。这种锁其实是采用一种特殊的表锁机制,为了提高插入的性能,锁不是在一个事务完成后才释放,而是在完成对自增长值插入的SQL语句后立即释放。

        185.从MySQL 5.1.22版本开始, InnoDB存储引擎中提供了一种轻量级互斥量的自增长实现机制,这种机制大大提高了自增长值插入的性能。

        186.在InnoDB存储引擎中,自增长值的列必须是索引,同时必须是索引的第个列。

        187.对于外键值的插入或更新,首先需要查询父表中的记录,即 SELECT父表。但是对于父表的 SELECT操作,不是使用一致性非锁定读的方式,因为这样会发生数据不一致的问题,因此这时使用的是 SELECT…LOCK IN SHARE MODE方式,即主动对父表加一个S锁。

        188.InnoDB存储引擎有3种行锁的算法,其分别是

  • Record Lock:单个行记录上的锁
  • Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
  • Next-Key Lock: Gap Lock + Record Lock,锁定一个范围,并且锁定记录本身

        189.Record Lock总是会去锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时 InnoDB存储引擎会使用隐式的主键来进行锁定。

        190.当查询的索引含有唯一属性时, InnoDB存储引擎会对 Next-Key Lock进行优化,将其降级为Record Lock,即仅锁住索引本身,而不是范围。

        191.有两个索引,其需要分别进行锁定。

        192.采用Next-Key Lock的锁定技术称为Next-Key Locking。其设计的目的是为了解决 Phantom problem。

        193.当查询的索引含有唯一属性时, InnoDB存储引擎会对Next-Key Lock进行优化,将其降级为Record Lock,即仅锁住索引本身,而不是范围。

        194.对于辅助索引,其加上的是Next-Key Lock,特别需要注意的是, Innodb存储引擎还会对辅助索引下一个键值加上gap lock。

        195.常见的解决幻读的方法有两种:

  • 将隔离级别升级到SERIALIZABLE;
  • 加锁。

        196.在理解脏读(Dirty Read)之前,需要理解脏数据的概念。而所谓脏数据是指事务对缓冲池中行记录的修改,并且还没有被提交(commit)。

        197.不可重复读和脏读的区别是:脏读是读到未提交的数据,而不可重复读读到的却是已经提交的数据,但是其违反了数据库事务一致性的要求。

        198.InnoDB存储引擎的默认事务隔离级别是READ REPEATABLE,采用Next-Key Lock算法,避免了不可重复读的现象。

        199.在当前数据库的任何隔离级别下,都不会导致数据库理论意义上的丢失更新问题。这是因为,即使是READ UNCOMMITTED的事务隔离级别,对于行的DML操作,需要对行或其他粗粒度级别的对象加锁。

        200.需要牢记的是,在默认情况下InnoDB存储引擎不会回滚超时引发的错误异常。其实InnoDB存储引擎在大部分情况下都不会对异常进行回滚。

        201.除了超时机制,当前数据库还都普遍采用wait-for graph(等待图)的方式来进行死锁检测。较之超时的解决方案,这是一种更为主动的死锁检测方式。InnoDB存储引擎也采用的这种方式。wait-for graph要求数据库保存以下两种信息:

  • 锁的信息链表
  • 事务等待链表

        202.通过上述链表可以构造出一张图,而在这个图中若存在回路,就代表存在死锁,因此资源间相互发生等待。在 wait-for graph中,事务为图中的节点。

        203.若存在则有死锁,通常来说InnoDB存储引擎选择回滚undo量最小的事务。

        204.InnoDB存储引擎并不会回滚大部分的错误异常,但是死锁除外。发现死锁后, InnoDB存储引擎会马上回滚一个事务,这点是需要注意的。因此如果在应用程序中捕获了1213这个错误,其实并不需要对其进行回滚。

        205.此外还存在另一种死锁,即当前事务持有了待插入记录的下一个记录的X锁,但是在等待队列中存在一个S锁的请求,则可能会发生死锁。

        206.InnoDB存储引擎不存在锁升级的问题。因为其不是根据每个记录来产生行锁的,相反,其根据每个事务访问的每个页对锁进行管理的,采用的是位图的方式。因此不管个事务锁住页中一个记录还是多个记录,其开销通常都是一致的。

        207.InnoDB 存储引擎中,每个数据页对应一个锁位图,用于记录该页上每个行的锁状态。锁位图是一个由位组成的数据结构,每一位代表该页上的一个数据行。当一个事务需要锁定某个数据行时,它会将该行所在的页的锁位图中对应的位设置为 1,表示该行被当前事务所锁定。当事务释放该行锁定时,会将该页的锁位图中对应的位设置为 0,表示该行不再被锁定。这样就能够实现基于页的并发控制。

        208.从事务理论的角度来说,可以把事务分为以下几种类型:

  • 扁平事务(Flat Transactions)
  • 带有保存点的扁平事务(Flat Transactions with Savepoints)
  • 链事务(Chained Transactions)
  • 嵌套事务(Nested Transactions)
  • 分布式事务(Distributed Transactions)

        209.对于InnoDB存储引擎来说,其支持扁平事务、带有保存点的事务、链事务、分布式事务。对于嵌套事务,其并不原生支持,因此,对有并行事务需求的用户来说MySQL数据库或InnoDB存储引擎就显得无能为力了。然而用户仍可以通过带有保存点的事务来模拟串行的嵌套事务。

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

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

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

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

        214.MySQL数据库上层的二进制日志是一种逻辑日志,其记录的是对应的SQL语句。而InnoDB存储引擎层面的重做日志是物理格式日志,其记录的是对于每个页的修改。

        215.二进制日志只在事务提交完成后进行一次写入。而 InnoDB存储引擎的重做日志在事务进行中不断地被写入,这表现为日志并不是随事务提交的顺序进行写入的。

        216.在InnoDB存储引擎中,重做日志都是以512字节进行存储的。这意味着重做日志缓存、重做日志文件都是以块(block)的方式进行保存的,称之为重做日志块(redo log block),每块的大小为512字节。

        217.InnoDB存储引擎运行过程中,log buffer根据一定的规则将内存中的log block刷新到磁盘。这个规则具体是:

  • 事务提交时
  • 当log buffer中有一半的内存空间已经被使用时
  • log checkpoint时

        218.不同的数据库操作会有对应的重做日志格式。此外,由于 InnoDB存储引擎的存储管理是基于页的,故其重做日志格式也是基于页的。虽然有着不同的重做日志格式,但是它们有着通用的头部格式。通用的头部格式由以下3部分组成:

  • redo_log_type:重做日志的类型。
  • space:表空间的ID。
  • page_no:页的偏移量。

        219.LSN代表的是日志序列号。表示事务写入重做日志的字节的总量。页中的LSN用来判断页是否需要进行恢复操作。

        220.InnoDB存储引擎在启动时不管上次数据库运行时是否正常关闭,都会尝试进行恢复操作。因为重做日志记录的是物理日志,因此恢复的速度比逻辑日志要快很多。

        221.redo存放在重做日志文件中,与redo不同,undo存放在数据库内部的一个特殊段(segment)中,这个段称为undo段(undo segment)。undo段位于共享表空间内。

        222.undo是逻辑日志,只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了,但是数据结构和页本身在回滚之后可能大不相同。当InnoDB存储引擎回滚时,它实际上做的是与先前相反的工作。

        223.除了回滚操作,undo的另一个作用是MVCC,即在 InnoDB存储引擎中MVCC的实现是通过undo来完成。

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

        225.在InnoDB存储引擎中, undo log分为:

  • insert undo log
  • update undo log

        226.update undo log记录的是对delete和update操作产生的undo log。该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。

        227.update主键的操作其实分两步完成。首先将原主键记录标记为已删除,因此需要产生一个类型为TRX_UNDO_DEL_MARK_REC的undo log,之后插入一条新的记录,因此需要产生一个类型为TRX_UNDO_INSERT_REC的undo log。

        228.purge用于最终完成delete和update操作。这样设计是因为InnoDB存储引擎支持MVCC,所以记录不能在事务提交时立即进行处理。这时其他事务可能正在引用这行,故InnoDB存储引擎需要保存记录之前的版本。

        229.当InnoDB存储引擎的压力非常大时,并不能高效地进行purge操作。会“延缓”DML的操作。

        230.BLGC的步骤分为以下三个阶段:

  • Flush阶段,将每个事务的二进制日志写人内存中。
  • Sync阶段,将内存中的二进制日志刷新到磁盘,若队列中有多个事务,那么仅一次fsync操作就完成了二进制日志的写入,这就是BLGC。
  • Commit阶段, leader根据顺序调用存储引擎层事务的提交, InnoDB存储引擎本就支持group commit,因此修复了原先由于锁prepare_commit_mutex导致group commit失效的问题。

        231.START TRANSACTION、BEGN语句都可以在MySQL命令行下显式地开启事务。但是在存储过程中, MySQL数据库的分析器会自动将BEGIN识别为 BEGIN…END,因此在存储过程中只能使用START TRANSACTION语句来开启一个事务。

        232.COMMIT和COMMIT WORK语句基本是一致的,都是用来提交事务。不同之处在于COMMIT WORK用来控制事务结束后的行为是CHAIN还是 RELEASE的。如果是CHAIN方式,那么事务就变成了链事务。

        233.另一个容易犯的错误是ROLLBACK TO SAVEPOINT,虽然有ROLLBACK,但其并不是真正地结束一个事务,因此即使执行了ROLLBACK TO SAVEPOINT,之后也需要显式地运行COMMIT或ROLLBACK命令。

        234.Microsoft SQL Server数据库中,即使是DDL也是可以回滚的。

        235.另外需要注意的是, TRUNCATE TABLE语句是DDL,因此虽然和对整张表执行DELETE的结果是一样的,但它是不能被回滚的。

        236.SQL标准定义的四个隔离级别为:

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE

        237.READ UNCOMMITTED称为浏览访向(rowse access),针对事务血的READ COMMITTED称为游标稳定(cursor stability)。 REPEATABLE READ是2999°的隔离,没有幻读的保护。 SERIALIZABLE称为隔离,或3°的隔离。SQL和SQL2标准的默认事务隔离级别是 SERIALIZABLE。

        238.InnoDB存储引擎默认支持的隔离级别是REPEATABLE READ,但是与标准SQL不同的是, InnoDB存储引擎在REPEATABLE READ事务隔离级别下,使用Next-Key-Lock锁的算法,因此避免幻读的产生。

        239.InnoDB存储引擎提供了对XA事务的支持,并通过XA事务来支持分布式事务的实现。在使用分布式事务时, InnoDB存储引擎的事务隔离级别必须设置为SERIALIZABLE。

        240.在MySQL数据库中还存在另外一种分布式事务,其在存储引擎与插件之间,又或者在存储引擎与存储引擎之间,称之为内部XA事务。最为常见的内部XA事务存在于binlog与InnoDB存储引擎之间。

        241.不好的事务习惯:在循环中进行事务的提交(每一次提交都要写一次重做日志)

        242.对于不同语言的API,自动提交是不同的。MySQL C API默认的提交方式是自动提交,而 MySQL Python API则会自动执行 SET AUTOCOMMIT=0,以禁用自动提交。

        243.事务是数据库区别于文件系统的一个关键特性。

        244.根据备份的方法不同可以将备份分为:

  • Hot Backup(热备)
  • Cold Backup(冷备)
  • Warm Backup(温备)

        245.按照备份后文件的内容,备份又可以分为:

  • 逻辑备份--文件内容是可读的,一般是文本文件。
  • 裸文件备份--复制数据库的物理文件。

        246.若按照备份数据库的内容来分,备份又可以分为:

  • 完全备份
  • 增量备份
  • 日志备份

        247.冷备的优点是:

  • 备份简单,只要复制相关文件即可。
  • 备份文件易于在不同操作系统,不同 MYSQL版本上进行恢复。
  • 恢复相当简单,只需要把文件恢复到指定位置即可。
  • 恢复速度快,不需要执行任何SQL语句,也不需要重建索引。

        248.冷备的缺点是:

  • InnoDB存储引擎冷备的文件通常比逻辑文件大很多,因为表空间中存放着很多其他的数据,如undo段,插人缓冲等信息。
  • 冷备也不总是可以轻易地跨平台。操作系统、 MySQL的版本、文件大小写敏感和浮点数格式都会成为问题。

        249.mysqldump备份工具最初由Igor Romanenko编写完成,通常用来完成转存(dump)数据库的备份及不同数据库之间的移植,如从MySQL低版本数据库升级到MySQL高版本数据库,又或者从MySQL数据库移植到Oracle、 Microsoft SQL Server数据库等。

        250.SELECT....NTO OUTFILE语句也是一种逻辑备份的方法,更准确地说是导出一张表中的数据。

        251.通过mysqldump可以恢复数据库,但是经常发生的一个问题是, mysqldump可以导出存储过程、导出触发器、导出事件、导出数据,但是却不能导出视图。

        252.通常来说并发地对同一张表进行导入,其效果一般都不会比串行的方式好。

        253.如果需要恢复多个二进制日志文件,最正确的做法应该是同时恢复多个二进制日志文件,而不是一个一个地恢复。

        254.ibbackup是InnoDB存储引擎官方提供的热备工具,可以同时备份MyISAM存储引擎和InnoDB存储引擎表。对于InnoDB存储引擎表其备份工作原理如下:

  • 1)记录备份开始时, InnoDB存储引擎重做日志文件检查点的LSN。
  • 2)复制共享表空间文件以及独立表空间文件
  • 3)记录复制完表空间文件后, InnoDB存储引擎重做日志文件检查点的LSN。
  • 4)复制在备份时产生的重做日志。

        255.XtraBackup备份工具是由Percona公司开发的开源热备工具。支持MySQL5.0以上的版本。XtraBackup在GPL v2开源下发布,官网地址是:https://launchpad.net/percona-xtrabackup。

        256.MySQL数据库本身提供的工具并不支持真正的增量备份,更准确地说,二进制日志的恢复应该是point-in-time的恢复而不是增量备份。而XtraBackup工具支持对于InnoDB存储引擎的增量备份,其工作原理如下:

  • 1)首选完成一个全备,并记录下此时检查点的LSN。
  • 2)在进行增量备份时,比较表空间中每个页的LSN是否大于上次备份时的LSN,如果是,则备份该页,同时记录当前检查点的LSN。

        257.MySQL数据库本身并不支持快照功能,因此快照备份是指通过文件系统支持的快照功能对数据库进行备份。备份的前提是将所有数据库文件放在同一文件分区中,然后对该分区进行快照操作。

        258.LVM使用了写时复制(Copy-on-write)技术来创建快照。当创建一个快照时,仅复制原始卷中数据的元数据(meta data),并不会有数据的物理操作,因此快照的创建过程是非常快的。当快照创建完成,原始卷上有写操作时,快照会跟踪原始卷块的改变将要改变的数据在改变之前复制到快照预留的空间里,因此这个原理的实现叫做写时复制。

        259.从服务器有2个线程,一个是I/O线程,负责读取主服务器的二进制日志,并将其保存为中继日志;另一个是SQL线程,负责执行中继日志。

        260.MySQL的复制是异步实时的,并非完全的主从同步。若用户要想得知当前的延迟,可以通过命令SHOW SLAVE STATUS和SHOW MASTER STATUS得知。

        261.复制可以用来作为备份,但功能不仅限于备份,其主要功能如下:

  • 数据分布。由于MySQL数据库提供的复制并不需要很大的带宽要求,因此可以在不同的数据中心之间实现数据的复制。
  • 读取的负载平衡。通过建立多个从服务器,可将读取平均地分布到这些从服务器中,并且减少了主服务器的压力。一般通过DNS的Round-Robin和Linux的LVS功能都可以实现负载平衡。
  • 数据库备份。复制对备份很有帮助,但是从服务器不是备份,不能完全代替备份
  • 高可用性和故障转移。通过复制建立的从服务器有助于故障转移,减少故障的停机时间和恢复时间。

        262.可以说OLAP是CPU密集型的操作,而OLTP是IO密集型的操作。

        263.如何判断当前数据库的内存是否已经达到瓶颈了呢?可以通过查看当前服务器的状态,比较物理磁盘的读取和内存读取的比例来判断缓冲池的命中率,通常 InnoDB存储引擎的缓冲池的命中率不应该小于99%。SHOW GLOBAL STAUTS LIKE 'innodb%read%'\G;

        264.闪存提供的读写速度是非对称的。读取速度要远快于写入的速度,因此对于固态硬盘在数据库中的应用,应该好好利用其读取的性能,避免过多的写入操作。

        265.对于数据库应用来说,RAID 10是最好的选择,它同时兼顾了RAID 1和RAID 0的特性。但是,当一个磁盘失效时,性能可能会受到很大的影响,因为条带(strip)会成为瓶颈。

        266.RAID Write Back功能是指RAID控制器能够将写人的数据放入自身的缓存中,并把它们安排到后面再执行。这样做的好处是,不用等待物理磁盘实际写入的完成,因此写入变得更快了。对于数据库来说,这显得十分重要。

        267.但是,当操作系统或数据库关机时, Write Back功能可能会破坏数据库的数据。这是由于已经写人的数据库可能还在RAID卡的缓存中,数据可能并没有完全写入磁盘,而这时故障发生了。为了解决这个问题,目前大部分的硬件RAID卡都提供了电池备份单元(BBU, Battery Backup Unit),因此可以放心地开启Write Back的功能。

        268.MySQL数据库本身提供了一些比较优秀的工具,常用的基准测试工具:sysbench和mysql-tpcc。

猜你喜欢

转载自blog.csdn.net/zaimeiyeshicengjing/article/details/119936534
今日推荐