事务
1 什么是数据库事务?
事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。
事务最经典也经常被拿出来说例子就是转账了。
假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明
的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。
所以事务就是保证这两个关键操作要么都成功,要么都要失败。
2 事物的四大特性(ACID)介绍一下? 以及对应的实现原理说下
四大特性也就是原子性,一致性,隔离性,持久性
首先我们看原子性:说白了就是当前事务的操作要么同时成功,要么同时失败,而原子性其实是由undo log日志来保证的
(这里简单介绍下undo log,undo log他其实是一种用于撤销回退的日志,也就是说在事务没提交之前 mysql会先记录更新前的数据到undo log日志文件中,当事务回滚时,可以用undo log来进行数据回滚,其实说白了undolog就是用执行的反向sql语句来进行回滚)
接着看隔离性:隔离性其实就是事务在并发执行过程中,不能互相干扰,而隔离性其实是由mysql中的各种锁以及mvcc机制实现的
(之后会具体介绍mvcc等机制)
再看持久性:持久性简单的理解就是一旦你提交了事务,那么它对于数据库的改变就是持久的,而持久性其实是由redo log日志来保证的
(这里也简单的说下redo log日志,首先 我们要知道当我们修改某条记录的时候其实并不是立刻刷入到磁盘的,而是会写buffer pool并将buffer pool标记为脏页,与此同时将本次对页的修改以redo log的形式记录下来,而后续innodb会在合适的时候通过后台线程将缓存再buffer pool中的脏页刷入到磁盘,这其实就是WAL技术
说白了WAL 就是mysql的写操作并不是立刻写到磁盘,而是先写文件,然后再合适的时间在写磁盘,
而正是因为有redo log的存在,即便系统崩溃了,还没来得及持久化脏页数据,mysql在重启后,会根据redo log的内容,将所有数据恢复到最新的状态。)
一致性:使用事务的最终目的,而一致性其实是由原子性,隔离性,持久性,这三个特性,以及业务代码正确逻辑来保证的
为啥说要保证正确逻辑呢?举个例子 比如说现在你先下单,在扣减库存,你下单成功了,但是扣减库存报错了,而你在try中执行的业务逻辑,catch中捕获时只是输出了日志并没有抛出异常,这就是导致了问题,即下单成功了,但是扣减库存失败了,所以说还需要保证逻辑的正确性
3 什么是脏读?幻读?不可重复读?
脏读:就是一个事务读取到了另外一个未提交事务的修改过的数据,就意味着发生了脏读,
简单解释下 就是比如事务A更新了一份数据,但是没有提交,而与此同时事务B也来读取相同的数据,因为事务A没有提交所以他随时是可以回滚的,一旦事务A发生回滚,那么事务B所读取的数据就是过期数据,这种现象就是脏读;
不可重复读:在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了不可重复读现象
而发生不可重复读可能是因为两次查询过程中间插入了事务更新原有数据的操作;
比如说现在有两个事务A和B,事务A先读取数据,之后继续执行逻辑处理,而在这过程中事务B更新了这条数据并提交了事务,那么当事务A再次读取数据的时候,就会发现前后两次读取到的数据不一致性
幻读:在一个事务的两次查询中,数据的记录条数不一致,这就意味着幻读
简单说下 比如说现在有事务A和B,他们同时在处理,一开始事务A进行范围查询,查询的记录条数为3条,同时事务B也按相同的查询条件去查,也查出了3条记录,紧接着,事务A在这个范围中插入了一条记录,并提交了事务,那么此时数据库的条数就变成了4条,而事务B再次去查的时候就是4条记录,就和之前查询的记录数量不一致了,这种现象就是幻读
4 那什么又是隔离级别,有哪几种隔离级别呢?
首先我们要知道 正是因为事务在并发执行的过程中可能会遇到上面的各种问题–即脏读,幻读,不可重复读,也就是会对事务的一致性产生不同程度的影响,所以才引入隔离级别来去解决这些问题
有哪几种隔离级别-有读未提交,读已提交,可重复读,串行化四种隔离级别,隔离级别是由低到高,同时隔离级别越高,性能也就越差
读未提交:最低的隔离级别,也就是允许读取尚未提交的数据变更,可能会发生脏读,幻读或者不可重复读
读已提交:见名知义-即允许读取并发事务中已经提交的数据,可能会发生幻读或者不可重复读
可重复读:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
串行化: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是性能最差
这里需要注意的是:
Mysql 默认采用的 REPEATABLE_READ隔离级别
Oracle 默认采用的 READ_COMMITTED隔离级别
事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多
版本并发控制),通过undo log版本链和readview机制来支持并发一致性读和回滚等特
性。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是
READ-COMMITTED(读取提交内容):,
但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)并不会有任何性能损失。
InnoDB 存储引擎在 分布式事务 的情况下一般会用到SERIALIZABLE(可串行
化)**隔离级别。
5 MVCC机制了解吗?
首先我们要理解mvcc多版本并发控制,它其实主要是为了解决在RC,RR这种隔离级别下,读和写并发冲突的问题
这里的多版本就是多个undo日志版本链,
只要你对数据做了修改,不管你有没有提交 它都会加入到版本链中去,每条记录-id 都有自己的版本链
ReadView 是针对当前事务的,RR隔离级别下也就是说当你开启一个事务,进行查询的时候
它在那一刻查询到的readview 和你之后查询到的都是一样的,
注意你执行了更新操作,那其实是执行了当前读,之后这个视图再去查询,查询到的这条记录都是你更新后的。 但是注意readview并没有变,如果你查询其他的记录,还是需要拿着readview根据可见性视图算法规则去查找
而RC隔离级别是每一次查询都会查到当前最新的readview视图,都取了当前事务活跃事务和未活跃事务的状态
在可重复读隔离级别,当事务开启,执行任何查询sql时会生成当前事务的一致性视图read-view,该视图在事务结束之前永远都不会变化(如果是读已提交隔离级别在每次执行查询sql时都会重新生成read-view),这个视图由执行查询时所有未提交事务id数组(数组里最小的id为min_id)和已创建的最大事务id(max_id)组成,事务里的任何sql查询结果需要从对应版本链里的最新数据开始逐条跟read-view做比对从而得到最终的快照结果。
版本链比对规则:
- 如果 row 的 trx_id 落在绿色部分( trx_id<min_id ),表示这个版本是已提交的事务生成的,这个数据是可见的;
- 如果 row 的 trx_id 落在红色部分( trx_id>max_id ),表示这个版本是由将来启动的事务生成的,是不可见的(若 row 的 trx_id 就是当前自己的事务是可见的);
- 如果 row 的 trx_id 落在黄色部分(min_id <=trx_id<= max_id),那就包括两种情况
a. 若 row 的 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见(若 row 的 trx_id 就是当前自己的事务是可见的);
b. 若 row 的 trx_id 不在视图数组中,表示这个版本是已经提交了的事务生成的,可见。
所以RR隔离级别和RC隔离都是适用上述的可见性算法规则,区别只是查询到的readView视图不同