本文地址:https://www.askmac.cn/archives/mysql-storage-engines-innodb.html
8.2.2 InnoDB存储引擎
使用InnoDB存储引擎管理的表具有以下等特点:
- 每个InnoDB表在数据库底层磁盘目录中以一个.frm格式文件代表其存在,而其数据和索引的存储会被放在InnoDB表空间(tablespace):
-
- 表空间是一个单独的逻辑存储区,其由一个或多个文件或磁盘分区组成。
- 默认情况下,MySQL仅使用单个InnoDB表空间,其被所有InnoDB表所共享。
- 表的大小可以超过文件系统所允许最大文件大小。
- 可以配置InnoDB以建立每个表时使用其自己的表空间
- 支持事务,可使用COMMIT和ROLLBACK语句
- 完全遵循ACID原则
- 在MySQL服务端或运行的主机崩溃后提供自动恢复
- 提供多版本(Multi-versioning)存储和行级锁(row-level)支持
- 支持外键和参考一致性,包括级联删除和级联更新。
InnoDB表空间和日志:
InnoDB操作使用两种基于磁盘的资源:一个存储表内容的表空间和一堆记录事务活动的日志文件。
每个InnoDB表在数据库磁盘目录中拥有一个.frm格式文件。这和由其它MySQL存储引擎管理的表相同,如MyISAM。然而,InnoDB在管理表内容(数据行和索引)在磁盘上的存储和MyISAM存储引擎不同。默认是,InnoDB使用一个共享的“表空间”,其是由一个或多个文件组成的单个逻辑存储区。所有InnoDB表被存储在此表空间中,但并不是如MyISAM表的针对特定表的对应数据文件和索引文件形式。表空间中还包含有一个回滚段(rollback segment)。当事务修改了行数据,undo日志信息会被存储于此回滚段中,这些信息被用于回滚失败的事务。
尽管InnoDB将共享表空间作为一个简单的逻辑存储空间,它可以包含一个或多个文件。每个文件可以是一个普通文件或一个裸分区(raw partition)。在共享表空间中的最终文件可以被设置为可自动扩展,这样当表空间被填满后InnoDB就会自动扩展它们。因为共享表空间被用于所有数据库(非特定数据库)中的InnoDB表,因此表空间文件默认存储与服务端数据文件目录中,而不是某个特定数据库目录下。
如果你希望使用共享表空间来存储表内容,你可以在启动服务端时使用 --innodb_file_per_table
项。这样,每个InnoDB建立的新表除了建立表的.frm文件外,对应数据库目录下还会建立特定此表的.ibd文件,此.ibd文件作为其表的自有表空间文件,InnoDB将此表的数据都存在相应的.ibd中。(共享表空间仍然是需要的,因为它包含了InnoDB数据字典和回滚段)
使用 --innodb_file_per_table
项并不会影响到任何已经建立在共享表空间中的InnoDB表的可用性,那些表仍然可访问。
除了表空间文件,InnoDB存储引擎还管理了一堆InnoDB特定的日志文件,其包含了关于正进行事务的信息。当客户端执行一个事务,数据的改动会被记录在InnoDB日志中。更新的日志内容被缓存在内存中。一般,缓存的日志信息会在事务commit时被刷出缓存并写入日志文件中,尽管可能写入会发生得更早一些。
如果在表正被更新时发生了崩溃,日志文件就会被用于自动恢复:当MySQL服务端重启,它会将记录与日志中的更新操作重新应用,以保证表反应了所有已经commit的事务。
InnoDB 和 ACID规则:
InnoDB完全满足并支持ACID,并假定其日志flush行为已经做了很好的设置。InnoDB可以对于日志flush进行设置以提供了对ACID的支持,或为了获取更佳的性能但在出现服务端崩溃时有丢失部分最近事务风险的flush设置。默认情况下,InnoDB日志日志flush是设置为遵守ACID规则的。
InnoDB锁:
- 由于InnoDB使用多版本(multi-versioning)控制,因此其不需要设置锁来获取一致性读:修改那些行的事务看到的是那些行对此事务的版本,而undo日志允许其它事务看到那些行的最初版本。当然你也可以通过在select语句上增加锁修饰标识符来进行加锁操作。
- 当锁是必要的时,InnoDB会使用行级锁。和多版本控制一起,这将获得更好的查询并行效果,因为一张表可以同时被不同客户端进行读和写操作。行级并行属性有以下特点:
-
- 不同客户端可以同时读取相同的行。
- 不同客户端可以同时修改不同的行。
- 不同客户端不能在同时修改相同的行。如果一个事务修改了一行,而另一个事务则不能修改此行直到第一个事务完成此修改才可。其它事务也不能读取到此修改的行,除非它们使用READ UNCOMMITTED隔离级。这意味着,它们只能看到最初未被修改时候的行。
- 在一个事务过程中,InnoDB在发现需要锁时会去获取行锁。尽管InnoDB确实可以使用表锁来做一些操作。但是,它不会主动去升级锁(如,升级转换为页锁或表锁),这将保持锁的争用处于一个最小范围并提高并行性。
- 因为InnoDB在事务中只有在需要时,才会去获取锁,因此碰到死锁的情况是可能的。被死锁的事务最终会由于等待时间耗尽而失败,而InnoDB则会对事务进行回滚。
InnoDB支持在SELECT语句最后加上两种锁修饰符。它们可用于获取共享锁或排它锁(exclusive lock),并可将非带锁读转变为带锁读:
- 使用LOCK IN SHARE MODE,InnoDB会使用共享锁锁住被查询出的每一行。共享锁意味着其它事务都不能对这些行获取排它锁,但它们任然能获取共享锁。由于正常都并不会锁任何行,因此它们不会受到这些共享锁的影响。
- 如果SELECT会查询到一些已被修改但是还未被事务commit的行,如果SELECT语句后加上了LOCK IN SHARE MODE,那将会使得SELECT被堵住,直到事务commit完成。
- 而在SELECT语句后加FOR UPDATE,InnoDB会使用排它锁来锁住所查询出的每一行,避免其它事务来获取到这些行的任何锁,但允许读取这些行。
在REPEATALE READ隔离级别,你可以对SELECT语法来增加LOCK IN SHARE MODE语法来强制其它希望修改那些被锁定的行的事务来等待你的事务。这和处于SERIALIZABLE隔离级的操作类似,在SELECT进行查询时候未加显式锁定修饰符,而在SERIALIZABLE隔离级,此SELECT语句句尾会被隐式增加LOCK IN SHARE MODE。
InnoDB隔离级别,多版本控制和并行处理:
之前提到,多个事务可以在服务端并行执行,每个客户端一个事务。这里潜在可能会发生问题:如果一个客户端的事务修改了数据,那么其它客户端的事务是否应该看到那些改变或者说它们是否应该和这些修改保持隔离呢?事务的隔离级别确定了在事务之间可视的级别 —— 即,同时进行的事务之间互相访问相同数据的方式。注意,不同的数据库,其定义也会有不同,因此在InnoDB中实现的隔离级别不可能完全和其它数据库实现的级别功能一致。
当多个客户端同时运行事务时,会出现的3个问题有脏读,非重复读和幻读。InnoDB实现了4中隔离级别来控制事务之间的可视性,它们是:READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ和SERIALIZABLE。
注意:具体隔离级别细节内容已经在《事务》这节中有提到。
InnoDB的优点(缺点正在不断改进中…):
优点 |
|
Comment