2.2 队列锁(Enqueue Lock)
2.2.1 Lock与Latch的区别
Oracle锁(Lock)也被称为队列锁(Enqueue Lock),而Latch则被称作为“细粒度”的锁。Lock与Latch的区别如表2-1所示。
不难看出:
Oracle使用Latch保护内存数据被间歇地、短持续地访问;
Latch不适合保护持续时间相对较长的资源(Resource),在这种情况下,就需要使用Lock持续锁定;
Lock允许并发等待:当前不可获取的Resource会被放入请求队列中进行排队(FIFO),这样就避免了类似Latch的Spin等待;
Lock允许多个Session访问同一个Resource(Session互相兼容),但是Latch必须是独占地排他访问。
2.2.2 常见的Lock
Oracle常见的Lock有四种,下面将逐一介绍。
1.事务锁和行级锁
行级锁(Row-Level Lock)是Oracle引以为傲的锁定模式。当事务(Transaction)更改了一行,那么该事务的唯一标志(ITL)就会被记录在数据库块头(Data Block Header)中。
在Transaction开始前,系统从回滚段头的事务表(Undo Transaction Table List)中分配一个ITL事务槽,同时将ITL记录到被事务影响的Data Block中。与此同时,被该事务影响更改的行将会使用Lock Bytes索引指向该ITL。
有关ITL以及事物的内容可参考第3章、第4章的相关内容。
这样,当新的事务需要更改同一行时,将会发现有尚未提交的事务(Uncommited Tran-saction)正在修改该行,那么该事务就会被阻塞等待(Transaction Block Wait)。
一旦事务提交或者回滚,事务锁就会被释放。即那个被阻塞等待的事务就锁定该行了。
Rollback To Savepoint不能释放先前的行级锁,因此会阻塞后续对该行级锁的获取。只有回滚到事务开始的最初状态或者事务提交后才能获取该行级锁。
- Buffer Lock
行级锁是保护数据中粒度较小的锁,Oracle提供了Buffer Block级别的锁(Buffer Lock)来保护修改Buffer Cache中的Buffer Block。Buffer Lock仅提供了Read/Write锁定模式,用于保护Buffer Block的完整性。 - Data Dictionary Lock
当数据库字典对象(Procedure、View等)被调用的时候,必须保证数据字典对象的完整性、有效性。数据字典锁(Data Dictionary Lock)就是用于保护数据字典对象完整性的,通过Row Cache Enqueue Locks进行保护。 - PCM实例锁
PCM实例锁(Parallel Cache Management Instance Lock)主要用于集群环境中,保护分布式集群资源的协调分配。
2.2.3 Lock相关参数
Oracle队列锁是通过Enqueue Hash Chain结构实现的。当需要访问Enqueue Hash Chain时,必须在“enqueue hash chains latches”保护下才能进行。
“enqueue hash chains latches”数量由参数_enquenue_hash_chain_latches决定,默认值为CPU数量。
1)查看CPU数量:
可以看到,“enqueue hash chains latches”数量与CPU数量是一致的。
2.2.4 Lock先请求先服务机制
在多并发Session的场景中,需要先获取Resource的X锁定才能对该Resource进行修改。与此同时,当其他Session也同样请求修改该Resource时,则必须按照先请求先服务的方式进入请求队列进行排队等候。可以通过下面示例验证。
可以看出:Enqueue Lock的获取遵循先请求先服务的原则。因此,在高并发业务环境中,业务架构设计必须遵循这个原则,以保证数据库的高并发运行。