相信大部分小伙伴在面试过程中只会回答面试官提出的一些肤浅的问题。事实上,面试官提出的每一个问题都是经过深思熟虑的。面试时间比较短。面试官不可能在短时间内非常了解你。他想通过几个问题来考察你知识的深度和广度。如果只回答面试官表面上的问题,就像挤牙膏一样,问一点,回答一点,结果不用说,一定爽。面试题什么是交易?并发事务会带来什么问题?问题分析从表面上看,面试官问了两个问题。一是:什么是交易,就是让你讲一下交易的基本概念;另一个是:并发事务会带来什么问题。事实上,并非如此。面试官问这个问题的时候,不要随便回答。思考面试官想要得到什么样的答案需要很短的时间。对于第一个问题:什么是交易?就简单说一下交易的基本概念吧?相信学过数据库的小学生都会知道基本的概念。为什么面试官会问你这个问题?这时候,你需要去猜测面试官的心理。面试官这时候其实是想问你交易的基本概念,他也想让你说出交易的特点,也就是四大属性。这就是这个问题的核心!对于第二个问题:并发事务会带来哪些问题?只想问一下会带来什么问题?知道问题,不知道如何解决问题,这样的面试官要功能吗?本质上面试官是想问你并发事务会导致什么问题,有什么方案可以解决这些问题!这才是面试官想要的答案!综上所述,面试官本质上问的问题是:什么是交易?交易的四大特征是什么?并发事务会带来什么问题?有哪些解决方案?只有深入了解面试官问题的本质,才能更好地回答面试官提出的问题。否则,你对付面试官,面试官也会对付你。什么是交易?事务的概念比较容易理解:事务是指作为单个逻辑工作单元执行的一系列操作,要么完全执行,要么根本不执行。事务确保面向数据的资源不会永久更新,除非事务单元内的所有操作都成功完成。通过将一组相关操作分组到一个全有或全无单元中,可以简化错误恢复并提高应用程序的可靠性。对于要成为事务的逻辑工作单元,它必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的一个逻辑工作单元,DBMS中的事务管理子系统负责事务处理。事务的四大特性原子性(Atomicity)一个事务必须是一个原子工作单元;对于它的数据修改,要么全部执行,要么都不执行。例如转账成功,账户余额会增加(减少);否则转账失败,账户余额不变。一致性(Consistency)当事务完成后,所有数据必须保持一致状态。在关系数据库中,所有规则都必须应用于事务修改以保持所有数据的完整性。所有内部数据结构(如B树索引或双向链表)在事务结束时必须正确。保持一致性的部分责任落在应用程序开发人员身上,他们必须确保应用程序强制执行所有已知的完整性约束。例如,在开发转账应用程序时,避免在转账过程中随意移动小数点。隔离并发事务所做的修改必须与任何其他并发事务所做的修改隔离开来。一个事务查看数据时数据的状态要么是另一个并发事务修改前的状态,要么是另一个事务修改后的状态,中间状态下事务不查看数据。这称为隔离。因为它可以重新加载起始数据,并重放一系列交易,使数据最终处于与原始交易执行相同的状态。当事务可序列化时获得最高隔离级别。在这个层次上,从一组可以并行执行的事务中得到的结果与连续运行每个事务得到的结果是一样的。因为高度隔离限制了可以并行执行的事务数量,所以一些应用程序降低隔离级别以换取更大的吞吐量。一个持久(Durability)事务完成后,它对系统的影响是永久性的。即使在发生致命系统故障的情况下,此修改也会持续存在。比如我们使用JDBC操作数据库时,提交事务方法后,提示用户完成事务操作。当我们的程序执行到看到提示时,我们就可以识别交易并正确提交。即使此时数据库有问题,我们的事务也必须全部执行完,否则会导致我们看到一个重大错误,提示事务已经处理,但是数据库由于没有执行事务到失败。并发事务的问题脏写问题当两个或多个事务选择同一行,然后根据最初选择的值更新该行时,就会出现丢失更新问题。每个事务都不知道其他事务的存在。最后一次更新将覆盖其他事务所做的更新,这将导致数据丢失。比如T1和T2同时修改一条数据,T2的修改覆盖了T1的修改;如果T2只能在T1之后进行修改,就可以避免这个问题。让我们看一个经典的传输问题。一开始,小明和小刚都有1000元。在交易T1中,小明转100元给小刚。在交易T2中,小刚转账200元给小明。正常情况下,结果是:小明有1100元,小刚有900元。如果出现脏字问题,结果可能是:小明1200元,小刚800元。如下所示。脏读问题一个事务正在修改一条记录。在交易完成提交之前,这条记录的数据处于不一致状态;这时,另一个事务也读取了同一条记录。如果不受控制,前两个事务会读取“脏”数据,并根据它做进一步的处理,导致未提交的数据依赖。这种现象被形象地称为“脏读”。例如:在交易T1中,小明转100元给小刚。在转账过程中,当事务未提交或回滚时,事务T2读取事务T1未提交的内容,也就是说,在事务T2中读取了小明的900元和小刚的1100元的记录。可以用下图表示。不可重复读问题一个事务在读取了一些数据后,在某个时间再次读取之前读取的数据,却发现读取的数据发生了变化!这种现象称为“不可重复读”。意思是事务T2读取数据后,事务T1进行更新操作,使得T2无法读取到之前的结果。例如,在交易T1中,小明转100元给小刚。在交易提交之前,交易T2中读取的数据仍然是小明1000元,小刚1000元。事务T1提交后,事务T2中读取的数据为小明900元,小刚1100元。如下所示。幻读问题一个事务按照相同的查询条件重新读取之前检索到的数据,却发现其他事务插入了满足其查询条件的新数据。这种现象称为“幻读”。事务t2读取了事务t1主体提交的新增和删除的数据,不满足隔离要求。幻读和不可重复读都是读取另一个已经提交的事务(这与脏读不同)。不同的是,不可重复读查询的是同一个数据项,而幻读则是针对一批数据作为一个整体(比如数据的条数)。比如在事务T1中插入两条名为小明和小刚的数据,在事务提交前,事务T2中读取的数据记录为10,然后事务T1提交,则事务T2中读取的记录为12,如下。事务并发问题的解决方案为了避免上述情况,在标准的SQL规范中,定义了四种事务隔离级别,不同的隔离级别对事务的处理方式不同。下面四种不同的隔离级别限制从低到高,性能从高到低。ReadUncommitted未提交读(ReadUncommitted):允许脏读,但不允许更新丢失。如果一个事务已经开始写入数据,则不允许另一个事务同时写入,但允许其他事务读取这一行数据。这种隔离级别可以通过“独占写锁”来实现。不可避免的脏读、不可重复读、幻读。ReadCommittedReadCommitted(已提交读):允许不可重复读,但不允许脏读。这可以通过“瞬时共享读锁”和“独占写锁”来实现。读取数据的事务允许其他事务继续访问数据行,但未提交的写事务将阻止其他事务访问该行。脏读是可以避免的,不可重复读和幻读是不可避免的。Oracle使用已提交读。可重复读(RepeatableRead):禁止不可重复读和脏读,但有时会出现幻像数据。这可以通过“共享读锁”和“独占写锁”来实现。读取数据的事务不允许写入事务(但允许读取事务),并且写入事务不允许任何其他事务。可以避免脏读、不可重复读和不可避免的幻读。MySQL使用可重复读。Serializable:提供严格的事务隔离。它要求事务串行执行,事务只能一个接一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制来保证新插入的数据不会被刚刚执行过查询操作的事务访问到。可以避免脏读、不可重复读和幻读。事务的四种隔离级别总结如下图所示。经常看到当前数据库的事务隔离级别:showvariableslike'tx_isolation';设置事务隔离级别:settx_isolation='REPEATABLE-READ';Mysql默认的事务隔离级别是repeatableread,在使用Spring开发程序时,如果不设置隔离级别,默认为Mysql设置的隔离级别。如果由Spring设置,它将使用已经设置的隔离级别。转载本文请联系冰川科技公众号。
