innodb存储引擎

innodb存储引擎关键点

[TOC]

缓存池

  • 索引页
  • 数据页

  • undo页

  • 插入缓存

  • 自适应哈希索引

  • innodb引擎的锁信息

  • 数据字典信息

innodb 内存数据对象

mysql> use information_schema;                                                   Database changed
mysql> select pool_id,pool_size, free_buffers,database_pages from innodb_buffer_pool_stats \G;
*************************** 1. row ***************************
       pool_id: 0
     pool_size: 8192
  free_buffers: 1024
database_pages: 7160
1 row in set (0.00 sec)

缓存池管理

innodb_buffer_pool_list

  • LRU(默认)
    • 频繁使用的在LRU列表前端
    • 优化:新访问的页不是放在列表前端,而是放在Midpoint。避免扫描全表的sql,将热点数据挤压刷出
    • midpoint之前称为新表(new sublist),表示热点数据
    • midpoint之前称为旧表(old sublist)
  • unzip_LRU
    • 管理非16KB的页
    • LRU列表中包含unzip_LRU列表中的页

    在LRU列表中的页被修改后,成改页为脏页(dirty page)。

    数据块通过checkpoint机制将脏页刷新回磁盘

    Flush列表中的页为脏页列表。脏页同时存在于LRU列表和Flush列表

重做日志缓存(redo log buffer)

  • 先将redo信息放入redo log buffer
  • 然后按照一定频率刷新到redo log file(一般1秒一刷)
  • flush redo log
    • master thread 一秒刷redo log buffer to redo log
    • 每个事务提交时
    • 当redo log buffer 剩余空间少于1/2时

额外的内存池

  • 一些数据结构本身的内存优先从(额外的内存池)中分配
  • 当(额外的内存池)内存不足,会从缓存池中分配

checkpoint技术

面对的难题

  • 缓存池不可能缓存数据库所有数据
  • redo log不可能无限大

用于解决

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

通过LSN(log sequence Number)来标记版本的。

  • LSN是8字节的数字
  • 每个页有LSN
  • redo log有LSN
  • checkpoint 有LSN

有两种checkpoint

  • Sharp checkpoint,数据库关闭时,将所有脏页刷新回磁盘
  • Fuzzy checkpoint,数据库运行中使用,进行页的刷新,只刷新一部分脏页回到磁盘
    • master Fuzzy checkpoint。异步定时从缓冲池刷脏页到磁盘
    • FLUSH_LRU_LIST Checkpoint 为了保证LRU LIST有差不多100个空闲页可供使用,Page Cleaner线程中执行
    • Async/Sync flush Checkpoint.
    • dirty page too much Checkpoint

MASTER THREAD

  • 具有最高的线程优先级
  • 由多个loop循环组成
    • 主循环(loop):每秒操作和每十秒操作
    • 后台循环(backgroup loop)
    • flush loop
    • 暂停循环(suspend loop)

loop

每秒操作

  • 刷redo log buffer到磁盘,即使这个事务还没有提交(总是)

    解析了为什么再大的事务commit都很快

  • 合并插入缓冲(可能)

    只有io压力很低,前一秒的io总数<5,才会执行。

  • 至多刷100个Innodb 缓冲池中的脏页到磁盘(可能)

    当前缓存池中的脏页比例buffer_get_modified_ratio_pct大于配置值

  • 当前没有用户,切换到backupgroup loop状态(可能)

每十秒操作

  • 刷100个Innodb 缓冲池中的脏页到磁盘(可能)
  • 合并至多5个插入缓冲(总是)
  • 刷redo log buffer到磁盘(总是)
  • 删除无用的undo页(总是)
  • 刷100或者10个Innodb 缓冲池中的脏页到磁盘(总是)

backupgroup loop

  • 删除无用的undo页(总是)
  • 合并20个插入缓冲(总是)
  • 跳回主循环(总是)
  • 不断刷100个Innodb 缓冲池中的脏页到磁盘(总是)(可能跳转到flush loop中完成)

InnoDB1.0.x之后

1.0.x版本之前,Master Thread 有了一定的hard coding。随着硬件性能日新月异,这对编码方式不能很好的匹配不同性能的硬件。

  • 使用配置代替hard coding
  • 增加了一些参数自适应

InnoDB1.2.x之后

刷新脏页的操作从Master Thread分离到一个独立的Page Cleaner Thread。减轻Master Thread 的工作,同时进一步提高了系统的并发性。

Innode关键特征

  • insert buffer
  • double write
  • adaptive hash index
  • async IO
  • Flush Neighbor Page

insert buffer

insert buffer 和数据页一样,也是物理页的一个组成部分,用于非唯一辅助索引的插入操作。

每一张表上除了有一个聚集索引(primary key),有可能还有多个非聚集的辅助索引。

在进行插入操作的时候,数据页还是按照primary key进行顺序存放的。但是对于非聚集索引叶子节点的插入不再是顺序的。这时需要离散的访问非聚集索引页,由于随机读取的存在,进而影响了插入性能。

mysql引入insert buffer,对非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若是在,则直接插入;若不在,则先放入到一个insert buffer对象中。

然后再以一定的频率和情况进行insert buffer和辅助叶子节点的merge操作。

辅助索引不能是唯一的,因为在插入缓冲时,数据库为了避免离散读取的情况发生,并不去查找索引页来判断插入的记录的唯一性。

Change Buffer

Innodb 1.0.x版本开始引入Change Buffer,可将其视为Insert BUFFER的升级。Innodb可以对DML操作–INSERT、DELETE、UPDATE都进行缓冲,它们分别是:

  • Insert Buffer
  • Delete Buffer
  • Purge Buffer

Innodb 提供参数Innodb_change_bufferiing,用于开启各种Buffer的选项

对一条记录进行UPDATE操作可能分为连个过程:

  • Delete buffer复制将记录标记为删除
  • Purge buffer负责将记录真正的删除

Insert Buffer的内部实现

  • 使用b+树

    mysql 4.1之前每张表有一颗insert buffer B+树。现在全局只有一颗Insert Buffer B+树

  • 它保存在共享空间中(ibdata1),因此,试图通过独立表空间ibd文件恢复表中数据时,往往会导致CREATE TABLE失败。这是因为表的辅助索引中的数据可能还在Insert Buffer中。所以通过idb文件进行恢复后,还需要进行REPAIR TABLE操作来重建表上所有的辅助索引。

  • 由叶子节点和非叶子节点组成

  • 非叶子节点存放的是查询的search key(键值),结构如下

space marker offset
表示待插入记录所以在表的表空间id 用于兼容向后兼容 页所在的偏移量
  • 当一个辅助索引要插入到页(space,offset)时,如果这个页不再缓冲池中,那么Innodb首先根据上述规则构造一个search key,接下来查询insert buffer,然后将记录插入到insert buffer B+树的叶子节点中。
space :marker offset metadata
  • metadata中IBUF_REC_OFFSET_COUNT(2字节的整数)用来排序每个记录进入insert buffer的顺序

  • insert buffer bitmap:一个特殊的页,用来标记每个辅助索引页的可用空间,保证每次merge insert buffer页必须成功。

  • 每个insert buffer bitmap用来追踪16384个辅助索引页,也就是256个区(Extent)

  • 每个辅助索引页在insert buffer bitmap中存储结构

merge insert buffer

  • 辅助索引页被读取到缓冲池中
    • 检查insert buffer bitmap页,确认该辅助索引页是否存放在insert buffer B+树中
    • 存在,则将B+树中该页的记录插入到该辅助索引页中。对该页多次的记录操作通过一次合并到原有的辅助索引页
  • insert buffer bitmap页追踪到该辅助索引页已无可用空间时。(小于1/32页,会强制进行一次合并操作)
  • Master Thread中每秒或者每十秒的合并操作。

两次写

避免数据库宕机时,出现部分写失效(partial page write)。例如某个页16KB,只写了4KB就发生了宕机。

通过redo log进行数据恢复,由于redo日志记录的是对页的物理操作(如偏移量800,写‘aaa’记录),需要基于页是好的。如果这个页本身已经发生了损坏,无法通过redo 日志进行重放。

doublewrrite:当写入失效发生时,先通过页的副本还原该页,再进行重做。

doublewrrite由两部分组成:

  • 内存中的doublewrrite buffer,2MB
  • 物理磁盘上共享表空间中连续的128个页,即2个区(extent),2MB

在对缓冲池进行flush时,并不是通过写磁盘,而是通过memcpy函数将脏页先复制到内存中的doublewrite buffer,之后通过doublewrite buffer再分两次,每次写1MB顺序写入共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘避免缓冲写带来的问题(顺序写)。doublewrite buffer中的页写入到各个表空间文件中。(离散写)

自适应哈希索引(AHI)

自适应哈希索引:Innodb存储引擎会监控对表上各索引的查询,如果观察到建立哈希索引可以带来速度提升,则建立哈希索引。

AHI是通过缓冲池的B+树页构造,不需要对整张表构建哈希索引。

条件:

  • 访问模式一样,即查询的条件一样。
  • 以该模式访问了100次
  • 页通过该模式访问了N次,其中(N=页中记录*1/16)。

异步IO

Innodb支持AIO。

  • read ahead 方式的读取(借助aio,将所有io发送完毕后,然后等待所有IO操作的完成)
  • 脏页的刷新(可以将多个连续io进行IO Merge)

刷新邻近页

当刷新一个脏页时,Innodb会检测改页所在区(extent)的所有页,如果是脏页,那么一起进行刷新。然后通过AIO将多个io写入操作合并成一个IO操作。

缺点:

  • 可能会将不怎么脏的页进行了写入,而该页之后很快又会变成脏页。
  • 固态硬盘有着较高的IOPS,不需要这个特性。

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.