Fork me on GitHub

MySQ-存储引擎

注意:所有文章除特别说明外,转载请注明出处.

数据库与实例

数据库的概述:

​ 简单说就是存放数据的仓库,这个仓库按一定的数据结构(数据的组织形式、或数据之间的联系)来组织和存储的,数据库分为关系型数据库(Oracle,MySQL,SQLServer)和非关系型数据库(NOSQL,Memcaced,redis等)

实例

​ MySQL实例是由线程和内存组成,它是真正用于操作数据库文件的,一般情况下一个实例操作一个或多个数据库,在实例启动的时候MySQL会读取配置文件,MySQL如果找不到配置文件则会按照默认参数设置启动实例

​ 只要由多个查询需要在同一时刻修改数据,都会产生并发控制问题,解决方法:并发控制

读写锁
  • 共享锁:读锁,它是共享的,互相不阻塞,多个用户在同一时刻可以读取同一个资源且互不干扰
  • 排他锁 :写锁,它会阻塞其他的写锁和读锁,写锁比读锁具有更高的优先级
锁策略
  • 表锁:它是mysql中最基本的锁策略,并且开销最小,锁定整张表
  • 行锁:最大程度支持并发处理,InnoDB就使用了行锁,但它的行锁不是那么简单(后续讲)

事务

​ ==实现了ACID,会需要更强的cpu处理能力,更大的内存和更大的磁盘空间==

  • 原子性Atomicity:一个事务被视为一个不可分割的最小工作单元,整个事务的操作要么全部成功,要么全部 失败
  • 一致性consistency:就是一个事务没有提交commit,系统崩溃,那么事务中所做的修改不会保存到数据库中
  • 隔离性isolation:一个事务所作的操作在提交之前,对其他事务是不可见的
  • 持久性durability:一旦事务提交,所做的修改就会永久保存到数据库中,即使系统崩溃,改的数据也不会丢失

​ MySQL==默认采用自动提交==AUTOCOMMIT模式,如果不是显示开始一个事务,每个查询都会被当作一个事务执行提交操作,可通过AUTOCOMMIT变量来启用或禁用自动提交模式

InnoDB

​ 它是一个存储引擎,用来==负责mysql中的数据的存储和提取==,不会去解析SQL,但是会解析外键定义,它的数据都存储在表空间中tablespace,由一系列的数据文件组成,它采用MVCC来支持高并发,实现了四个标准的隔离级别,默认级别是:==REPEATABLE READ(可重复度读)==

​ InnoDB的表是基于==聚簇索引==建立的

​ 表空间(tablespace)是存储引擎中最高的存储逻辑单位,在表空间的下面又包括段(segment)、区(extent)、页(page),表空间中的页大小都为 16KB,每个 16KB 大小的页中可以存放 2200 行的记录,对nnodb_page_size 选项对默认大小进行修改,需要注意的是不同的页大小最终也会导致区大小的不同

表的定义数据索引等信息分开存储,其中表的定义存储在 .frm文件中,数据索引储在 .ibd 文件中

在InnoDB中,它为每行记录都增加了三个隐藏的字段
  • 事务id:6字节

  • 回滚指针:7字节

  • 隐藏的id

为了支持事务,引入如下概念:

​ redo log:保存执行的SQL语句,当MySQL执行recovery时重新执行redo log记录里面的sql操作就好,当客户端每执行一个sql时,先被写到log buffer,当执行commit时,log buffer再被视情况刷新到磁盘

​ undo log:主要是用来回滚,就是复制事务前的数据库内容到undo buffer,再合适的时间undo buffer中的内容刷新到磁盘,它两都是环形缓冲,undo buffer均放在ibd数据文件(表空间)中,undo log被划分为多个段,具体某行的undo log就保存在某段,即回滚段

当事务1更改此行的值时,会进行如下操作:

用排他锁锁定该行

记录redo log

把改行修改前的值copy到undo log

修改当前行的值,填写事务编号,使用回滚指针指向undo log中的修改前的行

​ 当有多行修改记录时,通过回滚指针连在一起,可通过当前指针的回滚指针回溯,InnoDB的purge线程,它会查询比现在最老的活动事务还早道德undo log,删除它们

隔离级别

  • 未提交读:就是脏读,事务的修改即使没有提交,对其他事务都是可见的,事务可以读取未提交的数据
  • 提交读:一个事务所作的操作在提交之前,对其他事务是不可见的,即不可重复读(隔离性)
  • ==可重复读==:解决了脏读,该级别保证在同一个事务中多次读取同样的结果是一致的,但是它解决不了幻读,幻读即在读取数据的时候,另一个事务又插入了 新的记录,当之前事务再次读取时,产生了幻行,InnoDB的通过MVCC解决了幻读
  • 可串行化:最高的级别,强制事务串行执行,避免了幻读,采用行锁

显示隐式锁

​ 采用两阶段锁定协议,在事务执行过程中,随时都能锁,锁只有在执行commit或rollback时才会被释放,所有锁在同一时刻被释放,这是隐式锁定,InnoDB会根据隔离级别自动加锁

MVCC实现方式

​ 它是行级锁的一个变种,通过保存数据在某个时间点的快照来实现,更新前建立undo log,根据各种策略读取时非阻塞就是MVCC,每行数据都存在一个版本,每次数据更新时都会更新该版本,各个事务之间无干扰;保存时比较版本号,成功则覆盖,失败则rollback,在默认隔离级别下,MVCC的具体操作(innoDB): ==InnoDB的MVCC通过在每行记录后面保存两个隐藏的列来实现,一个保存创建时间,一个保存删除时间==

  • select:查找版本早于或=当前事务版本的数据行,确保读取的行在事务开始之前就已经存在,要么是事务自己插入的
  • insert:为插入的每一行保存当前系统版本号
  • delete删除每一行保存当前系统版本号为行删除标志
  • update:保存当前系统版本号,同时保存当前系统版本号到原来的行作为行删除标志

​ InnoDB并没有实现核心的饿多版本共存,undo log里面的内容只是串行化的结果,不属于多版本共存,当事务影响到多行数据时,MVCC就无能为力,InnoDB只是提供读的非阻塞而已

InnoDB缓存池

​ 缓存索引、缓存行的数据、自适应哈希索引、插入缓冲、锁以及内部数据结构,使用缓存池可以延迟写入,就是合并多个写入操作,然后一起顺序写回,InnoDB严重依赖缓存池

innodb相关磁盘文件:

    • ibdata1:
      • 回滚段
      • 表元数据
      • double write
      • insert buffer dump等
    • ib_logfile0/1
    • .frm:表定义文件
    • .ibd:数据文件,innodb_file_per_table=1

    性能相关参数:

  • innodb_log_file_size

  • innodb_log_files_in_group

  • 原因:当redo log 采用轮寻范式ib_logfile0写完,写ib_logfile1完,清楚ib_logfile0并继续写入ib_logfile0;当ib_logfile1写完,ib_logfile0中还有数据没有持久化到磁盘,又来了新的写入,此时会阻塞新写入,强制刷新ib_logfile0到磁盘,再将新写,写入ib_logfile0;这样就是说,logfile越大其写入越不容易阻塞,写入性能也就越好。

  • 数据节点每页16K

  • innodb数据块缓存池

  • 数据读写经过缓存池

    • 数据以整页为单位读取

    • LRU策略(最少使用)换出,

    innodb数据持久化:通过事务日志

  • innodb_flush_log_at_trx_commit

    • 0:每秒写入并持久化一次(不安全,性能高,无论mysql或服务器宕机,都会丢数据),延迟写
    • 1:每次commit都持久化(安全,性能低,IO负担重),实时写,实时刷
    • 2:每次commit都写入内存的redo log缓存,每秒再刷新到磁盘(安全,性能折中,mysql宕机数据不会丢失,服务器宕机数据会丢失),实时写,延时刷

    事务日志

    ​ 通过事务日志来减少提交事务时的开销,因为事务修改的数据和索引通常会映射到表空间的随机位置,这样刷新到磁盘需要很多随机IO,InnoDB用日志把随机变为顺序,InnoDB最后还是把变更写到数据文件,日志有固定的大小,它以环形方式写入,写到日志尾部时,会重新跳到头部开始,不会覆盖还没有写入到数据文件的日志记录

    ​ InnoDB变更任何数据时,会写一条变更记录到内存日志缓冲区,在缓冲区满的时候或事务提交的 时候,InnoDB都会刷写缓冲区的内容到磁盘日志文件,它默认通过一个后台线程来写脏页,并且会合并写入,更有效地顺序写出到磁盘

    ​ 要修改日志文件大小,就要完全关闭MySQL,将旧的日志文件移到其他地方,重新配置参数,然后重启

    InnoDB刷新日志缓存

    ​ 当它把日志缓冲刷新到磁盘日志文件上时,会使用一个Mutex锁住缓冲区,InnoDB有一个group Commit功能,可以在一个IO操作中提交多个事务,

    日志缓冲文件写到日志文件:就是简单地把数据从InnoDB的内存缓冲转移到操作系统的缓存,即内存,并没有真正持久化

    把日志刷新到持久化缓存:这是一个阻塞IO的调用,将缓存刷出缓存,并确认写到磁盘中,直到数据被完全写回才会完成

    双写缓存

    ​ InnoDB用双写缓存来避免当页没有写完整所导致的数据损坏,保证了数据完整性

    ​ 当InnoDB从缓存池刷新页面到磁盘时,会先把它们放在双写缓冲(顺序的,只调用一次fsync()),然后再写到其所属的数据区域,保证每个页面的写入都是完整的并且是持久化的

    ​ 如果一个不完整的页写到了双写缓存,InnoDB恢复时,用原始页面替换掉这个损坏页面

    ​ 如果页的真实位置损坏了,InnoDB恢复时,用双写缓存的页面替换掉这个损坏页面,在每个页面末尾都会有一个校验值,能知道它什么时候损坏

    innodb关键特性

  • 插入缓冲(insert buffer)

  • 两次写(Double write)

  • 自适应哈希索引(adaptive hash index)

  • 异步io(Async IO)

  • 刷新领接页(Flush Neighbor Page)

本文标题:MySQ-存储引擎

文章作者:Bangjin-Hu

发布时间:2019年10月15日 - 09:22:26

最后更新:2020年03月30日 - 08:05:36

原始链接:http://bangjinhu.github.io/undefined/MySQL的存储引擎/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Bangjin-Hu wechat
欢迎扫码关注微信公众号,订阅我的微信公众号.
坚持原创技术分享,您的支持是我创作的动力.