面试必考:深度解析数据库事务及其应用
在数据库技术面试中,事务机制常常是考官关注的重点之一。理解事务不仅对通过面试至关重要,更是掌握数据库管理和优化的基础。事务的四大特性(ACID)、不同的隔离级别以及常见的并发问题和解决方案,都是面试中常见的考题。本文将逐一解析这些关键概念,帮助你在面试中脱颖而出,展现你对数据库事务的深入理解。
什么是事务?
事务是代表单个工作单元的一组SQL语句,当我们需要对数据库进行多次更改的情况下,要使用事务,我们希望所有这些更改作为一个单元一起成功或失败
事务的四大特性 (ACID)
- 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成;
- 一致性(Consistency):事务完成后,数据库必须从一个一致状态转化到另一个一致状态,数据库始保持一致的状态;
- 隔离性(Islation):一个事务的执行不应影响其他事务的执行;
- 持久性(Durability):一旦事务提交,其结果应该永久保存在数据库中,即使系统发生故障;
事务隔离级别
- 标准的SQL定义了4个事务隔离级别,隔离级别逐渐增高,性能和可扩展性逐渐降低,因为限制了并发。在MySQL中,默认的事务隔离级别是‘可重复读’。
- 读未提交:允许读取未提交的数据,最低的隔离级别
- 读已提交:给予了我们的事务一定的隔离,使得该事务只能读取已提交的数据,避免了脏读。
- 可重复读:我们读取的数据是可重复和一致的,就算有其他事务更改了数据,我们会看到首次读取就创建的快照。
- 序列化:它能保证当有别的事务在更新数据时,我们的事务能够知晓变动,如果有其他事务修改了可能影响查询结果的数据,我们的事务必须等它们完成,这样事务就会按序列化执行。
常见的并发问题和解决方法
1.丢失更新
- 当两个事务尝试更新相同的数据并且没有上锁时,就会发生这种情况,比如两个事务更新同一条记录的不同列的信息,较晚提交的事务会覆盖较早事务做的更改,使得较早事务做的更改缺失。
- 使用锁,防止两个事务同时更新同样的数据,MySQL提供的默认锁的锁粒度是行级锁。
2.脏读
- 一个事务读取了尚未被提交的数据,如果该数据被退回的话,该事务就是读取了一个不存在的数据,就是脏读。
- 为了解决这个问题,我们需要为事务建立隔离级别,“读已提交”,这样事务修改的数据不会立马被其他事务读取,除非它提交了。
3.不可重复读(不一致读)
- 当我们在事务中添加更多隔离时,我们可以保证事务只能读取已提交的数据,但如果在事务过程中,读取了某个数据两次,并得到了不同的结果就是不可重复读问题。
- 我们就需要增加事务隔离级别,我们要将它与其他事务隔离,“可重复读”,确保数据更改对该事务不可见,只看事务开始前那一刻的数据信息。
4.幻读
- 对于突然出现或者缺失的数据,我们无法在查询中看到它们,因为它们是在执行查询后才添加、更新、删除的。
- 为此,我们有另一个隔离级别为”序列化“,它能保证当有别的事务在更新数据时,我们的事务能够知晓变动,如果有其他事务修改了可能影响查询结果的数据,我们的事务必须等它们完成,这样事务就会按序列化执行。
MVCC
MVCC(多版本并发控制)是一种用于数据库管理系统的并发控制机制,是为了提高数据并发访问的效率,并减少锁竞争,减少对锁的依赖。
Read View
Read View 有4个重要的字段,m_ids,min_trx_id,max_trx_id,creator_trx_id
- m_ids:指的是在创建 Read View 时,当前数据库中活跃事务的事务id列表,活跃事务是指启动但未提交的事务。
- min_trx_id:指的是m_ids的最小值。
- max_trx_id:创建Read View 时,数据库中最大的事务的id+1。
- creator_trx_id:指的是创建该 Read View 的事务的事务id。
InnoDB存储引擎中的记录
记录的行格式包含两个隐藏字段:
- trx_id,当一个事务对某条记录进行改动时,就会把该事务的事务id记录在trx_id中。
- roll_pointer,每次对某条记录进行改动时,都会把旧版的记录写到 undo 日志中,然后,这个字段是一个指针,指向每一个旧版记录,这样就可以通过指针找到修改前的记录,这就是大名鼎鼎的版本链。
具体实现
通过Read View 和版本链实现的。
- 在创建Read View后,根据事务的可见性进行读取数据,可见的就是能读取的,不可见的,就是不能读取的。在id列表里的,就是不可见的,大于等于 max_trx_id的事务id的事务是不可见的,小于 min_trx_id的事务id的事务是可见的。
- 当查询数据时,系统会检查每个记录的 trx_id,看是否符合可见性,如果不符合,就根据roll_pointer寻找之前的版本。
- 通过这样的机制,InnoDB 能够在高并发环境下保持事务的隔离性,同时确保每个事务都能读取到基于其 Read View 创建时的可见数据。这种多版本并发控制(MVCC)技术有效地解决了读写冲突的问题,同时允许事务以一致的方式读取数据。
快照读和当前读
MySQL虽然支持4种隔离级别,但是与SQL标准种规定的各级隔离级别允许发生的现象却有些出入。MySQL InnoDB引擎的默认隔离级别虽然是可重复读,但是它很大程度上避免了幻读现象,解决的方案有两种:
- 针对快照读,普通的select语句,是通过MVCC的方式解决了幻读。
- 针对当前读,select…for update等,是通过加临界锁(记录锁+间隙锁)。当执行当前读时,会在范围加上临界锁,其他事务如果在锁的范围内插入或删除一条记录,就会被阻塞,很好地避免了幻读问题。