MySQL MySQL

InnoDB实现原理

2019-02-12  本文已影响111人  岳峙

MVCC

MVCC (Multiversion Concurrency Control),即多版本并发控制技术,它使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能

InnoDB体系架构

image.png

数据页

数据页,操作系统通过机械磁头读取硬盘中的文件,读取指定的某数据之后,自动读取大约4KB的页数据,将与此数据有关的一并读取,为了机器性能考虑,因为在磁盘,磁道,扇区中机械查找数据并不容易,直接读取的数据页为了提升机器效率

索引页

索引页,MySQL的InnoDB引擎通过B+实现索引页,相比B树,大大减小了树深,在磁盘中读取数据,减小读取次数,有效提升速度,B+树搜索之后并不能直接找到数据,只能找到索引页,将该页加载入内存,通过key在其中进行二分查找,找到key对应的slot,在slot管理的记录中查找最终的数据

插入缓冲

学长在讲解的时候,只是提到了插入缓冲对于提高数据的插入和修改有很大的帮助,对于非聚集索引的插人或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插人;若不在,则先放入到一个 Insert Buffer 对象中,好似欺骗。数据库这个非聚集的索引已经插到叶子节点,而实际并没有,只是存放在另一个位置。然后再以一定的频率和情况进行 Insert Buffer 和辅助索引页子节点的 merge(合并)操作,这时通常能将多个插人合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚集索引插人的性能。

自适应哈希索引

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

日志文件

错误日志(ErrLog)

错误⽇志对 MySQL 的启动, 运⾏, 关闭过程进⾏了记录, 不仅记录了所有错误信息, 也记录了⼀些警告信息, 遇到问题是应该⾸先查看
该⽂件以便定位问题. 当 MySQL 不能正常启动, 或者 MySQL 在运⾏期间遇到的内存不⾜等问题都可以在其中找到详细记录.

慢查询日志(SlowLog)

慢查询⽇志能够定位到可能存在问题的 SQL 语句, 可以设定⼀个阈值, 将运⾏时间超过该值的 SQL 语句都记录到慢查询⽇志⽂件中.
另⼀个可⽤的参数是 log_queries_not_using_indexes, 如果SQL 语句没有使⽤索引, 同样会将这条 SQL 语句记录到慢查询⽇志⽂件.

查询日志(QueryLog)

查询⽇志记录了所有对 MySQL 请求的信息, 不论是否得到了正确的执⾏. 默认⽂件名: 主机名.log.

二进制日志

BinLog 记录了对 MySQL 执⾏更改的 (不包括SELECT 和 SHOW 等对数据本身没有修改) 的操作. 不过若⼀个修改操作本身没有导致
数据库发⽣变化也会被写⼊ BigLog 中(如修改了⼀条不存在的记录). 此外, ⼆进制⽇志还包括了 DB 修改操作的时间以及其他额外信
息, BinLog 主要的⽤途有:

  1. 数据恢复: 某些数据的恢复需要 BinLog.
  2. 集群同步: 通过复制和执⾏ BinLog, 使⼀台远程的 MySQL 从库与当前主库进⾏实时同步.
  3. 数据同步: 不同于集群同步, 业务场景中经常需要其他组件(搜索引擎, 业务报表等)需要感知数据库的修改, 此时可以通过同步
    BinLog 实现.

InnoDB表存储

在 InnoDB 中, 表都是根据主键顺序组织存放的, 这种存储⽅式成为索引组织表. 每张表都有⼀个主键, 如果没有显
式指定, InnoDB 会使⽤第⼀个⾮空的唯⼀索引, 如果没有唯⼀索引, InnoDB 会⾃动创建⼀个 6 字节⼤⼩的指针作
为主键.
所有的数据被存放在⼀个表空间 (tablespace) 中, 表空间⼜由 段(segment), 区(extent), ⻚(page) 组成.
• 表空间(Tablespace), 是 InnoDB 存储引擎逻辑存储的顶层对象, 所有数据都存放在表空间. 其中包括数据, 索引,
插⼊缓冲, 回滚信息, 系统事务信息等.
• 段(Segment), 表空间时由多个段组成的, 常⻅的段有数据段, 索引段, 回滚段等. InnoDB 由于索引组织表的特点,
数据即索引, 索引即数据. 因此数据段是 B+ 树的叶⼦节点, 索引段是 B+ 树的⾮叶⼦节点. 回滚段
• 区(Extent), 区是由连续⻚组成的空间, 在任何情况下每个区的⼤⼩都为 1MB, 为了保证区中⻚的连续性, InnoDB
⼀次从磁盘申请 4-5 个区, 默认情况下 InnoDB ⻚⼤⼩为 16KB, ⼀个区中⼀共有 64 个连续的⻚.
• ⻚(Page): 是 InnoDB 磁盘管理的最⼩单位, 默认 16KB, 常⻅的⻚有: 数据⻚ / undo log⻚ / 系统⻚等.
• ⾏(Row): InnoDB 中数据按⾏进⾏存放, 每⻚最多允许存放 16KB / 2 - 200 = 7992 ⾏记录

InnoDB 中⻚是管理数据库的最⼩磁盘单位, ⻚类型为 B-tree Node 的⻚存放的就是
表中的实际数据.InnoDB 数据⻚由⼀下 7 部分组成:
• File Header(⽂件头), 固定 38 字节, ⽤来记录头信息, 以及相邻⻚的指针.
• Page Header(⻚头), 固定 56 字节, ⽤来记录数据⻚的状态信息.
• Infinum 和 Suprenum Records, 虚拟的⾏记录, ⽤来限定记录的边界
• User Recodes(⽤户记录, 即⾏记录)
• Free Space(空闲记录)
• Page Directory(⻚⽬录), 存放记录的相对位置, 找到 B+ 树叶⼦节点后, 再通过
Page Directory 再进⾏⼆分查找.
• File Trailer(⽂件结尾信息), 固定 8 字节, ⽤于检测⻚是否已经完整地写⼊

B+树

image.png

⼀条索引的命运啊, 当然要靠查询复杂度, 但也要考虑到与硬件的兼容.
B+ 树结构本身⽐较复杂, 是为磁盘或其他直接存取设备设计的⼀种平衡查找树, 所有记录节
点都是按照键值⼤⼩顺序放在同⼀层叶⼦节点上, 由各叶⼦指针进⾏连接.
B+ 树的插⼊必须保证插⼊后叶⼦节点依然有序, 同时不同的情况可能会导致不同的插⼊算法.

索引

image.png

锁是数据库系统区别于⽂件系统的⼀个关键特性, ⽤于管理对共享资源的并发访问.
InnoDB 会在⾏记录上加锁. 同时也会在内部其他地⽅使⽤, 如 LRU 列表. InnoDB 提
供⼀致性的⾮锁定读, ⾏锁, 可以同时得到并发性和⼀致性.
InnoDB 实现了⼀下两种标准的⾏级锁:
• 共享锁, 允许事务读⼀⾏数据.
• 排他锁, 允许事务删除或更新⼀⾏数据.
此外, InnoDB ⽀持多粒度锁定, 允许事务在⾏级别和表级别同时存在, 因此引出了意
向锁(Intention Lock), 意向锁意味着事务希望在更细粒度上进⾏加锁
⾮⼀致性锁定读(consistent nonblocking read) 是值 InnoDB 通过多⾏版本控制的
⽅式来读取当前执⾏时间数据中的数据. 如果当前数据正在执⾏写操作, 这是读取操
作不会因此阻塞, ⽽是去读取⼀个快照数据.
快照数据就是当前⾏数据的历史版本, 每⾏记录可能⼜多个版本, READ
COMMITTED 和 REPEATABLE READ 下, InnoDB 都会使⽤⼀致性⾮锁定读, 但⽅式
不同:
• READ COMMITTED 下, ⼀致性⾮锁定读总是读取被锁定⾏的最新⼀份快照.
• REPEATABLE READ 下, ⼀致性⾮锁定读总是读取事务开始时的⾏版本数据.

锁带来的问题

死锁

多个事务, 以不同的顺序去获取相同的资源, 就容易引发死锁.


image.png
解决
  1. 超时检测: 设置⼀个阈值, 当任意⼀⽅等待时间超过预设的阈值时, 其中⼀个事务回滚.
  2. Wait-for-graph 主动检测: 通过 “等待获取的锁” 和 “等待获取该锁的事务”, 构造出⼀张有向图,
    如果图中存在回路, 就代表存在死锁.
    InnoDB 将各个事务看成⼀个个节点, 资源就是各个事务占⽤的锁,
    当事务1需要等待事务2的锁时, 就⽣成⼀条有向边从1指向2, 最终
    形成⼀个有向图.
    InnoDB 会定时遍历该图, ⼀旦发现回路, 就将其中⼀个回滚, 另⼀个
    事务就得以继续执⾏. 被回滚的事务会返回 “dead lock

事务

事务的实现

Redo Log
重做⽇志⽤来实现事务中的持久性, 由两部分组成:
• 内存中的重做⽇志缓冲(redo log buffer)
• 磁盘中的重做⽇志⽂件(redo log file)
当事务提交的时候, 必须先将该事务的所有⽇志写⼊到磁盘中的重做⽇志⽂件进⾏持久化, 待事务
提交结束才算完成.
Undo Log
重做⽇志记录了事务的⾏为, 可以很好的对⻚进⾏”重做”操作, 但事务有时候需要进⾏回滚, 此时
就需要 undo log, redo log 存放在数据库内部的回滚段中, 位于共享表空间.
重做⽇志的主要⼯作是将数据库逻辑地恢复到原来的样⼦, 但数据结构和⻚本身在回滚之后可能
和事务开始前不太相同, 因为与此同时有⼤量的并发事务存在, 不能简单的将⼀个⻚回滚到事务开
始时的样⼦, 否则会影响其他事务.
Undo Log 的另⼀个功能是 MVCC, 当需要读取的记录已经被其他事务加锁的时候, 当前事务可以
通过 undo 读取之前的版本, 以此实现⼀致性⾮锁定读

回滚段

image.png
上一篇 下一篇

猜你喜欢

热点阅读