面试题答案
一键面试锁机制
- 行级锁:
SELECT ... FOR UPDATE
语句可获取行级排他锁,防止其他事务同时修改该行数据,保证在并发环境下对特定行数据修改的一致性。例如,当一个事务执行SELECT * FROM users WHERE user_id = 1 FOR UPDATE;
时,其他事务无法同时修改users
表中user_id
为1的行。SELECT ... FOR SHARE
语句获取行级共享锁,允许多个事务同时读取该行数据,但阻止其他事务获取排他锁对该行进行修改。多个事务可以同时执行SELECT * FROM users WHERE user_id = 1 FOR SHARE;
来读取数据。
- 表级锁:
LOCK TABLE
语句可以对整个表获取锁,有多种锁模式,如SHARE
(共享锁)、ROW EXCLUSIVE
(行排他锁)、EXCLUSIVE
(排他锁)等。例如,使用LOCK TABLE my_table IN EXCLUSIVE MODE;
可以阻止其他事务对my_table
进行读写操作,直到持有锁的事务结束,用于需要对整个表进行一致性操作的场景,如批量数据插入或删除且不希望其他事务干扰。
同步原语
- 信号量:PostgreSQL使用信号量来控制对共享内存区域的访问。信号量是一种计数器,它可以用来控制同时访问共享资源的进程数量。例如,在共享内存中维护一个计数器,当一个进程要访问共享内存中的特定资源时,它首先获取信号量,如果信号量的值大于0,计数器减1,进程可以访问;如果信号量的值为0,进程则需要等待,直到信号量的值大于0。这样可以避免过多进程同时访问共享内存导致的数据冲突。
- 自旋锁:自旋锁在PostgreSQL中用于短期保护共享资源。当一个进程尝试获取自旋锁时,如果锁可用,它立即获得锁并继续执行;如果锁不可用,进程不会进入睡眠状态,而是在原地不断尝试获取锁,直到锁可用。自旋锁适用于锁持有时间较短的情况,因为避免了进程上下文切换的开销,从而提高系统性能。例如,在一些内核数据结构的快速访问场景中使用自旋锁。
事务管理
- ACID特性:
- 原子性(Atomicity):PostgreSQL确保一个事务中的所有操作要么全部成功,要么全部失败。例如,在一个涉及多个表更新的事务中,如果其中一个更新操作失败,整个事务将回滚,所有已执行的更新操作将被撤销,保证数据的一致性。
- 一致性(Consistency):事务执行前后,数据库始终保持一致性状态。PostgreSQL通过各种约束(如主键约束、外键约束等)以及并发控制机制来确保一致性。例如,在插入数据时,如果违反了主键唯一性约束,事务将失败,不会破坏数据库的一致性。
- 隔离性(Isolation):通过不同的事务隔离级别来控制并发事务之间的影响。PostgreSQL支持
READ COMMITTED
(读已提交)、REPEATABLE READ
(可重复读)、SERIALIZABLE
(可串行化)等隔离级别。例如,在READ COMMITTED
级别下,一个事务只能看到其他已提交事务的修改;而在SERIALIZABLE
级别下,所有并发事务的执行效果等同于串行执行,进一步保证了数据一致性。 - 持久性(Durability):一旦事务提交,其对数据库的修改将永久保存,即使系统崩溃也不会丢失。PostgreSQL通过预写式日志(Write - Ahead Logging,WAL)机制来实现持久性,事务的修改先记录到WAL日志中,然后再应用到实际的数据文件,确保在系统故障后可以通过重放WAL日志恢复数据。
多版本并发控制(MVCC)
- 原理:PostgreSQL使用MVCC允许并发读写操作而不会相互阻塞。每个数据行都有多个版本,每个事务在开始时会获取一个快照,该快照包含了当时数据库的可见状态。事务在读取数据时,根据快照读取相应版本的数据,而写入操作则创建新的数据版本。例如,当一个事务更新数据时,它不会直接修改旧版本数据,而是创建一个新版本,旧版本数据仍然保留,供其他持有旧快照的事务读取。这样,读操作不会阻塞写操作,写操作也不会阻塞读操作,大大提高了并发性能。
- 可见性规则:MVCC通过一系列可见性规则来确定事务可以看到哪些数据版本。例如,一个事务只能看到在其开始之前已提交的事务创建的数据版本,并且看不到在其开始之后启动的未提交事务创建的数据版本。这些规则确保了在并发环境下数据读取的一致性。