前言
目前多数数据库的并发控制采用的是两阶段锁(Two-Phase Locking,2PL)协议,2PL保证了并发事务执行的可串行化。
在多用户环境中,数据库使用多版本并发控制(Multiversion Concurrency Control,MVCC)和多种锁相结合来维护数据一致性。
事务锁竞争是数据库性能瓶颈常见问题
一、封锁机制
数据库自动使用不同类型的封锁来控制对数据的并发存取,防止用户之间的干扰。
1. 自旋锁(spinlock)
- 通过不停的循环检查锁的状态来获得封锁的机制
- 不支持死锁检测
- 自旋锁在锁等待期间会占用CPU
- 场景:一般用于并发的修改全局变量时使用
2. 轻量级锁(LWLock)
- 轻量锁通常使用自旋锁实现
- 支持读锁(LW_SHARED)和写锁(LW_EXCLUSIVE)
- 读锁和读锁不冲突,读锁和写锁、写锁和写锁冲突
- 不需要支持死锁检测
- 场景:一般用于内存结构的封锁,在一些数据库中,统计信息以及扩展功能的相关数据会放在共享内存中。如对大量临时表进行create和truncated会出现消息失效,产生大量SInvalWriteLock锁,可以考虑对执行计划进行缓存或将临时表的封锁消息不进行发送处理。
3. 常规锁(Lock)
- 常规是数据库特有的锁机制,主要保护sql可见的对象
- 提供了多种封锁模式,支持自动死锁检测
- 封锁对象:数据库对象(表、事务)
- 封锁时间(2PL):对象打开时加锁,事务结束放锁
锁编号 | 常规锁类型 | 操作 | 与之冲突的模式 |
---|---|---|---|
1 | AccessShareLock | select | 8 |
2 | RowShareLock | select for update/share | 7,8 |
3 | RowExclusiveLock | insert/update/delete | 5,6,7,8 |
4 | ShareUpdateExclusiveLock | vacuum | 4,5,6,7,8 |
5 | ShareLock | create index | 3,4,6,7,8 |
6 | ShareRowExclusiveLock | 内部使用 | 3,4,5,6,7,8 |
7 | ExclusiveLock | 内部使用,例如分配空间、事务锁等 | 2,3,4,5,6,7,8 |
8 | AccessExclusiveLock | drop table/truncate/alter table/reindex/cluster | 全部 |
二、封锁对象类型
封锁类型一般包含了表、事务和其它类型。表包括系统表、用户表、索引,事务锁实现了通常意义行级锁的概念。
死锁:指两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种互相等待的现象。
对象类型 | 等待事件名称 | 说明 |
---|---|---|
表级锁 | relation | 等待获得一个关系上的锁 |
事务锁 | transactionid | 等待一个事务结束 |
事务锁 | virtualxid | 等待获得一个虚拟xid锁 |
其它锁 | extend | 等待扩展一个关系,关系分配空间 |
其它锁 | page | 等待获得一个关系上的页面的锁 ,只有hash索引使用 |
其它锁 | tuple | 等待获得一个元组上的锁,select for update/share |
其它锁 | object,userlock,advisory | 等待获得一个用户锁 ,文件、大对象等 |
三、封锁查询
封锁系统视图定义
test=# \d sys_locks
视图 "sys_catalog.sys_locks"
栏位 | 类型 | 校对规则 | 可空的 | 预设
--------------------+----------+----------+--------+------
locktype | text | | |
database | oid | | |
relation | oid | | |
page | integer | | |
tuple | smallint | | |
virtualxid | text | | |
transactionid | xid | | |
classid | oid | | |
objid | oid | | |
objsubid | smallint | | |
virtualtransaction | text | | |
pid | integer | | |
mode | text | | |
granted | boolean | | |
fastpath | boolean | | |
test=#
封锁查询
test=# select locktype,virtualxid,transactionid,pid,mode,granted from sys_locks order by pid;
locktype | virtualxid | transactionid | pid | mode | granted
------------+------------+---------------+-------+-----------------+---------
relation | | | 18796 | AccessShareLock | t
relation | | | 18796 | AccessShareLock | t
virtualxid | 4/24 | | 18796 | ExclusiveLock | t
(3 行记录)
test=# select locktype,transactionid,mode,granted,count(*) from sys_locks group by locktype,transactionid,mode,granted;
locktype | transactionid | mode | granted | count
------------+---------------+-----------------+---------+-------
relation | | AccessShareLock | t | 2
virtualxid | | ExclusiveLock | t | 1
(2 行记录)