事务的基本要素
1、原子性(Atomicity)
事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
2、一致性(Consistency)
事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
3、隔离性(Isolation)
同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
4、持久性(Durability)
事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
事务的并发问题
1、脏读
事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读
事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
3、幻读
系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
事务的隔离级别
1、读未提交
读的都是最新版本的数据,会出现脏读;
2、读已提交
读的都是已提交的数据,会出现不可重复读;
3、可重复读
解决了不可重读的问题,InnoDB解决了幻读问题;
4、串行化
用加锁的方式实现串行化。
隔离级别的实现
每行数据其实在数据库都是多个版本的,可能同一时间有很多事务在更新一条数据,事务在开始的时候会申请一个id,这个id是严格随着时间递增的,先开始的事务id总是小的,数据库的版本就是事务id的版本。
1、读未提交
每次读的都是最新版本,这样速度是最快的,使用中的业务场景基本上没有
2、读已提交
如果当前数据版本的号(最新事务对这条数据的操作)比事务的id大,就会根据版本的id查看事务是否提交了,如果提交了,就会承认这条数据,如果查到这个事务还没有提交,就会查看上个版本,直到找到已提交的版本,获取那个版本的数据,那有没有读到的版本是已提交的,上个版本还没提交呢,当然是不会的,更新的时候会加上一个行锁,上个事务如果没有提交,这个事务是不可能提交的
3、可重复读
可重复读在事务启动的时候获取一个数组,记录未提交的事务,可重复读取数据的时候多了一个验证,如果事务提交了但是数据的版本号(操作这个数据事务的id)比当前事务高,说明这个事务是在当前事务启动后启动并且提交的,这条数据是不会被承认的,如果当数据的版本号比当前事务id低的话,说明操作是在当前事务开启之前就开启了,这条数据是被当前事务承认的。
可以发现InoDB引擎是通过MVCC解决了幻读的问题。
4、串行化
用加锁的方式来避免并行访问
5、视图
读已提交和可重复读都有视图概念的,读已提交获取的是最新提交的视图,可重复读在事务启动的时候就开启,保证事务内读到的数据是一样的,比如一个事务 执行了两次 select city from tb_user where id = 100 ,中间有一个新的事务执行了修改操作,对于可重复读,两次查询结果都是一样的,对于读已提交,两次结果就不一样了。