目录

mysql事务特性以及隔离级别(MySQL事务隔离级别与可重复读的实现)(1)

内容概览

事务是访问并更新数据库中各种数据项的一个程序执行单元。在事务中的操作,要么都执行修改,要么都不执行,这就是事务的目的,也是事务模型区别于文件系统的重要特征之一。

MySQL Server系统结构包括三层:SQL层、存储引擎层和物理文件,事务操作是在存储引擎层完成的。

mysql事务特性以及隔离级别(MySQL事务隔离级别与可重复读的实现)(2)

MySQL Server系统结构

MySQL数据库支持多种存储引擎,有InnoDB,MyISAM,NDB,Memory等,并不是所有存储引擎都支持事务操作。

本文介绍InnoDB存储引擎对事务的支持。《MySQL技术内幕》对InnoDB存储引擎的介绍:“InnoDB通过使用多版本并发控制(MVCC)来获得高并发性,并且实现了SQL标准的4种隔离级别,默认为REPEATABLE(可重复读)级别,同时使用一种称为netx-key locking的策略来避免幻读(phantom)现象的产生。

一、事务特性(ACID)1)原子性 Atomicity

事务是一个原子操作,事务中的操作要么都执行,要么都不执行,任何一个SQL语句执行失败 ,那么执行成功的SQL也必须撤销,数据库状态应该退回到执行事务前的状态。

2)一致性 Consistency

一致性指事务操作前和操作后都必须满足业务规则约束,数据库的完整性约束没有被破坏。

3)隔离性 Isolation

隔离性也称为并发控制(concurrency control)、可串行化(serializability)、锁(locking)。多个事务之间的操作是相互隔离的,即该事务提交前对其他事务都不可见,通常使用锁来实现。

4)持久性 Durability

事务一旦提交,其结果是永久性的,即使发生宕机等故障,数据库也能将数据恢复。需要注意的是,持久性只能从事务本身的角度来保证结果的永久性,如果不是数据库本身发生故障,而是一些外部的原因,如RAID卡损坏、自然灾害等导致数据库发生问题,那么所有提交的数据可能会丢失。

二、事务并发问题

由于对数据库的操作不同,事务之间并不是顺序执行,而是并发执行的,事务并发可能会带来如下问题:

1)更新丢失 (Lost Update)

当多个事务操作同一行数据,由于每个事务都不知道其他事务的存在,更新数据时,一个事务的结果可能会被其他事务覆盖,发生丢失更新的问题。

2)脏读(Dirty Reads)

如果一个事务对数据进行了更新,但事务还没有提交,另一个事务读到了该事务没有提交的数据,那么如果第一个事务回滚,第二个事务读到的数据就是脏数据。

3)不可重复读(Non-Repeatable Reads)——针对同一数据

指在一个事务内两次读到的数据是不一样的。比如事务A读取某一数据,事务B修改了该数据,事务A为了对数据进行验证而再次读取该数据,便得到了不同的结果。

一种更易理解的说法是:在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问同一数据并修改数据。那么,在第一个事务的两次读数据之间,由于另一个事务的修改,第一个事务两次读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。

4)幻读(Phantom Reads) ——针对多条数据

幻读是指同样一个查询在整个事务过程中多次执行后,查询的结果集是不一样的,也就是事务中读取到了其他事务新增的数据,仿佛出现了幻象。不符合事务的隔离性,幻读针对的是多条记录。

三、事务隔离级别

并发事务带来的“更新丢失”问题,通常是可以完全避免的。防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决。

“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。

为了解决“隔离”与“并发”的矛盾,ANSI SQL标准定义了 4个事务隔离级别,分别是:

READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE。

1)未提交读(READ UNCOMMITTED)

在一个事务中,可以读取到其他事务未提交的数据变化,这种隔离级别会造成脏读、不可重复读、幻读的问题。

2)已提交读(READ COMMITTED)

在一个事务中,可以读取到其他事务已经提交的数据变化,这种隔离级别会造成不可重复读、幻读的问题。

例如事务A读到了数据a, 事务B正好对数据a进行了更改,并且提交了。那么事务A再次读数据a时发现数据已改变。两次同样的查询可能会得到不一样的结果。

3)可重复读(REPEATABLE READ)

在一个事务中,直到事务结束前,都可以反复读取到事务刚开始时看到的数据,并一直不会发生变化,避免了脏读、不可重复读问题,但无法解决幻读问题。

4)可串行化(SERIALIZABLE)

这是最高的隔离级别,它强制事务串行执行,不会出现脏读、不可重复读、幻读问题。

5)隔离级别总览

隔离级别

数据一致性

脏读

不可重复读

幻读

未提交读

最低级别

已提交读

语句级别

可重复读

事务级别

可序列化

最高级别,事务级别

四、InnoDB存储引擎事务隔离级别

1)InnoDB存储引擎使用MVCC实现了REPEATABLE READ(可重复读),它的默认隔离级别也是REPEATABLE READ。

2)InnoDB存储引擎在REPEATABLE READ事务隔离级别下,使用Next-Key Lock的锁算法,避免了幻读的产生。InnoDB存储引擎已经能完全保证事务的隔离性要求,即达到SQL标准的SERIALIZABLE隔离级别。

实现事务隔离的方式,基本上可分为以下两种。

下面分别介绍一下InnoDB存储引擎的锁和MVCC。

五、InnoDB锁1)锁的介绍

MySQL 不同的存储引擎支持不同的锁机制,有3种粒度的锁:

InnoDB存储引擎既支持行级锁,也支持表级锁,但默认情况下是采用行级锁。

InnoDB实现了两种类型的行锁:共享锁、排他锁。

共享锁(S)——行锁排他锁(X)——行锁

为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB 还有两种内部使用的意向锁(Intention Locks):意向共享锁、意向排他锁。这两种意向锁都是表锁,意向锁的主要作用是处理行锁和表锁之间的矛盾。

意向共享锁(IS)——表锁意向排他锁(IX)——表锁

举例说明:

事务A在更新行数据之前,先在表级别上加意向锁,再加X锁;加意向锁是表示此表某一行被加了X锁; 当事务B要对这个表加S锁,如果表中数据很多,那么事务B需要逐行检查表是否能加S锁,这样会很耗时;如果此时在表级别上有意向锁,那么,事务B先检查该表上是否存在意向锁,存在的意向锁是否与自己准备加的锁冲突,如果有冲突,则等待直到事务A释放,而无须逐条记录去检测,这样速度会很快。

InnoDB行锁模式兼容性

X锁

IX锁

S锁

IS锁

X锁

冲突

冲突

冲突

冲突

IX锁

冲突

兼容

冲突

兼容

S锁

冲突

冲突

兼容

兼容

IS锁

冲突

兼容

兼容

兼容

2)行锁的实现

InnoDB行锁是通过给索引上的索引项加锁来实现的,如果没有索引,InnoDB将通过隐藏的聚簇索引来对记录加锁。InnoDB存储引擎有3种行锁算法

Record lock

单个行记录上的锁,通过对索引项加锁实现,锁住的是索引,而不是记录本身。

Gap lockNext-key lock六、MVCC多版本并发控制

数据库并发时,一般是读写并发、写-写并发。MVCC主要用于处理读-写冲突,MVCC可以做到不加锁,非阻塞并发读。

基本原理

MVCC是通过保存某个时间点的快照来实现的,快照数据是指该行之前的历史版本数据。 InnoDB为每次修改保存一个版本,版本与事务时间戳关联,每行记录可能有多个版本。读取快照数据是不需要上锁的,因为没有事务需要对历史数据进行修改。

对于不同的事务隔离级别,读取到的快照数据是不同的。

MVCC的好处

mysql事务特性以及隔离级别(MySQL事务隔离级别与可重复读的实现)(3)

InnoDB非锁定的一致性读

参考书籍

《MySQL技术内幕:InnoDB存储引擎(第2版)》

《MySQL技术内幕——SQL编程》

《深入浅出MySQL:数据库开发、优化与管理维护(第2版)》

,