面试题答案
一键面试可能遇到的数据冲突情况
- 读写冲突:当一个事务正在读取数据,而另一个事务尝试修改同一数据时可能发生冲突。例如,事务A正在读取某行数据,事务B要对该行数据进行更新操作。
- 写写冲突:多个事务同时尝试修改同一数据。比如事务C和事务D都想更新某一行记录。
PostgreSQL利用MVCC解决冲突的方式
- 读写冲突解决:
- PostgreSQL通过MVCC机制,读操作不会阻塞写操作,写操作也不会阻塞读操作。读操作始终读取的是数据的可见版本,即事务开始时所看到的数据版本。例如,事务A开始读取数据,此时事务B对数据进行更新,事务A读取到的依然是事务B更新前的数据版本,不会受到事务B更新的影响。
- 写写冲突解决:
- 对于写写冲突,PostgreSQL采用了基于时间戳(内部实现为xmin和xmax)的机制。当一个事务尝试更新数据时,它会检查该数据当前版本的xmin(创建该版本的事务ID)和xmax(删除该版本的事务ID)。如果发现有其他未提交事务正在修改同一数据(通过检查xmin和xmax),当前事务会等待,直到冲突的事务提交或回滚。例如,事务C和事务D同时尝试更新同一行数据,PostgreSQL会根据内部时间戳机制判断哪个事务先开始更新操作,后开始的事务会等待先开始事务完成(提交或回滚),以确保数据一致性。
举例说明
假设我们有一个表users
,包含id
和name
字段。
- 读写冲突示例:
- 事务A:
BEGIN; SELECT name FROM users WHERE id = 1; -- 读取操作 -- 事务在此处停留,未提交
- 事务B:
BEGIN; UPDATE users SET name = 'new_name' WHERE id = 1; -- 更新操作 COMMIT;
- 事务A在执行
SELECT
时,读到的是事务B更新前的name
值,不会因为事务B的更新操作而阻塞或读取到不一致的数据。
- 事务A:
- 写写冲突示例:
- 事务C:
BEGIN; UPDATE users SET name = 'name_c' WHERE id = 1; -- 开始更新操作 -- 事务在此处停留,未提交
- 事务D:
BEGIN; UPDATE users SET name = 'name_d' WHERE id = 1; -- 尝试更新同一行数据 -- 此时事务D会等待事务C完成(提交或回滚),以避免写写冲突
- 当事务C提交或回滚后,事务D才会继续执行更新操作。如果事务C提交,事务D更新的是事务C提交后的新数据版本;如果事务C回滚,事务D更新的是原始数据版本。
- 事务C: