我们先来明确一下什么是事务:我们在业务中往往不止有一条sql,多条sql要么全部执行成功,要么全部执行失败,不能一条成功一条失败,这就叫做事务。全部执行成功就会commit,将数据写入数据库,有一条失败就会rollback。
其次我们要确定一下,以下所有内容都是基于InnoDB引擎的,因为MyISAM不支持事务。
事务的ACID
事务有着四大特性:
A:原子性,事务是一个不可分割的整体,要么全执行,要么全不执行,不允许事务部分完成。
C:一致性,事务执行前后数据库状态不变,一致性必须由用户负责,并发控制机制实现。比如数据层和缓存层的数据一致性问题。
I:隔离性,当两个或者多个事务并发执行时,为了保证数据的安全性,将一个事物内部的操作与其它事务的操作隔离起来,不被其它正在执行的事务所看到,使得并发执行的各个事务之间不能互相影响。
D:一致性,事务完成(commit)以后,DBMS保证它对数据库中的数据的修改是永久性的,即使数据库因为故障出错,也应该能够恢复数据。
其中ACD三特性是由事务日志(undo log,redo log)保证的,I(隔离性)是由MVCC+锁保证的。
一个业务中可能会有多个事务,如果这些事务全部是串行执行的话,就会使效率变得低下。而支持并发执行就需要保证事务的安全性和一致性,所以要保证事务的隔离性。对了,还要注意并发效率问题。
事务并发执行导致的问题
事务是支持并发执行的,而并发执行就会导致一些问题:
脏读:读取到另一个事务未commit的数据。
不可重复读:一个事务的操作导致另一个两次获取到的数据不同。
幻读:一个事务的操作导致另一个事务两次查询到的数据量不同。
事务的隔离级别
为了解决上述问题,MySQL提出了四中隔离级别:
读未提交:没有任何并发控制。
读已提交:解决了脏读问题。使用MVCC实现。
可重复读:解决了不可重复读问题,解决了部分幻读问题。使用MVCC+锁实现。
串行化度:解决了上述三种问题,但是并发效率会变差。使用锁实现。
MySQL默认的隔离级别是可重复读,Oracle默认的隔离级别使读已提交。
串行化读可以解决事务并发导致的所有问题,但这也会是事务并发执行的效率变得差。因为在串行化读隔离级别下,读写数据都会锁住整张表。所以我们并不需要去追求串行化读,因为在不同的业务场景下事务的并发问题并不算是问题。
比如我们在小破站搜索视频时,第一次根据标签搜索到的视频只有两个,过了一秒闪退之后重新搜索,发现变成了三条或者更多,但这对于我们业务场景来说并不算问题。所以无需去追逐更高的隔离级别,那样会使我们的业务执行效率变差。而是选择和自己业务合适的隔离级别。