1.事务是什么?
数据库的事务是一种机制、一个操作序列,包含了一组操作命令。事务把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行,因此事务是一个不可分割的工作逻辑单元。
在数据库上执行并发操作时,事务是作为最小的控制单元来使用的,特别适用于多用户同时操作的数据库系统。
事务具有四个特性ACID,即原子性(Automicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)
一致性是最基本的属性,其他三个属性都是为了保证数据一致性而存在的。
2.ACID
1.一致性:当事务完成后,数据必须处于一致状态。也就是说在事务开始之前,数据库中存储的数据处于一致状态。在正在进行的事务中 ,数据可能处于不一致的状态,比如数据可能有部分被修改。然而,当事务成功完成时,数据必须再次回到已知的一致状态 。通过书屋对数据所做的修改不能损坏数据,或则说事务不能使数据存储处于不稳定的状态。
比如账户A有200元,账户B有200元,此时两人的余额总额是400元,此时A账户给B账户转了100元,此时A有100元,B有300元,此时的余额总额还是400元,也就是事务提交完成后再次回到一致性状态,而如果此时B的数据没有变,则成账户的总额处于不一致状态。
2.原子性:事务是一个完整的操作。事务的各元素是不可分的。十五中的所有元素必须作为一个整体提交或则回滚。如果事务中的任何元素失败,则整个事务将失败。
以银行转账事务为例:如果该事务提交了,则这两个账户的数据都将会更新。如果由于某种原因,事务在成功更新这两个账户之前终止了,则不会更新这两个账户的余额。
为了实现原子性,需要通过日志:将所有对数据的更新操作都写入日志,如果一个事务中的一部分操作已经成功,但以后的操作,由于断电、系统崩溃、其他软件错误而无法继续,则通过回溯日志,将已经执行成功的操作撤销,从而达到“全部操作失败的”目的。
但是原子性并不能完全保证一致性。在多个事务并行进行的情况下,即使保证了每一个事务的原子性,仍然可能导致数据不一致的结果。例如:事务1需要将100元转入账号A:此时将先读取账号A的值,然后再这个值的基础上加上100。但是在事务1操作的时候,另一个事务2也修改了账号A的值,为它增加了100元。那么最后的结果应该是增加200元。但是事实上,账号A只增加了100元,因为事务2操作的结果被事务1覆盖了。
为了保证并发情况下的一致性,引入了隔离性。
3.隔离性:对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其它事务。
那么隔离性是怎么实现的呢?那当然是加锁了,原则上无非是两种锁:悲观锁和乐观锁
悲观锁是什么呢?就向它的字面意思,对数据的操作总是持有悲观的态度,直白一点就是想着总会发生冲突,因此只要对数据进行操作,就对其进行加锁。常见的悲观锁:行锁、表锁、读锁、写锁
乐观锁是什么呢?乐观锁就是持有乐观的态度,对数据进行操作是不会对其进行加锁,只有在对数据进行提交的时候,才会通过一种机制来验证数据是否存在冲突。 通常通过cas和版本号来实现冲突验证。
数据库事务有不同的隔离级别,不同的隔离级别 对锁的使用是不同的,锁的应用最终导致不同事务的隔离级级别:
读未提交:锁的释放实在事务提交之前,也就是数据修改完成后,锁就释放了。会出现脏读、不可重复读、幻读
读已提交:锁的释放放在事务提交之后,解决了脏读、但是还存在不可重复读、幻读。(大多数数据的默认隔离级别)
可重复读:采用的是MVCC(多版本并发控制),解决了脏读、不可重复读,但是还存在幻读。(Mysql数据库的默认隔离级别)
序列化:(加了表锁)解决了所有问题
脏读:比如事务A更新了一条数据,此时事务A还未提交事务,事务B对该条数据进行了读取,但是事务A进行了事务回滚,然后提交了事务,此时事务B读的数据就是错误的或则无用的数据,这种现象就叫做脏读。
不可重复读:比如事务A对一条数据进行一次查询,此时事务B对当前数据记录进行修改,事务B提交事务后,A再对表进行查询,发现和上次查询的结果不同。这种现象称为不可重复读。
幻读:比如事务A往表中插入了一条数据,但是还未提交,此时事务B对该表进行查询,并没有发现A插入的这条数据,此时事务A提交事务,事务B再次对表进行查询发现查询的结果和上次查询的结果不同。这种现象称为幻读。
注意:幻读和不可重复读都是读取了一个已经提交的事务,所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体。
4.持久性:事务的持久性是指不管系统是否发生了故障,事务处理的结果都是永久的。也就是说,一旦事务被提交,事务对数据所做的任何变动都会被永久的保存在数据库中。