MySQL--InnoDB笔记

1.InnoDB存储引擎 

1.1.InnoDB介绍 

        InnoDB是一个平衡了高可靠性和高性能的通用存储引擎.在MySQL的5.6版本中,InnoDB是默认的MySQL存储引擎. 除非你配置了不一样的默认存储引擎,不然不带ENGINE=字句的CREATE TABLE语句将建立一个InnoDB的表.html

像mysql和INFORMATION_SCHEMA这些实现了MySQL内部结构的数据库,仍是使用的MyISAM引擎.而且不能将它们中的受权表切换为InnoDB.  

1.2.主要优点

  • DML(Data manipulation Language)遵循ACID模式,而且事务具备commit、rollback和crash-recovery的功能以保护用户的数据.
  • 行级锁(row-level lock)和Oracle风格的一致性读(consistent reads)提高了多用户的并发性和性能.
  • InnoDB表会根据主键在磁盘上排列数据以优化查询.每个InnoDB表都有一个称为聚簇索引(clustered index)的主键索引来组织数据以减小主键查询时的I/O操做.
  • 为了维护数据的完整性,InnoDB支持外键(FOREGIN KEY)约束.有了外键,将检查插入、更新和删除操做,以确保它们不会致使不一样表之间的不一致.

1.3.使用InnoDB的好处

        这里只挑选了一些我的认为对比较重要的点,更多细节请参考Benefits of Using InnoDB Tables.mysql

  • 故障恢复(crash recovery).无论你的服务器由于硬件仍是软件的问题发生了故障,无论当时数据库正在发生什么,你不须要在重启数据库以后作任何特殊的操做.InnoDB的故障恢复机制会自动的完成故障前已经提交的修改,而故障前未提交的数据将被丢弃.
  • 缓存池(buffer pool):当数据被访问时,InnoDB引擎会在主存中维护本身的缓存池来缓存表以及索引数据.频繁使用的数据将直接从内存中访问.该缓存能适用于多种不一样类型的信息,并能提高处理速度.在一些专用的数据库服务器上,一般超过80%的物理内存会分配给缓存池.
  • 外键(foreign keys):当把相关联的数据拆分到不一样的表中时,可使用外键.当修改或者删除数据时,在其余表的相关联的数据将会被自动修改或删除.当尝试向一个二级表中插入数据而没有修正主表中的数据时,脏数据将被自动剔除.
  • 当你为你的数据库中的每一个表设计了适当的主键列时,包含这些列的操做将自动被优化.当在WHERE语句、ORDER BY语句、GROUP BY语句和join操做中引用这些主键列时会变得很是快.
  • 插入、修改和删除会被一种称为change buffering的自动机制优化.InnoDB不只容许并发的读写同一张表,它还会对修改进行缓存以提升磁盘的I/O效率.
  • 你能够在对性能和可靠性产生很小影响的状况下建立和删除索引.

1.4.InnoDB表的最佳实践

  • 为每一张表指定一个主键.最好使用会被频繁查询的一列或多列,若是没有明显的主键也可使用一个被标记为自增(auto-increment)的列.
  • 当基于指定的ID值从多个表中拉取数据时要使用join操做.为提升性能,能够将这些作join操做的列都定义为外键,并在每一个表中都将这些列声明为相同的数据类型.定义为外键能够确保这些列会被索引化,这样能够提升性能.
  • 关闭自动提交(autocommit).一秒钟几百次提交可能影响性能(受限于你的存储设备的写入速度).
  • 将相关联的DML操做放入事务中.
  • 不要使用锁表(LOCK TABLES)语句.InnoDB能够同时处理多个读写同一张表的会话而不会影响可靠性和高性能.为了独占式的获取一些列的写入权限,可使用SELECT ... FOR UPDATE语法来锁住你想要修改的列.
  • 开启innodb_file_per_table选项,使一个表的数据和索引放入不一样的文件中(也被称为独立表空间),而不是放入系统表空间(system tablespace)中.这个设置会要求使用其余的特性,好比表压缩(compression)(默认开启的).
  • 评估你的数据和访问模式是否受益于InnoDB的表压缩功能.
  • 使用 --sql_mode=NO_ENGINE_SUBSTITUTION参数运行你的服务器以防止表被不一样的存储引擎建立.

2.InnoDB和ACID

        ACID模式是一系列强调对业务数据和关键任务应用程序很重要的可靠性方面的数据库设计原则.算法

  • A:atomicity,原子性.
  • C:consistency,一致性.
  • I:isolation,隔离性.
  • D:durability,持久性.

3.InnoDB多版本

3.1.多版本

        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

3.2.多版本和二级索引

       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都会到聚簇索引中查找记录.

4.InnoDB内存结构

4.1.Buffer Pool

        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看成两个子列表:

  • 在头部,是一个存放最近被访问的新页(new "young" page)的子列表,new sublist.
  • 在尾部,是一个存放最近不多被访问的老页(old page)的子列表,old sublist.

下图是Mysql官网中的buffer pool链表的结构图


        这个算法使被频繁访问的pages处于new sublist中.Old sublist中存放不多使用的pages,这些pages是将被丢弃的候选pages.

默认状况下,算法操做以下:

  • 3/8的buffer pool被分配给old sublist.
  • 中点(midpoint of the list)是new sublist的尾节点和old sublist的头节点相遇的边界处. 如上图.
  • 当InnoDB将一个page读入到buffer pool中时,它最初是被插入到中点(old sublist的头节点).
  • 当访问一个在old sublist中的page时,将使该page变得年轻(young),并将它移动到buffer pool的头节点(也就是new sublist的头节点).若是一个page是由于用户操做而被读取(例如一个query语句),那么第一次访问(first access)就会当即触发并将该page变得年轻(young).若是一个page是由于预读(read-ahead)操做而被读取,那么第一次访问(first access)可能在该page被丢弃以前都不会发生.
  • 当数据库运行时,那些在buffer pool中没有被访问的pages经过向队列的尾节点移动来增长年龄(age),越靠近buffer pool的尾节点,年龄越大.在new sublist和old sublist中的pages的年龄都会由于其余page变得年轻而增长,换句话来讲就是每当有一个page变年轻了(该page被移动到buffer pool的头结点),那么new和old sublist中的其余page都会依次向后移动.在old sublist中的page也会由于在中点插入页而向尾节点移动.最终,当一个未被访问的页到了old sublist的尾节点时将被丢弃.

默认状况下,经过query语句读取的pages会立刻被移动到new sublist中,意味着他们能在buffer pool存活更长的时间.一个全表扫描(例如一次mysqldump操做,或者一个没有where条件的select语句)会使大量的数据进入buffer pool中而且会丢弃相同数量的老数据,尽管这些新的数据将不会被再次使用.相似的还有经过预读后台线程加载的pages,在被访问一次以后就会被放入new sublist的头节点中,尽管以后可能并不会访问这些pages.这些状况将致使那些真正会被常常访问的pages进入到old sublist,这样他们就变成了可能被丢弃的对象.

Buffer pool配置

你能够配置buffer pool的各个方面提升性能.

  • 理想状况下,能够 将buffer pool的大小设置为一个尽量大的值,为服务器上的其余进程保留足够的内存,以便在不过分分页的状况下运行.buffer pool越大,InnoDB越像一个内存数据库,只从磁盘上读取一次数据,以后的query操做都直接访问内存.经过 innodb_buffer_pool_size来配置buffer pool的大小.
  • 在有充足内存的64位系统中,能够将buffer pool分割成多个部分来减小
  • 并发操做对内存结构的竞争.参考 Configuring  Multiple Buffer Pool Instances.
  • 你能够将常常被访问的数据保持在内存中,而忽略掉那些在高峰时刻可能带来大量不会常常被访问的数据到buffer pool中的操做.参考 Making the Buffer Pool Scan Resistant.
  • 你能够控制后台刷新什么时候发生而且是否根据workload来动态调整刷新频率.参考 Configuring InnoDB Buffer Pool Flushing.
  • 您能够控制什么时候以及如何执行预读请求,以异步地将pages预取到buffer pool中,以预期很快将须要这些pages.参考 Configuring InnoDB Buffer Pool Prefetching(Read-Ahead).
  • 能够微调缓冲池刷新行为的各个方面,以提升性能.参考 Fine-tuning InnoDB Buffer Pool Flushing.
  • 您能够配置InnoDB如何保留当前buffer pool的状态,以免服务器从新启动后长时间预热.参考 Saving and Restoring the Buffer Pool State.

4.2 Change Buffer

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值以下:

  • all:默认值,缓存inserts,delete-marking操做,和清除操做.
  • none:不缓存任何操做.
  • inserts:只缓存插入操做.
  • deletes:只缓存delete-marking操做.
  • changes:缓存inserts和delet-marking操做.
  • purges:缓存在后台发生的物理删除操做.

你能够经过在MySQL的配置文件(my.cnf或者my.ini)中的 innodb_change_buffering 设置或者经过 SET GLOBAL 语句来进行动态配置.

配置Change Buffer最大大小

innodb_change_buffer_max_size参数容许配置change buffer占buffer pool的最大百分比.默认是25,最大能设置为50.

4.3 自适应Hash索引

基于观察搜索的模式,来利用索引key的前缀来建立hash索引.这个前缀能够是任意长度的,可能只有一个B-tree的的值会出如今hash索引中.Hash索引是针对那些常常被访问的索引页来创建的.

若是一个表的数据基本均可以在内存中匹配,那么hash索引能够提升查询的速度.InnoDB有一种机制监控索引查询.若是InnoDB以为查询能够受益于建立hash索引,那么它就会自动创建hash索引.

在某些工做负载下,hash索引查询的速度提高远大于监控索引查询和维护hash索引结构的额外工做.在过量工做负载的状况下,访问自适应hash索引有时可能会成为竞争的一个源头,例如多个并发链接(我理解当存在并发状况的时候,只有一个线程会去建立hash索引,所以会存在竞争).LIKE语句不会使用hash索引.在MySQL 5.6版本使得禁用自适应hash索引比以前的版本更加合适.

4.4 Log Buffer

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的刷新频率.

5.InnoDB磁盘(On-Disk)结构

过长的事务:当事务开启时,系统会在事务开始时保存一份数据的快照,所以若是存在大量的插入、修改或删除操做使,会致使大量的开销.

  • 当事务完成时,始终进行COMMIT或ROLLBACK操做.
  • ROLLBACK是相对昂贵的操做,最好使大部分的修改能成功commit,而进行较少的rollback.
  • 对于进行大量插入操做的语句,应该周期性的进行commit操做,避免一次性commit很长时间(例如几个小时).若是出错,最好truncate表而后从头开始执行,而不要执行rollback操做.

太短的事务:太短的事务会致使频繁的I/O操做,也会产生大量开销.

  • 对InnoDB表的大部分操做,最好设置autocommit = 0.从效率的角度来看,这样能够避免没必要要的I/O操做.从安全的角度来讲,这样也能够在发生错误的时候执行ROLLBACK进行回滚.
  • 即便是一个SELECT语句也会建立一个事务,所以在最后也须要加上COMMIT操做或者关闭mysql session.


处理死锁(Deadlocks):对InnoDB表来讲,deadlock并非一个严重的问题,一般也不须要相应的纠正措施.当两个事务开始修改不一样的表,而且以不一样的顺序访问这些表时,就可能会由于互相等待而都不能再继续执行.MySQL会当即发现这个状况,而且取消(roll back)较小(smaller)的事务,而后容许其余的事务继续执行.若是死锁发生的频率比较高的恶化,你可能须要review代码而后对SQL操做进行从新排序,或者将一个大的事务拆分红几个更小的事务.

5.1 InnoDB中处理AUTO_INCREMENT

InnoDB提供了一种可配置的锁机制,它能够显著提升带AUTO_INCREMENT列的SQL语句的扩展性和性能,这里的SQL语句是指向表中添加数据的SQL语句.

术语:

  • "INSERT-like"语句:全部能在表中产生新行的语句,包括INSERT、INSERT ... SELECT、REPLACE、REPLACE ... SELECT和LOAD DATA.包括"simple-inserts"、"bulk-inserts"和"mixed-mode inserts".
  • "Simple inserts": 可以提早知道将被插入的行的数量的语句.包括单行和多行的INSERT和REPLACE语句(不包含子查询),不包括INSERT ... ON DUPLICATE KEY UPDATE.
  • "bulk inserts":没法提早知道将被插入行的数量的语句.包括INSERT ... SELECT、REPLACE ... SELECT和LOAD DATA,不包括简单INSERT语句.在处理每一行时,InnoDB为AUTO_INCREMENT列分配一个新的值.
  • "mixed-mode inserts":在"simple inserts"的语句中一部分(不是所有)指定了auto-increment的值.以下所示.

INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');复制代码

innodb_autoinc_lock_mode配置参数可选的配置为0、一、2,分别表示traditional、consecutive、interleaved.

  • traditional锁模式:在这种模式下,全部的'INSERT-like'语句获取一个特殊的table-level AUTO-INC锁来进行带AUTO-INCREMENT列的插入.这个锁一般会持有到语句结束的时候(不是事务结束的时候)以保证以可预测和可重复的顺序为给定的insert语句序列分配自动增量值,同事也保证为语句分配的自动增量值是连续的.





TO BE CONTINUED...

相关文章
相关标签/搜索