您的位置:首页 > 数据库 > SQL

MySQL架构二:与存储引擎相关的事物

2018-12-18 11:05 134 查看

1.事物日志

事物日志可以帮助提高事物的效率。使用事物日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把改修改行为记录到持久在影片上的事物日志中,而不用每次都将修改的数据本身持久到磁盘。事物日志采用的是追加的方式,因此写日志的操作时磁盘上一小块区域内顺序I/O,而不像随机I/O需要在磁盘的多个地方移动磁头,所以采用事物日志的方式相对来说要快得多。事物日志持久以后,内存中被修改的数据在后台可以慢慢地刷回到磁盘。目前大多数存储引擎都是这样实现的,我们通常称之为预写式日志(Write-Ahead Logging),修改数据需要写两次磁盘。

如果数据的修改已经记录到事物日志并持久化,但数据本身还没有写回磁盘,此时系统崩溃,存储引擎在重启时能够自动恢复这部分修改的数据。具体的恢复方式则视存储引擎而定。

2.MySQL中的事物

MySQL提供了两种事物型的存储引擎:InnoDB 和 NDB Cluster。另外还有一些第三方存储引擎也支持事物,比较知名的包括XtraDB 和PBXT。 后面将详细介绍他们各自的一些特点。

(1)自动提交(AUTOCOMMIT)

    MySQL采用自动提交模式。也就是说,如果不是显式的开启一个事物,则每个查询都被当成一个事物执行提交操作。在当前连接中,可以通过设置AUTOCOMMIT变量来启用或禁止自动提交模式;

其中Value:1或ON表示启用,0或OFF表示禁用。当AUTOCOMMIT=0时,所有的查询都是在一个事物中,知道显式的执行commit 提交或者rollback回滚,该事物结束,同时又开始了另一个新事物。修改AUTOCOMMIT对非事物型的表,比如MyISAM或者内存表,不会有任何影响。对这类表来说,没有commit或者rollback的概念,也可以说是一直处于AUTOCOMMIT启用的模式。

了,另外还有一些命令,在执行之前会强制执行commit提交当前的活动事物。典型的例子,在数据定义语言(DDL)中,如果是会导致大量数据改变的操作,比如alter就是如此。另外还有lock tables等其他语句也会导致同样的结果。如果有需要,请检查对应版本的官方文档来确认所有可能导致自动提交的语句列表。

MySQL可以通过执行set transaction isolation level命令来设置隔离级别。新的隔离级别会在下一个事物开始的时候生效。可以在配置文件中设置整个数据库的隔离级别,也可以只改变当前会话的隔离级别

MySQL能够识别所有的4个ANSI隔离级别,InnoDB引擎也支持所有的隔离级别。

(2)在事物中混合使用存储引擎

    MySQL服务器层不管理事物,事物是由下层的存储引擎实现的。所以在同一个事物中,使用多种存储引擎是不可靠的。

如果在事物中混合使用了事物型和非事物型的表,比如:InnoDB 和MyISAM ,在正常提交的情况下不会有什么问题。

但是如果该事物需要回滚,那么非事物型表上的变更就无法撤销,这会导致数据库处于不一致的状态,这种情况很难修复,事物的最终结果也无法确定。所以,为每张表选择合适的存储引擎非常重要。

在非事物型的表上执行事物相关操作的时候,MySQL通常不会发出提醒,也不会报错。有时候只有回滚的时候才会发出一个警告,单大多数情况下,对非实物型表的操作都不会有提示。

(3)显式和隐式锁定

   InnoDB采用的是两阶段锁定协议(two-phase locking protocol)。在事物执行过程中,随时都可以执行锁定,锁只有在执行commit或者rollback的时候才会释放,并且所有的锁是在同一时刻被释放。前面描述的锁定都是隐式锁定,InnoDB会根据隔离级别在需要的时候自动加锁。

另外,InnoDB也支持通过特定的语句进行显示锁定,这些语句不属于SQL规范,例:

select ... lock in share mode

select ... for update

MySQL也支持LOCK TABLES 和UNLOCK TABLES 语句,这是在服务器层实现的,和存储引擎无关。他们有自己的用途,但并不能替代事物处理。如果应用需要用到事物,还是应该选择事物型存储引擎。

经常可以发现,应用已经将表从MyISAM转换到InnoDB,但还是显示的使用LOCK TABLES语句。这不但没有必要,还会严重影响性能,实际上InnoDB的行级锁工作的更好。

注:

    LOCK TABLES和事物之间相互影响的话,情况会变得非常复杂,在某些MySQL版本中甚至会产生无法预料的结果。因此,建议除了事物中禁用了AUTOCOMMIT,可以使用LOCK TABLES之外,其他任何时候不管使用的是什么存储引擎,都不要显式执行LOCK TABLES。

(4)多版本并发控制 MVCC

MySQL的大多数事物型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)。不仅是MySQL,包括Oracle,PostgreSQL等其他数据库系统也都实现了MVCC,但各自的实现机制不尽相同,因为MVCC没有一个统一实现标准。

可以认为MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。

MVCC的实现,是通过保存数据在某个时间点的快照来实现的。也就是说,不管需要执行多长时间,每个事物看到的数据都是一致的。根据事物开始的时间不同,每个事物对同一张表,同一时刻看到的数据可能是不一样的。如果之前没有这方面的概念可能有点迷惑。熟悉了以后会发现,这句话很好理解。

前面说到不同存储引擎的MVCC实现使不同的,典型的有乐观(optimistic)并发控制和悲观(pessimistic)并发控制。下面我们通过InnoDB的简化版行为来说明MVCC是如何工作的。

InnoDB的MVCC,是通过在每行记录的后面保存两个隐藏的列来实现的。这两个列一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事物,系统版本号都会自动递增。事物开始时的系统版本号会作为事物的版本号,用来和查询到的每行记录的版本号进行比较。下面看一下在REPEATABLE READ隔离级别下,MVCC具体是如何操作的。

select:  InnoDB会根据一下两个条件检查每行记录:

    1.InnoDB只查找版本早于当前事物版本的数据行,这样可以确保事物读取的行,要么是在事物开始前已经存在的,要么是事           物自己插入或修改的。

    2.行的删除版本要么未定义,要么大于当前事物版本号。这可以确保事物读取到的行,在事物开始前未被删除

   只有符合上述两个条件的记录,才能返回作为查询结果。

insert: InnoDB为新插入的每一行保存当前系统版本号作为行版本号

delete:InnoDB为删除的每一行保存当前系统版本号作为删除标识

update:InnoDB为插入一行新纪录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识

保存这两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计使得读数据操作更简单,性能更好,并且也能保证只会读取到符合标准的行。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

MVCC只在REPEATABLE READ和READ COMMITTED两个隔离级别下工作。其他两个隔离级别都和MVCC不兼容,因为READ UNCOMMITTED总是读取最新的数据行,而不是符合当前事物版本的数据行。而SERIALIZABLE则会对所有读取的行都加锁。

下一篇将概要的介绍下MySQL中所有的存储引擎 及其特性和应用场景,如果对存储引擎已经有一定了解的可以跳过下一篇

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: