事务的简单表述:
就是一个方法中设计到多个步骤,一旦有步骤跑出了异常,就会回滚前面的步骤,把数据库中做过的修改进行复原。
ACID特性:
1. 原子性(Atomic)
此事务涉及的所有操作都是不可分割的,这些操作要么全部执行,要么全都不执行。
2. 一致性(Consistency)
事务开始前后,数据具备一致性;具体可以认为是事务前后各种约束都还在(符合各种约束);
这里我们举个大家都在说的财务系统的例子.
A要向B支付100元,而A的账户中只有90元,并且我们给定账户余额这一列的约束是,不能小于0.那么很明显这条事务执行会失败,因为90-100=-10,小于我们给定的约束了.
这个例子里,支付之前我们数据库里的数据都是符合约束的,但是如果事务执行成功了,我们的数据库数据就破坏约束了,因此事务不能成功,这里我们说事务提供了一致性的保证.然后我们再看个例子
A要向B支付100元,而A的账户中只有90元,我们的账户余额列没有任何约束.但是我们业务上不允许账户余额小于0.因此支付完成后我们会检查A的账户余额,发现余额小于0了,于是我们进行了事务的回滚.
这个例子里,如果事务执行成功,虽然没有破坏数据库的约束,但是破坏了我们应用层的约束.而事务的回滚保证了我们的约束,因此也可以说事务提供了一致性保证(ps:事实上,是我们应用层利用事务回滚保证了我们的约束不被破坏).
3. 隔离性(Isolation)
隔离性:在并发环境中,会有不同的事务同时操作相同的数据。
4. 持久性(Durability)
事务成功提交后,事务中所有的数据操作都必须被持久化到数据库中。即使在事务提交后,数据库马上崩溃,在数据库重启时,提交的数据还存在,这个是因为事务操作日志会保存起来,数据库恢复之后会依据事务日志来更新数据,这个是需要数据库支持的。
重点说一下隔离性,也就是事务在并发时,可能出现的问题:
1. 更新丢失:
例如:两个并发的事务(A和B)同时修改商品库存,A将库存改成了99,B将库存改成了98.在多线程并发的时候,A还没有执行完,B就开始执行了,所以最终导致A的更新结果为98;
2. 脏读:事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,回滚了,那么事务B读到的就是脏数据。例如:有两个并发的事务(A和B)同时修改商品库存,商品原本的库存为1,A将库存改成了0,B去读的时候,发现库存为0,就无法购买商品,但其实A因为报错会马上回滚,因此B本来是可以买到这最后一个商品的,脏读导致B程序无法买到。
3. 不可重复读:在同一个事务中,对于同一行数据读两次,却可能得到不同的结果。
虚读:事务A(一次事务中)两次读,因为事务B的中途修改,导致A读到2次不同的结果;
幻读: 事务A在两次查询的时候,事务B对表进行了修改,事务A第二次查询的结果不同了;
隔离级别从低到高有:
Read Uncommitted 读未提交:原理是一个事务对一行数据做修改时,可以允许另一个事务来读取,但是不允许另一个事务来修改,这样就不会导致数据同时被两个事务修改,可以避免更新丢失。
Read Committed (Oracle默认)读提交:只有在事务提交后,其更新结果才会被其他事务看见。可以解决脏读问题。
Repeated Read(MySql默认) 重复读:读事务禁止写事务,可以解决脏读、不可重复读。
Serialization:事务串行化执行,隔离级别最高,牺牲了系统的并发性,也就是都不能并发了,一般不用这个。
连接MySql数据库之后,可以用一下Sql命令查询查看当前的隔离级别:
也可以手动进行修改: