InnoDB是一个平衡了高可靠性和高性能的通用存储引擎.在MySQL的5.6版本中,InnoDB是默认的MySQL存储引擎. 除非你配置了不一样的默认存储引擎,不然不带ENGINE=字句的CREATE TABLE语句将建立一个InnoDB的表.html
像mysql和INFORMATION_SCHEMA这些实现了MySQL内部结构的数据库,仍是使用的MyISAM引擎.而且不能将它们中的受权表切换为InnoDB.
这里只挑选了一些我的认为对比较重要的点,更多细节请参考Benefits of Using InnoDB Tables.mysql
ACID模式是一系列强调对业务数据和关键任务应用程序很重要的可靠性方面的数据库设计原则.算法
InnoDB是一个多版本存储引擎(multi-versioned storage engine):它保存了被修改行的老版本信息来支持像并发性和rollback这样的事务性功能.该信息被存储在表空间中一个叫回滚段(rollback segment)的数据结构之中.InnoDB使用这些信息来支持事务回滚中的undo操做,也使用这些信息来为一致性读(consistent read)操做构建一行数据记录的早期版本.sql
InnoDB会为数据库中的每一行数据添加3个字段:数据库
DB_TRX_ID:6-byte大小,用来存储上一个插入或者修改当前行的事务id.删除也被当作一个特殊的修改,用一个bit位来标记当前数据行已被删除.
DB_ROLL_PTR:7-byte大小,该指针指向一个回滚段中的undo日志记录.若是当前行被修改,那么undo日志中包含重建修改前数据的全部必要信息.
DB_ROW_ID:6-byte大小,存储一个自增id值.若是InnoDB自动生成 聚簇索引(用户没有指定主键的状况下),索引中就会包含该字段的值.
回滚段中的undo日志分为插入和修改.插入undo日志只在事务回滚时使用,而且事务一旦提交就能够丢弃.修改undo日志不只用于事务回滚,也会用于一致性读,只有当没有事务须要使用修改undo日志来为一致性读构建数据记录早期版本的快照(snapshot)时才能进行丢弃.所以,要常常提交你的事务,包括那些只与读操做相关的事务.不然,InnoDB不会丢弃修改undo日志,这样就会致使回滚段变得愈来愈大以致于填满你的表空间(tablespace).一个回滚段中的undo日志记录的物理大小通常来讲会比被插入或被修改的相应的数据行要小,你能够据此来推算回滚段须要的空间大小.缓存
在InnoDB的多版本方案中,当你用SQL语句删除一个数据行的时候,它并不会立刻从数据库中被物理删除.只有当为删除操做创建的修改undo日志被丢弃以后InnoDB才会物理的删除相应的数据行和它的索引.这种移除操做被称为清除(purge),它很是快,一般采用和执行删除操做的SQL语句相同的时间顺序.安全
若是以恒定的速率在表中小批量的插入和删除数据行,清除线程(purge thread)会由于来不及处理清除操做而开始滞后,表就会由于那些已经‘死亡’的数据行而变得愈来愈大,进而产生disk-bound而且会变得很慢.能够经过调整innodb_max_purge_lag来避免上述状况,可是应该根据具体状况进行分析.bash
InnoDB的MVCC(multi-version concurrency control)对待二级索引(secondary index)和聚簇索引(clustered index)不太同样.在聚簇索引中的数据是实时更新,而且他们包含隐藏的系统列.二级索引数据不包含隐藏的系统列也不是实时更新的.服务器
当一个二级索引列被修改时,老的二级索引数据被标记为删除,新的数据会被插入,最终被标记为删除的数据会被清除.当一个二级索引记录被标记删除或二级索引页被一个新的事务修改时,InnoDB将会在聚簇索引中查询数据库记录.在聚簇索引中会检查记录的DB_TRX_ID字段,以便在读事务开始后若是记录被其余事务修改的状况下返回正确的undo日志.session
若是一个二级索引记录被标记为删除或者二级索引页被一个新的事务修改,覆盖索引(covering index)技术将不会被使用.InnoDB会在聚簇索引中查找记录,而不是直接从索引结构中返回数据.
若是启用了index condition pushdown(ICP)优化,而且能够仅使用索引中的字段来评估部分Where条件,则mysql服务器仍会将这部分Where条件下推到存储引擎中,并使用索引对其进行评估.若是找不到匹配的记录,那么就避免了去聚簇索引中查找.若是找到了匹配的记录,即便是被标记为删除的记录,InnoDB都会到聚簇索引中查找记录.
Buffer Pool是一个用于存放被访问的表和索引数据的内存区域.Buffer Pool越大,mysql越像一个内存数据库.
为了提高大数据量读操做的效率,buffer pool被划分为可能包含多行数据的页(pages).为了高效的管理缓存,buffer pool被实现为一个linked list,链表中的每个节点就是一个page.Buffer pool使用一个LRU(least recently used)算法的变体来实现数据的管理.
Buffer Pool的LRU算法.
当须要空间来添加一个新的page时,最近最少使用的page将被丢弃而且新的page将被插入到链表的中间.这个中点插入算法(midpoint insertion strategy)将该list看成两个子列表:
下图是Mysql官网中的buffer pool链表的结构图
这个算法使被频繁访问的pages处于new sublist中.Old sublist中存放不多使用的pages,这些pages是将被丢弃的候选pages.
默认状况下,算法操做以下:
默认状况下,经过query语句读取的pages会立刻被移动到new sublist中,意味着他们能在buffer pool存活更长的时间.一个全表扫描(例如一次mysqldump操做,或者一个没有where条件的select语句)会使大量的数据进入buffer pool中而且会丢弃相同数量的老数据,尽管这些新的数据将不会被再次使用.相似的还有经过预读后台线程加载的pages,在被访问一次以后就会被放入new sublist的头节点中,尽管以后可能并不会访问这些pages.这些状况将致使那些真正会被常常访问的pages进入到old sublist,这样他们就变成了可能被丢弃的对象.
Buffer pool配置
你能够配置buffer pool的各个方面提升性能.
innodb_buffer_pool_size
来配置buffer pool的大小.Change buffer是一个特殊的数据结构用来缓存不在buffer pool中的二级索引页的变动 .这些变动,可能来自INSERT,UPDATE或者DELETE操做,将在这些pages被其余读操做加载到buffer pool中以后被合并.
参考MySQL官网中的结构图:
不像聚簇索引,二级索引一般并非惟一的,并且插入二级索引是以一个相对随机的顺序.相似的,删除和修改操做可能会影响在索引树种不相邻的二级索引页.当pages被其余操做读取到buffer pool中时才去合并缓存的变动,这样避免了大量的随机I/O访问将二级索引页从disk中读取到buffer pool中.
按期的,在系统空闲或者在slow shutdown期间运行的清洗操做(purge operation),会将被修改的索引页写入到磁盘中.清洗操做(purge operation)能够将一系列索引数据写入到磁盘块中,这样比将每个数据立马写入磁盘更加高效.
当有不少被影响的行和不少二级索引须要更新时,Change buffer合并可能要花费好几个小时.在这期间,磁盘I/O可能会增长,这可能致使disk-bound的查询显著的变慢.Change buffer合并可能在一个事务被提交,甚至在一个服务被showdown或restart以后也会持续发生.
在内存中,change buffer占据了一部分buffer pool.在磁盘上,change buffer是系统表空间的一部分,这是当服务shut down的时候来缓存索引变动的.
在change buffer中缓存的数据的类型是受 innodb_change_buffering
变量控制的.
当一个索引包含降序的索引列或主键包含一个降序的索引列时,二级索引不支持变动缓存.
配置Change Buffering
当INSERT、UPDATE和DELETE操做在一个表上被执行时,被索引列的值一般是未排序的,须要大量的I/O来使二级索引实时更新.当相关的page不在buffer pool中时,Change buffer会缓存对这些二级索引项的修改,这样就避免了当即从磁盘读取page的昂贵的I/O操做.这些被缓存的变动会在相关的page被加载到buffer pool中时被合并到page中,这些被修改的page会在随后被刷新到磁盘上.InnoDB的主线程会在服务空闲或在slow shutdown期间对这些缓存变动进行合并.
由于它能减小磁盘的读写,所以change buffer对那些磁盘绑定的工做负载最优价值,例若有高频DML操做(例如大量的insert)的应用.
Change buffer占用了一部分的buffer pool,所以会减小缓存数据页的内存空间.若是工做集大部分都能在buffer pool中(也就是说要操做的大部分pages都已经在buffer pool中),或者你的表只有相应的一些二级索引,那么禁用change buffering多是有效的.由于change buffer只对那些不在buffer pool中的pages有用.
能够经过 innodb_change_buffering
配置参数来控制change buffering的范围.一个修改操做是一个插入操做和删除操做的联合.可用的 innodb_change_buffering
值以下:
你能够经过在MySQL的配置文件(my.cnf或者my.ini)中的 innodb_change_buffering
设置或者经过 SET GLOBAL
语句来进行动态配置.
配置Change Buffer最大大小
innodb_change_buffer_max_size
参数容许配置change buffer占buffer pool的最大百分比.默认是25,最大能设置为50.
基于观察搜索的模式,来利用索引key的前缀来建立hash索引.这个前缀能够是任意长度的,可能只有一个B-tree的的值会出如今hash索引中.Hash索引是针对那些常常被访问的索引页来创建的.
若是一个表的数据基本均可以在内存中匹配,那么hash索引能够提升查询的速度.InnoDB有一种机制监控索引查询.若是InnoDB以为查询能够受益于建立hash索引,那么它就会自动创建hash索引.
在某些工做负载下,hash索引查询的速度提高远大于监控索引查询和维护hash索引结构的额外工做.在过量工做负载的状况下,访问自适应hash索引有时可能会成为竞争的一个源头,例如多个并发链接(我理解当存在并发状况的时候,只有一个线程会去建立hash索引,所以会存在竞争).LIKE语句不会使用hash索引.在MySQL 5.6版本使得禁用自适应hash索引比以前的版本更加合适.
Log buffer是存储要那些将要写入磁盘日志文件中的数据的一块内存区域.能够经过innodb_log_buffer_size
来定义log buffer的大小.默认大小是16MB.Log buffer中的内容是周期性的刷新到磁盘中.一个大的log buffer可使那些大的事务在事务提交前不须要将redo log数据写入到磁盘中.所以,若是你有会修改、插入或者删除不少行的事务,那么你能够增长log buffer的大小来减小I/O操做.
innodb_flush_log_at_trx_commit
参数控制log buffer的数据是如何写入和刷新到磁盘的.innodb_flush_log_at_timeout
参数控制log的刷新频率.
过长的事务:当事务开启时,系统会在事务开始时保存一份数据的快照,所以若是存在大量的插入、修改或删除操做使,会致使大量的开销.
太短的事务:太短的事务会致使频繁的I/O操做,也会产生大量开销.
处理死锁(Deadlocks):对InnoDB表来讲,deadlock并非一个严重的问题,一般也不须要相应的纠正措施.当两个事务开始修改不一样的表,而且以不一样的顺序访问这些表时,就可能会由于互相等待而都不能再继续执行.MySQL会当即发现这个状况,而且取消(roll back)较小(smaller)的事务,而后容许其余的事务继续执行.若是死锁发生的频率比较高的恶化,你可能须要review代码而后对SQL操做进行从新排序,或者将一个大的事务拆分红几个更小的事务.
InnoDB提供了一种可配置的锁机制,它能够显著提升带AUTO_INCREMENT列的SQL语句的扩展性和性能,这里的SQL语句是指向表中添加数据的SQL语句.
术语:
INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');复制代码
innodb_autoinc_lock_mode
配置参数可选的配置为0、一、2,分别表示traditional、consecutive、interleaved.
TO BE CONTINUED...