关于事务,在Oracle中似乎是习以为常的,但是在学习MySQL的过程中,发现各种灵活的存储引擎,一个很大的焦点就是对于事务的支持,足以看出事务的实现还是有一定难度的,自己在学习数据库理论和Oracle的时候,对于事务的特性也都是一笔带过,ACID似乎就是书本中的概念和术语,感觉太偏向理论了,但是今天在学习看书中,也借鉴了不少宝贵的经验,重新审视来发现事务的强大和重要性。
好多书中都会提到的ACID,先来看看是怎么说的。
原子性:Atomicity,一致性:Consistency,隔离性:Isolation,持久性:Durability
举个例子来说明,比如使用银行卡转账,给某个朋友转账100块,就需要执行几个步骤:
首先会查看银行卡中的余额是否满足条件。
其次会从银行卡中转出100块,
然后对方的银行卡中会转入100块。
这个例子很简单也很明显,
首先原子性,就是这个转账的过程中,转账操作是一个不可再分的单元了,转账的的操作,卡1转出成功,卡2转入成功,整个过程要不全部完成,要不直接回退。
其次是一致性,就是在数据库中,事务总是从一个一致性的状态转换为另外一个一致性的状态,比如我们在操作的第2步,银行卡转出100块的瞬间,系统奔溃,电脑死机,你的账户也不会平白无故少100块钱。因为事务最终没有提交,所做的修改也不会保存在数据库中。
再次是隔离性,这个一般来说,一个事务所做的修改,对其它事务是不可见的,这一点解释起来稍微有些困难,先放一放。
最后一点是持久性,就是在数据库中,所做的事务变更最后都保存在数据库中,这一点还是从逻辑上保证的。至少在Oracle中你做了commit不会立刻写入数据文件,也是一个异步的过程。
ACID确实可以保证银行卡里不会弄丢钱,但是在逻辑层面要实现这个事务还是很困难的,从MySQL的存储引擎的发展就能看出,InnoDB存储引擎相比MyISAM来说,对于资源的消耗和性能会有一定的打折,但是相比来说就更加有优势。
其中一个难点就是隔离性,在SQL标准里面也提供了4种隔离级别,每一种级别规定了在一个事务的修改过程中,哪些在事务间是可见的,哪些是不可见的。
比如我们用sql语句来表示两个并发的事务。
事务1中开始查看余额,有1000块,然后向卡里转入100块。在这个过程中事务1还是没有提交,是否对于事务2可见这个1100块新余额。
如果从生活实际来考虑是不应该的。如果对于事务2可见,则这种情况下隔离级别就是未提交读( READ COMMITED),就是我们常说的脏读,当然这种隔离级别的开销是很低的,但是会导致很多问题,所以一般的在实际应用还是比较少的。
然后我们进一步思考,如果事务1在充值100块之后,在事务没有提交之前,对于事务2是完全不可见的,这种隔离级别就是不可重复读,或者提交读(READ COMMITED),Oracle中就是采用这种隔离级别的。但是在MySQL中缺默认不是这种隔离级别。
我们来看看第三种隔离级别,可重复读( REPEATABLE READ),这个级别保证了在同一个事务中多次读取同样的记录的结果是一致的。
这种情况,事务1先充值100块,这样余额就是1100,事务2充值100块,余额就是1200,但是在事务1未提交,所以再次查询的时候还是显示余额1100,这种情况就是幻读(Phantom Read)。
最后一种是最高级别,即可串行化(SERIALIZABLE),这种情况下会强制事务串行执行,可以避免幻读的问题,但是他会在读取的每一行数据上都加锁,所以可能导致大量的性能问题。这种情况下使用的场景也很少。
在Oracle中可以使用set transaction isolation level 来指定四种隔离级别。
比如未提交读,就可以运行
> set transaction isolation level read committed;
Transaction set.
好多书中都会提到的ACID,先来看看是怎么说的。
原子性:Atomicity,一致性:Consistency,隔离性:Isolation,持久性:Durability
举个例子来说明,比如使用银行卡转账,给某个朋友转账100块,就需要执行几个步骤:
首先会查看银行卡中的余额是否满足条件。
其次会从银行卡中转出100块,
然后对方的银行卡中会转入100块。
这个例子很简单也很明显,
首先原子性,就是这个转账的过程中,转账操作是一个不可再分的单元了,转账的的操作,卡1转出成功,卡2转入成功,整个过程要不全部完成,要不直接回退。
其次是一致性,就是在数据库中,事务总是从一个一致性的状态转换为另外一个一致性的状态,比如我们在操作的第2步,银行卡转出100块的瞬间,系统奔溃,电脑死机,你的账户也不会平白无故少100块钱。因为事务最终没有提交,所做的修改也不会保存在数据库中。
再次是隔离性,这个一般来说,一个事务所做的修改,对其它事务是不可见的,这一点解释起来稍微有些困难,先放一放。
最后一点是持久性,就是在数据库中,所做的事务变更最后都保存在数据库中,这一点还是从逻辑上保证的。至少在Oracle中你做了commit不会立刻写入数据文件,也是一个异步的过程。
ACID确实可以保证银行卡里不会弄丢钱,但是在逻辑层面要实现这个事务还是很困难的,从MySQL的存储引擎的发展就能看出,InnoDB存储引擎相比MyISAM来说,对于资源的消耗和性能会有一定的打折,但是相比来说就更加有优势。
其中一个难点就是隔离性,在SQL标准里面也提供了4种隔离级别,每一种级别规定了在一个事务的修改过程中,哪些在事务间是可见的,哪些是不可见的。
比如我们用sql语句来表示两个并发的事务。
事务1中开始查看余额,有1000块,然后向卡里转入100块。在这个过程中事务1还是没有提交,是否对于事务2可见这个1100块新余额。
如果从生活实际来考虑是不应该的。如果对于事务2可见,则这种情况下隔离级别就是未提交读( READ COMMITED),就是我们常说的脏读,当然这种隔离级别的开销是很低的,但是会导致很多问题,所以一般的在实际应用还是比较少的。
然后我们进一步思考,如果事务1在充值100块之后,在事务没有提交之前,对于事务2是完全不可见的,这种隔离级别就是不可重复读,或者提交读(READ COMMITED),Oracle中就是采用这种隔离级别的。但是在MySQL中缺默认不是这种隔离级别。
我们来看看第三种隔离级别,可重复读( REPEATABLE READ),这个级别保证了在同一个事务中多次读取同样的记录的结果是一致的。
这种情况,事务1先充值100块,这样余额就是1100,事务2充值100块,余额就是1200,但是在事务1未提交,所以再次查询的时候还是显示余额1100,这种情况就是幻读(Phantom Read)。
最后一种是最高级别,即可串行化(SERIALIZABLE),这种情况下会强制事务串行执行,可以避免幻读的问题,但是他会在读取的每一行数据上都加锁,所以可能导致大量的性能问题。这种情况下使用的场景也很少。
在Oracle中可以使用set transaction isolation level 来指定四种隔离级别。
比如未提交读,就可以运行
> set transaction isolation level read committed;
Transaction set.