Oracle 内存内宽表和全数据库缓存深度解析

最后更新: 02/18/2026
作者: C 源跟踪
  • Oracle 从简单的 LRU 算法发展到更智能的缓存算法和内存列式格式,以加速扫描、连接和聚合操作。
  • 完整数据库缓存改变了小型、中型和大型表的处理方式,只有当整个逻辑数据库都能加载到内存中时,它才能发挥最佳效果。
  • 宽表需要精心设计、索引、分区和压缩策略,尤其是在分析、人工智能工作负载和操作方面。
  • 当权限有限时,对大型表的大规模删除必须分批执行,以避免撤销耗尽。

Oracle 内存宽表

使用完全缓存在 Oracle 内存中的宽表进行操作,感觉就像驾驶一级方程式赛车:一切调整妥当时速度极快,但一旦出现问题,就会造成严重的后果。 随着数据库向拥有数百甚至数千列的模式发展,我们对数据建模、缓存和查询的方法也必须随之改变。Oracle 提供了强大的内存缓存和缓冲区缓存功能,但只有当我们理解它们如何处理小型、中型和大型表,以及这如何与宽表设计交互时,这些功能才能真正发挥作用。

本指南将介绍 Oracle 如何处理内存格式、完整数据库缓存,以及超宽表对分析、OLTP 工作负载和操作的实际影响。 在此过程中,您将看到缓存算法如何从简单的 LRU 演变而来,Oracle 为什么区别对待大表,何时进行全数据库缓存才有意义,以及所有这些如何影响索引策略、分区、AI/分析工作负载,甚至在严格的安全限制下进行大规模删除。

Oracle 中的列式内存格式和 SIMD 扫描

Oracle 内存数据库引入了一种列式表示法,专为在内存中实现极快的扫描、连接和聚合而设计。 Oracle 不会从面向磁盘的块中读取整行数据,而是将选定的对象存储在内存列式存储中,其中每一列都经过压缩和优化,以适应涉及大量行但相对较少列的分析查询。

除此之外,Oracle 还利用 CPU 级别的 SIMD(单指令多数据)向量处理技术,以每个核心每秒处理数十亿行数据,从而满足合适的工作负载需求。 当查询主要为只读操作,并且涉及范围筛选、聚合和分析函数时,数据库可以在单个 CPU 指令内并行评估多个值,与传统的逐行执行相比,吞吐量将大幅提高。

对于宽表来说,这一点非常重要,因为传统的基于行的格式使得每次读取都要为块中的所有列付费,即使查询只访问其中的少数几列。 当某些宽表或分区启用内存列存储时,Oracle 可以完全跳过不相关的列,从而减少内存带宽使用和 CPU 工作,这对于实时分析和仪表板至关重要。

实际上,这意味着以前需要几个小时才能完成的分析通常可以缩短到几秒钟,从而能够对运营数据进行近乎实时的决策。 如果配置正确,压缩、列式访问和向量化处理的结合将有利于对海量事实表进行报告、对遥测数据进行临时调查以及对商业智能进行查询。

Oracle 全数据库缓存

从基本的LRU算法到更智能的缓冲区缓存算法

在深入了解完整的数据库缓存之前,了解 Oracle 历史上是如何决定哪些块保留在缓冲区缓存中以及哪些块被驱逐的会很有帮助。 在早期版本中,Oracle 依赖于一个简单的 LRU(最近使用)列表:当缓冲区缓存填满时,列表末尾最近最少使用的块将被丢弃,以便为新块腾出空间。

简单的 LRU 方法的问题在于,在混合 OLTP 工作负载下会出现争用和不公平现象。 每次访问数据块时,它都必须被移到列表的“热点”端。在高并发访问的情况下,许多会话竞相提升数据块的优先级,使得列表的该区域成为热点区域。此外,对大型表进行完整扫描可能会将大量数据块推到列表顶部,从而迅速将真正热门的数据块从频繁访问的小型表中移除。

为了解决这个问题,Oracle 改进了缓冲区缓存算法,在每个块中添加了使用计数器和时间戳,而不是盲目地将每个被访问的块移动到最顶部。 每次访问区块时,其计数器都会加一,并且其最后使用时间也会更新。较高的计数器值表明该区块很受欢迎,但时间戳的更新时间仍然很重要;一个一小时前被频繁访问但之后就没再被访问过的区块,可能不如最近几秒内被频繁访问的区块那么重要。

因此,驱逐决定是根据街区的使用频率和最近使用情况综合考虑的。 Oracle 通过平衡这两个维度,使得短时间内频繁读取的缓存块不会总是占据主导地位,从而避免访问频率较低但持续稳定的缓存块被过度占用。这种混合策略可以有效缓解大规模扫描造成的异常情况,并减少缓存“热点”区域的争用。

Oracle 如何处理缓冲区缓存中的小型、中型和大型表

由于内存并非无限,Oracle 会根据表相对于总缓冲区缓存的大小,采用不同的缓存策略。 当您处理宽表或非常大的事实表时,这一点至关重要,因为这些表很容易超出您的可用内存。

对于小型表,Oracle 对缓存非常友好。 当表的总大小小于缓冲区缓存的 2% 左右时,Oracle 通常会在读取数据块后将其全部缓存,从而将整个表保留在内存中。小型查找表和引用数据通常属于这种情况,这非常理想,因为它们经常被频繁访问,并且能从完全缓存中获益匪浅。

中等大小的表属于一个更微妙的类别,通常占缓冲区缓存的 2% 到 10% 左右,尽管确切的阈值可能会有所不同。 对于这些表,Oracle 会考虑几个信号来决定是否积极地缓存数据块:表上次被完整扫描的时间、缓存中已有的数据块最近一次被使用的时间、缓冲区缓存中剩余的空间以及表的大小。换句话说,中等大小的表的处理方式是基于对象大小和访问模式的成本效益决策。

默认情况下,对于大型表(尤其是大小远远超过缓冲区缓存 10% 的表),会采取非常保守的处理方式。 通常情况下,Oracle 在完成全表扫描后不会将所有数据块都填充到缓冲区缓存中,因为这样做可能会清除来自较小、访问频繁的表中真正重要的数据。您可能会在缓存中看到一些元数据或少量数据块,但除非您使用 KEEP 缓冲区池或其他指令等机制显式地指示 Oracle,否则大型表不会被完全缓存。

当处理跨越数GB或数TB的大型事实表时,这种策略尤为重要。 对这类表进行一次每周或每月的扫描不应该将所有 OLTP 工作集从内存中清除。相反,Oracle 会优先释放那些对大多数查询最有益的对象。

完整数据库缓存:当所有内容都能放入内存时

完全数据库缓存(Full Database Caching)是在 Oracle Database 12.1.0.2 中引入的,它专为缓冲区缓存足够大,可以容纳整个数据库逻辑大小的环境而设计。 在这种情况下,对大表和小表应用复杂的驱逐规则已经没有意义了;如果一切都合适,目标就是保持现状。

启用全数据库缓存后,Oracle 假定从用户数据中读取的所有块都可以而且应该保留在内存中。 在缓存方面,小型、中型和大型对象之间的传统区别在很大程度上被忽略了;无论你读取的表有多宽或多大,它的数据块都会在被访问时保存在缓冲区缓存中,直到达到内存的物理极限。

需要注意的是,启用完整数据库缓存并不会立即将所有对象的所有数据块读入内存。 相反,它采取机会主义策略:当应用程序查询表和段时,访问的数据块会被缓存并保留,而不是像以前那样根据经验法则进行替换。随着时间的推移,工作负载访问的数据库内容越来越多,缓冲区缓存最终会收敛到整个数据集都驻留在内存中的状态。

在多租户环境中,如果在 CDB 级别启用完整数据库缓存,则该行为将扩展到该容器数据库中的所有 PDB。 这意味着该容器下的每个可插拔数据库都可以受益于该功能,并且您无法在 RAC 配置中针对每个实例选择性地启用或禁用它;它是数据库的一个非此即彼的属性。

另一个微妙但重要的影响是,即使是标记为 NOCACHE 的段(包括 LOB 段),在强制启用完整数据库缓存时最终也会被缓存。 由于数据库的全局假设是内存足以容纳所有内容,因此它有效地覆盖了通常的对象级缓存提示。

完整数据库缓存的大小规则和检查

在启用完整数据库缓存之前,您需要确认缓冲区缓存是否足够大,能够满足数据库工作负载的需求。 Oracle 在检查可行性时会区分单实例数据库和 Real Application Clusters (RAC)。

对于非 RAC 数据库,数据库的逻辑大小应小于缓冲区缓存的总大小。 这里所说的逻辑大小指的是实际工作负载需要缓存的数据量,而不是每个很少使用的归档信息的每一个字节。不过,在实践中,您仍然需要留出一定的余量,以确保增长和活动高峰不会突然打破“一切都能容纳”的假设。

在 RAC 环境中,该规则更加严格,必须在实例级别和集群级别都成立。 逻辑数据库的大小必须小于每个实例的缓冲区缓存大小,并且小于集群中所有实例缓冲区缓存总和的约 80%。这种双重约束确保不会出现单个实例成为瓶颈的情况,同时又能保证整个集群都能从该特性中受益。

您可以使用针对 V$SGAINFO 视图的查询快速检查当前缓冲区缓存大小。 常用的查询方法是将数据大小(以字节为单位)除以 1024 的幂,从而以 GB 为单位显示结果,便于与数据库大小和增长预测进行比较。对数据字典视图执行类似的查询,可以帮助您估算用户数据的逻辑大小。

要验证完整数据库缓存当前是否处于活动状态,您可以查询 V$DATABASE 并检查 FORCE_FULL_DB_CACHING 列。 值为 YES 表示数据库已启用该功能并已启动,而 NO 表示缓存正在根据小型、中型和大型表的常规启发式方法运行。

未启用完整数据库缓存时的行为:大规模扫描和缓存驱逐模式

考虑这样一个场景:同一个模式中有三个表:两个非常大的表和一个非常小的表。 每个大表大约占用 1.1 TB 的空间,而小表只有大约 1 MB。缓冲区缓存本身只有几 GB,因此每个大表显然都远远超过缓存的 10%,而小表则远远低于 2% 的阈值。

重启或刷新 SGA 后,从 V$BH 等与 DBA_OBJECTS 连接的视图查询块头时,通常不会看到任何这些表的缓存块。 对第一个大表执行全表扫描后,默认算法的预期结果是数据库将避免用其数据块填充缓存。

事实上,扫描之后,您可能会发现只有少数几个大表的数据块被缓存,通常只有几个与元数据相关的数据块。 尽管处理了数百万或数十亿行数据,Oracle 仍选择不保留这些数据块,因为相对于缓存而言,该表被认为是“大的”,保留这些数据块会对更常用的段造成不利影响。

如果扫描第二个大表,就会发现类似的模式:只有少量数据块被缓存。 数据库继续应用其大表处理规则,防止任何一个大表过度占用缓存。这保护了较小表的工作集,而这些较小表对于日常 OLTP 性能可能更为关键。

然而,当你最终扫描这个仅有 1 MB 大小的表时,情况就发生了巨大的变化。 由于表大小低于缓冲区缓存的 2% 阈值,Oracle 会立即缓存所有数据块,导致以后每次访问该表都将直接占用内存。从性能角度来看,这对于小型查找表和跨多个事务共享的配置数据来说是理想的选择。

启用完整数据库缓存后的行为

现在想象一下,在同一环境下通过挂载数据库并发出 FORCE FULL DATABASE CACHING 命令来启用完整数据库缓存。 打开数据库后,您可以通过 V$DATABASE 再次确认该功能是否已激活,然后再重复相同的扫描序列。

重启后,一开始,这三个表仍然没有缓存块,和以前一样。 然而,当你对第一个大表执行全表扫描时,你会发现几乎所有的数据块都驻留在缓冲区缓存中。不再只是缓存一个标记,而是读取的几乎全部 1.1 TB 数据都会保存在内存中。

扫描第二个大表会向缓冲区缓存中添加另外 1.1 TB 的数据块,而不会从第一个表中清除先前缓存的数据块。 在全数据库缓存模式下,系统实际上会“囤积”读取的每个数据块,其假设是内存大小已针对此行为进行了调整,因此无需进行数据驱逐。

当你最终扫描小表时,它的所有块也会被缓存,并且同样,大表的任何块都不会被丢弃。 随着时间的推移,查询访问的对象越来越多,数据库会逐步构建整个活动数据集的内存映像。对于读取密集型或混合型工作负载(此时内存占用远超逻辑数据大小),这可以带来卓越的性能和高度可预测的缓存行为。

启用完整数据库缓存但内存不足时会发生什么情况?

当强制启用完整数据库缓存时,会出现一个有趣的极端情况,但缓冲区缓存实际上太小,无法容纳整个工作数据集。 您不会立即遇到 ORA-600 或明显的严重错误;数据库在处理内存硬限制的同时,仍然会尝试遵循该功能。

假设你减少缓冲区缓存,使其实际上只能完整地容纳一个大表。 启用全数据库缓存并清除现有缓存块后,对第一个大表进行全扫描会再次将几乎所有缓存块填充到缓存中。此时,内存实际上已被该单个对象完全占用。

当你扫描第二个大表时,Oracle 仍然表现得好像想要缓存所有内容,但现在它必须从第一个表中驱逐数据块来腾出空间。 结果是,第二个表最终被完全缓存,而第一个表只被部分缓存;其很大一部分数据块将被从缓存中移除。

如果再次扫描第一个表,则过程会反转:第一个表将被完全缓存,而第二个表将丢失一部分数据块。 最终会导致内存争用频繁,每次全盘扫描时,大型对象都会互相挤占内存空间。磁盘 I/O 飙升,而全盘数据库缓存原本旨在提供的优势也几乎荡然无存。

因此,对于逻辑数据大小大于有效内存的数据库,使用全数据库缓存通常不是一个好主意。 在这种情况下,通常最好让 Oracle 应用其久经考验的缓冲区管理算法,该算法可以保护小型且经常使用的段免受不常发生的大型扫描的影响。

彻底禁用完整数据库缓存

如果您认为完整数据库缓存不适合您的环境,禁用它很简单,但需要进行受控重启。 您需要关闭数据库,重新挂载,然后发出停止强制完整数据库缓存的命令,之后才能再次打开它。

数据库重新打开后,快速检查 V$DATABASE 将显示 FORCE_FULL_DB_CACHING 已设置回 NO。 从那时起,缓冲区缓存恢复其默认行为,优先考虑小表,中表视具体情况而定,而大表则大多不进入缓存,除非通过 KEEP 池等功能明确锁定。

宽桌:设计、建模和性能方面的考虑

拥有数百或数千列的超宽表的趋势改变了我们设计模式的方式,以及内存列存储和缓存等功能的利用方式。 这些表格可以简化某些读取密集型模式,让报表团队的工作更轻松,但它们在灵活性、维护性和 I/O 行为方面存在严重的权衡取舍。

当您优先考虑快速读取并希望避免复杂的连接时,非规范化宽表非常有用,尤其适用于分析、遥测或 AI 特征存储。 将多个属性打包到一行中可以减少连接深度,使查询更直接,这对于 BI 工具、数据科学家和批处理流程来说很有吸引力,因为它们只需要每个实体或事件一条大的记录。

然而,并非每个概念实体都值得被变成一张巨大的、单一的桌子。 过度反规范化会导致列填充稀疏、NULL 值存储过多以及数据操作语言 (DML) 复杂,尤其是在多个应用程序更新同一巨型行的不同切片时。它还会掩盖建模错误,例如将不同的生命周期或基数强行塞进同一个结构中。

在兼顾宽桌便利性和合理设计的同时,通常需要结合控制性非规范化、垂直分区以及半结构化属性的替代存储方式。 例如,可以将一些可选属性集移到 JSON 列、单独的子表或主要用于分析工作负载的列式优化结构中,而核心事务属性则保留在更精简、更适合 OLTP 的模式中。

对宽表进行索引是另一个挑战:尝试对几十列或几百列进行索引是不可持续的。 最佳方案是只对 WHERE 子句或 JOIN 条件中频繁出现的谓词进行索引,而对于更复杂的分析访问路径,则依赖于内存中的列式特征、分区修剪和物化视图。

大型宽表的分区、物化视图和压缩

对于包含数十亿行数据的宽表来说,分区几乎是必不可少的,以控制性能和维护工作。 范围分区、列表分区或复合分区允许您针对数据子集进行查询、统计信息收集和清理操作,从而减少 I/O 和争用。

子分区可以进一步细化数据在存储和缓冲区缓存中的分布方式。 例如,范围哈希组合可以更均匀地分布热点子集,而列表范围设置可以更紧密地契合业务语义(例如区域加日期)。使用内存列式存储时,您可以在分区或子分区级别决定哪些数据块适合进行内存优化。

物化视图是另一种使宽表易于管理以进行分析的强大方法。 与其每次都访问庞大的基础表,不如预先计算聚合或特定领域的投影,这些投影范围更窄,也更容易缓存。这些物化视图可以定期或按需刷新,从而以更低的资源占用支持 BI 查询和仪表板。

压缩在磁盘和内存中都起着至关重要的作用,尤其是在许多列具有重复值或稀疏值的情况下。 Oracle 的高级压缩和内存压缩算法能够显著减少存储空间占用,并通过减少需要读取的数据量来加快扫描速度。虽然这会增加一些 CPU 工作量,但借助现代处理器和向量化指令,对于许多分析工作负载而言,这最终会带来性能上的提升。

超宽表格的运营和人工智能/分析影响

除了纯粹的性能之外,宽表还会带来操作上的影响,例如影响备份、复制和维护窗口。 大量数据行会增加批量复制、逻辑导出和下游复制流程的成本。任何结构变更,例如添加或删除列,都需要进行更深入的分析,以避免对工具和流程产生意想不到的连锁反应。

当宽表位于架构的核心时,监控和可观测性就变得至关重要。 您不仅需要跟踪 CPU 和内存使用情况,还需要跟踪缓冲区缓存命中率、撤销表空间压力以及实际工作负载下内存存储的行为。上线前的负载测试至关重要,它可以发现性能瓶颈,并找到分区、缓存和索引方面的优化机会。

从人工智能和高级分析的角度来看,宽表通常用作特征存储或分析视图,为机器学习模型和智能代理提供数据。 将许多属性集中在一个地方可以简化特征向量的提取,并降低预处理的复杂性,尤其是在与列式存储和 SIMD 加速扫描结合使用时。

与此同时,人工智能密集型应用案例也带来了关于数据治理、安全和合规性的额外担忧。 当您将许多敏感属性聚合到一个宽泛的结构中时,任何访问配置错误的影响范围都会扩大。因此,适当的基于角色的访问控制、数据脱敏和审计变得必不可少,尤其是在受监管的行业中。

专业咨询公司和内部架构团队可以帮助组织决定何时宽表才是真正正确的选择,以及何时其他模式的扩展性更好,从而创造巨大的价值。 这包括为跨 AWS 和 Azure 的多云部署提供建议,与 Power BI 等 BI 平台集成,以及设计安全、高性能的数据管道,将运营数据库与分析和 AI 服务连接起来。

权限严格下的大规模删除:批量策略

处理巨型表(无论宽窄)时,一个经常被忽视的方面是如何在权限有限的情况下安全地删除大量数据。 在许多企业中,数据库管理员无法自由地运行 DDL、创建新分区或重组生产环境中的对象;他们可能只能执行 DML 操作,例如 DELETE,在某些情况下还可以执行 TRUNCATE。

发出一条巨大的 DELETE 语句,删除数十亿行表中的三分之一,会导致表空间耗尽和事务长时间运行。 此类操作可能会占用行锁数小时,导致 UNDO 和 TEMP 使用量激增,如果操作过程中出现问题,恢复时间将变得无法接受。

常见的缓解策略是使用 PL/SQL 的 BULK COLLECT 和 FORALL 分批进行受控删除。 该方法首先打开一个游标,选择满足删除条件的 ROWID,然后以固定大小的块(例如,一次 100,000 万行)提取这些 ROWID,批量删除这些行,提交,然后重复此过程直到游标耗尽。每次迭代消耗的撤销量都在可控范围内,从而保持事务窗口较小。

这种增量式方法可以减轻撤销表空间的压力,并提供更可预测的进度,但代价是需要多次提交。 在无法依赖分区或在线表重定义的情况下,这通常是最务实的选择。您可以根据观察到的撤销使用情况、I/O 能力和可接受的事务持续时间来调整 LIMIT 的大小。

理想情况下,如果您拥有更广泛的权限,您可能会更喜欢基于分区的策略,例如删除或截断分区以几乎立即清除历史数据。 其他方法包括创建一个只包含你想保留的行的新表,然后进行替换。但如果无法使用直接数据删除 (DDL),精心编写的批量删除代码仍然是你的主要工具。

将所有这些要素整合起来——智能缓存算法、全数据库缓存、内存列式格式、宽表设计、分区、压缩以及批量维护的操作实践——就能让你对 Oracle 如何支持极其苛刻的工作负载有一个连贯的理解。 当内存大小与数据库容量相符,并且模式设计同时满足 OLTP 和分析需求时,您可以在完全或大部分存储在内存中的非常宽的表之上,提供亚秒级的分析、稳定的事务性能和可靠的 AI 数据管道。

相关文章: