数据库事务和MVCC

1.事务概念
事务就是一组原子性的SQL,事务内的语句,要么全部执行成功,要么不执行。
例如:A要给B转账200元,那么A的账户要减少200,B的账户要增加200,这两个操作要在同一个事务中,要么全部执行成功,要

么不执行。

2.ACID特性

Atomicity(原子性):一个事务必须被视为一个不可分割的工作单元,事务中的操作要么全部成功,要么全部失败回滚。

Consistency(一致性):数据库总是从一个一致性的状态转换到另外一个一致性的状态。拿之前的转账举例,事务开始之前和事

务结束之后,总金额是不变的。

Isolation(隔离性):一个事务所做的修改在最终提交之前,对其他事务不可见。仍然拿之前的转账来举例,事务提交之前,

如果有其他的事务查询A的账户余额,那么这是A的账户余额并没有减少200。

Durability(持久性):一旦事务提交,则其所作的修改就会永久的保存到数据库中。即使此时数据库崩溃,修改的数据也不会消

失。


3.事务隔离级别

SQL标准定义了四种隔离级别,每一种级别都规定了一个事务中所作的修改,哪些在事务内和事务间是可见的,哪些是不可见的

。低级别的隔离通常可以执行更高的并发,系统的开销也更低。

隔离级别由低到高分别为:

READ UNCOMMITED(未提交读):事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这称

为脏读。 因为这样会带来很多问题,所以实际中一般不用。

READ COMMITED(提交读):一个事务开始时,只能看到已经提交的事务所做的修改。也就是说一个事务提交之前,所做的修改

对其他事务不可见。这个级别也称为不可重复读,因为同样的查询,执行两次可能得到不一样的结果。
大多数数据库的默认级别都是READ COMMITED。

REPEATABLE READ(可重复读):该级别保证同一次事务中多次读取同样记录的结果是一致的。但是此隔离级别还是无法解决幻读

问题,幻读就是当一个事务读取某一范围内的记录时,另外一个事务又在该范围内插入了新的记录,这样之前的事务再次读取该

范围内的记录时,会产生幻行。

SERIALIZABLE(可串行化):它强制事务串行执行。它会导致大量的锁争用问题,所以一般很少用。

隔离级别   脏读  不可重复读  幻读
READ UNCOMMITED yes    yes         yes
READ COMMITED           no     yes    yes
REPEATABLE READ         no     no    yes
SERIALIZABLE no     no          no

4.MVCC
InnoDB通过MVCC(MultiVersion Concurrency Control)(多版本并发控制)解决了幻读的问题,oracle数据库也是。
它是行级锁的一个变种,实现了非阻塞的读,写操作也只锁定必要的行,跟ConcurrentHashMap原理相似,虽然技术不同,解决

方案都是相通的。
MVCC保证不管事务执行多长时间,每个事务看到的数据都是一致的。根据事务的开始时间不同,每个事务看到的数据可能是不一

样的。真心有些拗口啊,仔细体会一下吧。
MVCC通过在每行记录后面保存两个隐藏的列实现,一个是创建时间,一个是删除时间,这个时间不是真正的时间,是系统的版本

号,每次开始一个新的事务,系统版本号都自动增加。这个解决方案也类似于CVS操作的ABA问题的解决方案,历史总是相似的啊



SELECT:
1.返回行的创建时间小于等于当前系统版本号的数据行,这样保证要么是事务开始之前已经存在的,要么就是事务本身写入的。
2.返回行的删除时间为空的,或者删除时间大于当前系统版本号的数据行,这样保证要么没有删除,要么就是读取之后的其他事

务删除的。
INSERT:
为新插入的行保存当前系统版本号作为行的创建时间。
DELETE:
为删除的行保存当前系统版本号作为行的删除时间。
UPDATE:
插入一条新的记录,用当前系统版本号作为行的创建时间,同时将当前系统版本号记录到原记录的行删除时间中。


查询-插入,先查询1 后插入2,那么查询的时候比较找小于等于的,没问题。其中1和2代表系统版本号,下同。
如果先插入1 后查询2 那么就能找到,没问题。
查询-删除,先查询1 后删除2,那么查询删除记录不为空,并且大于查询的系统版本号,所以返回,没问题。
先删除1,后查询2 ,按照规则不返回,没问题。
查询-修改,先查询1,后修改2,修改导致新增的行,创建时间2,原记录删除时间2,由于新的行版本号大于查询事务所以新的

行不返回,原有的行删除时间不为空,并且大于查询的系统版本号,所以返回,没问题。
先修改1,后查询2,修改导致新增的行,创建时间1,原纪录的删除时间为1,由于新的行版本号小于等于查询事务所以新的行返

回,原有的行的删除时间不为空,并且小于查询的系统版本号,所以不返回,没问题。



参考资料:

高性能MySQL
http://sharong.iteye.com/blog/2164123
http://suhuanzheng7784877.iteye.com/blog/2047209

猜你喜欢

转载自frank1234.iteye.com/blog/2164232