咱们都熟悉mysql数据库服务架构,也清楚 sql 的执行顺序,mysql的数据在磁盘和内存中的存储结构是采用B+树的数据结构,可是在InnoDB引擎中,数据在内存和磁盘中的展现形式以及怎么和mysql的服务架构创建联系,sql 查询和 InnoDB 引擎以前的联系,可能就不是不清楚了。 mysql 的逻辑架构图以下所示: mysql
InnoDB存储引擎的逻辑存储结构是什么呢,其实全部的数据都被逻辑地放在了一个空间中这个空间中的文件就是实际存在的物理文件,即表空间。默认状况下,一个数据库表占用一个表空间,表空间中存放该表对应的数据、索引、insert buffer bitmap undo信息、insert buffer 索引页、double write buffer 等是放在共享表空间中的。算法
# 默认一个数据库表单独占有一个表空间 show variables like '%innodb_file_per_table%' innodb_file_per_table=ON # 修改设置 SET GLOBAL innodb_file_per_table=OFF;
每一个表空间由 段 segment 区 extent 页 page 组成。页是数据存储数据的基本单位,默认大小为 16kb。 区是由连续页组成的空间,默认大小为 1MB。多个区构成表的段。 InnoDB 逻辑存储结构
sql
在咱们执行sql时,不管是查询仍是修改,myql 总会把数据从磁盘读取内内存中,并且在读取数据时,不会单独加在一条数据,而是直接加载数据所在的数据页到内存中,而读取的方式有两种,现行预读方式和随机预读方式,默认采用线性预读方式。数据库
InnoDB 引擎架构 缓存
线性预读是以 extent 为单位,而随机预读是以 extent 中的page 为单位,线性预读着眼于将下一个extent 数据读取到 buffer pool 中,而随机预读是将当前extent中剩余的page读到 buffer pool 中。 若是一个extent 区中被顺序读取得page数量超过必定的数量( innodb_read_ahead_threshold),则直接加载 extent 中剩余的数据页。数据结构
插入缓冲(Insert Buffer/Change Buffer)为了提高插入性能,insert buffer 是 insert buffer 的加强版,insert buffer 只对插入有效,而change buffer对 insert/update/delete 都有效。插入缓存只对非惟一索引和辅助索引有效,对每一次的插入不是写到索引页中,而是先判断插入的非汇集索引页是否在缓存中,若是在则直接插入,不存在则插入到 insert buffer 中,按照必定的频率进行合并操做,写回到磁盘。这样将多个插入操做合并进一个操做中,目的是为了减小随机IO带来的性能损耗。架构
插入缓存给 InnoDB 存储引擎带来了性能上的提高,而 double write 则是保障 InnoDB 存储引擎操做数据页的可靠性。double write 分为两部分组成,一部分在内存中的 double write buffer, 大小为 2MB,另外一部分是物理磁盘上共享表空间中连续的128个数据页,即2个区大小(一样是2MB)。在对缓冲池的脏页进行刷新时,并非直接写磁盘,而是经过 memcpy 函数将脏页复制到内存中的 doublewrite buffer,以后经过doublewrite buffer 在分两次,每次1MB 顺序地写入共享表空间的物理磁盘上,而后立刻调用 fsync 将数据同步至磁盘。因为doublewrite 是连续的空间,这样的顺序写IO开销不大。在doublewrite页写完后,再次离散写入各个表空间。若是操做系统在将数据页写入磁盘发生崩溃,那么在恢复的过程当中,InnoDB 引擎会从共享表空间中的doublewrite找到该页的一个副本,将其复制到表空间文件,再应用重作日志。异步
hash是一种等值查询,InnoDB 存储引擎会监控对表上各个索引页的查询,若是观察到创建hash索引会带来速度提高,则创建相应的索引,所以称为自适应哈希索引(Adaptive Hash Index,AHI)。AHI是经过缓冲池中的B+树页构造而来,创建速度比较快,并且不须要对整张表创建哈希索引,只是创建热点页的索引。AHI默认是开启的状态。ide
为了提升磁盘的操做性能, 当前的数据库系统通常采用异步IO(Asynchronous IO,AIO)的方式来处理磁盘操做,InnoDB 存储引擎也是如此,AIO的优点在于减小SQL查询须要的时间,另外也能够进行IO Merge 操做,就是将多个IO合并为1个IO,这样就能够提升IOPS的性能。函数
# 开启本地 AIO show valiables like 'innodb_use_native_aio';
InnoDB 存储引擎提供了 Flush Neighbor Page(刷新邻接页)的特性,当刷新一个脏页时,InnoDB 存储引擎会检测该区内是否存在其它脏页,若是存在,则一并进行刷新,这样作得好处显而易见,能够将多个操做合并成一个操做,对于机械硬盘有着明显的优点,但对于固定硬盘,本事就有较高的IOPS,是否开启须要根据状况而定,参数设置以下:
show varables like 'innodb_flush_neighbors'
mysql写文件有2块缓存。一块是本身定义在内存的log buffer, 另外一个是磁盘映射到内存的os cache。 mysql能够 调用 flush 主动将log buffer 刷新到磁盘内存映射,也能够调用 fsync 强制操做系同步磁盘映射文件到磁盘。默认状况下innodb_flush_log_at_trx_commit和sync_binlog 配置都为1。
不只InnoDB引擎中有 buffer 的概念,这个是在用户空间中,并且在内核空间中也有 OS buffer的概念
还能够同时调用 flush + fsync, 将缓存直接落盘。 innodb_flush_log_at_trx_commit = 0 就是每秒调用 flush + fsync ,定时器本身维护。 innodb_flush_log_at_trx_commit = 1 就是实时调用 flush + fsync 无法批处理,性能很低。 innodb_flush_log_at_trx_commit = 2 就是实时flush ,定时 fsync 交给OS维护定时器。 sync_binlog 配置 等于0:表示每次提交事务只write不fsync 等于1:表示每次提交事务都执行fsync 等于n:表示事务在write后,会累积N个事务后才fsync。 show variables like 'sync_binlog'; show variables like 'innodb_flush_log_at_trx_commit'; # 查看 mysql 正在执行的进程 show processlist
InnoDB引擎BufferPool、LogBuffer、OS Buffer、Log files 之间的关系。
mysql 在执行增删改sql时,InnoDB 引擎的执行步骤以下:
先写redo log 再写 bin log的缘由: 因为mysql 是经过 bin log 进行复制传输的,若是先提交了 redo log,尚未写bin log时出现了宕机,mysql 实例恢复时根据 redo log进行恢复,就会形成 从库和主库之间的数据不一致。
二进制日志文件的记录格式为 statement、row 和 mixed,statement 模式就是直接执行sql,若是其中有函数操做(好比数据库时间设置为 now() )那就会形成数据不许确。row 模式就是同步全部行的数据,若是全表操做修改状态,那这种模式就不合适了,所以在数据同步时须要根据状况采用 mixed 的混合模式。
记录全部未被占用的数据页,按照顺序将加载到内存的数据放入buffer pool 中,并删除对应 Free List 中的节点
将冷热数据块链接起来,根据 LRU 算法进行维护。若是加载进内存的数据一次性放入列表头部,再不肯定这批数据的热度状况下,会形成一部分数据的淘汰,mysql InnoDB 的作法是将数据放置在靠后的位置,若是数据在1s内被访问了,才能进入链表头部,即数据热区。
# 将新加载的数据放置在链表的位置 默认为 37 即5/8处, show variables like 'innodb_old_blocks_pct'; # 冷区数据间隔多久访问才放入链表的热端,默认为1000ms show variables like 'innodb_old_blocks_time';
记录内存中修改的数据页,使用双向链表进行链接,在方便的时候作落盘操做。
InnoDB 中的 redo log 大小是固定的,是保证事务持久性的,其文件个数也是能够根据须要进行配置,经过循环写文件的方式来实现的,当 write pos 追遇上 checkpoint 后,这个时候就不能再继续执行新的命令,须要把check point 往前推动,也就是把redo log 里的内容持久化,腾出空间继续写日志。
数据操做
redo log buffer 循环写入
这里先写日志再写磁盘的关键点也是一个技术,Write-Ahead Logging(WAL技术)。
关于 redo log 的配置能够参见以下命令执行查看。
show variables like '%innodb_log%' ------- 执行结果 ------ innodb_log_buffer_size 16777216 innodb_log_checksums ON innodb_log_compressed_pages ON innodb_log_file_size 50331648 innodb_log_files_in_group 2 innodb_log_group_home_dir ./ innodb_log_write_ahead_size 8192 innodb_log_buffer_size 为内存中 redo log buffer 的大小,16777216/1024/1024=16MB innodb_log_file_size 为每一个redo log 的大小,50331648/1024/1024=48MB innodb_log_files_in_group 为 redo log 文件组中文件的个数,默认为2个 查看数据库表状态 show table status like 'my_table';