-----------------------------------------------------------
ACID指数据库事务正确执行的四个基本要素:原子性(Atomicity),一致性(Consistency),隔离性(Isolation),持久性(Durability)。
1、原子性
原子性是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生。
a.案例A给B转账100元钱
begin transaction update account set money= money - 100where name=‘A‘; update account set money= money +100where name=‘B‘; if Error then rollback else commit
b.分析
在事务中的扣款和加款两条语句,要么都执行,要么就都不执行。否则如果只执行了扣款语句,就提交了,此时如果突然断电,A账号已经发生了扣款,B账号却没收到加款,在生活中就会引起纠纷。
c.解决方法
在数据库管理系统(DBMS)中,默认情况下一条SQL就是一个单独事务,事务是自动提交的。只有显式的使用start transaction开启一个事务,才能将一个代码块放在事务中执行。保障事务的原子性是数据库管理系统的责任,为此许多数据源采用日志机制。
为了实现原子性,db将所有对数据的更新操作都写入日志,如果一个事务中一个部分操作已经成功,但以后的操作由于断电\系统崩溃等原有而无法继续,则通过回溯日志,将已经执行成功的进行撤销,从而达到“全部操作失败”的效果。最常见的场景是数据库系统崩溃后重启,此时数据库处于不一致的状态,需要先执行一个crash recover过程:读取日志进行redo,再对所有到崩溃时未提交的事务进行undo。
2、一致性
一致性是指在事务开启之前和事务结束之后,数据库的完整性约束没有被破坏。这个说的太抽象了,感觉一直没有理解透彻。
我的理解:一致性就是指数据处于一种正确的有现实意义的状态,这种状态是语义上的而不是语法上的。例如:A转账到B账户上,如果A账户减少了而B没有增加,则我们认为此事数据处于不一致的状态。
单纯的原子性并不能保证一致性,多事务并行的情况下,即使保证每一个事务的原子性,仍然不能保证数据的一致性。例如:事务1将100元转入A账户,它先读取A账户的余额,然后在此基础上加100,然后更新A的余额;这个过程中事务2修改了A的值,为他增加了100元,最终结果应该是A增加了200,但事实上可能是100,因为事务1把事务2的结果给覆盖了。
3、隔离性
正是因为2中覆盖的原因,才有了隔离性。多个事务并发访问的时候,事务之间应该是隔离的,一个事务不应该影响到其它事务的运行效果。
为了实现这一点,我们创造了各种锁机制。所有的锁,无非两种类型,悲观或乐观。
悲观锁:将当前事务所涉及的所有对象加锁,操作完成后再是否给它人使用。为了尽可能提高性能,发明了各种粒度的锁(数据库级/表级/行级),各种性质的锁(共享锁,排他锁,共享意向锁,排他意向锁,,,)。为了解决死锁问题,又发明了两阶段锁协议/死锁检测等一系列技术。
乐观锁:不同的事务可以同时看到同一对象(一般是数据行)的不同历史版本,如果两个事务同时修改了同一行数据,那么较晚的事务提交时进行冲突检测。
锁也是数据库实现中最复杂的部分之一,如果涉及到分布式系统,会更加复杂的多。
实际生产中,完全的事务隔离是不现实的,因为这会导致同一时间只执行一条事务,严重影响性能。
几个概念:
a、脏读
一个事务读取了另一个事务未提交的数据,而这个数据是有可能回滚的;
b、不可重复度
不可重复读意味着,在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。如下案例,事务1必然会变得糊涂,不知道发生了什么。
c、幻读
幻读,是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。(跟情况b类似)
d、丢失更新
两个事务同时读取同一条记录,A先修改记录,B也修改记录(B是不知道A修改过),B提交数据后B的修改结果覆盖了A的修改结果。
mysql的四种事务隔离级别:
默认隔离级别是 读已提交(Read committed)
4、持久性
持久性,意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。即使出现了任何事故比如断电等,事务一旦提交,则持久化保存在数据库中。