本文内容基本摘抄自《MySQL技术内幕 InnoDB存储引擎》,以供复习之用,没有多少参考价值。想要更详细了解请参考原书。前端
数据库是物理操做系统文件或其余形式文件类型的集合。MySQL数据库实例由后台线程以及一个共享内存区组成。MySQL是一个单进程多线程的架构的数据库算法
MySQL数据库区别于其余数据库的特色是插件式的表存储引擎:sql
InnoDB存储引擎:支持事务,主要面向在线事务处理的应用。特色是行锁设计、支持外键,并支持相似于Oracle的非锁定读,即默认读取操做不会产生锁。InnoDB经过使用多版本并发控制(MVCC)来得到高并发性,而且实现SQL标准的4种隔离级别,默认REPEATABLE。同时,使用next-key locking策略来避免幻读的产生。除此以外,InnoDB还提供了插入缓冲、二次写、自适应哈希索引、预读等功能。数据库
MyISAM存储引擎:不支持事务、表锁设计,支持全文索引,主要面向在线分析处理的应用。MyISAM存储引擎的另外一个不同凡响的地方是它的缓冲池只缓存索引文件,而不缓冲数据文件,自己不支持外键,对于外键的定义只是起到注释的做用。数组
链接MySQL的几种方式:TCP/IP、命名管道和共享内存、UNIX域套接字。缓存
后台线程:1.Master Thread主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲、UNDO页的回收等;2.InnoDB大量使用AIO来处理写IO请求,IO Thread的工做复制这些IO请求的回调处理;3.事务被提交后,所使用的undolog可能再也不须要,所以须要Purge Thread来回收已经使用并分配的undo页;4.Page Cleaner Thread做用是将以前版本中脏页的刷新操做都放入到单独的线程中来完成,目的是为了减轻原Master Thread的工做及对于用户查询线程的阻塞。数据结构
内存:1.缓冲池简单来讲就是一块内存区域,经过内存的速度来弥补磁盘速度较慢对数据库性能的影响(页从缓冲池刷新回磁盘的操做并非在每次页发生更新时触发,而是采用Checkpoint机制),从1.0版本开始容许多个缓冲池实例;2.InnoDB存储引擎首先将重作日志信息放入重作日志缓冲这个缓冲区,而后按必定频率将其刷新到重作日志文件;3.在对一些数据结构自己的内存进行分配时,须要从额外的内存池中进行申请,当该区域内存不够时,会从缓冲池中进行申请。多线程
一般来讲,数据库中的缓冲池是经过LRU算法进行管理。最频繁使用的页在前端,最少使用的页在尾端。InnoDB对传统的LRU算法作了一些优化,LRU列表中添加了midpoint位置。新读取到的页,虽然是最新访问的页,但不是直接放入到LRU列表的首部,而是放入到LRU列表的midpoint位置。能够设定页读取到mid位置后须要等待多久才会加入到LRU列表的热端。这样作是避免相似索引或数据的扫描操做会使缓冲池中的页被刷新出,从而影响缓冲池的效率。架构
为避免数据丢失问题,当前事务数据库系统广泛采用了Write Ahead Log策略,即当事务提交时,先写重作日志,再修改页。Checkpoint解决下面问题:缩短数据库恢复时间、缓冲池不够用时,将脏页刷新到磁盘;重作日志不可用时,刷新脏页。并发
当数据库发生宕机时,数据库不须要重作全部的日志,由于Checkpoint以前的页都已经刷新回磁盘;当缓冲池不够用,根据LRU算法会溢出最近最少使用的页,若此页为脏页,须要强制执行Checkpoint,将脏页刷回磁盘;重作日志是循环使用而不是无限增大的,当被重用的部分仍须要使用时,必须强制产生Checkpoint,将缓冲池中的页至少刷新到当前重作日志的位置。
Master Thread具备最高的线程优先级别。内部由多个循环组成:主循环、后台循环、刷新循环、暂停循环。
Loop分为每秒操做和每10秒操做,每秒操做包括:日志缓冲刷新到磁盘,即便这个事务尚未提交(老是);合并插入操做(可能);至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能);若是当前没有用户活动,则切换到background loop(可能)。每10秒操做包括:刷新100个脏页到磁盘(可能的状况下);合并至多5个插入缓冲(老是);将日志缓冲刷新到磁盘(老是);删除无用的Undo页(老是);刷新100个或者10个脏页到磁盘。
若当前没有用户活动或者数据库关闭,就会切换到这个循环。background loop会执行:删除无用的undo页(老是);合并20个插入缓冲(老是);跳回到主循环(老是);不断刷新100个页直到符合条件(可能,跳转到flush loop中完成)。
若flush loop无事可作,会切换到suspend loop,将Master Thread挂起,等待事件的发生。
Insert Buffer:
汇集索引是指该索引中键值的逻辑顺序决定了表中相应行的物理顺序;而非汇集索引索引的逻辑顺序与行的物理存储顺序无关。主键是一种特殊的具备惟一约束的索引,能够是汇集索引或是非汇集索引。
InnoDB设计了Insert Buffer,对于非汇集索引的插入或更新操做,不是每一次直接插入到索引页中,而是先判断插入的非汇集索引页是否在缓冲池中存在,是的话直接插入,不然先放到一个Insert Buffer对象中,而后再以必定的频率和状况进行Insert Buffer和辅助索引页子节点的merge操做,这时一般能将多个插入合并到一个操做中。
Insert Buffer的使用须要同时知足两个条件:索引是辅助索引和索引非惟一。插入汇集索引通常是顺序的,不须要磁盘的随机读取,而非惟一是由于若是索引惟一,每次插入时还要去离散查找页来肯定惟一性,从而致使Insert Buffer失去意义。
Change Buffer时Insert Buffer的升级,InnoDB能够对DML都进行缓冲。
Insert Buffer的数据结构时一棵B+树。非叶节点存放查询的search key。当一个辅助索引要插入到页(space,offset)时,若是这个页不在缓冲池中,那么InnoDB存储引擎首先根据上述规则构造一个search key,接下来查询Insert Buffer这棵B+树,而后再将这条记录插入到Insert Buffer B+树的叶子节点。同非叶节点同样,叶子节点也须要先构造,再插入。
Merge Insert Buffer的操做发生在如下状况:辅助索引页被读取到缓冲池时;Insert Buffer Bitmap页追踪到该辅助索引页无可用空间;Master Thread。
两次写:
Insert Buffer带来性能上的提高,double write带来数据页的可靠性。double write分两部分,一部分是内存中的doublewrite buffer,另外一部分是物理磁盘上共享表空间的doublewrite。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会经过memcpy函数将脏页先复制到内存中的doublewrite buffer,以后经过doublewrite buffer两次顺序写入到doublewrite,而后调用fsync函数同步磁盘。若是操做系统在将页写入到磁盘时发生了崩溃,恢复时先从共享表空间找到副本复制到表空间文件再应用重作日志。
自适应哈希索引:
InnoDB存储引擎会监控对表上各索引页的查询。若是观察到创建哈希索引能够带来速度提高,则创建哈希索引。
AHI经过缓冲池的B+树索引页构造,创建速度快,且无需对整张表创建哈希索引。
异步IO:
异步IO在发送完IO请求后能够继续发出新的IO请求,等待通知便可,不用等待请求结果;且AIO能够进行IO Merge。
刷新邻接页:
当刷新一个脏页时,InnoDB会检测该页所在区的全部页,若是是脏页,那么一块儿进行刷新。这样能够利用AIO合并多个IO操做。
参数文件(my.cnf):当MySQL实例启动时,数据库会先去读一个配置参数文件,用来寻找数据库的各类文件所在位置以及指定某些初始化参数,这些参数一般定义了某种内存结构有多大。
日志文件:错误日志、慢查询日志、查询日志、二进制日志。二进制日志记录了对MySQL数据库执行更改的全部操做,不包括select、show这类。二进制日志有几种做用:某些数据的恢复须要二进制日志;复制;经过二进制日志信息进行审计,判断是否有对数据库进行注入的攻击。
套接字文件:在UNIX系统下本地链接MySQL能够采用UNIX域套接字方式,须要一个套接字文件。
pid文件:MySQL实例启动时,会将本身的进程ID写入pid文件。
表结构定义文件:MySQL数据的存储是根据表进行的,每一个表都有与之对应的文件,不论采用何种存储引擎,都有一个后缀frm的文件记录了该表的表结构定义。
InnoDB存储引擎文件:默认配置下,表的数据存放在默认表空间文件ibdata1中。能够设置为每一个表设计独立表空间存放数据,但仍有相关信息须要存放在共享表空间。每一个InnoDB存储引擎至少有一个重作日志文件组,每一个组至少有两个重作日志文件,ib_logfile0和ib_logfile1,以循环写入方式运行。
索引组织表:InnoDB存储引擎中,表都是根据主键顺序组织存放的,称为索引组织表。若是建立表时没有显式定义主键,InnoDB会首先判断表中是否有非空惟一索引,存在则以第一个定义的非空惟一索引为主键,不存在则InnoDB自动建立6字节大小的指针rowid。
表空间:从InnoDB存储引擎的逻辑存储结构来看,全部数据都被逻辑地存放在一个空间中,称为表空间。表空间又由段(segment)、区(extent)、页(page)组成。段分为数据段、索引段、回滚段等。数据段即为B+树的叶子节点、索引段即为B+树的非索引节点。区是由连续页组成的空间,每一个区大小1MB。页是InnoDB磁盘管理的最小单位,有数据页、undo页、系统页、事务数据页等。InnoDB存储引擎是面向列的,也就是说数据是按行进行存放的。
InnoDB行记录格式:
InnoDB存储引擎能够将一条记录中的某些数据存储在真正的数据页面以外。若是能够在一个页中至少放入两行数据,那varchar(TEXT或BLOB)类型的行数据就不会存放到BLOB页中去。
InnoDB数据页格式:数据页由文件头、页头、Infimun和Supremum Records、User Records、Free Space、Page Directory、File Trailer组成。
约束:数据库提供约束机制来保证数据库中数据的完整性:实体完整性、域完整性、参照完整性。InnoDB提供以下约束:Primary Key、Unique Key、Foreign Key、Default、Not Null。约束是一个逻辑的概念,用来保证数据的完整性,而索引是一个数据结构,既有逻辑上的概念,还表明着物理存储的方式。
Mysql不支持传统Check约束,可是经过Enum和Set类型能够解决部分需求,对于连续值的范围约束或更复杂的约束,则须要经过触发器。触发器的做用是在执行Insert、Delete、Update命令before或after自动调用SQL命令或存储过程。InnoDB在外键创建时会自动地对该列添加一个索引Key。
视图:Mysql中视图是一个命名的虚表,由一个SQL查询来定义,能够看成表使用。与持久表不一样,视图中的数据没有实际的物理存储。
分区表:分区的过程是将一个表或索引分解为多个更小、更可管理的部分。MySQL数据库支持水平分区,并不支持垂直分区。Mysql数据库的分区是局部分区索引,一个分区中既存放了数据又存放了索引。而全局分区是指,数据存放在各个分区中,可是全部数据的索引存放在一个对象中。目前Mysql支持:RANGE分区、List分区、Hash分区、key分区、Columns分区。子分区容许在分区的基础上再进行分区,也称符合分区。Mysql容许在RANGE和LIST的分区上再进行Hash或Key的子分区。
对于OLAP的应用,分区的确能够很好提升查询性能,由于OLAP应用大多数查询须要频繁扫描一张大表。OLTP则不必定,若根据主键分区,那么进行主键查询或许有帮助,但对于别的列索引查询反而会下降性能。
InnoDB存储引擎支持如下几种常见索引:B+树索引、全文索引、哈希索引。自适应哈希索引会根据表的使用状况自动生成,不能人为干预。B+树索引找到被查找数据行所在的页,而后数据库把页读入内存,再在内存中进行查找,获得查找的数据。
B+树索引经过高扇出性保证高度通常在2~4层。B+树索引分为汇集索引和辅助索引,汇集索引和辅助索引不一样的是,叶子节点存放的是不是一整行的信息。
汇集索引就是按照每张表的主键构造一棵B+树,同时叶子节点存放的即为整张表的行记录数据,汇集索引的叶子节点称为数据页。每一个数据页经过一个双向链表来进行链接。经过查看表空间文件发现非数据页节点存放的仅仅是键值及指向数据页的偏移量,而不是一个完整的行记录,而数据页上存放的是完整的每行的记录。汇集索引的存储其实并非物理上连续的,而仅仅逻辑上连续。页之间经过双向链表进行维护,每一个页中的记录之间也经过双向链表维护。汇集索引还有一个优势是它对于主键的排序查找和范围查找速度很是快。
辅助索引也是B+树,叶子节点并不包含行记录的所有数据。叶子节点除了包含键值外,每一个叶子节点中的索引行中还包含了一个书签,因为InnoDB是索引组织表,所以InnoDB存储引擎的辅助索引的书签就是相应行数据的汇集索引键。
索引的建立和删除有两种方法,一种是alter table add/drop key,另外一种是create/drop index。
并非全部的查询条件中出现的列都须要添加索引。对于何时添加B+树索引,经验是具备高选择性的列才有必要添加索引,经过show index from tablename 能够查看cardinality值来查看列是否具备高选择性。
联合索引是指对表上的多个列进行索引。联合索引也是一棵B+树,不一样的是联合索引的键值数量大于等于2.联合索引已经对后面的键值进行了排序处理。
InnoDB存储引擎支持索引覆盖,即从辅助索引中就能够获得查询的记录,而不须要查询汇集索引中的记录。使用覆盖索引的一个好处是辅助索引不包含整行记录的全部信息,能够减小IO操做。
InnoDB存储引擎使用哈希算法来对字典进行查找,其冲突机制采用链表方式哈希函数采用除法散列方式。自适应哈希索引基于此实现,对于字典类型的查找很是迅速,可是对于范围查找无能为力。
全文检索经过使用倒排索引来实现。倒排索引在辅助表中存储了单词与单词自身在一个或多个文档中所在位置之间的映射,一般使用关联数组实现。
InnoDB存储引擎提供一致性的非锁定读、行级锁支持。行级锁没有相关额外的开销,并能够同时的到并发性和一致性。
数据库中有两种锁:lock和latch。latch锁定时间短,目的是保证并发线程操做临界资源的正确性,没有死锁检测机制,分为互斥量和读写锁。lock的对象是事务,用来锁定数据库中的对象,如表、页、行。而且通常lock的对象仅在事务commit或rollback后进行释放,lock有死锁机制。
InnoDB存储引擎实现了以下两种标准的行级锁,容许事务读一行数据的共享锁(S锁)和容许事务删除或更新一行数据的排他锁(X锁)。InnoDB支持意向锁为表级别的锁,意向共享锁和意向排他锁。
一致性的非锁定读是指InnoDB存储引擎经过行多版本控制的方法来读取当前执行时间数据库中行的数据。若是读取的行正在执行delete或update操做,这时读取操做不会所以去等待行上锁的释放。相反,InnoDB会去读取行的快照数据。快照数据是指该行的以前版本的数据,这是经过undo段来完成。undo段是用来在事务中回滚数据,所以快照数据自己没有开销。且快照数据无需上锁。不一样事务隔离级别不都是采用非锁定的一致性读,即便采用非锁定一致读,对于快照数据的定义也各不相同。
某些状况下,须要显示地对数据库读取操做进行加锁以保证数据逻辑的一致性。InnoDB对于select提供两种一致性锁定读操做:
InnoDB存储引擎有3种行锁的算法:
InnoDB存储引擎默认事务隔离级别是Repeatable read,该隔离级别下,其采用next-key locking加锁。而在Read committed下,其仅采用Record Lock。当查询的索引含有惟一属性时,InnoDB存储引擎会对Next-Key
Lock进行优化,将其降级为Record Lock。Next-key Lock用来解决幻读问题,组织多个事务将记录插入到同一个范围。
脏读是指一个事务能够读到另一个事务中未提交的数据;不可重复读是指在一个事物内屡次读取到同一个数据集合,但因为另外一事务的修改,第一个事务两次读到的数据不同。丢失更新是指一个事务的更新操做会被另外一个事务的更新操做所覆盖,从而致使数据不一致,避免的话须要让事务串行化,相似整个事务阶段都加锁。
死锁是指两个或以上的事务在执行过程当中,因争夺锁资源而形成的一种互相等待的现象。最简单的办法是超时机制,但超时机制根据FIFO的顺序选择回滚对象,若超时事务所占权重较大,效率就会低。因此也会经过等待图来进行死锁检测,存在死锁时回滚undo量最小的事务。
InnoDB中的事务彻底符合ACID的特性,即原子性、一致性、隔离性、持久性。从事务理论角度,能够分为如下几类:扁平事务、带有保存点的扁平事务、链事务、嵌套事务、分布式事务。
事务隔离性由由锁实现,。原子性、一致性、持久性经过数据库的redo log和undo log来完成,redo log称为重作日志,用来保证事务的原子性和持久性。undo log用来保证事务的一致性,用来帮助事务回滚及MVCC的功能。
重作日志都是以512字节进行存储的。这意味着重作日志缓存、重作日志文件都是以块的方式进行保存,称为重作日志块。因为重作日志块的大小和磁盘扇区的大小同样,都是512字节,所以重作日志的写入能够保证原子性,不须要doublewrite技术。
重作日志记录了事务的行为,能够很好地经过其对页进行“重作”操做。可是事务有时还须要进行回滚操做,这时就须要undo。redo存放在重作日志文件中,undo存放在数据库内部的一个特殊段中,称为undo段。undo段位于共享表空间中。undo只是逻辑地将数据库恢复,好比用insert恢复delete,而不是把页回滚为事务开始的样子,由于可能别的事务对同一个页进行修改。undo的另外一个做用是MVCC。undo的产生会伴随着redo log的产生,由于undo log也须要持久性的保护。
purge用于最终完成delete和update操做。这样设计是由于InnoDB存储引擎支持MVCC,因此记录不能在事务提交时当即进行处理,这时其余事务可能正在引用这行,故InnoDB存储引擎须要保存记录以前的版本。而是否能够删除该条记录经过purge来判断。若该行记录不被任何其余事务引用,那么能够进行真正的delete操做。
InnoDB存储引擎提供了对XA事务的支持,并经过XA事务来支持不一样数据库之间分布式事务的实现。使用分布式事务隔离级别必须设置为Serializable,全局事务要求在其中的全部参与的事务要么都提交,要么都回滚。XA事务由一个或多个资源管理器、一个事务管理器以及一个应用程序组成。