做RAC性能诊断这几年,我最大的感受是:很多人不是在跟数据库故障较劲,而是在跟一堆术语较劲。明明几百个会话都在等一个事件,你翻着AWR报告看到gc cr request、gc buffer busy acquire,又看到enq: TX - row lock contention,心里隐约知道都跟“锁”有关,但要让你说清楚哪些是内存融合在传数据块,哪些是全局队列在串行化,立刻就开始含糊。更不用说PCM、非PCM、GCS、GES这些缩写放在一起,简直就是一场声部交错的交响乐。这篇东西我打算换个讲法:不摆官方架构图,而是从一条update语句跨节点执行的全过程入手,把内存融合技术里的PCM资源和非PCM资源各自的角色、管理机制、典型等待事件,以及我在实际环境里排查这些问题的经验,一次讲明白。如果你正在维护11.2、12c、19c的RAC,或者准备从单实例转RAC,这篇文章能帮你少走很多弯路。
需要先提醒一句:RAC里的PCM,全称是Parallel Cache Management,指GCS针对数据块资源的全局缓存管理。别一搜PCM搜出来一堆音频编码、PCM线缆的文章,那是另一个世界的东西。我们这里聊的是Oracle集群里最核心的缓存一致性协议。
1. 先看一次跨节点更新:PCM和非PCM是怎么分工的
1.1 单机缓存命中变成了全局协调
在单实例数据库里,一条update语句要修改某一行的数据时,数据库把该行所在的数据块先读入本地buffer cache,然后在内存里做修改。修改前要把数据块状态改成“当前版本”,并通过undo生成旧版本,便于回滚和一致性读。整个过程不需要告诉别人“我来了”,因为整个数据库只有我一个实例。
到了RAC,事情就不一样了。同样一条update语句可能落在节点1,也可能落在节点2。如果节点2要修改某个数据块,而这个块的“最新版本”此刻正被节点1的内存缓存着,节点2最笨的办法是等节点1把块写回磁盘,然后再去磁盘读。但Oracle没有这么干。RAC的私网上直接把这个块的内存版本从节点1传给节点2,写盘变成了跨节点传内存,这就是内存融合(Cache Fusion)。也是RAC区别于普通共享存储集群的关键:块不需要写进磁盘,就能在实例之间移动使用权。
这一瞬间的传块动作,靠的是PCM资源管理。数据块并不是单纯被“传”过去,而是伴随着一个全局权限转移。谁有权限读、谁有权限写、谁保留了旧版本,这些都要被记录清楚,否则一个块在不同节点的内存里就出现了不一致。
1.2 两类资源的分界:GCS管块,GES管锁
PCM资源在Oracle内部由GCS(Global Cache Service)负责。GCS管理的是buffer cache里实实在在的数据块。包括表块、索引块、undo块,它们的全局读写权限全部走GCS。
然而数据量里除了数据块,还有一类东西没法传块,例如一个表的结构定义、一条SQL的解析游标、数据字典信息、SCN、系统变更号、事务表条目等。这些东西如果跨节点不一致,同样会出大问题。它们由GES(Global Enqueue Service)管理,日常我们叫它非PCM资源。非PCM是相对于PCM的一个说法,意思是“不是通过数据块融合方式来协调的全局资源”。
GCS和GES一起构成了RAC底层的分布式锁管理机制。有些资料把两者统称为DLM或者Cache Fusion体系,但严格讲:涉及数据块内容的并发协调是GCS的活,涉及各种队列锁和对象级一致性的是GES的活。后面你会看到,一条简单SQL会同时踩到这两个服务。
1.3 一条update语句的完整旅程
我举个例子。假设一个订单表orders,节点1上的会话A执行了:
sql复制update orders set status = 'PAID' where order_id = 100;
此时节点1需要做几件事。
第一,解析SQL,检查用户权限。解析过程中会访问数据字典、库缓存。如果orders的表定义在节点2的内存字典缓存里还没有同步,节点1就需要通过GES对相关字典资源做全局请求,保证看到一致的定义。这里出现的等待是类似row cache lock这样的非PCM等待。
第二,在buffer cache里找到order_id=100这一行的数据块。如果节点1没有该块,它就要先向该数据块的资源主节点(resource master)发起PCM请求。主节点查看全局资源目录,发现这个块正被节点2以X模式持有。于是节点2把块的最新版本通过私网直接传给节点1。这个过程会出现gc cr request或gc current block busy等以gc开头的事件。这就是PCM资源的协调。
第三,拿到了数据块之后,会话A需要在该行上加行级TX锁。这个锁需要被两个节点共同看到,否则节点2另一个事务也来改同一行时,无法知道已经被节点1锁住。TX锁的全局协调由GES完成。于是节点2上更新同一行的会话B会阻塞在enq: TX - row lock contention。你看到这个事件时,就说明GES在工作,它管理的非PCM资源产生了冲突。
你看,一次简单的update,PCM资源负责“谁能拿到数据块并修改块内容”,非PCM资源负责“谁能拿到全局队列锁来保证并发事务之间相互可见”。两者不在同一个层面,但缺一不可。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. PCM资源深度:数据块的全局权利交接
2.1 GRD、资源主节点和持有者列表
想看懂PCM,先要接受一个概念:RAC里的每个数据块,都在一个名为GRD(Global Resource Directory)的目录里有登记。GRD并不存数据,只记录资源名、资源主节点、当前持有者、请求者队列状态等信息。
这里的“资源名”不是文件名,而是根据数据块地址、对象ID、文件号、块号生成的内部标识。资源主节点由资源名参数用哈希算法分散到不同实例上。例如编号为123的资源主节点可能在节点1,编号为456的资源主节点在节点2。这样设计是为了让资源管理请求的负载在集群中均匀分布,而不是所有节点都跑到一个内存目录去询问。
当节点A需要读某个块时,它必须先知道这个块的资源主节点在哪。最简单的办法是本地算哈希,或者通过LMON进程维护的映射。拿到主节点地址后,节点A给主节点发送请求,主节点查询GRD,判断块的权限目前被谁持有,然后调度持有者把数据传输给请求节点,最后更新GRD中的持有者列表。
这个模型解释了一个常见现象:为什
