OceanBase 4.x 架构师知识库

涵盖架构、存储、事务、备份、调优、资源隔离及新增高级特性(向量化引擎、Auto DOP、分区裁剪、OBProxy运维、租户克隆、Oracle兼容等)。

第1章:系统架构与核心概念

1.1 Shared-Nothing 对等架构

OceanBase 4.x 采用 Shared-Nothing 分布式集群架构。整个集群由多个 OBServer 节点组成,每个节点独立拥有自身的 CPU、内存、磁盘等资源,节点之间不共享任何硬件资源,通过网络进行通信与协调。所有 OBServer 节点在功能上完全对等,每个节点都包含完整的 SQL 引擎、存储引擎、事务引擎以及 Paxos 副本管理能力,无单点瓶颈。

核心设计理念

Shared-Nothing 是分布式数据库实现高可扩展性、高可用性和低成本的基础。相比 Shared-Disk(如 Oracle RAC)或 Shared-Everything(如单机数据库),OceanBase 的设计避免了共享存储带来的锁竞争和缓存一致性开销,并允许系统在通用 X86/ARM 服务器上线性扩展。

✅ 核心优势
  • 高可扩展性: 增加节点即可线性提升系统处理能力和存储容量,支持数千节点、数百 PB 数据规模。
  • 高可用性: 基于 Paxos 协议的多副本机制,数据零丢失(RPO=0),故障恢复时间小于 8 秒(RTO<8s)。
  • 高性能: 数据分片(Tablet)分布在多个节点,查询可并行扫描多节点数据;单机部署时性能超越传统单机数据库。
  • 低成本: 采用通用硬件,基于 LSM-Tree 的高压缩比(通常 5:1 到 10:1),无需专用存储设备。

数据分片:Tablet

OceanBase 4.x 将数据进一步细粒度分片,最小物理存储单元称为 Tablet。每个非分区表对应一个 Tablet,每个分区表的一个分区也对应一个 Tablet。Tablet 是数据迁移、负载均衡和故障恢复的最小单位。Tablet 的大小通常建议控制在约 2GB 左右(基于运维经验的推荐值,并非绝对限制)。RootService 会根据 Tablet 上的请求负载(如 CPU 耗时、IO 次数、网络流量)动态调度 Tablet 在节点间迁移,避免形成热点。

多副本与 Paxos 角色

每个 Tablet 通常维护 3 个或更多副本(数量由租户的 replica_num 决定)。每个副本承担不同的 Paxos 角色:

  • Leader 副本: 提供强一致性读写服务,所有写请求必须路由到 Leader。Leader 负责发起 Paxos 日志同步。
  • Follower 副本: 参与 Paxos 日志投票,可提供弱一致性读(通过 READ_CONSISTENCY = WEAK 配置)。

Leader 的选举和切换完全由 Paxos 协议自动完成,客户端无感知。切换时间通常在 2~8 秒内。

负载均衡与弹性伸缩

OceanBase 集群内置了 RootService 作为总控服务,负责:

  • Unit 调度: 资源单元(Unit)在节点间的分布均衡,保证各节点 CPU/内存利用率相近。
  • Tablet 负载均衡: 监控每个 Tablet 的请求负载,自动将高负载 Tablet 的 Leader 副本迁移到低负载节点。
  • 扩容/缩容: 添加新节点后,系统自动将部分 Unit 或 Tablet 迁移到新节点,实现数据再平衡;删除节点前,数据会自动迁移走,不丢失数据。
-- 查看当前集群中的节点与资源使用情况
        SELECT * FROM oceanbase.DBA_OB_SERVERS;

        -- 查看各节点的 Tablet 数量分布(使用 DBA_OB_TABLET_REPLICAS 视图)
        SELECT SVR_IP, COUNT(*) AS tablet_count 
        FROM oceanbase.DBA_OB_TABLET_REPLICAS 
        GROUP BY SVR_IP;

与传统架构对比

对比维度Shared-Nothing (OceanBase)Shared-Disk (传统)单机数据库
资源扩展节点独立扩展,线性提升存储可扩展,计算扩展受共享锁限制垂直扩展,有物理上限
故障恢复节点故障自动切换,RTO<8s依赖集群件,切换时间通常分钟级需人工介入或主备切换
成本通用硬件,高压缩比需共享存储(SAN/NAS)通用硬件,但容量有限
写扩展性不同 Tablet 的写操作分布在不同 Leader 上,无全局锁竞争写入受共享存储锁竞争影响单点写入

4.x 架构优化亮点

  • 日志流(Log Stream):将多个 Tablet 的日志合并为一个 Paxos 日志流,减少网络开销和存储冗余,大幅提升写入性能。
  • 元数据服务独立化:系统租户与 Meta 租户分离,各租户元数据管理不再相互干扰。
  • 动态线程模型:工作线程与租户 CPU 配额解耦,租户间资源隔离更精确。
Shared-Nothing 架构并非银弹,它带来了分布式事务、跨节点 Join 等复杂性。OceanBase 通过优化 2PC、全局时间戳(GTS)和并行执行框架,将这些复杂性对上层透明化,用户使用体验近似单机数据库。
1.2 多租户架构详解

OceanBase 原生支持多租户架构,允许在一个集群内创建多个相互隔离的数据库“实例”(即租户)。每个租户拥有独立的资源(CPU、内存、IOPS)、数据和系统变量,租户之间完全隔离,互不影响。这种架构使得 OceanBase 能够以极低的成本为多个业务或用户提供数据库服务,同时保证安全性和性能。

多租户核心概念

多租户的实现基于三个核心抽象:资源单元(Unit)资源池(Resource Pool)租户(Tenant)

🏗️ 资源单元(Resource Unit)

资源单元是 OceanBase 中给租户分配资源的最小逻辑单位。一个 Unit 定义了 CPU(最小/最大)、内存、IOPS 和日志磁盘空间等规格。每个 Unit 归属于一个特定的 OBServer 节点,并占用该节点的一部分物理资源。一个节点上可以创建多个 Unit,但所有 Unit 的资源总和不能超过节点总容量。

-- 创建资源单元配置
        CREATE RESOURCE UNIT unit1
            MAX_CPU=8, MIN_CPU=4,
            MEMORY_SIZE='32G',
            MAX_IOPS=128000, MIN_IOPS=128000,
            LOG_DISK_SIZE='2T';

⚠️ 注意:IOPS 的最小值为 1024[reference:0]。若不指定 MIN_CPU,则默认等于 MAX_CPU[reference:1]。从 V4.0.0.0 开始不再支持内存超卖[reference:2]。

📦 资源池(Resource Pool)

资源池是一组资源单元的集合,这些单元具有相同的 Unit 配置,分布在多个 Zone 中。一个资源池只能属于一个租户。通过资源池,租户可以声明自己能够使用的全部物理资源范围。

-- 创建资源池,分布在三个 Zone,每个 Zone 一个 Unit
        CREATE RESOURCE POOL pool1
            UNIT='unit1',
            UNIT_NUM=1,          -- 每个 Zone 内 Unit 的数量
            ZONE_LIST=('zone1', 'zone2', 'zone3');

UNIT_NUM 表示资源池在单个 Zone 下 Unit 的个数,其值需小于目标 Zone 内 OBServer 节点的个数,且同一个租户在每个节点上最多只能分布一个 Unit[reference:3]。所有 Zone 的 UNIT_NUM 必须相同,是租户水平扩展的关键参数。

🏢 租户(Tenant)

租户是资源的消费者,也是数据库实例的抽象。一个租户绑定一个或多个资源池,并拥有独立的系统表空间、用户表、系统变量、权限体系以及兼容模式(MySQL 或 Oracle)。

-- 创建 MySQL 兼容模式的租户
        CREATE TENANT tenant_mysql
            CHARSET='utf8mb4',
            ZONE_LIST=('zone1','zone2','zone3'),
            PRIMARY_ZONE='zone1;zone2,zone3',  -- Leader 优先分布在 zone1
            RESOURCE_POOL_LIST=('pool1'),
            SET ob_tcp_invited_nodes='%';

租户类型

OceanBase 4.x 集群中包含以下三种租户[reference:4]:

  • 系统租户(sys tenant):ID 为 1,是集群内置的管理员租户,拥有集群管理的最高权限。系统租户负责集群的元数据管理、资源调度、备份恢复等全局操作。系统租户是单点写入,不支持水平扩缩容[reference:5]。
  • 普通租户(user tenant):业务租户,由用户创建,运行具体应用的数据。每个普通租户可配置为 MySQL 或 Oracle 兼容模式。
  • Meta 租户(meta tenant):自 4.x 版本引入,每个用户租户会自动创建对应的 Meta 租户(生命周期与用户租户一致),用于存储该租户的私有元数据,从而减轻系统租户的负担,增强资源隔离效果[reference:6][reference:7]。租户 ID 规则:偶数表示用户租户,奇数表示 Meta 租户[reference:8]。

资源隔离机制

OceanBase 通过多级资源隔离保证租户之间不会相互干扰:

资源类型隔离方式说明
CPU 用户态调度 + cgroup(4.x 起支持) 每个租户的 CPU 使用被限制在 [MIN_CPU, MAX_CPU] 范围内,超额时会被限流。cgroup 隔离需要提前在操作系统层面配置目录并开启功能[reference:9]。
内存 硬隔离 租户内存分为 MemStore(写缓存)、Cache(读缓存)、SQL 工作区等,独立占用,一个租户内存耗尽不会影响其他租户。
IOPS cgroup blkio 控制 通过 Unit 配置的 MAX_IOPS / MIN_IOPS 限制磁盘读写次数,最小值为 1024[reference:10]。
日志存储 独立目录 每个租户的 Clog(提交日志)和数据存储在独立的目录或文件中。

元数据隔离

除了资源隔离,OceanBase 还实现了元数据的租户级别隔离:

  • 每个租户拥有独立的系统表(如 __all_table__all_column 等),通过租户 ID 前缀区分。
  • 系统租户可以查看所有租户的元数据,但普通租户只能看到自己的对象。
  • DDL 操作(如 CREATE TABLE)不会阻塞其他租户的元数据查询。

4.x 多租户优化亮点

  • Meta 租户独立: 每个普通租户创建时自动生成一个对应的 Meta 租户,专用于存储其元数据、日志流等私有数据,避免系统租户成为元数据瓶颈[reference:11]。
  • 更精细的 CPU 隔离: 支持 cgroup 绑定,减少了 CPU 争抢导致的性能抖动。需注意 OceanBase 4.x 依赖 cgroup v1 版本,cgroup v2 环境下隔离功能可能失效[reference:12]。
  • 租户克隆: 支持通过 CREATE TENANT new_tenant FROM source_tenant ... 语法快速克隆现有租户(第 10 章详述)[reference:13]。
  • 租户参数模板: 可以使用预定义的参数模板快速创建标准租户。
  • 内存不再超卖: 从 V4.0.0.0 开始,OceanBase 不再支持内存超卖,确保租户内存分配的确定性[reference:14]。

租户创建最佳实践

📌 推荐步骤:
  1. 评估业务需要的资源量(CPU 核数、内存、IOPS)。
  2. 创建对应的 RESOURCE UNIT,建议 MIN_CPUMAX_CPU 设为相同值(避免 CPU 突发争抢),MIN_IOPSMAX_IOPS 也保持一致(最小值为 1024)。
  3. 创建 RESOURCE POOL,根据冗余需求设置 UNIT_NUM(通常设为 1,多副本由 Zone 数量保证)。
  4. 创建租户时指定 PRIMARY_ZONE(不指定时默认为 RANDOM),建议将 Leader 集中在性能较好的 Zone。
  5. 创建完成后,可通过 ALTER TENANT 动态调整资源池或修改系统变量。
-- 查看集群中所有租户
        SELECT tenant_id, tenant_name, tenant_type, primary_zone FROM oceanbase.DBA_OB_TENANTS;

        -- 查看租户的资源使用情况(注意视图名从 V4.0.0 起由 GV$MEMORY 改为 GV$OB_MEMORY)
        SELECT tenant_id, SUM(hold)/1024/1024/1024 AS hold_gb
        FROM oceanbase.GV$OB_MEMORY
        GROUP BY tenant_id;
⚡ 注意:租户的资源隔离是在数据库内核层实现的,不依赖外部虚拟化技术,因此开销极低。生产环境中,建议为重要业务租户保留至少 20% 的 CPU 和内存余量,防止突发流量冲击。系统租户仅用于集群管理和运维,不建议在生产环境中运行业务负载[reference:15]。
1.3 Zone 与多地域高可用

Zone 是 OceanBase 实现高可用和容灾的核心逻辑概念。一个 OceanBase 集群由若干个 Zone 组成,每个 Zone 代表一组具有相似容灾属性的节点集合。通过将数据副本分布在不同的 Zone 中,OceanBase 能够容忍单节点、单机房甚至城市级别的故障,实现金融级的高可用性。

Zone 的定义与属性

Zone 是对节点进行管理的容器,从物理层面讲,一个 Zone 通常对应一个独立的物理部署单元,可以是一个数据中心(IDC)、云上的一个可用区,或者一个单独的机架(Rack)。每个 Zone 具有两个重要属性:

  • Region(地域):通常指 Zone 所在的城市或地理区域,用于支持跨地域容灾。
  • IDC(机房):指 Zone 所在的物理机房标识,用于同城多机房部署场景下的就近访问优化。

通过合理配置 Zone 的 Region 和 IDC 属性,集群内的自动容灾处理和优化策略能够更好地工作。

-- 查看集群中所有 Zone 的信息
        SELECT ZONE, STATUS, IDC, REGION, TYPE FROM oceanbase.DBA_OB_ZONES;

        -- 修改 Zone 的 Region 和 IDC 属性
        ALTER SYSTEM ALTER ZONE zone1 SET REGION='hangzhou', IDC='HZ0';

OceanBase 集群支持多种部署模式,可以满足不同级别的高可用和容灾需求。

🏛️ Zone 级别的高可用性

OceanBase 集群为每个数据记录维护多个副本,这些副本基于 Paxos 协议构成高可用单元。通常情况下,每个 Zone 内部最多部署一个数据副本。当少数 Zone 发生故障时,其他 Zone 中的副本仍能基于 Paxos 协议继续提供服务,同时保证数据完整性。

典型的三副本集群包含 Zone1、Zone2 和 Zone3,每个数据记录拥有三个副本,分别部署在这三个 Zone 中,实现机房级容灾。

多地域高可用部署方案

OceanBase 支持 IDC、同城、跨城级别的高可用和容灾,提供多种部署方案以满足不同业务需求[reference:7]:

部署方案架构说明容灾能力适用场景
同城三机房三副本 同城 3 个机房各部署一个 Zone,机房网络延迟 0.5~2ms[reference:8]。 任意一个机房故障,剩余两个机房仍满足多数派(2/3),RPO=0,RTO<8s[reference:9]。 金融核心交易、同城双活或三活部署。
三地五中心五副本 三个城市的五个机房部署副本,数据分片在多地多机房间分布[reference:10]。 任意一个或多个 IDC 故障,只要剩余全功能副本满足多数派(3/5),服务自动恢复,可应对城市级灾难[reference:11]。 金融核心系统、国标金融 6 级容灾要求场景。
两地三中心主备部署 主城市与备城市组成一个 5 副本集群,备用城市建设独立的 3 副本集群作为备库,主备间异步同步数据[reference:12]。 主城市最多损失 2 份副本,剩余 3 份副本满足多数派。主城市遭遇灾难时,备城市可接管业务。 跨城级容灾、成本与容灾能力平衡场景。

基于 Paxos 的高可用实现

OceanBase 通过 Paxos 协议保证副本间数据强一致性,实现故障自动切换[reference:13]:

  • 多数派(Majority)机制:每次事务提交需要获得半数以上副本的确认。例如在三副本集群中,至少需要 2 个副本确认才能提交,这保证了单副本故障时集群仍然可用[reference:14]。
  • 自动故障切换:当 Leader 副本所在节点或 Zone 发生故障时,Follower 副本会自动发起选举,通常在 8 秒内完成 Leader 切换,应用无感知[reference:15]。
  • 数据零丢失(RPO=0):事务日志在多数派副本持久化后才返回成功,任何单个副本故障不会造成数据丢失[reference:16]。
  • 动态扩缩容:当集群容灾需求变化时,可以增加或减少 Zone 的数量。例如将三副本扩容为五副本,通过增加 Zone4 和 Zone5,再部署新的数据副本来提升系统可用性[reference:17]。

主备租户与角色切换

OceanBase 支持租户级别的主备容灾。通过主备租户特性,可以在不同集群间建立主备关系,实现跨集群的容灾能力。

  • 主租户(PRIMARY):正常创建的业务租户,提供完整的读写服务[reference:18]。
  • 备租户(STANDBY):通过物理备库特性创建的租户,通过日志同步从主租户接收数据变更。OceanBase V4.2.0 起,主备租户之间采用异步日志同步的方式[reference:19]。

租户角色可通过两种操作动态改变:

  • Switchover(无损切换):按计划执行的角色切换,主备租户交换角色,整个过程保证数据无损(RPO=0),通常在秒级完成[reference:20]。
  • Failover(容灾切换):当主租户发生不可恢复故障时执行,将备租户切换为新主租户。此操作在切换前建议使用 VERIFY 子句进行预检查,存在毫秒级数据丢失风险(取决于复制延迟)[reference:21]。
-- 执行 Switchover(在 primary 租户所在集群执行)
        ALTER SYSTEM SWITCHOVER TO STANDBY TENANT = tenant1;

        -- 执行 Failover 预检查
        ALTER SYSTEM ACTIVATE STANDBY TENANT = tenant1 VERIFY;

        -- 执行 Failover
        ALTER SYSTEM ACTIVATE STANDBY TENANT = tenant1;

仲裁服务(Arbitration Service)

OceanBase 数据库企业版从 V4.1.0 版本开始引入仲裁服务,这是一种基于 Paxos 多副本容灾方案提出的新型高可用方案。仲裁服务独立于 OceanBase 集群部署,是一个以特殊模式启动的轻量级 observer 进程,但社区版暂不支持此功能[reference:22]。

仲裁成员具备以下核心特征:

  • 仅参与选举和成员变更投票:参与 Leader 选举、Paxos Prepare 以及成员组变更投票,但不参与日志多数派投票(Paxos Accept)[reference:23]。
  • 无状态设计:不存储日志,无 MemTable 和 SSTable,资源(带宽/内存/磁盘/CPU)开销极小[reference:24]。
  • 不能当选 Leader:仲裁成员无法被选举为 Leader 提供服务[reference:25]。

仲裁方案的核心价值在于:

  • 解决了两地三中心场景下,同城副本故障时业务响应时间(RT)变大的问题[reference:26]。
  • 降低了跨城带宽开销,并将第三机房的成本降到极低[reference:27]。
  • 当半数以上全功能副本故障时,仲裁服务会自动触发降级以恢复服务,且保证 RPO=0;故障副本恢复后自动升级回原始成员列表[reference:28]。
📌 仲裁服务的关键前提与限制
• 仅当租户的 Locality 为 2F 或 4F 时,才可以开启仲裁服务[reference:29]。
• 单个 OceanBase 集群最多只能使用一个仲裁服务,且仲裁服务目前只支持单机部署[reference:30]。
• 仲裁方案在极端场景下可能存在数据丢失风险,例如第一个全功能副本故障降级后,第二个全功能副本也发生故障,之后即使第一个副本恢复了也可能有数据丢失[reference:31]。

Zone 与多地域高可用运维管理

日常运维中,可以通过以下命令管理和监控 Zone 状态:

-- 查看 Zone 分布和状态
        SELECT * FROM oceanbase.DBA_OB_ZONES;

        -- 停止 Zone(停止后该 Zone 不再参与服务)
        ALTER SYSTEM STOP ZONE zone2;

        -- 启动 Zone
        ALTER SYSTEM START ZONE zone2;

        -- 隔离 Zone(用于故障应急)
        ALTER SYSTEM ISOLATE ZONE zone2;

        -- 强制停止 Zone(不检查日志同步状态)
        ALTER SYSTEM FORCE STOP ZONE zone2;

当需要对 Leader 副本分布进行调整时,可以通过修改租户的 PRIMARY_ZONE 属性实现平滑切主:

-- 修改租户的优先 Leader Zone,实现 Leader 集中或分散
        ALTER TENANT tenant1 SET PRIMARY_ZONE = 'zone1;zone2,zone3';

4.x 版本高可用优化亮点

  • 仲裁服务引入:V4.1.0 起支持仲裁服务,可在 2F1A(两副本加仲裁)部署模式下实现低成本的高可用,RPO=0[reference:32]。
  • 日志流架构优化:以日志流作为 Paxos 复制的基本单元,取代旧版本中以分区为单位的模式,大幅降低海量分区带来的资源开销[reference:33]。
  • RTO 持续优化:通过并行 Paxos 和动态 Leader 切换,故障恢复时间从早前版本的 30 秒降低到 8 秒以内[reference:34]。
  • 更灵活的扩缩容:支持在线增减 Zone 和 OBServer 节点,集群扩容缩容期间业务不中断。
🏆 OceanBase 的“三地五中心”部署方案通过了金融级标准的验证,可实现城市级故障自动无损容灾,RPO=0 且 RTO<8 秒,已在网商银行、交通银行等核心系统成功落地[reference:35]。
1.4 OBProxy 路由机制

OBProxy(OceanBase Database Proxy,也称 ODP)是 OceanBase 集群的轻量级反向代理层。它作为客户端与 OBServer 节点之间的桥梁,负责将 SQL 请求路由到正确的 OBServer 节点,同时提供连接池管理、负载均衡、黑名单过滤、读写分离等高级功能。OBProxy 本身无状态,支持水平扩展,是 OceanBase 高可用架构中不可或缺的接入层组件。

基本工作流程

当客户端通过 OBProxy 发送 SQL 请求时,OBProxy 执行以下核心步骤:

  1. 解析 SQL 路由信息:从 SQL 文本中提取路由所需的关键信息(如分区键值、表名等)。
  2. 查询 Location Cache:根据解析结果查找目标分区所在 OBServer 节点的 IP 和端口。
  3. 选择目标 OBServer:结合路由策略(如 LDC 就近访问、读写分离等),决定最终转发的节点。
  4. 转发请求并缓存结果:将请求发送给选中的 OBServer,并将路由结果缓存到本地 Location Cache 中,加速后续请求。

三种路由计划

OBProxy 根据 SQL 访问的数据分布情况,生成三种不同类型的路由计划。目标是将 Remote 和 Distributed 计划尽量转化为 Local 计划,减少跨节点网络开销。

计划类型含义性能特征典型场景
Local 计划 SQL 访问的所有数据分区均位于当前被选中的 OBServer 节点上。 最优,无额外 RPC 开销 主键或唯一键等值查询、分区键裁剪精准的单分区操作
Remote 计划 SQL 访问的数据分区位于集群中另一个 OBServer 节点上,OBProxy 将请求转发到该节点。 一次额外的网络 RPC 非本地分区的主键查询(如分区键未命中当前节点)
Distributed 计划 SQL 访问的数据涉及多个分区,且这些分区分布在多个 OBServer 节点上。 最差,可能产生多次跨节点数据交换 跨分区查询、全表扫描、非分区键的多点更新
-- 通过 SQL Audit 查看 SQL 的 plan_type 分布(request_time 为微秒级)
        SELECT plan_type, COUNT(*) AS cnt 
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE request_time > UNIX_TIMESTAMP() * 1000000 - 3600000000
        GROUP BY plan_type;

        -- plan_type 含义:1=Local, 2=Remote, 3=Distribute, 0=无计划(如 COMMIT)

分区表路由计算

对于分区表,OBProxy 会根据 SQL 中的条件计算目标分区 ID(Partition ID),具体规则如下:

  • 等值条件:对于 WHERE partition_key = constant,OBProxy 直接计算常量对应的分区。
  • INSERT 语句:根据插入行的分区键值计算目标分区。
  • 范围条件:如果条件为 partition_key > 100,OBProxy 无法确定唯一分区,将生成 Distributed 计划或发送到所有分区。

如果 OBProxy 无法从 SQL 中计算出分区 ID(例如分区键使用了函数或隐式类型转换),则无法拉取准确的路由。可通过 obproxy.log 中的关键字 "calculate partition id" 排查路由失败的原因。

Location Cache 与内部依赖表

OBProxy 在内存中维护了一个 Location Cache,用于缓存分区与 OBServer 节点的映射关系。该缓存通过定期查询 OBServer 的内部表来刷新:

  • __all_virtual_proxy_schema:用于保存分区与其所在机器的对应关系信息,也有 Partition 的部分相关信息[reference:17]。
  • __all_virtual_proxy_partition_info:保存分区表相关分区信息[reference:18]。

OBProxy 启动时会从配置的 RootService 地址获取集群拓扑,后续按需(首次访问某分区时)或定期(默认 15 秒至 30 秒)拉取最新的路由信息。当发生 Leader 切换或副本迁移时,Location Cache 会自动失效并重新拉取。

🔧 路由相关配置参数(obproxy.conf / obproxy_config.yaml)
  • proxy_route_policy:用于设置 ODP 弱读请求路由策略,控制客户端请求被路由到哪个后端服务器上进行处理。默认值为 '',表示使用 ODP 默认的 MERGE_IDC_ORDER 路由策略[reference:19]。
  • location_cache_refresh_interval:Location Cache 刷新间隔,默认 30 秒。
  • enable_ob_protocol_v2:是否启用 OB 协议 V2,影响路由性能,默认 True。
  • enable_weak_read:是否允许弱一致性读路由到 Follower,默认 False。

黑名单与故障节点自动摘除

OBProxy 会主动检测后端 OBServer 的健康状态。当一个 OBServer 节点在 congestion_fail_window(默认 120 秒)内错误次数超过 congestion_failure_threshold 配置的值时,OBProxy 会将该节点加入黑名单[reference:20]。

黑名单机制有效避免了因个别 OBServer 故障导致的请求堆积和重试风暴。可通过 OCP 或 SHOW PROXYCONFIG LIKE '%congestion%' 查看相关参数。

连接管理与会话保持

OBProxy 内部维护了到 OBServer 的连接池,以减少频繁建立连接的开销:

  • 客户端连接:每个客户端与 OBProxy 建立一个 TCP 连接。
  • 后端连接:OBProxy 根据租户、数据库等维度复用到 OBServer 的连接,连接池大小由 max_connections 等参数控制。
  • 会话状态保持:由于 OBProxy 是无状态的[reference:21],客户端 Session 状态(如事务、临时表、用户变量)实际保存在 OBServer 上。OBProxy 通过版本号机制同步不同后端连接上的 Session 状态,保证功能正确性[reference:22]。

路由调优最佳实践

📌 常见问题与优化建议
  • 避免分区键隐式转换:例如分区键是 VARCHAR 但传入 NUMBER,会导致分区计算失败,触发 Distributed 计划。
  • 使用本地索引而非全局索引:全局索引的查询可能跨分区路由,产生 Remote 计划。
  • 合理配置 PRIMARY_ZONE:将 Leader 集中到少数 Zone 可使 OBProxy 更容易命中 Local 计划。
  • 监控路由分布:定期检查 GV$OB_SQL_AUDIT 中的 plan_type,若 Remote/Distribute 比例过高,需检查 SQL 与分区设计。
  • 只读地址访问到主副本:当 enable_cached_serverenable_primary_zone 同时开启且请求解析失败时,可能复用登录时选择的主副本连接,导致所有请求发往主副本。解决方法为关闭这两个参数[reference:23]。
-- 查看当前 OBProxy 的路由信息(通过连接到 OBProxy 执行)
        SHOW PROXYROUTE;

        -- 清理指定 OBServer 的路由缓存(强制重新拉取)
        ALTER SYSTEM FLUSH LOCATION CACHE TENANT = 'tenant1' SERVER = '192.168.1.1:2882';

        -- 查看 OBProxy 黑名单(需登录到 OBProxy 主机查看日志或 OCP)
⚡ 注意:OBProxy 仅做 SQL 路由转发,不执行 SQL 解析和计算(仅提取路由所需的最小信息)。对于复杂查询(如多表 Join、子查询),OBProxy 可能无法精确计算分区 ID,此时会将请求随机发往一个 OBServer,由 OBServer 内部的分布式执行引擎负责后续的数据重分布。因此,OLAP 类查询不受 OBProxy 路由计划影响。
1.5 LDC 逻辑数据中心路由

逻辑数据中心(Logical Data Center,LDC)路由是 OceanBase 为解决多地多中心部署时跨地域路由延迟问题而设计的核心机制。通过在集群 Zone 上设置 Region(地域)和 IDC(机房)属性,并在 OBProxy 上指定其所属的 IDC,系统能够将数据请求优先路由到同机房、同地域的副本,从而大幅降低网络延迟,提升数据库整体性能。

核心概念

LDC 路由机制的实现依赖于集群与 OBProxy 的两层配置:

  • Region(地域): Zone 所在的地域信息,通常设置为城市名(大小写敏感),例如 "SHANGHAI"、"HANGZHOU"。
  • IDC(机房/数据中心):Zone 所处的具体机房信息,通常设置为机房名(小写),例如 "zue"、"ztg"。
  • proxy_idc_name:OBProxy 进程启动时指定的参数,用于标识 OBProxy 所属的逻辑数据中心,需要与集群中某个 Zone 的 IDC 属性相对应。

一个 OceanBase 集群包含若干个 Region,每个 Region 包含若干个 Zone,每个 Zone 对应一个 IDC 属性。当 OBProxy 接收到请求时,它通过比对自身的 proxy_idc_name 与各 OBServer 的 IDC/Region 属性,来决定路由的优先级。

LDC 路由优先级

当数据请求到达 OBProxy 时,OBProxy 会严格按照以下优先级顺序选取目标副本进行路由转发:

  1. 选取本机房(同 IDC)不在合并中的副本。
  2. 选取同 Region 其他机房不在合并中的副本。
  3. 选取本机房(同 IDC)正在进行合并的副本。
  4. 选取同 Region 其他机房正在进行合并的副本。
  5. 随机选取跨 Region(异地)且不在合并中的副本。
  6. 随机选取跨 Region(异地)且正在进行合并的副本。
⚠️ 注意
如果没有设置 LDC,当 OceanBase 多地多中心部署时,弱一致性读请求会均匀访问所有主从副本,当访问非本地域机房上的副本时,系统响应时间将大大增加[reference:1]。

配置 LDC 路由的完整流程

启用 LDC 路由需要完成以下三个核心步骤[reference:2][reference:3]:

1. 配置 OceanBase 集群的 LDC

使用 root@sys 用户登录集群,为每个 Zone 设置 Region 和 IDC 属性。

-- 登录 sys 租户后执行
        ALTER SYSTEM MODIFY zone "zone1" SET region = "SHANGHAI";
        ALTER SYSTEM MODIFY zone "zone1" SET idc = "zue";

        ALTER SYSTEM MODIFY zone "zone2" SET region = "SHANGHAI";
        ALTER SYSTEM MODIFY zone "zone2" SET idc = "zue";

        ALTER SYSTEM MODIFY zone "zone3" SET region = "HANGZHOU";
        ALTER SYSTEM MODIFY zone "zone3" SET idc = "ztg";

参数说明[reference:4]:
- region:Zone 所在的地域信息,通常设置为城市名(大小写敏感)。
- idc:Zone 所处的具体机房信息,通常设置为机房名(小写)。
- zone:待修改的 Zone 名称。
验证配置:通过以下查询确认 LDC 设置是否生效,检查 DBA_OB_ZONES 视图中 REGIONIDC 字段的值是否符合预期。

SELECT * FROM oceanbase.DBA_OB_ZONES;

2. 配置 OBProxy(ODP)的 LDC

OBProxy 的 LDC 配置有两种方式[reference:5][reference:6]:

-- 【推荐】方式一:进程启动时通过参数指定(推荐方式)
        cd /opt/taobao/install/obproxy-1.5.5
        ./bin/obproxy -o proxy_idc_name=zue

        -- 方式二:通过 ALTER PROXYCONFIG 动态修改
        obclient> ALTER PROXYCONFIG SET proxy_idc_name = 'zue';

上述示例中,为 OBProxy 配置 LDC 后,如果应用部署在上海 zue 机房,当应用发起弱一致性读时,请求会被优先发往上海机房。

验证配置:通过以下命令确认 OBProxy 的 IDC 配置是否生效。

obclient> SHOW PROXYINFO IDC;

3. 应用配置弱一致性读

LDC 路由机制仅对弱一致性读(Weak Consistency Read)场景生效。通过将读请求配置为弱一致性,请求可以根据 LDC 策略被路由到最优副本,从而实现读写分离或就近读取。OceanBase 提供以下三种配置方式[reference:7]:

-- 方式一:HINT 指定(SQL 级别)
        obclient> SELECT /*+read_consistency(weak)*/ * FROM t1;

        -- 方式二:SESSION 变量(Session 级别)
        obclient> SET @@ob_read_consistency = 'weak';

        -- 方式三:GLOBAL 变量(租户级别,对所有连接生效)
        obclient> SET @@global.ob_read_consistency = 'weak';

有界旧一致性:OceanBase 提供租户级配置项 max_stale_time_for_weak_consistency 来定义弱读可容忍的最大落后时间(默认 5 秒),保证读取的数据不会落后于最新数据超过特定时间[reference:8]。
单调读一致性:通过设置租户级配置项 enable_monotonic_weak_read(默认为 True),可以保证弱一致性读的版本单调性,即客户端不会读到版本回退的数据[reference:9]。

📌 读写分离场景
LDC 路由与弱一致性读结合,可以实现在线读写分离:写入请求始终发往 Leader 副本,分析类或低延迟要求的查询请求配置为弱一致性读,根据 LDC 策略路由到同 Region 的 Follower 副本,从而有效分摊 Leader 节点的负载。

高级路由策略

在 OBProxy 中,除了 LDC 路由,还可以通过 proxy_route_policy 等参数进一步精细化控制路由行为。LDC 路由与路由策略结合使用,可以实现更灵活的路由控制。

  • 副本类型优先策略follower_first(请求优先路由到备副本,所有备副本不可用时才路由到 Leader)和 follower_only(请求仅路由到备副本,无备副本可用时断开连接)等[reference:10]。
  • 指定主可用区proxy_primary_zone_name 参数可强制 OBProxy 仅将请求路由到指定的主可用区[reference:11]。
  • 基于权重的负载均衡:将 proxy_route_policy 设置为 WEAKREAD_WEIGHT_LOAD_BALANCE,系统将按照为每个 Zone 设置的权重(通过 weakread_weight_zone 参数配置)来分配弱一致性读请求[reference:12]。

故障排查

🔧 常见问题及处理
  • 弱一致性读请求仍然访问异地或 Leader 副本? 首先检查租户副本在各 IDC 的分布情况,确保本地域确实存在 Follower 副本;同时验证 DBA_OB_ZONES 视图中 Region/IDC 标签是否设置正确,以及 OBProxy 的 proxy_idc_name 与集群 IDC 是否匹配。
  • LDC 路由未生效? 确认集群与 OBProxy 的 LDC 配置是否已正确设置,并检查应用 SQL 是否配置了弱一致性读。同时可查询 OBProxy 日志定位路由决策信息。
  • 读写分离效果不及预期? 进一步调整 OBProxy 的 proxy_route_policy 参数(如设置为 follower_first),并结合业务 SQL 的特征进行优化[reference:13]。
  • 4.x 版本支持:V4.3.1 及以上版本的 OBProxy 支持为 proxy_primary_zone_name 配置多个可用区[reference:14]。
⚡ 最佳实践建议
规范 IDC 命名:统一使用小写字母标识,确保与 OBProxy 的 proxy_idc_name 严格一致。
合理规划 LDC 拓扑:根据业务流量特征和数据分布,将强关联的业务租户部署在同 Region 的 Zone 中,以最大化 LDC 路由收益。
监控关键指标:定期检查各 Zone 的网络延迟和副本健康状态,分析跨 Region 路由请求的比例,及时发现潜在的路由策略问题。
搭配负载均衡:在多副本、多 IDC 场景下,建议为 OBProxy 部署独立的负载均衡器,确保接入层的高可用与 LDC 路由策略协同工作。
1.6 仲裁服务与降级容灾

OceanBase 数据库从 V4.1.0 版本开始支持仲裁服务,这是一种基于 Paxos 多副本容灾方案提出的新型高可用方案,旨在解决两地三中心场景下同城副本故障时响应时间升高的问题,同时还能有效降低跨城带宽的开销,并将第三机房的成本降到极低。需要注意的是,仲裁服务是 OceanBase 企业版的特有功能,社区版暂不支持。

⚖️ 仲裁服务是什么
仲裁服务是独立于 OceanBase 集群部署的、以特殊模式启动的轻量级 observer 进程。在分布式数据库中,当多个数据副本发生半数异常时(一半副本故障或与另一半网络隔离),可以通过集群之外的仲裁服务来参与选主或成员组变更等决策,进而恢复服务。

仲裁服务的特点

仲裁成员具有以下核心特征:

  • 仅参与投票,不参与日志多数派:仅在 Leader 选举、Paxos Prepare 以及成员组变更中投票,不参与 Paxos Accept 投票,这是其轻量化的关键。
  • 无状态、资源消耗极低:不存储日志,没有 MemTable 或 SSTable,因此对带宽、内存、磁盘和 CPU 的开销极小。
  • 不能当选为主提供服务:无法被选举为 Leader 对外提供服务,始终作为从副本(Follower)角色存在,不承载业务读写流量。
📈 仲裁服务的优势
  • 成本优化:仲裁成员可取代传统部署方案中的日志型副本,在可用性几乎不变的情况下大幅降低部署成本。
  • 自动降级与恢复:半数全功能副本故障时,仲裁服务会自动执行降级流程恢复服务,且能做到 RPO = 0。降级后业务响应时间不受影响,无需跨城同步日志。
  • 资源开销极低:仲裁成员无需同步/回放日志,也不存储 Redo 日志或基线数据,资源开销显著降低。
  • 无损双活容灾:基于两个全功能副本即可实现 RPO = 0 的双活容灾能力,支持自动升降级。
🎯 适用场景
  • 同城双机房(可跨城部署仲裁):在两个机房部署全功能副本,在第三个地理区域部署仲裁服务。
  • 跨城带宽资源稀缺:仲裁方案仅需同步日志至少数副本,大幅减少跨城带宽开销。
  • 成本敏感、预算有限:用极低的额外资源开销实现无损双活容灾。
⚠️ 不适合仲裁服务的场景
  • 没有额外资源部署仲裁服务:如果仅有 2 个机房且没有额外的服务器或机房可用于部署仲裁服务,更适合使用主备库容灾方案。
  • 对可用性有极致要求且预算充足:仲裁方案在成本更低的同时,可用性也略低于 3F 或 5F 的全功能副本方案。例如,2F1A 部署中,1 个 F 副本故障触发仲裁降级后,剩余唯一 F 副本如果也发生故障,即使第一个 F 副本后来恢复,也会有数据丢失,原因是仲裁成员不存储 Redo 日志,3F 部署场景则没有这个问题。

核心机制:自动降级与升级

📉 日志流降级(自动降级)

当租户开启仲裁服务后,如果日志同步失败且持续时间达到 arbitration_timeout(默认 5s),仲裁服务会自动执行日志流降级流程,将故障副本从成员列表中删除(但租户的 Locality 不会变化),从而恢复服务,且能做到 RPO = 0。

  • 触发条件:仅当故障副本(包括已降级的副本)总数等于全功能副本总数的一半时,仲裁服务才会执行日志流降级。例如,4F1A 部署下:
    • 1 个 F 副本故障(故障 1,总数 4)→ 不满足一半条件,不降级。
    • 2 个 F 副本故障(故障 2,总数 4)→ 满足条件,自动降级。
  • 触发异常:包括副本所在的 Server 宕机、被 STOP、被 KILL、网络中断、租户日志盘满,以及长时间故障后的 Rebuild 期间。
-- 查看租户当前的仲裁降级控制超时时间
        SHOW PARAMETERS LIKE 'arbitration_timeout';

        -- 修改租户的日志流降级控制时间(例如调整为 10 秒)
        ALTER SYSTEM SET arbitration_timeout = '10s';

配置说明
- 租户级配置项 arbitration_timeout 用于设置触发日志流自动降级的控制时间,默认值为 5s,取值范围为 [3s, +∞)
- 该配置项配置后实时生效,不需要重启 OBServer 节点
- 如果不希望发生降级,可将其设置为一个较大的值,例如 30d(30 天)。

📈 日志流升级(自动恢复)

如果当前日志流已经处于降级状态,仲裁服务会周期性检测 DEGRADED_LIST 中的副本。如果发现被降级的副本日志同步已恢复并且不存在降级策略中所列出的异常,就会自动执行日志流升级流程,将被降级的副本重新加入成员列表,提供更高的可用性保证。

监控降级状态:通过以下 SQL 判断当前是否存在降级的日志流:

SELECT * FROM oceanbase.GV$OB_LOG_STAT WHERE DEGRADED_LIST NOT LIKE '';

如果查询结果中有日志流对应的 DEGRADED_LIST 记录不为空,则表示该日志流发生了降级。

仲裁服务的生命周期管理

🏗️ 1. 部署仲裁服务
# 在独立的服务器上,以仲裁模式启动 observer 进程
        # 需准备相应硬件资源,并确保与 OceanBase 集群网络互通
        # 示例启动命令
        ./bin/observer -p 2882 -P 2882 -z zone_arb -d /data/1/arb_service --appname arb_service -o "arbitration_service=true"

前提条件
- 仲裁服务独立于 OceanBase 集群部署,需要一台单独的机器,其 CPU/内存/磁盘资源需求较小。
- 版本兼容性:新替换的仲裁服务版本需与 OceanBase 集群的大版本保持一致,以确保兼容性。

➕ 2. 向 OceanBase 集群中添加仲裁服务

仲裁服务部署完成后,需要在 OceanBase 集群中执行添加操作,然后集群内的租户才能使用该仲裁服务。

-- 登录 sys 租户执行
        ALTER SYSTEM ADD ARBITRATION SERVICE "10.xx.xx.3:2882";

        -- 验证仲裁服务是否添加成功
        SELECT * FROM oceanbase.DBA_OB_ARBITRATION_SERVICE;

关键限制
- 单个 OceanBase 集群最多只能使用一个仲裁服务。
- 仲裁服务目前只支持单机部署。

🔛 3. 为租户开启仲裁服务

仲裁服务以租户为单位启用或关闭。开启前需确认租户的 Locality 为 2F 或 4F(即 2 个或 4 个全功能副本)。开启仲裁服务仅对租户 Locality 中的 F 副本个数有要求,对租户 Locality 中的 R 副本无限制。例如,租户的 Locality 为 F@z1,F@z2F@z1,F@z2,R@z3 均可以开启仲裁服务。

-- 启用租户的仲裁服务(在 sys 租户中执行)
        ALTER TENANT tenant_name ENABLE_ARBITRATION_SERVICE = true;

        -- 停用租户的仲裁服务
        ALTER TENANT tenant_name ENABLE_ARBITRATION_SERVICE = false;

开启前置检查
- 通过 SELECT * FROM oceanbase.GV$OB_ARBITRATION_SERVICE_STATUS 检查各节点与仲裁服务的通信状态(STATUS 需为 ACTIVE)。只有所有节点均通信正常才可以为租户开启仲裁服务。
- 通过 SELECT * FROM oceanbase.DBA_OB_TENANTS WHERE tenant_name = '...' 查看当前 ARBITRATION_SERVICE_STATUS 字段(可能为 DISABLED、ENABLED、ENABLING、DISABLING)。

📊 4. 监控仲裁服务状态
  • DBA_OB_TENANTS.ARBITRATION_SERVICE_STATUS:租户的仲裁服务启用状态。
  • DBA_OB_ARBITRATION_SERVICE:集群添加的仲裁服务信息。
  • GV$OB_LOG_STAT.DEGRADED_LIST:查看日志流降级状态。
  • DBA_OB_SERVER_EVENT_HISTORY:查看日志流升降级事件历史。
♻️ 5. 升级与替换仲裁服务

注意事项:新替换的仲裁服务版本需与 OceanBase 集群的大版本保持一致,以确保兼容性。

-- 替换故障仲裁服务(在 sys 租户执行)
        ALTER SYSTEM REPLACE ARBITRATION SERVICE 'curr_arb_server_ip:curr_arb_server_port' WITH 'new_arb_server_ip:new_arb_server_port';

        -- 移除不再使用的仲裁服务
        ALTER SYSTEM REMOVE ARBITRATION SERVICE 'arb_server_ip:arb_server_port';

如果原仲裁服务在替换前为正常状态,系统会自动清理原仲裁服务器上残留的集群信息;如果原仲裁服务在替换前为故障状态,系统会有一个 Warning 信息,提示您仲裁服务替换成功后,原仲裁服务器需要手动清理。

在 OCP 平台创建的仲裁服务组中,当仲裁服务节点发生异常时,系统可根据优先配置实现秒级替换,实现仲裁服务本身的高可用。

仲裁方案的风险与权衡

仲裁方案与全功能副本部署相比,成本更低,但可用性也略低,在极端情况下可能存在数据丢失风险。

  • 2F1A(两副本一仲裁)的潜在问题:1 个 F 副本故障触发降级后,只剩唯一 F 副本。如果此副本也发生故障且无法恢复,之后即使第一个 F 副本恢复了,也会导致数据丢失,原因在于仲裁成员不存储 Redo 日志,无法像 3F 部署那样自动恢复数据。因此仲裁方案适合对成本高度敏感,且能接受略低于 3F 集群可用性的场景。
  • 自动降级依赖 timeout:仲裁服务的自动降级依赖于 arbitration_timeout 参数的设定,过短可能导致不必要的降级,过长可能影响 RTO。需要根据业务网络和容灾要求仔细权衡。
  • 依赖租户 Locality:仲裁服务要求租户 Locality 严格为 2F 或 4F,这为租户的部署模式带来一定限制,尤其在进行跨集群主备切换等复杂操作时需格外谨慎。
🏆 4.x 版本仲裁服务演进
V4.1.0:仲裁服务正式发布。
V4.2.1+:支持租户级仲裁服务启用/停用,支持仲裁服务替换。
V4.3.1+:增强 RTO 表现,支持更灵活的降级策略。
V4.4.0+:OCP 平台支持仲裁服务可视化创建、管理与替换。

第2章:LSM-Tree 存储引擎深度解析

2.1 LSM-Tree 架构与分层

OceanBase 数据库的存储引擎基于 LSM-Tree(Log-Structured Merge-Tree)架构。与 InnoDB 等传统数据库实时刷脏页不同,LSM-Tree 将内存中的随机写转化为对磁盘的顺序写,大幅提升写入性能。数据被分为两部分:MemTable(内存中的动态增量数据)和 SSTable(磁盘上的静态基线数据),SSTable 一旦生成即为只读,不再被修改。

🎯 LSM-Tree 核心思想

LSM-Tree 的本质是 基于归并排序的数据存储思想。所有数据更新操作(INSERT/UPDATE/DELETE)首先写入内存中的 MemTable,并同时记录 Redo Log 确保持久性。当 MemTable 内存使用达到阈值时,系统将 MemTable 中的数据转储到磁盘成为 SSTable,这一过程将随机 I/O 转化为顺序 I/O,显著提升了数据库的写入吞吐量。

MemTable:内存存储引擎

MemTable 是 OceanBase 的内存存储引擎,负责处理所有数据的写入操作。当一个 DML 操作到达时,数据首先写入 MemTable,写入成功后即向客户端返回成功,无需等待落盘。

  • BTree + Hashtable 双引擎:MemTable 内部同时维护 BTree 和 Hashtable 两种索引结构,BTree 优化范围查询,Hashtable 优化点查性能,两个结构中存储的都是指向实际数据内存块的指针。
  • Active + Frozen 双层设计:每个租户的内存中同时存在 Active MemTable(当前接收写入)和 Frozen MemTable(等待转储)。当 Active MemTable 内存占用达到阈值(即租户级配置 freeze_trigger_percentage * memstore_limit_percentage)时,系统执行 冻结(Freeze),将当前 Active 变为 Frozen 并激活新的 Active MemTable,确保写入永不阻塞。
-- 触发租户级别的转储(冻结 + 转储)
        ALTER SYSTEM MINOR FREEZE TENANT = tenant_name;

        -- 查看租户 MemStore 使用情况
        SELECT tenant_id, active_memstore_used, total_memstore_used, memstore_limit
        FROM oceanbase.GV$OB_MEMSTORE;

SSTable:磁盘存储结构

SSTable 是 OceanBase 在磁盘上的数据存储单元,一旦生成即为只读,不再被修改。多个转储生成的 SSTable 会在后续的合并过程中被归并,生成新的 SSTable。

  • 宏块(Macroblock):OceanBase 将数据文件按 2MB 固定大小切分为宏块,宏块是磁盘写入 IO 的基本单位。
  • 微块(Microblock):每个宏块内部进一步划分为多个变长微块(默认 16KB,可配置范围为 4KB 至 512KB),微块是磁盘读取 IO 的基本单位。微块支持 Flat(平铺)Encoding(编码)两种组织格式,基线数据(Major SSTable)可采用 Encoding 格式获得更高的压缩比。
-- 在建表时指定微块大小(单位:字节,可选 4096、8192、16384、32768、65536、131072 等)
        ALTER TABLE mytable SET block_size = 32768;

三层分层存储(Tiered & Leveled Compaction)

OceanBase 4.x 将磁盘上的 SSTable 划分为 L0、L1、L2 三层,采用 Tiered & Leveled 混合 Compaction 策略,在写入放大和读放大之间取得平衡。

层级存储内容Compaction 模式核心特点
L0 层 Mini SSTable Tiered(按层堆积) 多个 Mini SSTable 直接堆叠,延缓向下层合并,降低写放大。内部进一步划分为多个子层(level-0 到 level-n),每层可容纳固定数量的 SSTable。
L1 层 Minor SSTable Leveled(按范围有序) SSTable 按 Rowkey 全局有序,当 L0 总大小与 L1 大小比率达到阈值时触发合并。
L2 层 Major SSTable(基线) 只读(不参与日常转储) 存储合并后的全量基线数据,多副本间物理一致。

L0 层内部分层:L0 层内部进一步划分为 level-0 到 level-n 多个子层,每层可容纳固定数量的 SSTable。当某层 SSTable 数量达到上限时,触发本层内合并,输出到下一子层;只有最底层 SSTable 数量达到上限时,才触发 L0 → L1 的跨层合并,从而有效控制读放大和写放大之间的平衡[reference:22]。

转储 vs 合并:核心区分

  • 转储(Minor Compaction):将 MemTable 转为 Mini SSTable,由 Mini Compaction 和 Minor Compaction 两个子过程组成。转储只处理增量数据,不涉及基线数据,对业务性能影响更小。
  • 合并(Major Compaction):将租户下所有分区的基线 SSTable 与增量 SSTable 归并,生成新的 Major SSTable。合并是租户级的全局操作,最终形成租户级快照,与转储的核心区别在于合并是在统一快照点与静态数据进行归并的行为。

查询路径与多级缓存优化

由于 OceanBase 采用基线 + 增量的设计,每次查询需要同时读取 MemTable(增量)和 SSTable(基线),并将结果归并后返回。为加速查询,OceanBase 实现了多级缓存体系:

  • Block Cache:缓存从磁盘读取的微块(解压后),加速范围扫描和全表扫描。
  • Row Cache:缓存具体的数据行,对于 Get/MultiGet 点查询可极大加速。
  • Block Index Cache:缓存微块的索引,帮助在宏块中快速定位目标微块[reference:23]。
  • Bloom Filter Cache:基于宏块按需构建,当宏块上空查次数超过阈值时自动创建,帮助快速过滤不存在的行,避免无效磁盘 I/O。
  • Fuse Row Cache:缓存将多版本数据融合后的最终行,避免重复的版本合并计算,特别有利于热点行查询场景[reference:24]。

通过多级缓存体系,OLTP 类点查询可以接近内存数据库的性能,避免传统数据库解析整个数据块的开销。

4.x 架构优化亮点

  • 自适应合并(Adaptive Compaction):从 V4.2.0 版本开始支持,系统实时采集用户查询和写入信息,按需对分区调度合并任务,达到对用户无感知地解决慢查询问题。
  • 表级 Buffer 表优化:允许为每张表设置独立的表级配置项,当表中墓碑记录存量达到阈值时,系统自动触发针对性合并,有效应对高频更新场景下的读放大问题。
  • 列存引擎支持(V4.3.0+):在 LSM-Tree 架构基础上扩展列存能力,实现行存与列存一体化存储,兼顾 TP 和 AP 查询性能。
  • 每日合并机制:在多副本分布式部署基础上,引入每日合并概念,可定期或按需选择全局版本号进行合并。
📌 最佳实践建议
合理配置 memstore_limit_percentage 与 freeze_trigger_percentage:控制租户 MemStore 占可用内存的比例及冻结触发阈值,避免 MemStore 过早写满导致写入阻塞。
监控转储与合并频率:通过 GV$OB_COMPACTION_PROGRESS 视图监控 Compaction 进度,确保转储和合并任务在业务低峰期完成。
利用 Bloom Filter 优化空查:对于存在大量“不存在”点查的场景(如防重校验),Bloom Filter 可显著降低磁盘 I/O 开销。
微块大小调优:OLTP 点查为主的场景建议使用较小的微块(如 4KB~8KB),OLAP 大查询场景可适当增大微块提高压缩比。
2.2 Compaction 合并机制全解

Compaction(合并) 是 LSM-Tree 存储引擎的核心操作,本质上是数据的重新组织,其算法是一个多路归并排序——将多个 SSTable 按 Rowkey 排序后合并,最终输出为一个 SSTable。OceanBase 数据库通过设计精巧的 Compaction 策略,在写入性能、查询性能和空间利用率之间取得了良好的平衡。

🔑 核心理解

在 LSM-Tree 架构中,每个 Partition(Tablet)的 LSM-Tree 存储架构是完全独立的,即每个分区对应一个独立的 LSM-Tree(每个分区拥有独立的 MemTable 及对应的 SSTable 链)。

合并类型总览

OceanBase 数据库支持以下五种合并类型:

类型核心操作触发方式特点
Mini Compaction 将 MemTable 转换为 Mini SSTable 自动/手动 不产生全局快照,不回收多版本数据
Minor Compaction 将多个 Mini SSTable 合并为一个 Mini SSTable,或与一个 Minor SSTable 合并为一个 Minor SSTable 后台调度线程自动 不产生全局快照,回收多版本数据
Medium Compaction 将指定分区的基线 SSTable 与所有 Mini/Minor SSTable 合并为一个 Major SSTable 自动/手动 分区级合并,仅与特定分区的增量数据合并
Major Compaction 将租户全部分区的基线 SSTable 与所有增量 SSTable 合并为一个 Major SSTable 自动/手动 租户级合并,产生租户级快照,最消耗资源
Meta Major Compaction 将指定时间点之前的数据合并为一个 Meta Major SSTable 后台调度线程自动 数据格式与 Major SSTable 相同,但不含多版本数据和未提交事务数据

转储(Minor Compaction)详解

转储由两个子过程构成:Mini Compaction(mini 合并)Minor Compaction(minor 合并)

📝 Mini Compaction(mini 合并)

当 MemTable 内存占用超过指定阈值(由租户级参数 freeze_trigger_percentage 控制)时,系统会触发转储(Mini Compaction),将 MemTable 中的数据落盘为 Mini SSTable,以释放内存空间。该过程持续进行,随着用户数据的不断写入,Mini SSTable 的数量也会不断增多。

🔄 Minor Compaction(minor 合并)

当 Mini SSTable 数量超过指定阈值(配置项 minor_compact_trigger)时,后台调度线程会自动触发 Minor Compaction。Minor Compaction 有两种合并策略:

  • Mini Minor Compaction:当 L0 层 Mini SSTable 的个数超过配置项 minor_compact_trigger 的值时,将 L0 层的多个 Mini SSTable 合并为一个大的 Mini SSTable。
  • Minor Compaction(minor 合并):在满足第一种策略的同时,且 L0 层 Mini SSTable 的记录总数超过 L1 层 Minor SSTable 记录数的 25% 时,L0 层的多个 Mini SSTable 与 L1 层的 Minor SSTable 直接合并为一个新的 Minor SSTable。

合并(Major Compaction)详解

合并操作(Major Compaction) 是将动态增量数据和静态基线数据做归并,通常比较耗时。当转储产生的增量数据积累到一定程度时,系统通过 Major Freeze 实现大版本的合并。在最佳实践中,一般期望在一天只做一次合并操作,并且控制在业务低峰期进行,因此有时也会把合并称之为每日合并

⚡ 转储 vs 合并:核心区别
对比维度转储(Minor Compaction)合并(Major Compaction)
操作范围分区级别或租户级别,是 MemTable 的物化租户级别,产生一个租户级快照
冻结时机每个 OBServer 节点的每个租户独立决定全局分区一起做冻结操作
多版本数据可能包含多个不同版本的数据行只包含快照点的版本行
合并对象只与相同大版本的 Minor SSTable 合并,产生新的 Minor SSTable与前一个大版本的全量静态数据进行合并,产生新的全量数据
数据压缩不执行编码压缩,使用通用压缩算法执行两级压缩(编码压缩 + 通用压缩)

Major Compaction 的合并算法

按照合并数据量和合并方式,Major Compaction 可以分为以下四种算法:

  • 全量合并:将静态数据全部读出并和动态数据合并为最终的静态数据。合并时间长,耗费 IO 和 CPU,是 OceanBase 数据库最初的合并算法,目前一般不会主动做全量合并。
  • 增量合并:仅仅合并被修改过的宏块(Macroblock),没有改变的宏块进行复用。增量合并极大地减少了合并的工作量,是 OceanBase 数据库目前默认的合并算法
  • 渐进合并:每次全量合并一部分数据,若干轮次后整体数据被重写一遍。通过表级属性 progressive_merge_num 控制,特别适用于大表 Schema 变更场景。
  • 并行合并:将数据划分到不同线程中并行做合并,通过多线程并行加速合并过程。

合并的触发方法

OceanBase 数据库支持多种合并触发方式:

1️⃣ 自动触发合并

当转储次数(即冻结次数,也即 minor_compaction 次数)超过配置项 major_compact_trigger 指定的阈值时,系统自动为租户调度一次租户级合并任务。

-- 查看 major_compact_trigger 配置(租户级)
        SHOW PARAMETERS LIKE 'major_compact_trigger';

        -- 设置转储 10 次后触发合并
        ALTER SYSTEM SET major_compact_trigger = 10;

        -- 注意:从 V4.0.0 版本开始,major_compact_trigger 的生效范围由集群级调整为租户级,默认值由 5 调整为 0(表示无论冻结多少次都不会自动触发合并)

该参数的控制范围为租户级,在自适应合并开启时,不建议使用 major_compact_trigger,因为租户级合并粒度更大,系统消耗的资源更多。

2️⃣ 定时触发合并

通过集群级配置项 major_freeze_duty_time 指定每天定时合并的时间(默认 02:00)。

-- 查看 daily major freeze 时间(集群级,需在 sys 租户下设置)
        SHOW PARAMETERS LIKE 'major_freeze_duty_time';

        -- 设置合并时间为每天 03:00
        ALTER SYSTEM SET major_freeze_duty_time = '03:00';
3️⃣ 手动触发合并
-- 触发租户级别合并(在 sys 租户下执行)
        ALTER SYSTEM MAJOR FREEZE TENANT = tenant_name;

        -- 全局触发合并(影响所有租户)
        ALTER SYSTEM MAJOR FREEZE;
4️⃣ 自适应合并(Adaptive Major Compaction)

从 OceanBase 数据库 V4.1.0 版本开始支持自适应合并功能。开启后,系统会实时采集并统计用户的查询、写入等信息,并按需为分区调度分区级合并任务,从而有效减少慢查询的发生。自适应合并由租户级隐藏配置项 _enable_adaptive_compaction 控制,默认值为 True。

💡 自适应合并与 major_compact_trigger 的租户级合并相比,粒度更细(分区级 vs 租户级)、涉及分区更少、系统资源消耗更小。因此,在自适应合并能力开启时,不建议同时使用配置项 major_compact_trigger
-- 查询自适应合并是否开启(隐式配置项)
        SELECT * FROM oceanbase.GV$OB_PARAMETERS WHERE NAME LIKE '_enable_adaptive_compaction';

        -- 如需关闭自适应合并(建议联系技术支持),可将该配置项设置为 False

合并状态监控

合并的状态可以通过视图 DBA_OB_MAJOR_COMPACTION(或 CDB_OB_MAJOR_COMPACTION)中的 STATUS 列来查看:

状态值含义
IDLE未进行合并
COMPACTING正在进行合并
VERIFYING正在校验 Checksum
-- 系统租户查询合并状态
        SELECT * FROM oceanbase.CDB_OB_MAJOR_COMPACTION;

        -- 普通租户查询合并状态
        SELECT * FROM oceanbase.DBA_OB_MAJOR_COMPACTION;

STATUSIDLE,且 GLOBAL_BROADCAST_SCNLAST_VERSION 的值相等时,说明合并结束。

合并的压缩算法

OceanBase 数据库在合并期间会对数据进行两层压缩:

  • 第一层(编码压缩):数据库内部基于语义的编码压缩。
  • 第二层(通用压缩):基于用户指定压缩算法的通用压缩。

可以通过参数 default_compress_func 配置压缩方式,默认值为 zstd_1.3.8。其他可指定的值还有 nonelz4_1.0snappy_1.0zstd_1.0zlib_lite_1.0。更高的压缩率更节约磁盘空间,但也会带来一定的性能牺牲。

-- 创建表时指定压缩算法(MySQL 模式示例)
        CREATE TABLE mytable (id INT PRIMARY KEY, name VARCHAR(100)) COMPRESS='lz4_1.0';

        -- 修改表压缩算法
        ALTER TABLE mytable SET COMPRESS='zstd_1.3.8';

您也可以在建表时单独为表指定压缩算法。对于 OceanBase 这类 LSM-Tree 架构,数据压缩对写入性能几乎无影响。

合并的附加价值

合并除了数据归并的基本功能外,还带来以下价值:

  • 数据压缩:通过两级压缩提升磁盘空间利用率。
  • 数据校验:通过租户级一致快照,合并完成后多副本可以直接比对基线数据,确保业务数据在不同副本间一致;同时基于该快照基线数据做主表和索引表的数据校验。
  • Schema 变更:对于加列、减列等 Schema 变更,OceanBase 可以在合并中一起完成数据变更操作,使得 DDL 操作对业务更加平滑。

合并调优与参数配置

OceanBase V4.x 版本以租户为粒度执行每日合并,可以根据不同租户的具体场景和要求,设置不同的合并参数。

核心合并参数
配置项级别描述默认值建议(百万分区/列存场景)
enable_major_freeze集群级是否开启合并True保持 True
major_freeze_duty_time集群级每天定时合并的时间02:00设置为业务低峰期
major_compact_trigger租户级转储多少次后触发合并0(V4.x)自适应合并开启时不建议设置
default_progressive_merge_num租户级建表时默认的合并行为0(默认 100 轮)大表 Schema 变更时调整
merger_check_interval租户级每个 Zone 的合并进度检查间隔10m可根据合并频繁程度调整
compaction_dag_cnt_limit集群级Compaction DAG 队列上 DAG 数量的上限值15000百万分区建议调至 50000
compaction_schedule_tablet_batch_cnt集群级Compaction 分批调度时每个批次调度的分区数上限50000百万分区建议调至 100000
compaction_low_thread_score集群级合并任务的并行线程数0(实际 6 线程)百万分区建议调至 20
compaction_high_thread_score集群级转储任务的并行线程数0(实际 6 线程)建议保持默认

其中 compaction_dag_cnt_limitcompaction_schedule_tablet_batch_cntcompaction_low_thread_scorecompaction_high_thread_score 等配置项来源于合并慢的调优实践,在百万分区场景下对合并提速有显著效果。

合并慢的场景应对(OceanBase V4.x)

以下两种典型场景下可能需要进行合并调优:

  • 百万分区场景:得益于架构优化,OceanBase V4.x 版本的一个 OBServer 或一个租户可以支持高达百万分区,更多的分区意味着更长的合并耗时。建议调整 compaction_dag_cnt_limitcompaction_schedule_tablet_batch_cntcompaction_low_thread_score 等参数来优化合并速度。
  • 列存场景(V4.3+):列存表在合并时需要将行存的增量数据拆分到每个列的独立 SSTable 中,这会导致合并时间和资源占用相较于行存有较大增加。OceanBase 数据库对列存表的合并进行了大幅优化,支持水平拆分并行合并加速,还新增了垂直拆分加速机制,确保合并过程既高效又经济。
-- 百万分区场景示例配置
        ALTER SYSTEM SET compaction_dag_cnt_limit = 50000;
        ALTER SYSTEM SET compaction_schedule_tablet_batch_cnt = 100000;
        ALTER SYSTEM SET compaction_low_thread_score = 20;
📌 4.x 版本架构变化说明
轮转合并(Rotating Merge):由于 V4.x 版本不再支持 Zone 级别的轮转合并,配置项 enable_merge_by_turnzone_merge_concurrency 从 V4.0.0 版本开始删除。同时,配置项 merger_switch_leader_duration_time(合并时批量切主的时间间隔)也从 V4.0.0 版本开始删除。
合并默认值变化major_compact_trigger 从 V4.0.0 版本开始默认值由 5 调整为 0,范围调整为租户级。
major_freeze_duty_time 默认为 02:00,每天凌晨自动触发合并,将当日所有增量数据和基线数据进行归并。
2.3 行列混存与 HTAP

传统架构往往面临“行存数据库 + 列存分析库”的外挂方案,虽能分别应对事务与分析,却不得不忍受数据同步延迟、存储冗余和运维复杂的高昂代价。OceanBase 从 V4.3.0 版本开始,基于 LSM-Tree 存储引擎原生扩展实现了列存支持,在一套架构下实现行存、列存与行列混存的一体化,用户无需双写或 ETL,即可在同一套系统中获得实时分析能力。[reference:0]

🎯 一体化设计哲学:一套数据,两种表达

OceanBase 列存引擎的设计核心是“一体化而非外挂”——基于 LSM-Tree “基线-增量”分离架构,让列存与行存在存储、事务、SQL 三层语义上实现天然统一:

  • 增量数据保持行存:事务写入(TP 负载)仍写入行格式的 MemTable,保证高效更新与事务 ACID。
  • 基线数据转为列存:每次 Major Compaction 生成基线 SSTable 时,数据可存储为列存格式,实现极致压缩与扫描性能。
  • 上层透明,优化器自动选路:所有 DML、DDL、备份恢复等生态工具均可复用,只需一行 WITH COLUMN GROUP 即可获得实时分析能力。

列存实现原理

OceanBase 列存引擎在磁盘上对数据进行了列式组织的重新表达,每列数据存储为一个独立的 SSTable,所有列的 SSTable 组合成为一个虚拟 SSTable 作为用户的列存基线数据。根据用户建表时指定的 WITH COLUMN GROUP 子句,基线数据可以有三种形态:

  • 行存表:不指定 WITH COLUMN GROUP,增量数据保持行存,基线数据也存储为行存 SSTable,适合点查和高频写入场景。[reference:6]
  • 列存表:指定 WITH COLUMN GROUP(each column),基线数据存储为列存 SSTable,大幅提升 AP 扫描和压缩能力。[reference:7]
  • 行列冗余表:指定 WITH COLUMN GROUP(all columns, each column),基线数据同时存储行存和列存两份副本,优化器根据查询特征自动选择最优存储路径。[reference:8]

无论使用哪种存储模式,表的增量数据均保持行存格式,因此列存表的 DML、事务、上下游数据同步都不会受到影响。[reference:9]

-- 查看当前租户默认建表存储格式
        SHOW PARAMETERS LIKE 'default_table_store_format';

        -- 将租户默认建表格式设置为列存(V4.3.4+,新建表生效)
        ALTER SYSTEM SET default_table_store_format = 'column';

        -- 将租户默认建表格式设置为行列混存(V4.3.4+,新建表生效)
        ALTER SYSTEM SET default_table_store_format = 'compound';
💡 说明default_table_store_format 参数从 V4.3.4 版本开始支持,用于控制租户级别新建表的默认存储格式,不影响已存在的表。column 表示建表默认格式为纯列存表,compound 表示建表默认格式为行列冗余表。[reference:10]
📦 列存的三种使用方式(V4.3.3+)

OceanBase 数据库目前提供三种列存使用方式,供用户在不同场景下选择使用:[reference:11]

-- 1. 列存表:适合纯 AP 分析场景
        CREATE TABLE tbl1_cg (col1 INT PRIMARY KEY, col2 VARCHAR(50))
            WITH COLUMN GROUP(each column);

        -- 2. 行列冗余表:适合 TP+AP 双负载混合场景
        CREATE TABLE tbl2_cg (col1 INT PRIMARY KEY, col2 VARCHAR(50))
            WITH COLUMN GROUP(all columns, each column);

        -- 3. 列存索引:主表行存 + 索引列存,适合行存表中少量分析查询的场景
        CREATE TABLE tbl3 (id INT, name VARCHAR(18), date DATE, PRIMARY KEY (id),
            INDEX idx1_tbl3_cg (date) WITH COLUMN GROUP(each column));

智能查询路由:优化器自动选择行存或列存

当表采用行列冗余表(行存 + 列存两份数据)时,OceanBase 优化器会根据查询特征和代价模型,自动判断应使用行存还是列存来执行查询:[reference:12]

  • 点查或小范围扫描(如按主键精确读取):优化器自动选择走行存,避免多余 I/O。
  • 全表扫描或大范围聚合查询(如 SUMGROUP BY):优化器自动选择走列存,利用向量化执行引擎加速。
  • 用户也可以通过 Hint /*+ STORING_MODE(ROW) *//*+ STORING_MODE(COLUMN) */ 强制指定路由。[reference:13]

当扫描范围大到行存需要的 IO 数大于等于访问列数 IO 数时,列存通常表现更优。简化评估建议:宽表(多于 64 列)扫描行数大于 1K,或非宽表扫描行数大于 8K 时,适合使用列存。[reference:14]

列存副本:物理资源隔离

OceanBase 从 V4.3.3 版本开始支持列存副本,这是一种专用于 AP 场景的新型副本类型。[reference:15]

  • 核心特性:列存副本与只读副本类似,不参与选举和日志投票,拥有完整的 SSTable/clog/MemTable。用户表的基线数据使用列存方式存储。[reference:16]
  • 部署方式:可以将列存副本部署在独立的 Zone 上,OLAP 业务通过独立的 ODP 入口访问列存副本,并以弱读方式执行决策分析类业务。[reference:17]
  • 资源隔离效果:列存副本独立于 OLTP Leader 副本,使 AP 请求不再消耗 OLTP 业务的计算资源,实现物理层面的资源隔离。[reference:18]
  • 版本要求:列存副本需要 OceanBase V4.3.3 版本及以上和 OCP V4.3.3 及以上。[reference:19]
-- 创建包含列存副本的租户示例(V4.3.3+)
        CREATE TENANT tenant_c LOCALITY = 'F@zone1,F@zone2,C@zone3',
            primary_zone='zone1;zone2,zone3',
            RESOURCE_POOL_LIST=('pool1')
            SET ob_tcp_invited_nodes = '%';
列存副本使用限制:列存副本上,所有的用户表(包括复制表,不包括索引表、内部表、系统表)均为列存格式存储。索引表暂不支持使用列存格式。[reference:20]

Merge-On-Write(MOW)实时分析增强(V4.3.5+)

OceanBase 4.3.0 推出列存引擎后,AP 分析能力大幅提升,但当增量数据较多时,查询采用 Merge-On-Read 方式仍需处理增量数据,对实时分析有一定影响(“读放大”问题)。OceanBase 在 4.3.5 版本中提出了 Merge-On-Write 表,将更新拆分为 DELETE/INSERT 写入增量数据中,查询时分别对增量和基线数据进行处理,从而显著提高了更新频繁场景下 OceanBase 的实时分析能力。[reference:21]

常规生产环境测试结果显示,MOW 表相比普通表可实现 5 倍以上 的查询性能提升。[reference:22]

-- 创建 MOW 表(Merge-On-Write)
        CREATE TABLE t_mow (c1 INT, c2 INT)
            WITH COLUMN GROUP(each column)
            MERGE_ENGINE = delete_insert;

        -- 查看表是否为 MOW 表
        SHOW CREATE TABLE t_mow;
⚙️ MOW 表使用说明:MOW 表目前仅支持列存表和行列混存表,暂不支持纯行存表。生产中建议对更新频繁的核心分析场景启用 MOW 表,同时监控合并频率以维持最佳查询性能。[reference:23]

向量化执行引擎演进

OceanBase 从 4.0 版本开始向量化执行引擎默认开启,并在 4.3 版本中实现了向量化引擎 2.0,通过对数据格式、算子实现优化及存储向量化优化等方面的改进,大幅提升了向量化引擎的执行性能。向量化引擎 2.0 实现了新的按列的数据格式,将数据描述信息 null, len, ptr 信息分别按列的方式分开连续存放,避免了数据信息冗余存储,对 SIMD 使用也更友好。

性能基准与收益

OceanBase 列存引擎在 AP 场景下表现优异,TPC-H 100G 测试中,列存相比非向量化方式性能提升显著,已在网商银行、交通银行等金融核心系统中落地 HTAP 混合负载方案。相比传统的“OLTP + CDC + 数仓”方案,OceanBase 一体化 HTAP 架构有效降低了数据延迟、运维成本和存储冗余,支持实时交易与实时分析在单一系统中融合运行。

4.x 版本 HTAP 演进路线图

  • V4.3.0:正式推出列存引擎,行列混存一体化,向量化执行引擎 2.0 发布。[reference:26]
  • V4.3.3:正式支持列存副本,可在独立 Zone 上部署列存副本实现 HTAP 物理资源隔离。[reference:27]
  • V4.3.5:Merge-On-Write 表发布,向量化执行引擎持续增强。[reference:28]
  • V4.4.x+:复杂数据类型(Bitmap/Array/Map)、物化视图实时更新等能力持续演进。[reference:29]
2.4 Bloom Filter 调优与索引加速

Bloom Filter(布隆过滤器)是一种概率型数据结构,其核心功能是快速判断一个元素是否存在于一个集合中。在 LSM-Tree 架构中,数据被分散存储在多个 SSTable 内,如果某个键在 SSTable 中不存在,存储引擎仍需要扫描大量数据才能确定“不存在”,导致严重的读放大。Bloom Filter 通过在内存中维护一个极小的位图结构,使得系统可以快速过滤掉那些“肯定不存在”的查询请求,从而大幅降低磁盘 I/O,优化点查询和写入性能。

🎯 核心定义与概率特性

Bloom Filter 的本质是一个由二进制位数组(Bit Array)和多个独立的哈希映射函数(Hash Functions)组成的数学模型。它的工作遵循一条金科玉律:如果我说它没有,那就绝对没有;如果我说它有,你最好亲自去确认一下。

  • 零假阴性(No False Negative):如果 Bloom Filter 判断一个元素“不存在”,则该元素一定不存在于集合中,这是其作为数据拦截盾牌的根本保证。
  • 假阳性(False Positive):如果判断一个元素“存在”,它可能并不存在(存在一定的误判率,通常低于 1%)。当遇到假阳性时,系统会回退到实际磁盘读取进行最终确认,因此在存储层面是绝对安全的。

Bloom Filter 将传统字典查找所需的大容量内存(例如存储一亿个 ID 需几个 GB)通过位图压缩降维到了仅几 MB,以极低的内存成本,换取了极高的查询过滤效率。

按需自动构建机制

在 OceanBase 数据库中,Bloom Filter 构建在 SSTable 的宏块(Macroblock,2MB 定长)粒度上,并采用按需自动构建策略,而非用户手动创建:

  • 空查统计与阈值触发:系统会统计每个宏块上的空查次数(即查询该宏块后发现数据不存在的次数)。当一个宏块上的空查次数超过由配置项 bf_cache_miss_count_threshold 指定的阈值时(默认为 100),系统即判定该宏块为“读取效率低下”,会自动为该宏块构建对应的 Bloom Filter,并将其放入 Bloom Filter Cache 中[reference:15][reference:16]。
  • 构建时的空间权衡:Bloom Filter 本质上是利用极小的空间代价换取大量的 I/O 节省。OceanBase 内部采用动态策略来平衡误判率与内存开销,确保在有效过滤空查请求的同时,不会过度占用宝贵的内存资源。
  • 缓存管理:当内存压力增大需要淘汰缓存时,Bloom Filter Cache 会根据预设的缓存优先级(由配置项 bf_cache_priority 控制,默认值为 1)决定淘汰顺序[reference:17][reference:18]。
-- 查看 Bloom Filter 相关的系统配置项(集群级,在 sys 租户下执行)
        SHOW PARAMETERS LIKE 'bf_cache_miss_count_threshold';
        SHOW PARAMETERS LIKE 'bf_cache_priority';
        SHOW PARAMETERS LIKE 'bloom_filter_cache_size';

        -- 设置宏块空查触发阈值(低于该值时不构建 Bloom Filter,减少不必要的内存开销)
        ALTER SYSTEM SET bf_cache_miss_count_threshold = 100;

Bloom Filter 在多级缓存中的定位

OceanBase 的多级缓存体系以漏斗式层层过滤数据,Bloom Filter 处于整个查询路径的最前端,负责快速拦截绝大多数不存在的数据查询请求,避免底层 Cache 和磁盘被无效请求穿透。整个查询流程如下:

🔍 查询路径与缓存命中顺序

  1. Bloom Filter Cache:首先,系统会检查目标宏块对应的 Bloom Filter。如果 Bloom Filter 判断该数据“肯定不存在”,则直接返回空结果,完全跳过后续的所有 IO 和缓存查找步骤,实现最高的过滤效率。
  2. Row Cache:若 Bloom Filter 未能有效过滤,系统会尝试在 Row Cache 中查找缓存的具体数据行。
  3. Block Index Cache:如果 Row Cache 未命中,系统将访问 Block Index Cache,用于在宏块中定位目标微块的位置。
  4. Block Cache:若 Block Index 定位到目标微块,系统会尝试从 Block Cache 中读取已缓存的微块数据。
  5. 磁盘 SSTable:如果 Block Cache 也未命中,才会从磁盘上的 SSTable 中读取数据[reference:19]。

Bloom Filter 通过第一层拦截,避免了大量低效的空查询直接穿透到 Row Cache、Block Cache 甚至磁盘,极大降低了后端 Cache 的压力和磁盘 I/O 开销。

核心配置参数详解

参数名称默认值作用域描述
bf_cache_miss_count_threshold 100 集群级 控制 Bloom Filter 自动构建的触发阈值。当一个宏块上的空查次数达到该值时,系统为该宏块构建 Bloom Filter。设置为 0 表示关闭自动构建功能,修改后实时生效[reference:20][reference:21]。
bf_cache_priority 1 集群级 设置 Bloom Filter Cache 在全局缓存淘汰中的优先级,取值范围 [1, +∞),数值越高,被淘汰的优先级别越低,修改后实时生效[reference:22][reference:23]。
bloom_filter_cache_size 256MB 集群级 Bloom Filter Cache 可占用的最大内存容量。当内存压力增大需要淘汰缓存时,系统会根据缓存优先级和访问热度进行整体淘汰。
⚡ Bloom Filter 相关配置项均为集群级参数,在 sys 租户下使用 SHOW PARAMETERSALTER SYSTEM SET 进行查看和修改,修改后无需重启 OBServer 节点即可生效。

Runtime Filter:SQL 执行层面的动态 Bloom Filter

除了存储层作为 SSTable 宏块数据的预过滤手段外,OceanBase 还在 SQL 执行层面引入了 Runtime Filter 技术。它是一种用于优化 Hash Join 性能的轻量级动态过滤器,通过构建 Bloom Filter 在线广播给 Probe 表,在存储引擎层提前过滤数据,大幅减少参与 Hash Join 的数据量和网络传输。

  • 原理:执行 Hash Join 时,系统先扫描小表(Build 表),利用其 Join Key 实时构建一个 Bloom Filter,然后将其广播到大表(Probe 表)所在的节点。大表在扫描数据时,会利用该 Bloom Filter 预先过滤掉肯定不满足 Join 条件的数据[reference:24]。
  • 适用场景:特别适用于 Build 表结果集较小的场景,如星形模型中与事实表 Join 的多张维度表[reference:25]。
  • 分类:Runtime Filter 分为 Local(仅本节点)、Global(跨节点)和 Part Join Filter(分区剪裁)。标准 Runtime Filter 支持 Bloom Filter、In Filter 和 Range Filter 三种数据结构,而 Part Join Filter 当前仅支持 Bloom Filter[reference:26][reference:27]。
-- 启用 Runtime Filter 的 SQL Hint 示例(在 SELECT 语句中使用)
        SELECT /*+ PX_JOIN_FILTER(tt2) PARALLEL(4) */ *
        FROM tt1 JOIN tt2 ON tt1.v1 = tt2.v1;

        -- 通过 Session 变量开启 Runtime Filter(影响当前会话)
        SET SESSION runtime_filter_type = 'BLOOM_FILTER,RANGE,IN';

Bloom Filter 与列存/行列混存场景下的协同

在行列混存或列存模式下,Bloom Filter 的工作机制与行存场景有所不同,但仍扮演着关键的角色:

  • 列存场景的 Bloom Filter:在 OceanBase V4.3.x 版本中,列存表的 Bloom Filter 构建在列存引擎对应的 SSTable 上,同样按需自动构建,用于加速针对列存数据的空值/空查过滤。
  • 行列冗余场景的智能路由:当表采用行列冗余模式时,优化器会根据查询特征自动选择走行存还是列存。无论走哪种路径,Bloom Filter 都作为第一层防御,在各自的存储层上先行过滤无效请求。
  • 列存副本场景:在 V4.3.3+ 的列存副本部署中,Bloom Filter 同样在列存副本上发挥作用,为 AP 查询提供独立于 OLTP 查询的过滤路径。

监控与性能调优

Bloom Filter 的性能可以通过相关指标进行监控和分析:

-- 查看 Bloom Filter Cache 的命中率(参考指标,具体视图名称根据版本略有差异)
        SELECT * FROM oceanbase.GV$OB_CACHE WHERE CACHE_NAME = 'bloom_filter_cache';

        -- 检查宏块粒度上的 Bloom Filter 构建情况(通过系统日志或特定性能视图)

最佳实践建议:

  • 避免全局关闭 Bloom Filter:保持 bf_cache_miss_count_threshold 为默认值 100,除非在极其特殊的内存受限场景下,才考虑关闭。
  • 合理设置缓存优先级:对于点查密集型业务,可适当调高 bf_cache_priority,确保 Bloom Filter Cache 在内存紧张时不会被过早淘汰。
  • 结合业务特征进行分析:高频存在“空查”的业务场景(如防重复校验、用户不存在检查),Bloom Filter 的收益最为明显。
  • 监控 Bloom Filter 命中率:若命中率长期偏低,可能意味着业务请求模式偏重于实际“存在”的数据,或宏块粒度上的 Bloom Filter 构建策略可进一步调优。
  • Runtime Filter 使用建议:对于复杂 Join 查询,可适当使用 PX_JOIN_FILTER 等 Hint 开启 Runtime Filter 优化,显著提升 Join 查询性能。但该优化会引入一定的动态过滤计算开销,需结合实际情况权衡。
💡 Bloom Filter 是 LSM-Tree 架构下对抗读放大最核心的防御机制之一。正确理解和配置 Bloom Filter 相关参数,对于提升数据库整体性能、降低 IO 负载具有至关重要的意义。

第3章:分布式事务与 MVCC

3.1 两阶段提交与 Paxos 日志流

在 OceanBase 数据库中,**日志流(Log Stream,LS)** 是系统自动创建和管理的核心实体。它不仅有序地记录了数据的 Redo 日志,还包含了一组数据分片(Tablet)。每个日志流及其副本构成一个独立的 Paxos 组,日志流的 Leader 副本提供强一致性读写服务,Follower 副本通过 Paxos 协议同步日志,并支持弱一致性读。在 V4.x 架构中,**事务提交的基本单位从分区演进为日志流**。这一改变从根本上解决了 V3.x 版本中因海量分区而导致的元数据膨胀、心跳风暴、合并长耗时等问题,为支持百万级分区提供了架构基础。

📌 核心概念的关系
  • 分区(Partition):由用户创建的逻辑对象,用于划分和管理表数据。
  • Tablet(分片):是承载数据的物理存储单位,与分区一一对应。Tablet 是数据均衡的最小单位,可以在机器间迁移。
  • 日志流(LS):是若干 Tablet 的容器,负责有序记录其内数据的 Redo 日志,并通过 Paxos 协议在副本间同步,是整个高可用和事务处理的基石。Tablet 可以在不同日志流之间迁移。

基于日志流的分布式事务提交

在 OceanBase 4.x 中,日志流(LS)是分布式事务的基础单元和参与者。每个日志流内部通过自己的 WAL 机制来保证该日志流内所有数据修改的原子性。当事务涉及多个日志流时,系统会采用基于两阶段提交(2PC)的分布式协议来保证所有参与者的一致性。

传统 2PC 协议中存在“协调者单点”和“写日志多”的性能瓶颈。为此,OceanBase 对 2PC 进行了深度优化,其主要流程如下:

1. PREPARE 阶段

协调者向事务涉及的所有参与者(日志流 Leader)发起 Prepare 请求。参与者收到请求后,会决定是否可以提交。如果可以,则持久化 Prepare 日志,并返回 Prepare 成功;否则返回 Prepare 失败。在此阶段,参与者会持有受影响数据的行锁,直到事务最终 Commit 或 Rollback。

2. PRECOMMIT 阶段 (Commit Point)

这是 OceanBase 优化 2PC 协议的关键一步。当协调者收齐所有参与者的 Prepare 成功响应后,并不立即写入 Commit 日志,而是直接向客户端返回事务提交成功的信息。此时,事务进入一个中间过渡状态——**预提交(PRECOMMIT)**,事务的提交点确定,但全局清理工作尚未完成。

3. COMMIT 阶段

协调者向所有参与者发送事务 Commit 请求。参与者收到请求后,释放行锁,提交 Commit 日志,并在日志持久化后回复 Commit OK 消息,最后清理事务上下文。

4. CLEAR 阶段

OceanBase 引入的额外阶段,专门负责清理事务处理过程中产生的临时状态和数据,有助于系统更快地回收资源并减少对事务响应时间的影响。

💡 事务类型与提交优化
  • 本地单日志流事务:事务涉及的操作仅在一个日志流内,且该日志流的 Leader 与创建会话的 Server 相同。这是最简单、性能最高的事务模型。
  • 本地多日志流事务:涉及多个日志流,但这些日志流的 Leader 都位于同一台 Server 上。
  • 分布式事务:涉及多个日志流,且这些日志流的 Leader 位于不同的 Server 上。

OceanBase 对单机事务采用了更高的优化策略。当事务内的参与者都位于同一台 Server 时,系统会采用**一阶段提交(One-Phase Commit)** 优化。在所有参与者都写完 Prepare Log 后,协调者就会将 Commit OK 返回给客户端,从而大幅减少提交延时。从 V4.0.0 版本开始,因底层逻辑改变,原有的 `enable_one_phase_commit` 配置项已被删除。

Paxos 日志流与多副本高可用

OceanBase 的日志服务在 V4.0.0 版本被抽象为名为 **Palf(Paxos-backed Append-only Log File System)** 的组件,它像文件系统一样为数据库提供追加写的日志接口。

每个日志流与其副本构成一个独立的 Paxos 组。在这其中,一个副本担任 Leader(主副本),其余为 Follower(从副本)。Leader 负责处理写请求并将日志以 `LSN`(Log Sequence Number)的形式同步给 Follower。只有当日志在多数派(超过半数的 Paxos 成员)副本上持久化成功后,事务才能被视为提交成功,这保证了**RPO(恢复点目标)= 0**。当少数派副本(如一个 Follower)发生故障时,Paxos 协议能保证剩余的多数派副本(仍然过半)可以继续提供服务,并在 8 秒内完成 Leader 切换,确保**RTO(恢复时间目标)< 8s**。

-- 查看集群中所有日志流及其副本的角色分布
        SELECT LS_ID, SVR_IP, SVR_PORT, ROLE 
        FROM oceanbase.DBA_OB_LS_LOCATIONS;

        -- 查看日志流的详细信息,包括 Leader 信息
        SELECT LS_ID, STATUS, REPLICA_NUM, MEMBER_LIST, LEADER 
        FROM oceanbase.CDB_OB_LS_LOCATIONS 
        WHERE TENANT_ID = 1002;
🏆 4.x 版本架构演进总结
OceanBase 4.x 以日志流为基石,不仅将事务提交粒度从分区粒度收敛到日志流粒度,从而实现了性能的飞跃,还引入了广播日志流等新概念。当创建复制表时,广播日志流会在租户内每个 Server 上部署副本,保证了复制表在任意节点上的强一致性读能力。
3.2 MVCC 与全局时间戳(GTS)

多版本并发控制(MVCC,Multi-Version Concurrency Control)与全局时间戳服务(GTS,Global Timestamp Service)是 OceanBase 数据库实现高性能并发访问、读写不互斥以及分布式一致性的核心基石。每次数据更改会产生一个新版本,不同事务根据各自获取的全局时间戳读取对应的数据快照版本,从而使得读操作和写操作可以并行执行、互不阻塞。

🎯 MVCC 核心思想

MVCC 的核心思想是“读不阻塞写、写不阻塞读”。与传统数据库通过锁机制实现并发控制不同,OceanBase 在每次更新数据时都生成一个新版本的数据副本,而不是直接在原始数据上进行修改。这样,读事务可以读取某个时间点的数据快照,而写事务则在同时创建新版本,两者互不干扰。OceanBase 的 MVCC 深度结合了分布式架构特性,通过引入全局时间戳与事务表管理,确保了分布式场景下的外部一致性。

MVCC 核心组成

OceanBase 的 MVCC 实现依赖三大核心组件,协同完成版本管理与一致性保障:

  • 数据版本链(Version Chain):每一行数据的每次更新都会生成一个新的版本,每个版本包含值(val)、版本号(ts)与事务ID(txn),通过回滚指针串联形成版本链。读请求无需加锁,只需根据自身版本号读取对应版本的数据,有效避免与写请求的冲突。
  • 事务表(Transaction Table):内存中维护的事务状态管理表,记录每个事务的ID、执行状态(RUNNING、PREPARE、COMMIT)以及对应的版本信息,用于判断数据版本的可见性与事务冲突处理。
  • 全局时间戳服务(GTS):负责为所有事务分配全局唯一的读版本号与提交版本号,确保分布式环境下所有节点的时间戳一致性,是 MVCC 实现跨节点数据可见性的核心基础。

MVCC 版本管理流程

每个事务在执行过程中会维护两个核心版本号:读版本号(用于读取数据时匹配可见版本)与提交版本号(用于数据更新时生成新版本)。核心流程如下:

  • 事务启动:从 GTS 获取全局读版本号,作为本次事务读取数据的基准。
  • 写操作:不会直接覆盖原数据,而是生成一个新版本,暂存于 MemStore,关联当前事务 ID 与临时版本号。
  • 事务提交:进入两阶段提交流程,每个分区取本地最大读时间戳作为本地提交版本号,全局提交版本号取所有分区本地提交版本号的最大值。
  • 提交完成:更新事务表状态,将全局提交版本号回填至新数据版本,并异步更新全局最大提交时间戳,供后续读请求判断可见性。

全局时间戳服务(GTS)的设计与实现

GTS(Global Timestamp Service)是 OceanBase 数据库中用于生成全局递增时间戳的核心服务,每个租户都会独立启动一个 GTS 服务。系统统一使用 GTS,事务提交时通过本租户的时间戳服务获取事务版本号,保证全局的事务顺序。在 V4.0.0 及之后版本中,原参数 ob_timestamp_service 已被删除,不再需要配置。

⏰ 时间戳正确性保证

GTS 服务通过精细的设计确保时间戳始终全局单调递增,即使发生 Leader 切换也不会有时间戳回退风险:

  • 预分配区间机制:GTS Leader 会申请预分配的时间戳区间,并同步到仲裁日志。
  • 新 Leader 恢复基准:新 Leader 上任前会回放原 Leader 写下的预分配区间日志,获取旧区间的最大值作为自己时间戳授权的基准值,确保新服务提供的时间戳不会退回到旧值。
⚡ GTS 高性能设计

GTS 每秒可处理数百万次时间戳获取请求,通过聚合和批量优化极大降低了系统开销:

  • 语句快照优化:事务提交时更新 Global Committed Version,若查询只指向单个服务器,该语句可直接使用服务器的 Global Committed Version 作为 Read Version,从而减少对全局时间戳的请求压力。
  • 批量获取优化:多个事务可以统一获取全局时间戳,通过提前发送请求来缩短事务提交的整体耗时。
🛡️ GTS 高可用架构

GTS 服务本身也是基于 Paxos 实现的高可用服务,具有多副本架构,确保服务的高可用性:

  • 副本架构:每个租户的 GTS 服务默认有三个副本,分布在不同的 OBServer 节点上,实际对外提供时间戳服务的是 GTS 的 Leader 副本。
  • Leader 运行位置:GTS 服务由租户级别 1 号日志流(Log Stream)的 Leader 提供,该 Leader 基于日志同步机制保证高可用性。时间来源于 Leader 上的持续化预分配区间。
-- 查看租户的 GTS 状态(V4.2.0+)
        -- 主租户显示当前已分配的 GTS 值,备租户显示当前可读位点(STS)值
        SELECT * FROM V$OB_TIMESTAMP_SERVICE;

        -- 系统租户下可查看所有租户的时钟
        SELECT TENANT_ID, TIMESTAMP, SOURCE_TYPE 
        FROM oceanbase.V$OB_TIMESTAMP_SERVICE;
💡 虽然 GTS 不依赖于物理时钟,但 OceanBase 集群仍然要求各节点之间的时间保持同步(偏差不超过 2 秒),否则可能影响选举模块的正常工作,建议通过 NTP 或 chrony 服务进行时间同步。

快照读与数据可见性

在 OceanBase 中,查询语句默认执行的是快照读(Snapshot Read),即读取当前语句开始前所有已提交事务的数据:

  • 语句级快照:每条 SELECT 语句在执行前都会获取最新的全局快照,仅能读到语句开始前已经提交的数据,避免脏读。
  • 写操作的当前读:UPDATE、DELETE、SELECT FOR UPDATE 等操作需要获取当前读,会等待目标行上的未提交事务完成后重新执行语句,确保在最新数据基础上修改。

事务隔离级别支持

OceanBase 数据库在不同兼容模式下支持不同的隔离级别,但底层实际只实现了两种隔离级别:

兼容模式支持的隔离级别实际行为
MySQL 模式 读已提交(Read Committed)
可重复读(Repeatable Read)
可串行化(Serializable)
可重复读实际使用的是可串行化,不会出现幻读异常。
Oracle 模式 读已提交(Read Committed)
可串行化(Serializable)
行为与 Oracle 数据库保持一致。

OceanBase 数据库默认的隔离级别为读已提交。在底层只实现了读已提交和可串行化两种隔离级别,当用户在 MySQL 模式中指定可重复读时,实际使用的是可串行化,不会出现幻读的异常情况。读已提交不会出现脏读,但可能出现不可重复读和幻读。

⚖️ 强一致读 vs 弱一致性读

OceanBase 数据库提供了两种一致性级别:

  • STRONG(强一致性读):默认模式。读取最新数据,请求路由给 Leader 副本。写操作始终为强一致性。
  • WEAK(弱一致性读):不要求读取最新数据,请求优先路由给 Follower 副本,配合 LDC 可实现就近读取,显著降低读延迟。
-- 通过 Hint 指定弱一致性读
        SELECT /*+READ_CONSISTENCY(WEAK) */ * FROM orders;

        -- 通过 Session 变量设置弱一致性读
        SET SESSION ob_read_consistency = 'WEAK';

弱一致性读提供有界旧保证(Bounded Staleness),即保证读到的数据最多落后于最新数据不超过指定时间(默认为 5 秒)。同时 OceanBase 还提供 单调读一致性保障,通过设置租户级配置项 enable_monotonic_weak_read(默认 True),可以保证弱一致性读的版本单调性,即客户端不会读到版本回退的数据。

多版本数据回收机制

OceanBase 通过转储(Minor Compaction)和合并(Major Compaction)机制来回收多版本数据:

  • undo_retention 配置项:用于控制系统应保留的多版本数据范围,在转储时控制多版本数据的回收,默认值为 1800 秒。当 undo_retention 值为 0 时,表示未开启多版本转储,转储文件仅保留当前最新版本的行数据。
  • 转储(Minor Compaction):将内存中 MemTable 的数据落盘为 Mini SSTable。转储时会根据 undo_retention 保留指定时间范围内的多版本数据。
  • 合并(Major Compaction):合并过程中会回收旧版本数据,Major SSTable 中不会存放多版本数据,这是释放存储空间的关键操作。
-- 查看多版本数据保留时间(秒)
        SHOW PARAMETERS LIKE 'undo_retention';

        -- 调整多版本保留时间(默认为 1800 秒)
        ALTER SYSTEM SET undo_retention = 3600;
⚙️ 建议不要将 undo_retention 设置过大,防止因保留过多版本导致 SSTable 数量超限,从而引发空间膨胀。
3.3 ACID 在分布式环境下的实现

OceanBase 作为一个原生分布式数据库,需要在分布式环境下完整实现事务的 ACID 特性。与传统单机数据库不同,分布式架构下数据分布在多个节点上,实现 ACID 面临更大的技术挑战。OceanBase 通过一系列创新机制,在保证强一致性的同时实现了高性能和高可用。

🎯 ACID 四特性概述
  • 原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败回滚。
  • 一致性(Consistency):事务执行前后,数据库始终处于一致性状态。
  • 隔离性(Isolation):并发执行的事务之间互不干扰。
  • 持久性(Durability):事务一旦提交,其对数据的修改是永久性的,即使发生故障也不会丢失。

原子性(Atomicity):优化版两阶段提交

OceanBase 通过改进的两阶段提交(2PC)协议保证分布式事务的原子性,确保跨多个参与者的操作要么全部提交,要么全部回滚。与传统 2PC 相比,OceanBase 实现了多项关键优化,将分布式事务的提交延迟减少到几乎只有一次交互的程度。

核心优化点
  • 协调者无状态设计:传统 2PC 中协调者在 PREPARE 和 COMMIT 阶段都会写日志,成为性能和可靠性瓶颈。OceanBase 的协调者不负责写任何日志,大幅减少了磁盘 IO 操作。
  • 引入 CLEAR 阶段:OceanBase 在传统 2PC 基础上引入了一个额外的 CLEAR 阶段,专门负责清理事务处理过程中产生的临时状态和数据,有助于系统更快地回收资源并减少对事务响应时间的影响。
  • 提前返回客户端:在 PREPARE 阶段收齐所有参与者的成功响应后,协调者直接向客户端返回事务提交成功,无需等待第二阶段完成,性能大幅提升。
性能提升效果

一个分布式事务,OceanBase 的两阶段提交协议需要 2 次 RPC(Prepare 和 Commit 请求)和 2 次 IO 耗时,而传统两阶段提交协议需要 2 次 RPC 和 4 次 IO 耗时,性能显著提升。事务持有行锁的时机并没有延长,对事务的响应时间没有负面影响。

⚡ 参与者持久化与故障恢复

Prepare 阶段参与者会将事务 Redo 日志持久化到 Clog 文件,并持有行锁直至事务 Commit 或 Rollback 完成。当事务在 Commit 阶段发生参与者宕机时,OceanBase 会通过 Paxos 组的日志同步保障事务最终一致性,无需人工介入。参与者重启后,可以判断日志是否已提交,进行 Replay 或 Abort。

-- 查看事务超时相关参数
        -- 单条 SQL 超时时间(默认 10 秒)
        SHOW PARAMETERS LIKE 'ob_query_timeout';

        -- 整个事务超时时间(默认 100 秒)
        SHOW PARAMETERS LIKE 'ob_trx_timeout';

        -- 事务空闲超时时间(默认 24 小时)
        SHOW VARIABLES LIKE 'ob_trx_idle_timeout';

        -- 修改事务超时时间为 200 秒
        ALTER SYSTEM SET ob_trx_timeout = '200s';

一致性(Consistency):全局时间戳与约束保障

OceanBase 通过全局时间戳服务(GTS)和完整性约束来保证数据的一致性。其中一致性是 ACID 的最终目的,原子性、隔离性和持久性是保证一致性的手段。

全局时间戳服务(GTS)

GTS(Global Timestamp Service)为每个租户独立启动,为所有事务分配全局唯一的读版本号和提交版本号,保证分布式环境下的外部一致性。

  • 全局单调递增:所有事务版本号全局有序,保证跨节点的事务顺序。
  • 高可用设计:GTS 本身基于 Paxos 实现,具有多副本架构。新 Leader 上任前会回放旧 Leader 的预分配区间日志,确保时间戳不回退。
  • 高性能优化:通过语句快照优化和批量获取优化,GTS 每秒可处理数百万次时间戳请求。

隔离性(Isolation):MVCC + 锁机制

OceanBase 基于多版本并发控制(MVCC)和行锁机制实现并发控制,实现读写不互斥和写写互斥,大幅提升系统并发能力。

MVCC 多版本并发控制

OceanBase 的 MVCC 依赖三大核心组件协同完成版本管理与一致性保障:

  • 数据版本链:每行数据每次更新都生成新版本,包含值(val)、版本号(ts)与事务 ID(txn),通过回滚指针串联形成版本链。
  • 事务表:内存中维护的事务状态管理表,记录每个事务的 ID、执行状态(RUNNING、PREPARE、COMMIT)以及对应的版本信息,用于判断数据版本的可见性与事务冲突处理。
  • 全局时间戳服务(GTS):负责为所有事务分配全局唯一的读版本号与提交版本号,是 MVCC 实现跨节点数据可见性的核心基础。
隔离级别与版本号获取策略

OceanBase 底层实际只实现了两种隔离级别,不同隔离级别下读取版本号的获取时机不同:

隔离级别读版本号获取时机异常防护 读已提交(RC) 每条 SQL 语句开始执行时从 GTS 获取 避免脏读,可能出现不可重复读和幻读 可串行化 事务开始时从 GTS 获取,整个事务所有读取操作使用同一版本号 避免脏读、不可重复读和幻读
💡 MySQL 模式中的可重复读实际是可串行化:当用户在 MySQL 模式中指定可重复读隔离级别时,实际使用的是可串行化,不会出现幻读的异常情况,比标准 MySQL 的可重复读更为严格。
锁机制:行锁与表锁

OceanBase 对所有修改的行加互斥锁,实现写-写互斥。读操作读取特定快照版本的数据,实现读写互不阻塞。

  • 行锁等待管理(Lock Wait Mgr):系统在内存中维护锁的等待关系,当一个锁被释放时,会唤醒其他正在等待该锁的事务。
  • 锁等待类型:Lock Wait Mgr 维护三种锁等待关系——行锁等待、表锁等待和事务等待。
  • 表锁支持:OceanBase 支持锁定单个表、多个表或表的分区和子分区。
-- 查看当前锁等待信息
        SELECT * FROM oceanbase.GV$OB_LOCKS;

        -- 查看事务锁等待关系
        SELECT * FROM oceanbase.GV$OB_TRX_LOCK_WAIT;

        -- 通过 DBA_OB_DEADLOCK_EVENT_HISTORY 查看死锁事件
        SELECT * FROM oceanbase.DBA_OB_DEADLOCK_EVENT_HISTORY;
分布式死锁检测

OceanBase 支持分布式死锁自动检测功能,能够自动发现并解决死锁问题。

  • 主动检测与消除:相比基于事务超时处理死锁的方案,分布式死锁自动检测能够及时发现并解决死锁,减少业务死锁对系统性能的影响。
  • 可观测性:用户可以查询 DBA_OB_DEADLOCK_EVENT_HISTORY 视图,获得过去 7 天发生过的所有死锁事件、参与这些事件的事务,以及哪个事务被最终 Kill 掉,从而优化业务逻辑,避免死锁发生。
  • 高级算法:OceanBase 推出了 LCL+ 算法,是一套"无需全局视图、消息轻量、单受害者解锁"的分布式死锁检测算法,针对混合死锁做了加速优化,相关成果已入选 ACM TOCS。

持久性(Durability):Redo Log + Paxos 多数派

OceanBase 通过 Redo Log 预写日志(WAL)和基于 Paxos 的多副本同步机制保证事务持久性。

WAL 机制

事务提交前,Redo 日志必须先持久化。传统数据库中,一个事务的 Redo 日志在提交时一次性生成并落盘。OceanBase 从 V3.x 开始支持实时日志写入,当一个事务的数据达到 2MB 时,会实时生成并提交 Redo 日志到 Clog 模块,以提升大事务的性能。

Paxos 多数派同步

每个 Redo 日志需要通过 Multi-Paxos 协议同步到多数派副本(如三副本中的两个)后,才被视为成功写入:

  • RPO=0:日志在多数派副本持久化成功才返回客户端成功,保证单副本故障不会导致数据丢失。
  • RTO<8s:当少数派副本发生故障时,Paxos 协议能保证剩余多数派副本继续提供服务,并在 8 秒内自动完成 Leader 切换。
  • PALF 日志系统:OceanBase V4.0.0 将日志服务抽象为 "Paxos Backed Append Only Log File System"(PALF),作为数据库的基础组件,统一满足事务系统的 WAL 需求和分布式系统的高可用需求。
读写分离与弱一致性读

OceanBase 提供了两种一致性级别:

  • STRONG(强一致性):读取最新数据,请求路由给 Leader 副本。写操作始终为强一致性。
  • WEAK(弱一致性):不要求读取最新数据,请求优先路由给 Follower 副本,配合 LDC 可实现就近读取,显著降低读延迟。

弱一致性读提供 有界旧保证(Bounded Staleness),通过租户级配置项 max_stale_time_for_weak_consistency 定义最大弱读落后时间(默认 5 秒),保证读取的数据不会落后于最新数据超过特定时间。同时还提供单调读一致性,通过设置 enable_monotonic_weak_read(默认 True)保证弱一致性读的版本单调性。

多版本数据回收机制

OceanBase 通过转储(Minor Compaction)和合并(Major Compaction)机制来回收多版本数据:

  • undo_retention 配置项:用于控制系统应保留的多版本数据范围,在转储时控制多版本数据的回收,默认值为 1800 秒。当 undo_retention 值为 0 时,表示未开启多版本转储,转储文件仅保留当前最新版本的行数据。
  • 转储(Minor Compaction):将内存中 MemTable 的数据落盘为 Mini SSTable。转储时会根据 undo_retention 保留指定时间范围内的多版本数据。
  • 合并(Major Compaction):合并过程中会回收旧版本数据,Major SSTable 中不会存放多版本数据,这是释放存储空间的关键操作。
-- 查看多版本数据保留时间(秒)
        SHOW PARAMETERS LIKE 'undo_retention';

        -- 调整多版本保留时间(默认为 1800 秒)
        ALTER SYSTEM SET undo_retention = 3600;
⚙️ 建议不要将 undo_retention 设置过大,防止因保留过多版本导致 SSTable 数量超限,从而引发空间膨胀。
🏆 4.x 版本架构演进总结
OceanBase 4.x 版本以日志流为基石,通过优化的两阶段提交协议、全局时间戳服务、MVCC 多版本并发控制以及基于 Paxos 的多副本同步,在分布式环境下完整实现了 ACID 特性。这些技术创新使 OceanBase 能够在保持高性能的同时,确保数据强一致性和系统高可靠性。
3.4 分布式死锁检测与事务超时处理

在分布式数据库环境中,事务可能涉及多个节点,锁等待关系跨节点分布,使得死锁检测比单机环境更加复杂。OceanBase 通过基于锁链长度的分布式死锁检测算法(LCL/LCL+)以及多层级超时控制机制,实现了对死锁和悬挂事务的自动发现与处理,在保证 ACID 属性的同时,最大限度地降低了对业务性能的影响。

🎯 死锁检测 vs 超时机制:互补的双重保障

OceanBase 提供了两种互补的死锁/悬挂事务处理策略:

  • 主动死锁检测:通过 LCL/LCL+ 算法主动发现死锁环路,并只终止环中一个事务作为受害者,相比单纯的超时机制能更快发现并解决死锁,减少业务阻塞。
  • 兜底超时机制:当死锁检测未能及时处理,或事务因其他原因(如空闲、超长执行)持续占用资源时,多层超时参数(空闲超时、语句超时、事务超时、锁等待超时)作为兜底策略,确保系统资源不会被永久占用。

两种机制协同工作,既保证了死锁发现的及时性,又提供了多层次的防御纵深

分布式死锁检测算法:LCL 到 LCL+ 的演进

LCL 算法:解决多出度场景的死锁检测

在 OceanBase 3.x 时代,团队提出了基于锁链长度(Lock Chain Length,LCL)的分布式死锁检测与消除算法,旨在克服 M&M 算法和 CMH 算法的固有局限:

  • M&M 算法:简单且完全分布式,但其核心假设是“每个事务一次只等待一个资源”,无法处理现代分布式数据库中的多出度(multiple out-degree)场景(如并行查询中一个事务同时等待多个资源)。
  • CMH 算法(Chandy-Misra-Haas):采用“边追逐”机制通过探测消息发现环路,但存在多受害者缺陷——同一个死锁环路中多个并发探测可能导致多个事务被错误终止,与 OceanBase 所要求的“单受害者”原子性解决范式冲突。

LCL 算法通过维护每个等待节点的锁链长度(Lock Chain Length Value),该值会沿依赖链单向传播:有环时环内 LCLV 不断增大,无环路径则收敛到上界。算法通过三个核心阶段实现纯分布式的死锁检测:

  • Stage 1:Proliferation(繁殖)——沿依赖边单向传播并递增 LCLV,使无环路径收敛、环内数值持续上升,从数值上区分环与非环。
  • Stage 2:Spread(扩散)——同步环内的最大 LCLV,并基于预设“终止优先级”(如时间戳/事务优先级)选出唯一受害者。
  • Stage 3:Detection(确认)——满足“三等式”条件时,确认该节点即为受害者,随后仅终止一个事务即可打破死锁,严格实现单受害者解锁范式。
LCL+ 算法:加速混合死锁检测

随着分布式架构的演进,OceanBase 4.x 面临新的挑战:混合死锁(Hybrid Deadlock)——死锁环路中同时包含同一服务器内的本地等待关系和跨服务器的分布式等待关系。LCL 算法对本地和分布式等待一视同仁,导致快速的本地依赖关系被慢速的分布式协议拖累,形成性能瓶颈。

为解决这一问题,OceanBase 团队在 LCL 基础上推出了 LCL+ 算法——一套“无需全局视图、消息轻量、单受害者解锁”的分布式算法,其核心创新在于引入 Stage-0 预繁殖(Pre-proliferation)阶段,在算法早期就即时识别本地/混合死锁并加速处理。LCL+ 算法已在 OceanBase 4.x 中落地部署,并在 ACM Transactions on Computer Systems(ACM TOCS)顶刊发表论文。

💡 分布式死锁检测的开销与适用性
开启分布式死锁自动检测会消耗约 2% 的系统性能[reference:11]。对于 V4.0 及以上版本,系统默认开启分布式死锁自动检测[reference:12]。如果您的集群版本低于 V4.0,或出于特殊需求需要关闭该功能,可以通过 OCP 平台进行管理。社区版 OCP 暂不支持死锁检测功能。

多层级超时控制体系

OceanBase 提供了完整的多层级超时控制机制,作为死锁检测的兜底策略,同时也用于处理非死锁场景下的悬挂事务和长事务:

1. 空闲超时(Idle Timeout)——ob_trx_idle_timeout

控制事务内两条 SQL 之间的最大空闲间隔。当一个事务中的两条 SQL 间隔超过该阈值时,OceanBase 会强制终止会话并回滚事务[reference:13]。

  • 默认值:86400000000 微秒,即 24 小时[reference:14]。
  • 作用范围:租户级系统变量。
  • 应用场景:解决长事务、悬挂事务问题,避免事务因应用程序异常(如网络中断后未正确提交)而长时间占用锁资源。
⚠️ 该变量不建议轻易修改,如确有需求,建议联系 OceanBase 技术支持进行评估。
2. 语句超时(Query Timeout)——ob_query_timeout

控制单条 SQL 语句在 OBServer 端的最大执行时间。如果一条 SQL 执行时间超过该阈值,OBServer 会返回错误码 4012[reference:15]。

  • 默认值:10000000 微秒,即 10 秒[reference:16]。
  • 作用范围:会话级变量。
  • 应用场景:防止慢查询长期占用资源,影响系统整体性能。
3. 事务超时(Transaction Timeout)——ob_trx_timeout

控制整个事务从开始到结束的最大持续时间(包含空闲时间和活跃时间)。如果事务持续时间超过该阈值仍未结束,后续 SQL 将收到 Transaction is timeout 错误,事务会被回滚[reference:17]。

  • V3.x 默认值:100000000 微秒,即 100 秒
  • V4.x 默认值:86400000000 微秒,即 1 天[reference:18]
  • 作用范围:会话级变量。
  • 超时后行为:如果超时发生时事务尚未提交,OBServer 会回滚该事务;如果发生在提交阶段,由于事务状态未定,系统不会主动回滚[reference:19]。
-- 查看当前超时参数设置
        SHOW VARIABLES LIKE 'ob_trx_idle_timeout';
        SHOW VARIABLES LIKE 'ob_query_timeout';
        SHOW VARIABLES LIKE 'ob_trx_timeout';

        -- 修改超时参数示例(会话级别)
        SET ob_trx_timeout = 200000000;   -- 设置为 200 秒
        SET ob_query_timeout = 30000000;  -- 设置为 30 秒
📌 超时参数的层级关系与配置建议

建议遵循 ob_query_timeout < ob_trx_idle_timeout < ob_trx_timeout 的层级关系:

  • 语句超时应小于事务空闲超时:即使是最复杂的查询,也应比事务内的两次操作间隔更快完成。
  • 事务空闲超时应小于事务总超时:如果一个事务空闲太久,应该先被空闲超时机制回收,而不是等事务整体超时。

超时触发的优先级:通常是空闲超时最优先触发,然后才是事务超时,两者属于不同维度的超时机制,通常只有一个会先被触发。

4. 锁等待超时——ob_trx_lock_timeout

控制事务在等待行锁释放时的最长等待时间。当事务因锁冲突需要等待时,该参数决定了等待的上限。

  • 默认值:-1,表示不生效,关闭超时机制[reference:20]。
  • 取值范围:[0, +∞),单位为微秒。0 表示 NO WAIT[reference:21]。
  • 生效逻辑:当 ob_trx_lock_timeout 为 -1 时,行锁冲突等待时间使用 ob_query_timeout 的值;当为非 -1 值时,等待时间为 ob_trx_lock_timeoutob_query_timeout 中的较小值。
-- 设置 NO WAIT 模式(等待锁时立即返回错误)
        SET ob_trx_lock_timeout = 0;

        -- 设置锁等待超时为 3 秒
        SET ob_trx_lock_timeout = 3000000;
5. 悬挂事务处理——_xa_timeout

OceanBase 提供了隐藏配置项 _xa_timeout,用于控制悬挂事务(XA 事务)的自动清理机制。当事务停留时间超过该阈值(默认 300 秒)时,事务会被自动回滚,避免悬挂事务持续阻塞其他操作[reference:22]。

-- 查看悬挂事务超时设置(需 sys 租户)
        SHOW PARAMETERS LIKE '_xa_timeout';

        -- 调整悬挂事务超时时间
        ALTER SYSTEM SET _xa_timeout = '30s';
⚙️ 超时参数默认值快速参考
空闲超时 ob_trx_idle_timeout = 24 小时
语句超时 ob_query_timeout = 10 秒
事务超时 ob_trx_timeout = V4.x 为 1 天,V3.x 为 100 秒
锁等待超时 ob_trx_lock_timeout = -1(不生效,默认使用 ob_query_timeout

死锁可观测性:监控视图与历史记录

OceanBase 提供了完善的锁监控与死锁事件记录视图,帮助 DBA 快速定位和排查死锁问题。

🔍 GV$OB_LOCKS:实时锁状态监控

GV$OB_LOCKS 视图(V4.2.0+ 引入)展示当前用户各表持锁或请求锁的情况,是实时排查锁阻塞问题的核心视图[reference:23]。

  • 锁类型(TYPE):TM(表锁)、TX(事务锁)、TR(行锁)[reference:24]。
  • CTIME:持有或等待锁的时间(微秒)。
  • BLOCK:0 表示当前事务持有锁,1 表示当前事务被阻塞等待锁。
  • 锁模式(LMODE/REQUEST):RS(行共享)、RX(行独占)、S(共享)、X(独占)等[reference:25]。
-- 查看当前锁阻塞情况,找出被阻塞的事务及等待时间
        SELECT svr_ip, svr_port, trans_id, type, ctime, block
        FROM oceanbase.GV$OB_LOCKS
        WHERE block = 1;

        -- 查看特定租户的事务锁等待详情
        SELECT svr_ip, trans_id, type, lmode, request, ctime
        FROM oceanbase.GV$OB_LOCKS
        WHERE tenant_id = 1002 AND block = 1;
📜 DBA_OB_DEADLOCK_EVENT_HISTORY:死锁事件历史记录

DBA_OB_DEADLOCK_EVENT_HISTORY 视图(V4.0.0+ 引入)展示当前租户下死锁事件的历史记录,可查询过去 7 天内发生过的所有死锁事件、参与这些事件的事务,以及该死锁事件中被回滚的事务[reference:26][reference:27]。

-- 查看当前租户的死锁历史记录
        SELECT event_id, report_time, cycle_size, 
            role, priority_level, priority,
            object, visitor
        FROM oceanbase.DBA_OB_DEADLOCK_EVENT_HISTORY
        ORDER BY report_time DESC
        LIMIT 10;

        -- 从系统租户查看所有租户的死锁事件
        SELECT * FROM oceanbase.CDB_OB_DEADLOCK_EVENT_HISTORY
        ORDER BY report_time DESC;
📊 视图关键字段说明
  • EVENT_ID:死锁事件的唯一编号
  • ROLE:该节点在死锁事件中的角色(witness/victim)
  • CYCLE_SIZE:参与该死锁事件的节点数量(死锁环大小)
  • PRIORITY_LEVEL:优先级等级(extreme-low/low/normal/high/extreme-high)[reference:28]
  • OBJECT:被锁定的资源对象(如行键 row_key)[reference:29]

长事务与悬挂事务排查指南

当系统出现锁阻塞或性能问题时,可通过以下步骤排查长事务和悬挂事务:

1. 查询当前运行中的长事务
-- 查看当前租户中运行超过阈值的事务
        SELECT trans_id, tenant_id, svr_ip, svr_port, 
            trans_start_time, trans_timeout, state
        FROM oceanbase.GV$OB_TRANSACTION
        WHERE trans_start_time < DATE_SUB(NOW(), INTERVAL 30 MINUTE);
2. 定位阻塞源头
-- 找出当前被阻塞的事务及持有锁的事务
        SELECT blocked.trans_id AS blocked_trans,
            holder.trans_id AS holder_trans,
            blocked.ctime AS wait_time_us
        FROM oceanbase.GV$OB_LOCKS blocked
        JOIN oceanbase.GV$OB_LOCKS holder 
        ON blocked.object = holder.object
        WHERE blocked.block = 1 
        AND holder.block = 0;
3. 强制终止阻塞事务
-- 找出阻塞事务的 session_id 并 kill
        SELECT * FROM oceanbase.GV$OB_PROCESSLIST WHERE trans_id = [blocked_trans_id];
        KILL [session_id];
⚡ 运维最佳实践
  • 开启死锁自动检测:V4.x 默认已开启,无需额外配置。通过 OCP 平台可查看死锁分析报告,优化业务逻辑避免死锁。
  • 合理设置超时参数:根据业务特性(OLTP/OLAP)调整 ob_query_timeoutob_trx_timeout。OLTP 场景建议保持较小值,OLAP 场景可适当调大。
  • 定期监控长事务:通过 GV$OB_TRANSACTION_PARTICIPANTS 视图定期巡检,及时发现超过阈值的未提交事务[reference:30]。
  • 应用层防死锁:尽量避免跨多个分区的复杂事务,统一事务内获取锁的顺序,在 SQL 中使用 FOR UPDATE NOWAIT 快速失败并重试。
  • 避免热点行竞争:通过分区键设计将高并发写入分散到不同分区,减少行锁竞争的概率。
🏆 4.x 版本事务超时机制演进总结
OceanBase 4.x 在事务超时方面进行了显著优化:事务超时默认值从 V3.x 的 100 秒调整为 1 天,以适应更复杂的业务场景;同时引入了更完善的空闲事务自动清理机制。在死锁检测方面,LCL+ 算法的引入彻底解决了混合死锁检测效率问题,相关研究成果已发表于 ACM TOCS 顶刊。

第4章:备份恢复与高可用

4.1 物理备份架构

OceanBase 数据库提供了在线物理备份功能,由日志归档数据备份两大核心子功能组成。日志归档持续维护租户产生的日志,提供了准实时的备份能力;数据备份则维护快照点的备份。两者结合,可以实现将租户恢复到备份位点之后的任意时间点(PITR)。物理备份以租户为粒度进行,支持系统租户或用户租户发起备份任务,但不支持对 sys 租户和 Meta 租户进行备份[reference:14]。

🎯 物理备份的核心设计原则
  • 租户级隔离:备份和恢复以租户为单位,各租户的备份数据独立管理,互不干扰。
  • 日志+数据双重保障:日志归档保证增量数据的持续备份,数据备份提供全量/增量基线数据,两者结合实现任意时间点恢复。
  • 准实时备份能力:日志备份周期默认为 2 分钟,提供了准实时的数据保护能力。

日志归档

OceanBase 数据库提供了租户级别的日志归档能力,是物理备份的核心组成部分之一。

核心机制
  • 日志流 Leader 负责归档:日志归档的工作由租户内每个日志流(Log Stream)的 Leader 副本负责,按照日志流进行物理备份,是 Log Entry 级别的备份。
  • 准实时备份:日志备份的周期默认为 2 分钟,提供了准实时的备份能力,数据丢失风险窗口极小。
  • Piece 分片管理:系统默认按 24 小时拆分目录的功能,方便用户对于备份数据的管理。每个 Piece 包含该时间窗口内的所有日志,这种分片设计使得 PITR 可以快速定位到目标时间所在的 Piece,避免扫描全部历史日志[reference:15]。
  • V4.x 架构优化:与之前版本(V2.2.x 和 V3.x)的 OceanBase 数据库相比,V4.x 的日志归档不再基于分区级别的日志管理,大幅降低了归档时产生的 IO 次数,有效降低了对备份介质的性能要求[reference:16]。
-- 配置日志归档路径(sys 租户为指定租户配置)
        ALTER SYSTEM SET LOG_ARCHIVE_DEST='LOCATION=file:///obbackup/tenant1/clog
            BINDING=Optional PIECE_SWITCH_INTERVAL=1d' TENANT=tenant1;

        -- 配置日志归档路径(用户租户执行)
        ALTER SYSTEM SET LOG_ARCHIVE_DEST='LOCATION=file:///obbackup/clog 
            BINDING=Optional PIECE_SWITCH_INTERVAL=1d';

        -- 启动日志归档
        ALTER SYSTEM ARCHIVELOG;

        -- 停止日志归档
        ALTER SYSTEM NOARCHIVELOG;
📌 BINDING 模式说明
  • MANDATORY:表示备份优先。当日志归档速度跟不上日志生成速度时,用户写入会被阻塞,优先保证归档的完整性。
  • OPTIONAL:表示写入优先。当日志归档速度落后时,不会阻塞用户写入,但可能增加异常时数据的丢失风险。

数据备份

OceanBase 数据库提供了租户级别的数据备份能力,包括全量备份和增量备份两种类型。数据备份的流程由 RootService 节点调度,并按照日志流进行备份[reference:17]。

全量备份

全量备份是指备份所有需要作为基线数据存储的宏块(Macroblock)。备份数据包括分区的元信息(内存序列化后的值)和宏块数据(物理备份的核心内容)。

增量备份

增量备份是从上一次全量备份或增量备份开始,备份所有修改后的宏块,即仅针对已备份数据的增量部分进行备份。

  • 备份逻辑:如果租户最近一次是全量备份,系统会备份最近一次全量备份之后修改的所有宏块;如果最近一次是增量备份,则备份最近一次增量备份之后修改的所有宏块。
  • 转换规则:如果在不存在全量数据备份的情况下直接发起增量备份,系统默认会将增量备份转换为全量备份。
  • 升级场景处理:对于低版本集群升级到高版本集群的场景,即使当前租户在低版本中已经执行了全量备份,升级后直接发起增量备份,系统默认也会将增量备份转换为全量备份。
核心机制
  • 全局唯一逻辑标识:OceanBase 数据库的基线宏块具有全局唯一的逻辑标识,这个逻辑标识提供了增量备份重用宏块的能力。增量备份的恢复和全量备份的恢复在流程上基本一致,性能上也没有差别,只是会根据逻辑标识在不同的 backup_set 之间读取宏块。
  • 优先选择 Follower 节点:在 V4.x 版本中,OceanBase 数据库在选择备份服务器时会优先考虑日志流的 Follower 节点,这样可以减轻日志流 Leader 节点的压力,提高备份效率。具体表现在,系统会首先选择所有符合条件的 Follower 节点进行备份,只有在必要时才会选择 Leader 节点,确保备份过程更加高效和稳定。
  • V4.x 架构优化:与之前版本(V2.2.x 和 V3.x)相比,V4.x 的数据备份去掉了数据快照点保留的依赖,在数据备份期间发起 Major Freeze 不会再造成节点空间的膨胀。
-- 发起全量备份(sys 租户为指定租户发起)
        ALTER SYSTEM BACKUP TENANT = tenant1;

        -- 发起全量备份(sys 租户对所有租户发起)
        ALTER SYSTEM BACKUP DATABASE;

        -- 发起增量备份(sys 租户为指定租户发起)
        ALTER SYSTEM BACKUP INCREMENTAL TENANT = tenant1;

        -- 发起增量备份(sys 租户对所有租户发起)
        ALTER SYSTEM BACKUP INCREMENTAL DATABASE;

备份目的端支持

OceanBase 数据库支持两种类型的备份目的端存储介质:

  • Network File System (NFS):网络文件系统,适用于本地或同城备份场景。
  • Object Storage Service (OSS):对象存储服务(如阿里云 OSS、AWS S3 等),适用于云上备份场景,支持跨地域容灾。
💡 配置备份路径时,必须确保每个租户的归档路径是一个独立的空目录,不同租户不能使用相同的归档路径。

备份目录结构

OceanBase 数据库在备份目的端会创建规范的目录结构来组织备份数据:

backup/                          # 备份根目录
        └── ob1                          # 集群名称
            └── 1                        # 集群 ID
                └── incarnation_1        # 分身 ID
                    ├── 1001             # 租户 ID
                    │   ├── clog/        # 日志备份根目录
                    │   │   ├── 1/       # 日志备份轮次 ID
                    │   │   │   ├── data/      # 日志数据文件
                    │   │   │   └── index/     # 日志索引文件
                    │   │   └── tenant_clog_backup_info  # 日志备份元信息
                    │   └── data/        # 数据备份根目录
                    │       ├── backup_set_1/       # 第 1 次全量备份
                    │       │   ├── backup_1/       # 第 1 次增量备份
                    │       │   ├── backup_2/       # 第 2 次增量备份
                    │       │   ├── backup_set_info/
                    │       │   └── data/           # 宏块数据
                    │       └── tenant_data_backup_info/
                    ├── clog_info/                 # Server 启动日志备份信息
                    ├── cluster_clog_backup_info/  # 集群级日志备份元信息
                    ├── cluster_data_backup_info/  # 集群级数据备份元信息
                    └── tenant_info/              # 租户信息
                        └── tenant_name_info/     # 租户名称与 ID 映射关系

更多目录结构说明,请参见备份架构章节中的完整目录树。

配置参数详解

V4.x 版本对物理备份恢复功能进行了架构优化调整,相关配置项也发生了重要变化:

1. 日志归档并发度——log_archive_concurrency

用于设置日志归档的并发度,该配置项从 V4.1.0 版本开始生效范围调整为租户级。

  • 默认值:0,表示使用 OceanBase 数据库自适应的日志归档并行度。从 V4.2.0 版本开始默认值从 1 调整为 0[reference:18]。
  • 取值范围:[0, 100],从 V4.2.0 版本开始取值范围由 [1,100] 调整为 [0,100][reference:19]。
  • 自适应规则:归档工作线程数根据租户 max_cpu 自适应值计算:若 max_cpu ≤ 8,则归档线程数 = max_cpu;若 8 < max_cpu < 32,则归档线程数 = max_cpu / 2(最小值为 8);若 max_cpu ≥ 32,则归档线程数 = max_cpu / 4(最小值为 16)。
  • 生效方式:修改后无需重启 OBServer 节点,立即生效[reference:20]。
-- 在 sys 租户中修改指定租户的日志归档并发度
        ALTER SYSTEM SET log_archive_concurrency = 10 TENANT = mysql_tenant;

        -- 在 sys 租户中修改所有租户的日志归档并发度
        ALTER SYSTEM SET log_archive_concurrency = 10 TENANT = all_user;

        -- 在用户租户中修改本租户的日志归档并发度
        ALTER SYSTEM SET log_archive_concurrency = 10;
2. 备份线程并发度——ha_low_thread_score

V4.x 版本中,根据任务的优先级和重要程度,原有的 backup_concurrency 配置项(用于设置备份时写文件系统的并发数量)从 V4.0.0 版本开始删除,被细分为三个高可用相关的配置项[reference:21]。

  • ha_high_thread_score:用于控制复制、Rebuild 和恢复等高可用高优先级线程的工作线程数[reference:22]。
  • ha_mid_thread_score:用于控制迁移任务等高可用中优先级线程的工作线程数。
  • ha_low_thread_score:用于控制备份、备份清理等高可用低优先级线程的工作线程数。默认值为 0,取值范围为 [0, 100]。在开始备份前,可以适当提高该值,建议每次将数值翻倍。
-- 设置集群中所有租户的备份并发度
        ALTER SYSTEM SET ha_low_thread_score = 10 TENANT = all_user;

        -- 设置集群中指定租户的备份并发度
        ALTER SYSTEM SET ha_low_thread_score = 10 TENANT = mysql_tenant;
3. 网络带宽限制——sys_bkgd_net_percentage

集群级配置项,用于设置后台系统任务(包含备份恢复任务)可占用的网络带宽百分比,默认值为机器网卡速率的 60%。带宽满时,在不影响业务请求的前提下,可以适当调大该值。

-- 查看当前网络带宽限制配置
        SHOW PARAMETERS LIKE 'sys_bkgd_net_percentage';

        -- 调整后台任务网络带宽占比为 70%
        ALTER SYSTEM SET sys_bkgd_net_percentage = 70;
4. 备份相关的配置项在 V4.x 版本中的演进
配置项V3.x 状态V4.x 状态替代方案
backup_concurrency 控制备份写文件系统并发数 从 V4.0.0 开始删除 ha_low_thread_score(备份、备份清理等低优先级任务)[reference:23]
restore_concurrency 控制恢复最大并发度 从 V4.0.0 开始删除 ha_high_thread_score(恢复等高优先级任务)[reference:24]和 log_restore_concurrency[reference:25]
log_archive_concurrency 集群级配置 V4.1.0 起调整为租户级,V4.2.0 起默认值改为 0 租户级自适应或手动指定

自动清理策略

OceanBase 数据库提供了当前配置路径下自动清理的功能,由 RootService 定期检查用户配置的备份清理策略,从而删除不需要的数据备份。在删除数据备份的同时,系统会自动根据保留的数据备份中最小的回放位点来删除不需要的日志备份。

-- 设置备份保留策略(保留最近 7 天的备份)
        ALTER SYSTEM SET BACKUP_RETENTION_POLICY = 'RETENTION 7 DAYS';

        -- 通过回收窗口设置保留策略
        ALTER SYSTEM SET BACKUP_RETENTION_POLICY = 'RECOVERY_WINDOW 7 DAYS';

        -- 查看备份清理策略
        SHOW PARAMETERS LIKE 'backup_retention%';

        -- 手动触发清理
        ALTER SYSTEM DELETE BACKUP;
💡 在 V4.x 版本中,不再支持通过配置项 backup_recovery_window 来设置备份数据可恢复的时间窗口,需使用 ALTER SYSTEM 语句进行设置[reference:26]。
🏆 V4.x 物理备份架构核心演进
日志归档不再基于分区:大幅降低归档 IO 次数,减轻备份介质性能要求。
数据备份去掉快照点依赖:Major Freeze 不再造成节点空间膨胀,备份过程更稳定。
优先选择 Follower 节点:减轻 Leader 节点压力,提高备份效率。
物理备份功能演进:自 V4.1.0 起,物理备份功能持续优化,支持更灵活的并发控制和更完善的监控视图。
4.2 备份恢复操作实战

OceanBase 数据库的物理备份操作需遵循"先配置、再开启、后执行"的科学流程:首先配置日志归档与数据备份的目的端,然后开启归档模式,最后才能执行数据备份。本节将通过实例,详细讲解从配置备份路径、启动归档、执行备份,到最终恢复租户数据的完整操作流程。V4.x 版本已将物理备份的配置和调度下沉到租户级别,相关 SQL 和视图也相应进行了重大调整,以下操作均基于 V4.x 规范。

💡 操作流程概览

本文围绕以下核心步骤展开,用户可对照实施:

  1. 恢复前准备:在目标集群为待恢复的新租户创建资源池等。
  2. 配置备份目的端:分别为租户设置日志归档路径和数据备份路径。
  3. 开启归档模式:执行 ARCHIVELOG 为租户开启归档。
  4. 执行数据备份:根据需求发起全量或增量数据备份。
  5. 监控备份进度:通过系统视图跟踪备份任务进度。
  6. 执行恢复:利用备份集和归档日志执行 PITR 恢复。
  7. 确认恢复结果:检查恢复后的租户状态和数据一致性。
  8. 设置清理策略:配置备份数据的自动过期清理。

1. 恢复前准备(针对恢复操作)

在进行物理恢复前,必须为目标租户准备充足的资源。

  • 检查原租户信息:记录原始租户的资源配置(Unit Config)、资源池、`PRIMARY_ZONE`、`LOCALITY` 等信息。
  • 创建目标资源:在目标集群中创建与原租户相同或更高的资源单元和资源池。
  • 确保版本兼容:确认备份集的版本与目标集群的版本相互兼容(详阅官方"恢复前准备"章节)。
-- 1. 创建资源单元(需符合集群现有资源规格)
        CREATE RESOURCE UNIT restore_unit MAX_CPU 8, MEMORY_SIZE '16G';

        -- 2. 创建资源池(分布在三个 Zone)
        CREATE RESOURCE POOL restore_pool UNIT='restore_unit', UNIT_NUM=1, ZONE_LIST=('zone1','zone2','zone3');

2. 配置备份与归档目的端

V4.x 版本不再支持通过配置项(如 backup_dest)来设置备份目的端,而是使用 ALTER SYSTEM 语句为租户独立配置路径。

日志归档路径(LOG_ARCHIVE_DEST)

日志归档路径的配置支持 LOCATIONBINDINGPIECE_SWITCH_INTERVAL 参数。

  • LOCATION:指定备份路径,支持 NFS(file://)或 OSS 等对象存储,且路径必须为独立空目录,不同租户不可共用。
  • BINDING:设置归档与业务的优先模式。MANDATORY 表示归档失败会阻塞用户写入(强同步),OPTIONAL 则不会阻塞。
  • PIECE_SWITCH_INTERVAL:按天切分 Piece 目录,方便管理 PITR。
-- 系统租户为业务租户 mysql_tenant 配置归档路径
        ALTER SYSTEM SET LOG_ARCHIVE_DEST='LOCATION=file:///data/backup/tenant1/clog BINDING=Optional PIECE_SWITCH_INTERVAL=1d' TENANT=mysql_tenant;

        -- 用户租户为自身配置归档路径
        ALTER SYSTEM SET LOG_ARCHIVE_DEST='LOCATION=file:///data/backup/tenant1/clog BINDING=Optional PIECE_SWITCH_INTERVAL=1d';
-- 查看租户的归档路径配置(系统租户查看所有租户)
        SELECT TENANT_ID, DEST_ID, VALUE FROM oceanbase.CDB_OB_ARCHIVE_DEST;

        -- 用户租户查看本租户的归档配置
        SELECT DEST_ID, NAME, VALUE FROM oceanbase.DBA_OB_ARCHIVE_DEST;
数据备份路径(DATA_BACKUP_DEST)

数据备份路径的配置需指定存储介质与路径。目前支持 NFS阿里云 OSS 等对象存储。

-- 系统租户为 mysql_tenant 配置 NFS 备份路径
        ALTER SYSTEM SET DATA_BACKUP_DEST = 'file:///data/backup/data' TENANT = mysql_tenant;

        -- 系统租户为 mysql_tenant 配置 OSS 备份路径
        ALTER SYSTEM SET DATA_BACKUP_DEST = 'oss://oceanbase-test-bucket/backup/data?host=xxx.aliyun-inc.com&access_id=****&access_key=****' TENANT = mysql_tenant;

        -- 用户租户为自身配置备份路径
        ALTER SYSTEM SET DATA_BACKUP_DEST = 'file:///data/backup/data';
⚠️ 注意:在 V4.x 版本中,必须为租户分别配置日志归档路径和数据备份路径,且路径必须是独立于其他租户的空目录,以避免冲突和权限问题。

3. 开启日志归档(ARCHIVELOG)

完成归档路径配置后,必须为租户开启 归档模式(ARCHIVELOG),数据备份才能进行。归档模式的开启权限要求为 root@sys 或各租户的管理员用户。

-- 系统租户为集群内所有用户租户开启归档
        ALTER SYSTEM ARCHIVELOG TENANT = all;

        -- 系统租户为特定租户 mysql_tenant 开启归档
        ALTER SYSTEM ARCHIVELOG TENANT = mysql_tenant;

        -- 用户租户为本租户开启归档
        ALTER SYSTEM ARCHIVELOG;

如需关闭归档模式,可使用 NOARCHIVELOG 命令。但请注意,关闭后,正在进行的归档任务将停止,且无法发起数据备份。

-- 系统租户为用户租户 mysql_tenant 关闭归档模式
        ALTER SYSTEM NOARCHIVELOG TENANT = mysql_tenant;
📌 状态检查与后续保障

在开启归档后,务必通过视图确认归档任务已进入正常状态。以下任一方法均可检查归档进度:

  • SELECT * FROM oceanbase.DBA_OB_ARCHIVELOG;
  • 查看 cdb_ob_archive_dest 中归档目的地的 state 是否为 ENABLE。
  • 对于恢复操作,需确保归档路径持续完好,以支持 PITR。

4. 发起数据备份(BACKUP DATABASE / TENANT)

V4.x 版本的数据备份分为全量备份增量备份两种类型。在执行数据备份前,务必确保目标租户的归档模式已开启且处于 DOING 状态。

全量备份(Full Backup)
  • 系统租户为所有用户租户发起全量备份:ALTER SYSTEM BACKUP DATABASE;
  • 系统租户为指定租户 mysql_tenant 发起全量备份:ALTER SYSTEM BACKUP TENANT = mysql_tenant;
  • 用户租户为本租户发起全量备份:ALTER SYSTEM BACKUP DATABASE;
-- 系统租户仅为租户 mysql_tenant 发起全量数据备份
        ALTER SYSTEM BACKUP TENANT = mysql_tenant;

        -- 用户租户(mysql_tenant 管理员)为本租户发起全量备份
        ALTER SYSTEM BACKUP DATABASE;
增量备份(Incremental Backup)

备份上一次备份后修改的宏块。如果在没有全量数据备份的情况下直接发起增量备份,系统会将增量备份转换为全量备份。同样,集群升级到高版本后,即使低版本中已执行全量备份,直接发起增量备份默认也会转为全量备份。

-- 系统租户为租户 mysql_tenant 发起增量备份
        ALTER SYSTEM BACKUP INCREMENTAL TENANT = mysql_tenant;

        -- 用户租户为本租户发起增量备份
        ALTER SYSTEM BACKUP INCREMENTAL DATABASE;
⚡ 高级功能:PLUS ARCHIVELOG

在发起备份时,可以通过指定 PLUS ARCHIVELOG 关键字,在备份数据的同时将归档日志一并备份,形成一个"全量数据+日志"的完整数据集。使用该数据集恢复时无需额外的归档日志,可直接恢复到该备份集的 MIN_RESTORE_SCN 位点。适用于需要独立存储完整恢复集的场景。

-- 全量备份 + 日志
        ALTER SYSTEM BACKUP TENANT = mysql_tenant PLUS ARCHIVELOG;

5. 监控数据备份与归档进度

OceanBase 提供了丰富的监控视图,帮助 DBA 实时跟踪备份任务。

监控数据备份
  • JOB 级任务(CDB_OB_BACKUP_JOBS / DBA_OB_BACKUP_JOBS):每次备份命令对应一条 JOB 记录,记录任务整体状态。STATUS 字段反映了当前任务的执行阶段,RESULTCOMMENT 字段用于记录执行结果和错误信息,便于排查。
  • TASK 级任务(CDB_OB_BACKUP_TASKS / DBA_OB_BACKUP_TASKS):每个备份集对应一个 TASK 记录,包含更细粒度的进度,如已备份的宏块数(FINISH_MACRO_BLOCK_COUNT),以及数据/日志进度百分比。
-- 系统租户查看所有租户的 JOB 级备份进度
        SELECT * FROM oceanbase.CDB_OB_BACKUP_JOBS\G

        -- 用户租户查看本租户的 TASK 级备份进度
        SELECT TASK_ID, STATUS, START_TIMESTAMP, FINISH_MACRO_BLOCK_COUNT, DATA_PROGRESS 
        FROM oceanbase.DBA_OB_BACKUP_TASKS;
监控日志归档

通过 CDB_OB_ARCHIVELOG 视图可查看每个租户每个日志流的归档位点和 Piece 状态。对于租户级监控,可使用 DBA_OB_ARCHIVELOG 视图。

-- 系统租户查看日志归档进度
        SELECT TENANT_ID, LS_ID, BEGIN_SCN_DISPLAY, END_SCN_DISPLAY, STATUS 
        FROM oceanbase.CDB_OB_ARCHIVELOG;
⚡ 进阶说明:在归档或数据备份过程中,如果遇到异常(如备份介质不可达),可以通过 ALTER SYSTEM CANCEL BACKUPNOARCHIVELOG 取消对应任务。取消备份任务前,请先确认业务影响面。

6. 执行物理恢复(RESTORE)

物理恢复是一种租户级操作,既支持在同集群内恢复,也支持跨集群恢复。OceanBase 数据库支持租户级别的基于时间点的全量恢复/快速恢复。

核心要点
  • 恢复模式full(全量恢复)或 quick(快速恢复)。
  • 权限要求:必须由 root@sys 租户执行 RESTORE 命令。
  • 恢复类型:恢复出的租户默认为备租户,如需提供读写服务,需执行 ACTIVATE STANDBY 将其转为主租户。
  • 恢复时间:支持恢复到具体时间点(TIME)或系统更改号(SCN)。不指定 UNTIL 子句则恢复到最新位点。
恢复前准备
  • 在目标集群中为新租户创建资源池。
  • 确保备份路径的完整性和可访问性。
(可选)使用 PREVIEW 预检恢复

在执行 RESTORE 之前,可以通过 ALTER SYSTEM RESTORE...PREVIEW 预检恢复所需的 backup_setarchivelog_piece

-- 1. 预检恢复所需备份集(示例:恢复到指定 SCN)
        ALTER SYSTEM RESTORE FROM 'file:///data/backup/data, file:///data/backup/archive' UNTIL SCN = 1712650554000909004 PREVIEW;

        -- 2. 执行成功后查看结果
        SHOW RESTORE PREVIEW;
执行恢复语句
  • uri:由数据备份路径和日志归档路径组成,英文逗号分隔。
  • pool_list:待恢复的新租户所绑定的资源池,多个资源池用英文逗号分隔。
  • locality/primary_zone:建议尽量与源租户保持同构。
  • concurrency:物理恢复的并行度,可根据硬件资源调整。
  • method:恢复模式,full(全量恢复)或 quick(快速恢复)。
-- 示例:将 mysql_tenant 恢复到新租户 new_mysql,恢复到时间点 2024-06-01 00:00:00
        ALTER SYSTEM RESTORE new_mysql
        FROM 'file:///data/backup/data, file:///data/backup/archive'
        UNTIL TIME = '2024-06-01 00:00:00'
        WITH 'pool_list=restore_pool&locality=F@zone1,F@zone2,F@zone3&primary_zone=zone1&concurrency=10&method=full';
💡 恢复后的租户角色转换
恢复出来的租户默认是备租户,需要执行以下命令才能将其转为主租户以对外提供服务:
ALTER SYSTEM ACTIVATE STANDBY TENANT = new_mysql;

7. 查看恢复状态与结果

可以通过以下视图监控和确认恢复任务的进度与最终结果:

  • CDB_OB_RESTORE_PROGRESS:展示进行中的恢复任务进度。
  • CDB_OB_RESTORE_HISTORY:展示已完成的恢复任务结果,COMMENT 字段记录任务失败时的错误信息。
  • DBA_OB_TENANTS:查询恢复后的租户信息,RESTORE_DATA_MODE 列为物理恢复相关信息。
-- 查看恢复进度
        SELECT * FROM oceanbase.CDB_OB_RESTORE_PROGRESS\G

        -- 查看恢复结果历史
        SELECT * FROM oceanbase.CDB_OB_RESTORE_HISTORY\G

        -- 在目标新租户中验证数据完整性
        -- 例如,对比原租户与新租户的表结构和行数
⚙️ 如果恢复过程中出现错误,可根据 COMMENT 字段进行排查。若恢复任务失败,可通过删除处于恢复状态的租户来取消恢复。常见失败原因包括:备份路径不可访问、资源池规格不足、版本不兼容等。

8. 配置备份清理策略

为避免备份数据过多占用存储空间,OceanBase 支持自动清理过期备份。V4.x 版本不再支持通过配置项 backup_recovery_window 来设置,而是使用 ALTER SYSTEM ADD DELETE BACKUP POLICY 语句配置清理策略。

-- 设置备份保留最近 7 天(系统租户为指定租户设置)
        ALTER SYSTEM ADD DELETE BACKUP POLICY 'default' RECOVERY_WINDOW '7d' TENANT = mysql_tenant;

        -- 用户租户为本租户设置保留策略
        ALTER SYSTEM ADD DELETE BACKUP POLICY 'default' RECOVERY_WINDOW '7d';

        -- 查看已配置的清理策略
        SELECT * FROM oceanbase.DBA_OB_BACKUP_DELETE_POLICY;

        -- 手动触发清理(如需要)
        ALTER SYSTEM DELETE BACKUP;
💡 在 V4.x 版本中,不再支持通过配置项 backup_recovery_window 来设置备份数据可恢复的时间窗口,需使用 ALTER SYSTEM ADD DELETE BACKUP POLICY 语句进行设置。
🏆 核心变化与最佳实践总结
租户级配置:备份恢复操作已全部下沉到租户级别,不再依赖集群级配置项。
PLUS ARCHIVELOG:利用此选项可生成完整数据集,在特定恢复场景下简化操作。
恢复后激活:通过 RESTORE 恢复出的租户默认为备租户,务必执行 ACTIVATE STANDBY 以提供服务。
持续监控:充分利用 CDB_OB_BACKUP_JOBSCDB_OB_RESTORE_PROGRESS 等视图对任务进行全链路跟踪。
4.3 PITR 时间点恢复与增量备份

PITR(Point-in-Time Recovery,时间点恢复) 是 OceanBase 数据库物理备份的核心能力,允许将数据恢复到任意历史时间点,是应对数据误操作(如误删表、误更新)的关键手段。PITR 依赖于全量/增量数据备份 + 完整的日志归档两部分:全量备份提供基线数据,增量备份捕获变化数据,日志归档则持续记录 Redo 日志,三者结合可实现任意时间点的精确恢复。

🎯 PITR 的核心原理

恢复本质上是备份的逆过程,其流程如下:

  1. 基线恢复(Restore):基于用户指定的时间点,自动找到该时间点之前最近的一个全量备份(如有增量备份也会被利用),将其中的宏块数据和元信息恢复到目标租户;
  2. 日志回放(Recover):完成基线恢复后,从基线数据生成的时间点开始,从日志归档中拉取并回放 Redo 日志,直至用户指定的恢复终点。

恢复后的租户默认为备租户,如需提供读写服务,需执行 ACTIVATE STANDBY 将其转为主租户。

增量备份(Incremental Backup)

在 OceanBase 数据库中,增量备份是提高备份效率、降低存储开销的关键机制,也是实现高效 PITR 的重要基础。

定义与原理

增量备份是指备份上一次备份以后新增和修改过的宏块(Macroblock),而全量备份则备份所有的宏块。

  • 备份内容:一次增量备份 = 全量元信息的备份 + 增量的数据宏块备份。
  • 核心依赖:OceanBase 数据库的基线宏块具有全局唯一的逻辑标识(Logical Identifier),该标识提供了增量备份重用宏块的能力,使得增量备份在不同 backup_set 之间读取宏块时能够高效复用。
  • 性能对比:增量备份的恢复和全量备份的恢复在流程上基本一致,性能上也没有差别,只是会根据逻辑标识在不同的 backup_set 之间读取宏块。
增量备份的触发与行为

OceanBase 提供 ALTER SYSTEM BACKUP INCREMENTAL 语句用于发起增量备份。

-- 系统租户为所有用户租户发起增量备份
        ALTER SYSTEM BACKUP INCREMENTAL DATABASE;

        -- 系统租户为指定租户发起增量备份
        ALTER SYSTEM BACKUP INCREMENTAL TENANT = mysql_tenant;

        -- 用户租户为本租户发起增量备份
        ALTER SYSTEM BACKUP INCREMENTAL DATABASE;
⚠️ 增量备份的重要行为规则
  • 必须有全量备份基础:在执行增量备份前,请确保已经有全量备份存在。如果无全量备份,请先发起全量备份。
  • 自动转换为全量备份:如果在不存在全量数据备份的情况下直接发起增量备份,系统默认会将增量备份转换为全量备份。
  • 升级场景的特殊处理:对于低版本集群升级到高版本集群的场景,即使当前租户在低版本中已经执行了全量备份,升级后直接发起增量备份,系统默认也会将增量备份转换为全量备份。

PITR 恢复前的准备与约束

在进行 PITR 前,需完成以下准备工作并了解约束限制:

1. 可恢复区间判断

恢复位点 timestampSCN 需要满足以下约束:

  • 大于等于数据备份视图 CDB_OB_BACKUP_SET_FILES 中记录的数据备份的最小恢复位点 MIN_RESTORE_SCN
  • 小于等于日志归档视图 CDB_OB_ARCHIVELOG 中的最大归档位点 CHECKPOINT_SCN
  • 由于日志归档可能出现断流,因此恢复位点的选择还需要考虑日志归档的不连续区间,对于可恢复区间的选择则可能是分段的。
2. 时区注意事项

对于指定时间戳的恢复方式,OceanBase 数据库将根据备份集中最新记录的时区将时间戳转换成对应的 SCN,此处的时区来源于备份租户的系统变量 system_time_zone,其默认值为 CST(即中国标准时间,+8:00),并保存在备份集的 locality_info.obbak 文件中。

3. 恢复前准备清单
  • 在目标集群中为待恢复的新租户创建资源池(建议与源租户保持同构)。
  • 如备份时设置了加密密码,需在执行恢复前使用 SET DECRYPTION IDENTIFIED BY 'password' 设置解密密码。
  • 如源租户配置了透明加密,还需设置 KMS 加密信息。
  • 确保备份目的端(NFS/OSS)可访问且路径正确。
💡 4.x 版本恢复的重要变化:V4.x 版本支持将表恢复到原租户环境,而无需像 V3.x 版本那样必须恢复到全新的资源环境。如果需要修改恢复的表名、用户和表组,可以通过 REMAP TABLEREMAP TABLEGROUP 命令实现。

执行 PITR 恢复

OceanBase 支持三种恢复方式:恢复到指定时间戳、恢复到指定 SCN、恢复到最新位点。恢复命令由 root@sys 租户执行。

📌 恢复命令语法
-- 恢复到指定时间戳(推荐方式,精确到秒)
        ALTER SYSTEM RESTORE dest_tenant_name 
            FROM uri 
            UNTIL TIME = 'timestamp' 
            WITH 'restore_option' 
            [WITH KEY FROM 'backup_key_path' ENCRYPTED BY 'password'] 
            [DESCRIPTION [=] description];

        -- 恢复到指定 SCN
        ALTER SYSTEM RESTORE dest_tenant_name 
            FROM uri 
            UNTIL SCN = scn 
            WITH 'restore_option' 
            [WITH KEY FROM 'backup_key_path' ENCRYPTED BY 'password'] 
            [DESCRIPTION [=] description];

        -- 恢复到最新位点
        ALTER SYSTEM RESTORE dest_tenant_name 
            FROM uri 
            WITH 'restore_option' 
            [WITH KEY FROM 'backup_key_path' ENCRYPTED BY 'password'] 
            [DESCRIPTION [=] description];
📌 参数详解
参数说明示例/约束
dest_tenant_name 待恢复的新租户名称。 必须唯一,不能与现有租户重名。
uri 备份路径,由数据备份路径和日志归档路径组成,英文逗号分隔。 'file:///data/backup/data,file:///data/backup/archive'
TIME = 'timestamp' 恢复终点时间戳,包括该位点。支持 YYYY-MM-DD HH24:MI:SS.FF 格式,精确到纳秒。 '2024-06-01 00:00:00'
SCN = scn 恢复终点系统更改号,包括该位点。 需为正整数
pool_list 必选。待恢复的新租户所绑定的资源池,多个资源池用英文逗号分隔。 pool_list=restore_pool
locality 可选。指定新租户副本分布的 Locality 信息,需与集群的 Zone 信息匹配。 locality=F@zone1,F@zone2,F@zone3
primary_zone 可选。指定新租户的 Leader 副本偏好位置。 primary_zone=zone1
concurrency 可选。指定数据恢复的并发度,默认等于该租户被分配的 MAX_CPU 数。 concurrency=8
method 可选。恢复模式:full(全量恢复)或 quick(快速恢复)。
快速恢复的租户不支持合并、数据备份和 Switchover/Failover。
method=full
📌 恢复命令示例
-- 示例1:恢复到指定时间点
        ALTER SYSTEM RESTORE new_mysql
            FROM 'file:///data/backup/data,file:///data/backup/archive'
            UNTIL TIME = '2024-06-01 00:00:00'
            WITH 'pool_list=restore_pool&locality=F@zone1,F@zone2,F@zone3&primary_zone=zone1&concurrency=8&method=full';

        -- 示例2:恢复到最新位点
        ALTER SYSTEM RESTORE new_mysql
            FROM 'file:///data/backup/data,file:///data/backup/archive'
            WITH 'pool_list=restore_pool&locality=F@zone1,F@zone2,F@zone3&concurrency=8';

        -- 示例3:恢复单个表到指定时间点(V4.x 支持)
        ALTER SYSTEM RECOVER TABLE USER01.TEST1 TO TENANT new_mysql
            FROM 'file:///obbackup/ob4zry/1699999999/tenant_incarnation_1/1002/data,file:///obbackup/ob4zry/1699999999/tenant_incarnation_1/1002/clog'
            UNTIL TIME='2024-05-23 18:00:00'
            WITH 'pool_list=restore_pool1&concurrency=8';
⚠️ 单表恢复语法说明:V4.x 版本 RECOVER TABLE 语句要求 TO TENANT dest_tenant_name 之间不使用等号,与 RESTORE 语句的语法存在差异,请注意区分。
📌 使用 PREVIEW 预检恢复

在执行恢复前,可以通过 RESTORE PREVIEW 预检恢复所需的 backup_set 和 archivelog_piece。

-- 预检恢复到指定 SCN
        ALTER SYSTEM RESTORE FROM 'file:///ob_backup/data, file:///ob_backup/archive' 
            UNTIL SCN = 1712650554000909004 PREVIEW;

        -- 预检恢复到指定时间戳
        ALTER SYSTEM RESTORE FROM 'file:///ob_backup/data, file:///ob_backup/archive' 
            UNTIL TIME = '2024-04-09 16:15:54' PREVIEW;

        -- 查看预检结果
        SHOW RESTORE PREVIEW;

恢复性能调优

OceanBase 提供了专门的配置项来加速恢复过程:

  • ha_high_thread_score:设置数据恢复任务队列使用的工作线程数。在待恢复租户对应的 Meta 租户创建成功后,可通过此配置项加速恢复。
  • log_restore_concurrency:设置日志恢复的并发数。
-- 在 sys 租户中为指定租户设置恢复并发线程数
        ALTER SYSTEM SET ha_high_thread_score = 10 TENANT = mysql;

        -- 设置日志恢复并发数(0 表示使用自适应默认值)
        ALTER SYSTEM SET log_restore_concurrency = 4 TENANT = mysql;

恢复可观测性

OceanBase 提供了完善的恢复监控视图:

  • CDB_OB_RESTORE_PROGRESS:展示各租户恢复的进度(系统租户)。
  • DBA_OB_RESTORE_PROGRESS:展示正在进行的恢复任务(用户租户)。
  • CDB_OB_RESTORE_HISTORY:展示各租户的恢复结果,是恢复任务结束时对应记录的快照。
  • DBA_OB_RESTORE_HISTORY:展示已完成的恢复任务。
-- 查看恢复进度(系统租户)
        SELECT * FROM oceanbase.CDB_OB_RESTORE_PROGRESS\G

        -- 查看恢复历史结果
        SELECT TENANT_ID, RESTORE_TENANT_NAME, RESTORE_SCN_DISPLAY, 
            STATUS, START_TIMESTAMP, FINISH_TIMESTAMP, COMMENT
        FROM oceanbase.CDB_OB_RESTORE_HISTORY
        ORDER BY FINISH_TIMESTAMP DESC;

        -- 查看恢复后的租户基本信息
        SELECT TENANT_ID, TENANT_NAME, RESTORE_DATA_MODE 
        FROM oceanbase.DBA_OB_TENANTS;
⚙️ CDB_OB_RESTORE_HISTORY 中各字段基本与 CDB_OB_RESTORE_PROGRESS 相对应,COMMENT 字段记录任务失败时的错误信息。如恢复失败,可通过删除处于恢复状态的租户来取消本次恢复。

备份清理策略与 PITR 窗口的关系

备份数据的保留策略与 PITR 的能力密切相关:

  • RECOVERY_WINDOW 策略:设置备份数据可恢复的时间窗口,例如 RECOVERY_WINDOW '7d' 表示从当前时间往前推 7 天内的数据保证可恢复。超过该窗口且与窗口内恢复无关的备份数据将被视为过期并自动清理。
  • RETENTION 策略:设置备份数据的绝对保留时长,例如 RETENTION 30 DAYS 表示保留最近 30 天的备份数据。
-- 设置恢复窗口策略(保证过去 7 天数据可恢复)
        ALTER SYSTEM ADD DELETE BACKUP POLICY 'pitr_policy' RECOVERY_WINDOW '7d';

        -- 设置绝对保留时长策略
        ALTER SYSTEM ADD DELETE BACKUP POLICY 'retention_policy' RETENTION 30 DAYS;

        -- 查看已配置的清理策略
        SELECT * FROM oceanbase.DBA_OB_BACKUP_DELETE_POLICY;
📌 PITR 最佳实践总结
  • 定期执行全量备份:根据数据增量,制定合理的全量备份频率(如每周一次),减少日志回放的时长。
  • 配置合理的恢复窗口:根据业务 RPO 要求设置 RECOVERY_WINDOW,保证必要的恢复能力同时控制存储成本。
  • 恢复前使用 PREVIEW:在执行 RESTORE 前,建议先用 RESTORE PREVIEW 验证所需的备份和日志是否连续完整。
  • 恢复后验证数据一致性:恢复完成后,在新租户中验证关键数据,确保恢复结果符合预期。
🏆 4.x 版本 PITR 核心演进
PITR 精度:支持恢复到秒级(纳秒级时间戳格式),最小精度为 1 秒;
备份粒度:数据备份和日志归档均以租户为粒度,配置和调度下沉到租户级别,相关 SQL 和视图已全面调整;
恢复方式:支持恢复到原租户环境,无需创建全新的资源环境,恢复效率和灵活性显著提升;
单表恢复:V4.x 版本支持单表级别的 PITR 恢复,通过 RECOVER TABLE 语句实现。
恢复性能优化:通过 ha_high_thread_scorelog_restore_concurrency 参数可显著加速恢复过程。
4.4 备份集校验与恢复演练指南

定期执行备份集校验和恢复演练是保障数据安全的最后一道防线。备份集校验用于确保备份数据的完整性和有效性;恢复演练则通过定期执行完整的恢复测试,验证备份数据的实际可恢复性,确保在真正的灾难发生时能够可靠地完成数据恢复。OceanBase 提供了多层次的校验机制和丰富的监控视图,帮助 DBA 构建企业级的数据安全验证体系。

🎯 核心概念
  • 备份集校验:验证备份数据的完整性和有效性,主要检查数据校验和(Checksum)是否正确,以及备份文件是否完整齐全。
  • 恢复演练:在实际恢复环境中执行完整的恢复流程,验证备份数据的真实可恢复性,是确保 RPO 和 RTO 达标的核心手段。
  • 互为补充:备份集校验是对备份文件的静态检查,恢复演练是对数据的动态验证,两者结合构成完整的数据安全保障体系。

备份集校验概述

OceanBase 数据库的备份数据校验主要检查数据的有效性和完整性。有效性校验通过计算数据校验和(Checksum)来识别数据文件中的比特位翻转;完整性校验检查数据文件的完整性。备份校验的操作是在备份数据的基础上扫描并计算其数据,并不真正恢复数据,因此消耗的资源较少、校验速度快,适合作为日常巡检手段。

校验类型

执行要求: 数据校验和恢复命令必须在 sys 租户中执行。[reference:10]

  • 集群级校验:校验当前集群的所有备份集和对应的日志归档数据。执行 ALTER SYSTEM VALIDATE DATABASE; 即可启动。[reference:11]
  • 备份集校验:仅校验指定的备份集(BackupSet),不校验日志归档数据。
  • 日志校验:仅校验指定的日志备份片(BackupPiece)。日志在分裂成 Piece 后,可通过此命令进行校验。
-- 校验当前集群的所有备份集和对应日志
        ALTER SYSTEM VALIDATE DATABASE;

        -- 校验指定的备份集
        ALTER SYSTEM VALIDATE BACKUPSET 1;

        -- 校验指定的日志备份片
        ALTER SYSTEM VALIDATE BACKUPPIECE 2;

备份介质预检:test_io_device

在配置备份路径后、正式执行备份前,可以使用 ob_admin test_io_device 命令提前验证备份介质的 I/O 接口和权限是否满足备份恢复的需要。这是一个极其重要的预防性检查步骤,可避免在执行正式备份时才发现介质不可访问。[reference:12]

-- 测试 NFS 路径
        ./ob_admin test_io_device -d'file:///data/nfs'

        -- 测试 OSS 路径
        ./ob_admin test_io_device -d'oss://home/admin/backup_info' 
            -s'host=http://oss-cn-hangzhou.aliyuncs.com&access_id=111&access_key=222'
⚠️ -d 参数指定的模拟备份路径必须为空目录。-s 用于对象存储校验信息,设置 NFS 存储时仅需配置 -d 参数。[reference:13]

校验监控视图

OceanBase 提供了完整的备份校验监控视图体系,帮助 DBA 实时跟踪校验任务的进度和结果。

视图清单
视图名称功能描述
CDB_OB_BACKUP_VALIDATION_JOB[reference:14] 展示进行中的数据校验任务(JOB 级进度)
CDB_OB_BACKUP_VALIDATION_JOB_HISTORY 展示已完成的备份校验 Job 历史记录
CDB_OB_TENANT_BACKUP_VALIDATION_TASK 展示各租户备份校验任务详情(Task 级)[reference:15]
CDB_OB_BACKUP_VALIDATION_TASK_HISTORY 展示已完成的备份校验 Task 历史记录
查询示例
-- 查看当前校验任务进度
        SELECT JOB_ID, STATUS, RESULT, START_TIMESTAMP 
        FROM oceanbase.CDB_OB_BACKUP_VALIDATION_JOB;

        -- 查看校验任务详情(已完成 Partition Group 个数等进度信息)
        SELECT JOB_ID, TASK_ID, BACKUP_SET_ID, STATUS, RESULT,
            FINISH_PG_COUNT, TOTAL_PG_COUNT, 
            FINISH_PARTITION_COUNT, TOTAL_PARTITION_COUNT
        FROM oceanbase.CDB_OB_TENANT_BACKUP_VALIDATION_TASK;

        -- 查看历史校验结果
        SELECT * FROM oceanbase.CDB_OB_BACKUP_VALIDATION_JOB_HISTORY
        ORDER BY START_TIMESTAMP DESC LIMIT 10;
结果判断:如果 STATUSSUCCESS,表示备份数据校验通过;如果 STATUSFAILED,表示备份数据存在异常。

OCP 自动化恢复抽检策略

OceanBase Cloud Platform(OCP)提供了自动化的恢复抽检功能,可以在备份完成后自动、定期地验证备份数据的可恢复性,无需人工干预,显著提升备份验证的效率和质量。[reference:16]

核心流程
  1. 配置恢复抽检策略,指定抽样的时间范围、备份模式(物理备份/逻辑备份)等。
  2. OCP 每天在指定时间范围内以随机顺序对已做过数据备份的租户依次进行恢复测试。[reference:17]
  3. OCP 自动将备份数据恢复到目标集群的临时租户中。
  4. 验证完成后,OCP 自动删除临时恢复租户,不产生资源遗留。[reference:18]
📌 OCP 恢复抽检的配置要点
  • 需在 OCP 控制台提前创建恢复目标租户的资源配置(如 CPU、内存等)。
  • 目标集群版本需与备份集版本兼容。
  • 当选择的集群为 V4.0 及以上版本时,系统不支持为单个 Zone 配置 Unit 数量,您在此填写的 Unit 数量将自动适用于新建恢复租户下的所有 Zone。[reference:19][reference:20]。
  • OCP 会自动清理每次恢复测试创建的临时租户,无需手动操作。

配置路径:OCP 控制台 → 备份恢复 → 恢复 → 新建抽检策略。

恢复演练完整流程

恢复演练的核心步骤包括:恢复前资源准备、确定恢复时间点、执行恢复、验证恢复后数据完整性、演练后清理。以下以恢复租户到指定时间点为例演示。

演练前准备
  • 在目标集群中创建资源单元和资源池(规格不低于源租户)
  • 确保备份介质(NFS/OSS)可正常访问
  • 记录原租户的关键配置(Locality、Primary Zone 等)
-- 创建演练用的资源单元和资源池
        CREATE RESOURCE UNIT drill_unit MAX_CPU 8, MEMORY_SIZE '16G';
        CREATE RESOURCE POOL drill_pool UNIT='drill_unit', 
            UNIT_NUM=1, ZONE_LIST=('zone1','zone2','zone3');
执行 PITR 恢复
-- 恢复到演练时间点(建议选择演练窗口内的一个历史时间点)
        ALTER SYSTEM RESTORE drill_tenant
            FROM 'file:///data/backup/data,file:///data/backup/archive'
            UNTIL TIME = '2025-06-12 10:00:00'
            WITH 'pool_list=drill_pool&locality=F@zone1,F@zone2,F@zone3&primary_zone=zone1';
数据一致性验证

恢复完成后,需要在新租户中验证数据完整性和正确性。核心验证方法:

  • 表行数对比:对比源租户与恢复租户中关键表的行数是否一致。
  • 抽样数据比对:选取典型表抽样查询,验证数据是否正确恢复。
  • 业务功能验证:执行典型业务 SQL,验证恢复后的数据能否满足业务功能。
  • 校验和验证:通过计算表数据的校验和进行快速比对(MySQL 模式可用 CHECKSUM TABLE)。[reference:21]
-- 在 MySQL 模式下执行校验和比对
        CHECKSUM TABLE orders;

        -- 抽样验证典型数据
        SELECT COUNT(*) FROM orders WHERE order_date >= '2025-06-01';
⚙️ 恢复后租户角色管理:通过 RESTORE 恢复出的租户默认为备租户(STANDBY),如需提供读写服务,需执行 ALTER SYSTEM ACTIVATE STANDBY TENANT = drill_tenant; 将其转为主租户。演练完成后,可以删除恢复租户,避免资源浪费。

恢复演练频率与策略建议

  • 核心业务租户:建议每 季度 执行一次完整恢复演练。
  • 普通业务租户:建议每 半年 执行一次恢复演练。
  • 备份介质或存储架构变更后:必须在变更完成后立即执行一次恢复演练。
  • OceanBase 版本升级后:建议在新版本环境中执行恢复演练,验证跨版本兼容性。
📌 演练结果记录要素
每次演练完成后,应记录以下关键信息,形成演练报告归档备查:
  • 演练时间、目标租户、备份集 ID(BackupSet ID)
  • 恢复时间点(恢复到的 SCN 或时间戳)及恢复耗时
  • 数据一致性验证结果(通过/失败,差异数据规模)
  • 异常情况及解决方案备注
  • 演练操作人及复核人
🏆 生产环境备份恢复最佳实践总结
  • 备份有效性是核心:成功执行备份 ≠ 数据可恢复,必须通过校验和恢复演练双重验证。
  • 演练环境隔离:建议使用独立的恢复演练集群,避免对生产环境造成影响。
  • 自动化验证:推荐使用 OCP 的恢复抽检策略,实现备份可恢复性的持续、自动化验证。
  • 定期复盘:每次演练后复盘恢复耗时,提前发现影响 RTO 的瓶颈。
  • 预案文档化:将恢复演练的操作流程固化为应急预案文档,灾难发生时可直接参照执行。

第5章:SQL 调优与执行计划

5.1 SQL 执行计划详解

执行计划(Execution Plan)是对一条 SQL 查询语句在数据库中执行过程的描述。它展示了优化器决定如何访问数据、以何种顺序连接表以及使用哪些连接算法等关键信息。理解执行计划是进行 SQL 调优的核心能力,通过分析执行计划可以快速定位性能瓶颈,确定优化方向。

🎯 执行计划的核心作用
  • 诊断性能问题:通过执行计划可以判断 SQL 是否存在全表扫描、连接顺序不合理等问题。
  • 验证索引有效性:确认优化器是否使用了预期的索引,是否符合业务预期。
  • 优化 SQL 语句:基于执行计划分析,通过改写 SQL 或添加索引等手段提升性能。
  • 对比计划差异:通过对比不同时期的执行计划,定位 SQL 性能劣化的原因。

执行计划的获取方式

OceanBase 数据库支持多种方式来查看 SQL 的执行计划,每种方式适用于不同的场景。

1️⃣ EXPLAIN 命令:获取逻辑执行计划

EXPLAIN 语句适用于 SELECTDELETEINSERTREPLACEUPDATE 等 SQL 语句,用于展示优化器所提供的有关语句执行计划的信息,包括如何处理该语句、如何联接表以及以何种顺序联接表等信息。EXPLAIN 命令不会实际执行 SQL,而是展示预估的执行计划,因此可以放心在生产环境执行。

OceanBase 数据库支持三种 EXPLAIN 模式,展示不同详细程度的执行计划信息:

模式说明使用示例
BASIC 显示最基本的计划框架 EXPLAIN BASIC SELECT * FROM t1;
默认模式 展示的信息可以帮助普通用户了解整个计划的执行方式 EXPLAIN SELECT * FROM t1;
EXTENDED 展示最完整的执行计划信息,通常在排查问题时使用 EXPLAIN EXTENDED SELECT * FROM t1;
-- BASIC 模式示例
        EXPLAIN BASIC SELECT * FROM orders WHERE order_id = 10086;

        -- 默认模式示例
        EXPLAIN SELECT * FROM orders o, customers c 
        WHERE o.cust_id = c.cust_id AND o.order_id = 10086;

        -- EXTENDED 模式示例
        EXPLAIN EXTENDED SELECT * FROM orders WHERE order_date > '2025-01-01';

        -- 为 EXPLAIN 计划设置标记信息,便于后续跟踪
        EXPLAIN SET STATEMENT_ID='test_plan' SELECT * FROM orders;
2️⃣ DBMS_XPLAN 系统包:查看历史执行计划和实际执行信息

DBMS_XPLAN 是 OceanBase 数据库提供的系统包,用于查询逻辑执行计划及历史执行计划的详细信息。OceanBase 数据库会保存用户执行过的所有查询的计划,包括物理计划以及逻辑计划,用户后续排查问题使用。

  • DISPLAY():显示通过 EXPLAIN 命令生成的逻辑执行计划。
  • DISPLAY_CURSOR():显示已执行的查询计划详细信息,反映了 SQL 在计划缓存中的真实执行计划,适用于查看已执行 SQL 的实际计划。在 MySQL 租户中,需在 SELECT 语句中直接调用该函数来输出计划信息[reference:10]。
  • DISPLAY_ACTIVE_SESSION_PLAN():显示指定会话中正在执行或刚执行完的 SQL 的实时计划详情,适合排查正在运行的慢查询。
-- 使用 DBMS_XPLAN 查看通过 EXPLAIN 生成的计划
        SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE', 'test_plan', 'TYPICAL'));

        -- 通过 SQL_ID 查看计划缓存中的实际执行计划
        SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('abc123def456'));

        -- 查看当前会话中最后一条 SQL 的实际执行计划
        SELECT DBMS_XPLAN.DISPLAY_CURSOR();
3️⃣ 实时执行计划展示:查询计划缓存中的物理执行计划

由于统计信息变化或 Session 变量设置变化,EXPLAIN 生成的逻辑执行计划可能与 SQL 实际执行时使用的物理执行计划不同。为了确定 SQL 在系统中实际使用的执行计划,可以通过查询计划缓存视图来获取。

在 OceanBase 数据库中,查询 SQL 实际物理执行计划的核心视图是 GV$OB_PLAN_CACHE_PLAN_EXPLAIN。该视图用于展示缓存在全部的 OBServer 节点中的计划缓存中的物理执行计划,从 V4.0.0 版本开始引入[reference:11]。

  • GV$OB_PLAN_CACHE_PLAN_STAT:记录每个执行计划的统计信息,包括执行次数、平均耗时等。
  • GV$OB_PLAN_CACHE_PLAN_EXPLAIN:记录每个执行计划的形状(算子树结构),展示完整的物理执行计划。该视图仅支持 GET 操作,查询时需指定 TENANT_IDPLAN_ID 字段[reference:12]。
-- 第1步:查询特定 SQL 在计划缓存中的 plan_id
        SELECT TENANT_ID, PLAN_ID, SQL_ID, QUERY_SQL
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
        WHERE QUERY_SQL LIKE '%orders%' AND QUERY_SQL NOT LIKE '%GV$OB_PLAN_CACHE%'
        LIMIT 1;

        -- 第2步:根据 plan_id 查询物理执行计划详情
        SELECT OPERATOR, NAME, ROWS, COST 
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_EXPLAIN
        WHERE TENANT_ID = 1002 AND PLAN_ID = 123;

        -- 查看所有 OBServer 节点上的计划缓存统计
        SELECT * FROM oceanbase.GV$OB_PLAN_CACHE_STAT;
💡 EXPLAIN 与计划缓存计划的关系EXPLAIN 生成的计划是优化器基于当前统计信息的预估,可能与 SQL 实际执行时缓存的计划不同。计划缓存计划才是真正被执行的物理计划,因此 SQL 性能诊断时应优先查询 GV$OB_PLAN_CACHE_PLAN_EXPLAIN 获取实际执行计划。

执行计划的阅读与理解

OceanBase 数据库的执行计划本质上是由物理操作符构成的一棵执行树,物理操作符一般对应一个关系操作,如表扫描、联接、聚合、排序等。通过将不同的物理操作符按照一定的先后顺序组织在一棵执行树中,最终完成整个 SQL 语句。

📖 执行计划的阅读顺序

阅读顺序的基本原则是:从内到外,从右到左。具体来说,执行计划中的执行顺序为:首先找到最内层最深且最靠前的算子,先执行该算子,将其结果返回给父节点,然后依次执行相同层次的其他算子,最后执行根节点。层次最深的优先执行,层次相同的以特定算子的执行顺序为标准来执行[reference:13]。

执行计划输出字段详解

OceanBase 执行计划输出的关键列字段及其含义:

字段说明
ID执行计划的算子编号,从 0 开始递增
OPERATOR算子名称,表示执行的操作类型(如 TABLE SCAN、NESTED-LOOP JOIN、SORT 等)
NAME操作的对象名称,如表名或索引名
EST. ROWS优化器预估该算子输出的行数,基于统计信息估算
COST优化器预估的执行代价,用于计划选择时的代价比较

核心算子介绍

执行计划由各种物理操作符(算子)构成,掌握常用算子的含义是阅读执行计划的基础。

表访问算子
  • TABLE SCAN(全表扫描/范围扫描):对表进行全表扫描或索引范围扫描,返回 0 行或多行数据。当查询没有可用索引时通常使用此算子。
  • TABLE GET(主键定位):通过主键或唯一键直接定位数据,返回 0 行或 1 行数据,是最高效的表访问方式。
  • TABLE LOOKUP(回表):使用二级索引扫描后,根据索引中的主键回表查询完整的行数据。
JOIN 算子

OceanBase 数据库支持的 JOIN 算子主要有三种:

  • NESTED-LOOP JOIN(NLJ,嵌套循环连接):对左表(驱动表)的每一行,在右表(被驱动表)中查找匹配的行。当左表数据量较小且右表连接键上有高效索引时性能较好[reference:14]。
  • MERGE JOIN(MJ,归并连接):先将两个表按照连接键排序,然后同时扫描合并。适用于两表数据量都较大且连接键已排序的场景。
  • HASH JOIN(HJ,哈希连接):构建右表的哈希表,然后扫描左表进行探查。适合大表连接且无索引的场景。HASH JOIN 和 MERGE JOIN 仅适用于等值 JOIN 条件[reference:15]。
-- 强制使用 NESTED-LOOP JOIN 的 Hint 示例
        SELECT /*+ LEADING(t1 t2) USE_NL(t2) */ * 
        FROM t1, t2 
        WHERE t1.id = t2.id;
排序与聚合算子
  • SORT / TOP-N SORT:对数据进行排序操作。TOP-N SORT 是带 LIMIT 的排序优化,只保留前 N 行。
  • HASH GROUP BY / MERGE GROUP BY:对数据进行分组聚合操作,如 COUNT(*)SUM(col) 等。
其他常用算子
  • LIMIT:限制返回的行数,通常会与其他算子配合,优化 LIMIT 查询。
  • MATERIAL:物化算子,将子查询的结果临时保存,避免重复扫描。常见于 NESTED-LOOP JOIN 的右表需要重复扫描的场景[reference:16]。
  • SUBPLAN FILTER:子计划过滤算子,用于驱动表达式中的子查询执行。左边取一行数据,然后执行右边的子计划。
  • EXCHANGE IN/OUT:分布式执行时的数据交换算子,用于并行查询中的数据重分布。
  • WINDOW FUNCTION:窗口函数算子,用于实现 SQL 中的分析函数(如 ROW_NUMBER()RANK() 等),计算窗口下的相关行的结果。
💡 在 EXPLAIN EXTENDED 输出中,Outputs & filters 部分会详细展示算子的输出字段信息和过滤条件,是深入分析执行计划的重要参考信息。

统计信息与预估行数分析

优化器生成的执行计划往往基于代价来评估,而统计信息是否准确直接影响算子的行数估算,进而影响计划的选择。统计信息准确,优化器能够准确评估各执行计划的代价,从而选出最优执行计划;反之则可能选择低效计划,导致 SQL 性能问题[reference:17]。

预估行数 vs 实际行数
  • 统计信息过期:如果表的数据量发生较大变化后未及时收集统计信息,预估行数可能与实际行数产生较大偏差,可能导致优化器选择了不合适的执行计划。
  • 数据分布不均:对于存在数据倾斜的列,若未收集直方图信息,优化器可能无法准确估算行数。
  • 复杂表达式WHERE 条件中包含复杂函数或隐式类型转换时,优化器的行数估算可能不准确。
  • Runtime Filter 影响:当计划启用并行计划并开启 JOIN FILTER 时(如 set global runtime_filter_type ='range,in,bloom_filter'),也可能出现估算的行数和实际的行数差距较大的情况。
如何分析统计信息准确性

通过执行计划可以分析统计信息是否有使用,以及统计信息是否过期:

-- 查看表的统计信息最后更新时间
        SELECT TABLE_NAME, LAST_ANALYZED, NUM_ROWS, AVG_ROW_LEN 
        FROM oceanbase.DBA_TABLES 
        WHERE OWNER = 'TESTDB' AND TABLE_NAME IN ('ORDERS', 'CUSTOMERS');

        -- 查看列的直方图信息
        SELECT COLUMN_NAME, NUM_DISTINCT, HISTOGRAM 
        FROM oceanbase.DBA_TAB_COL_STATISTICS 
        WHERE OWNER = 'TESTDB' AND TABLE_NAME = 'ORDERS';

        -- 手动收集统计信息(支持 ANALYZE 命令和 DBMS_STATS 系统包两种方式)
        ANALYZE TABLE orders COMPUTE STATISTICS;
        -- 或使用 DBMS_STATS 系统包(推荐在 V4.x 中使用)
        CALL DBMS_STATS.GATHER_TABLE_STATS('TESTDB', 'ORDERS');
        ANALYZE TABLE orders UPDATE HISTOGRAM ON order_status, customer_id;
📌 预估行数偏差的优化建议
  • 定期收集统计信息:设置合理的统计信息收集策略,确保统计信息与数据真实分布同步。
  • 收集直方图:对于存在数据倾斜的列,使用 UPDATE HISTOGRAM 收集直方图信息。
  • 避免隐式类型转换:确保查询条件中的数据类型与列定义完全一致,避免函数操作。
  • 使用实际行数反馈:通过 GV$SQL_PLAN_MONITOR 视图可以获取 SQL 执行过程中各算子的实际输出行数,进行精确对比分析。

SQL 调优基本流程

OceanBase 数据库的 SQL 调优遵循系统化的诊断流程:

  1. 查找慢 SQL:通过 GV$OB_SQL_AUDIT(SQL 审计表)查找耗时较长或消耗资源较高的 SQL 语句。
  2. 获取实际执行计划:通过 GV$OB_PLAN_CACHE_PLAN_EXPLAIN 获取 SQL 在计划缓存中的实际物理执行计划,而非仅靠 EXPLAIN 的预估计划。
  3. 分析执行计划形状:关注关键算子类型(是否有全表扫描、连接顺序是否合理、是否使用了正确的索引)、预估行数是否准确、是否涉及分布式数据重分布等。
  4. 收集最新统计信息:若统计信息过旧,执行统计信息收集命令更新。
  5. 优化 SQL:根据分析结果进行优化——创建合适索引、改写 SQL(避免复杂函数、优化连接顺序、使用分区裁剪等)、使用 Hint 绑定执行计划、应用 Outline 固定执行计划等。
  6. 验证优化效果:通过再次执行并比较执行计划差异来验证优化效果。
-- 查找最近 1 小时内的慢 SQL(执行时间 > 500ms)
        SELECT SQL_ID, QUERY_SQL, ELAPSED_TIME, PLAN_TYPE, RET_CODE
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE REQUEST_TIME > UNIX_TIMESTAMP() * 1000000 - 3600000000
        AND ELAPSED_TIME > 500000
        ORDER BY ELAPSED_TIME DESC
        LIMIT 10;

        -- 查看 SQL 在计划缓存中的执行统计信息
        SELECT PLAN_ID, SQL_ID, AVG_EXE_TIME_US, EXECUTIONS
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
        WHERE SQL_ID = 'abc123def456';
⚡ Plan Cache 工作机制与优化

SQL 优化比较耗时,为了避免反复执行 SQL 优化,OceanBase 数据库会将生成的计划放进 Plan Cache,再次执行时直接从 Plan Cache 获取计划,避免重复硬解析。每个租户在每个节点上都有一个独立的 Plan Cache,用以缓存在此节点上处理过的 SQL 计划。

  • 快速参数化:当 OceanBase 数据库收到 SQL 请求时,首先使用快速解析器模块对 SQL 文本进行快速参数化。
  • 监控 Plan Cache:通过 GV$OB_PLAN_CACHE_PLAN_STAT 视图可以查看每个计划的执行统计信息,发现占用 CPU 资源较高的 SQL 语句,进而优化数据库性能。
  • 计划淘汰:当 Plan Cache 内存达到上限时,系统会根据 LRU 策略淘汰不常用的执行计划。用户也可通过 ALTER SYSTEM FLUSH PLAN CACHE 命令主动清除缓存[reference:18]。
🏆 执行计划诊断最佳实践总结优先使用计划缓存计划EXPLAIN 是预估计划,GV$OB_PLAN_CACHE_PLAN_EXPLAIN 才是实际执行的物理计划,性能诊断时应优先查询计划缓存。
从全局到细节:先整体了解执行计划形状(操作类型和顺序),再深入分析关键算子的详细信息和预估行数。
关注行数偏差:预估行数与实际行数偏差过大时,需检查统计信息是否最新,并分析是否存在数据倾斜。
验证优化效果:每次优化后应对比优化前后的执行计划,确认优化方向正确。
活用 Hint:在无法改写 SQL 的情况下,通过 Hint 指定优化策略(如连接顺序、索引选择)仍是有效的优化手段。
推荐使用 DBMS_STATS:在 OceanBase 4.x 版本中,推荐使用 DBMS_STATS 系统包进行统计信息收集,支持并行度控制、分区表特殊处理等高级功能[reference:19][reference:20]。
5.2 优化器统计信息

优化器统计信息(Optimizer Statistics)是描述数据库中表、列和索引等对象特征的数据集合,是优化器进行执行计划代价评估的核心依据。准确的统计信息能够帮助优化器正确估算SQL执行的行数、成本和数据分布,从而选择最优的执行计划;而统计信息过期或不准确则可能导致优化器选择低效的执行计划,严重影响SQL性能。

🎯 统计信息的核心价值
  • 行数估算:优化器基于统计信息预估每个算子输出的行数,影响Join顺序、访问路径选择等决策。
  • 代价计算:CPU成本、I/O成本等均依赖于表大小、列基数等统计信息。
  • 执行计划稳定性:准确的统计信息是执行计划稳定的基础,避免因统计信息缺失或过期导致计划劣化。
  • 分布式优化:在分布式环境中,统计信息还影响数据分布策略和并行度决策。

统计信息的类型

在 OceanBase 数据库优化器中,统计信息主要分为四大类:

分类基本统计信息类型
表统计信息 表的行数(NUM_ROWS)、表的宏块数、表的微块数、表的平均行长(AVG_ROW_LEN)
列统计信息 列的 NDV(不同值的数量)、列的 NULL 值数量、列的平均列长、列的最大值/最小值、列的直方图
索引统计信息 索引表的行数、索引表的宏块数、索引表的微块数、索引表的平均行长
物化视图刷新统计信息 刷新操作的当前信息、刷新操作的历史信息、实际刷新执行时间

直方图详解

直方图(Histogram)是一种特殊的列统计信息。默认情况下,优化器会假设列的数据分布是均匀的,并依据这一特征进行行数估算。但在真实场景中,大多数表的数据分布都存在倾斜,此时就需要直方图来描述数据分布特征,帮助优化器进行更准确的行数估计。

直方图类型

OceanBase 数据库优化器支持三种类型的直方图,收集时会根据列数据的真实分布特征自动选择合适的类型:

  • 频率直方图(Frequency Histogram):当列的 NDV 不超过 bucket_size(默认为 254)时,每个不同的列值对应直方图中的一个桶。此时直方图能精确记录每个值的出现频次,适用于基数较低的列。
  • TopK 直方图(TopK Histogram):当列的 NDV 超过 bucket_size 时,系统会计算一个阈值 p = (1 - (1/bucket_size)) * 100(默认 bucket_size=254,则 p ≈ 99.6%)。只有占据数据比例 ≥ 该阈值的热点值才会被作为桶保存,其余低频值被忽略。这种设计在有限桶数下尽可能保留高频值信息,降低了不必要的内存开销。
  • 混合直方图(Hybrid Histogram):前两种直方图的折中策略。它在满足最大桶数限制的同时,尽可能保留更多值的频次信息。当数据分布介于前两者之间时,优化器会自动选择混合直方图以达到信息保留与内存使用的平衡。
📊 数据分布判断逻辑:优化器在收集统计信息时会根据列数据的真实分布情况自动选择直方图类型。若列基数(NDV ≤ 桶数)选用频率直方图,高频值集中的选用 TopK 直方图,分布复杂且基数较高的选用混合直方图,确保每种场景都有适合的直方图策略。
直方图监控视图

OceanBase 提供了多个视图用于查询直方图信息,支持 Oracle 和 MySQL 两种兼容模式:

  • DBA_TAB_HISTOGRAMS / ALL_TAB_HISTOGRAMS:查询表列的直方图信息。
  • DBA_PART_HISTOGRAMS:查询分区表分区级别的直方图信息。
  • DBA_SUBPART_HISTOGRAMS:查询分区表子分区级别的直方图信息。
-- 查询指定列的直方图信息
        SELECT COLUMN_NAME, ENDPOINT_NUMBER, ENDPOINT_VALUE, ENDPOINT_REPEAT_COUNT
        FROM DBA_TAB_HISTOGRAMS
        WHERE OWNER = 'TEST' AND TABLE_NAME = 'ORDERS' AND COLUMN_NAME = 'ORDER_STATUS'
        ORDER BY ENDPOINT_NUMBER;

        -- 查看列的统计信息,包括直方图类型
        SELECT COLUMN_NAME, NUM_DISTINCT, HISTOGRAM, NUM_NULLS
        FROM DBA_TAB_COL_STATISTICS
        WHERE OWNER = 'TEST' AND TABLE_NAME = 'ORDERS';

统计信息的收集方式

OceanBase 数据库 V4.x 版本实现了全新的统计信息收集机制,将统计信息收集和每日合并解耦,每日合并不再收集统计信息。OceanBase 提供了多种统计信息收集方式,以适应不同场景的需求。

1️⃣ 自动收集(推荐)

自动统计信息收集任务基于 DBMS_SCHEDULER 系统包实现,以周为单位定义了周期性执行的维护窗口,集群环境为每个租户在每个维护窗口上都会有后台任务调度,OceanBase 会为每个租户按照调度时间表启动维护任务,在窗口中自动收集缺失或过期的统计信息。

版本维护窗口开始时间最大收集时长
V4.2.1 版本 MONDAY_WINDOW ~ FRIDAY_WINDOW 22:00 4 小时
SATURDAY_WINDOW / SUNDAY_WINDOW 6:00 20 小时
V4.2.5 及之后版本 MONDAY_WINDOW ~ SUNDAY_WINDOW 22:00 4 小时

收集范围与过期标准:自动收集针对的是统计信息缺失或统计信息过期的表,采用增量收集方式——只收集数据发生变化的分区,无需重新收集整个表的统计信息。统计信息过期的判断标准是分区上的 DML(增删改)变化量超过 STALE_PERCENT 阈值,默认值为 10%。

2️⃣ 手动收集

OceanBase 数据库优化器支持两种手动收集统计信息的方式:DBMS_STATS 系统包和 ANALYZE 命令行。

  • DBMS_STATS 系统包(V4.x 推荐):提供功能更丰富的统计信息收集能力,支持并行收集、分区表增量收集、统计信息历史管理等高级特性。
  • ANALYZE 命令行:简单易用,在 Oracle 模式和 MySQL 模式下均可使用,适合快速收集统计信息。
-- 使用 DBMS_STATS 收集表级统计信息(推荐方式)
        -- 使用默认策略收集
        CALL DBMS_STATS.GATHER_TABLE_STATS('TEST', 'ORDERS');

        -- 不收集直方图
        CALL DBMS_STATS.GATHER_TABLE_STATS('TEST', 'ORDERS', 
            method_opt => 'FOR ALL COLUMNS SIZE 1');

        -- 指定并行度收集(大表加速)
        CALL DBMS_STATS.GATHER_TABLE_STATS('TEST', 'ORDERS', 
            DEGREE => 8, METHOD_OPT => 'FOR ALL COLUMNS SIZE AUTO');

        -- 指定列为直方图收集
        CALL DBMS_STATS.GATHER_TABLE_STATS('TEST', 'ORDERS', 
            METHOD_OPT => 'FOR COLUMNS ORDER_STATUS SIZE 254');

        -- 使用 ANALYZE 命令行收集
        ANALYZE TABLE orders COMPUTE STATISTICS;
        ANALYZE TABLE orders COMPUTE STATISTICS FOR ALL COLUMNS;
        ANALYZE TABLE orders COMPUTE STATISTICS FOR COLUMNS order_status;
⚠️ 手动收集统计信息的版本差异

V4.x 版本:Oracle 模式和 MySQL 模式下均支持 DBMS_STATS 系统包和 ANALYZE 命令行。
V3.x 版本:Oracle 模式支持 DBMS_STATS,但 MySQL 模式下仅支持 ANALYZE 命令行,且 DBMS_STATS 功能有限。3.x 版本若在 MySQL 模式使用 DBMS_STATS,需开启 enable_sql_extension,但存在一定风险,不建议常规使用。因此,推荐将集群升级到 V4.x 版本以获得完整的统计信息管理能力。

3️⃣ 批量收集与分区表收集策略

对于 Schema 级别的批量统计信息收集,可使用 GATHER_SCHEMA_STATS;对于分区表,需根据数据变更模式选择增量或全局收集策略。

-- 收集整个 Schema 的统计信息
        CALL DBMS_STATS.GATHER_SCHEMA_STATS('TEST');

        -- 增量收集分区表(仅收集变更的分区)
        ALTER TABLE orders SET INCREMENTAL = TRUE;
        CALL DBMS_STATS.GATHER_TABLE_STATS('TEST', 'ORDERS', GRANULARITY => 'APPROX_GLOBAL AND PARTITION');

        -- 查询表的统计信息收集策略
        SELECT TABLE_NAME, INCREMENTAL, LAST_ANALYZED, STALE_STATS
        FROM DBA_TAB_STATISTICS
        WHERE OWNER = 'TEST';

统计信息收集策略配置(Prefs)

OceanBase 数据库优化器支持用户根据业务实际情况修改默认的收集策略(Prefs),以适应不同的业务场景和数据特征。

常用 Prefs 参数说明
Prefs 名称默认值描述
APPROXIMATE_NDV TRUE 计算 NDV(不同值数量)是否使用估算方式。TRUE 表示使用估算,收集速度快;FALSE 表示精确计算,准确度高但资源消耗大。
DEGREE NULL(即并发度为 1) 统计信息收集的并行度。对于大表,调高 DEGREE 可显著加速统计信息收集。
ESTIMATE_PERCENT DBMS_STATS.AUTO_SAMPLE_SIZE 统计信息收集的采样比例,AUTO 模式下系统自动决定合适的采样率,平衡收集速度和准确度。
GRANULARITY AUTO 统计信息收集的分区粒度。AUTO 模式下系统根据表特征自动选择最优粒度。
METHOD_OPT FOR ALL COLUMNS SIZE AUTO 直方图收集策略:设置哪些列需要收集直方图以及桶的数量。
STALE_PERCENT 10% 自动收集任务触发的过期判断阈值。当分区的 DML 变化量超过该百分比时,系统将统计信息标记为过期,进入待收集列表。
STATS_RETENTION 31 统计信息历史数据的保留天数,用于执行计划历史回溯和问题排查。
Prefs 查询与修改

OceanBase 提供了一系列存储过程用于管理统计信息的收集策略。

-- 查询当前租户的 Prefs 设置
        SELECT DBMS_STATS.GET_PREFS('APPROXIMATE_NDV') FROM DUAL;
        SELECT DBMS_STATS.GET_PREFS('DEGREE') FROM DUAL;
        SELECT DBMS_STATS.GET_PREFS('METHOD_OPT') FROM DUAL;

        -- 查询指定表的 Prefs 设置
        SELECT DBMS_STATS.GET_PREFS('STALE_PERCENT', 'TEST', 'ORDERS') FROM DUAL;

        -- 设置租户级别的 Prefs(影响所有表)
        CALL DBMS_STATS.SET_GLOBAL_PREFS('APPROXIMATE_NDV', 'FALSE');
        CALL DBMS_STATS.SET_GLOBAL_PREFS('DEGREE', '8');

        -- 设置表级别的 Prefs(仅影响指定表)
        CALL DBMS_STATS.SET_TABLE_PREFS('TEST', 'ORDERS', 'DEGREE', '16');
        CALL DBMS_STATS.SET_TABLE_PREFS('TEST', 'ORDERS', 'STALE_PERCENT', '15');

        -- 删除表级别的 Prefs 设置(恢复使用全局默认值)
        CALL DBMS_STATS.DELETE_TABLE_PREFS('TEST', 'ORDERS', 'DEGREE');

        -- 还原全局所有 Prefs 为默认值
        CALL RESET_GLOBAL_PREF_DEFAULTS();

统计信息监控与诊断

核心监控视图

OceanBase 提供了丰富的视图用于查询统计信息收集状态和历史:

  • DBA_TAB_STATISTICS:查询表级统计信息,包括最后收集时间、数据行数、是否过期等。
  • DBA_TAB_COL_STATISTICS:查询 GLOBAL 级别的列级统计信息。
  • DBA_PART_COL_STATISTICS:查询分区级别的列统计信息。
  • DBA_OB_TASK_OPT_STAT_GATHER_HISTORY:统计信息收集任务的历史记录,用于排查自动或手动收集任务的执行情况。
  • DBA_OB_TABLE_STAT_STALE_INFO:查询统计信息过期的表及过期原因(DML 变化统计、过期间隔)。
-- 查询统计信息过期的表(需要关注)
        SELECT OWNER, TABLE_NAME, OBJECT_TYPE, STALE_STATS, LAST_ANALYZED, NUM_ROWS
        FROM DBA_TAB_STATISTICS
        WHERE STALE_STATS = 'YES' AND OWNER = 'TEST';

        -- 查看最近自动收集任务的历史记录
        SELECT * FROM DBA_OB_TASK_OPT_STAT_GATHER_HISTORY
        WHERE START_TIME > DATE_SUB(NOW(), INTERVAL 7 DAY)
        ORDER BY START_TIME DESC
        LIMIT 20;

        -- 查询统计信息过期的表及过期详情
        SELECT TABLE_NAME, PARTITION_NAME, STALE_TYPE, STALE_PERCENT, LAST_ANALYZED
        FROM DBA_OB_TABLE_STAT_STALE_INFO
        WHERE OWNER = 'TEST';
统计信息收集问题排查

当发现自动统计信息收集任务未正常执行时,可通过以下步骤排查:

-- 步骤1:检查自动收集任务是否正常调度(系统租户查看)
        SELECT tenant_id AS failed_scheduler_tenant_id 
        FROM oceanbase.__all_tenant t 
        WHERE NOT EXISTS(SELECT 1 
            FROM oceanbase.__all_virtual_task_opt_stat_gather_history h 
            WHERE TYPE = 1 AND start_time > date_sub(now(), interval 1 day) 
            AND h.tenant_id = t.tenant_id);

        -- 步骤2:查询当前租户最近是否有失败的自动收集任务
        SELECT task_id, start_time, end_time, ret_code, stat_refresh_failed_list 
        FROM DBA_OB_TASK_OPT_STAT_GATHER_HISTORY 
        WHERE ret_code != 0 AND start_time > date_sub(now(), interval 1 day);

统计信息与执行计划缓存的关系

统计信息的更新会影响 Plan Cache 中缓存的执行计划。在 OceanBase 4.x 版本中,统计信息收集默认会刷新该表的 Plan Cache,意味着原先基于旧统计信息生成的执行计划可能被淘汰,下次执行时会根据新统计信息重新生成计划。此设计有助于保证执行计划的质量,但在业务高峰期进行手动统计信息收集需慎重评估影响。

  • 收集统计信息后,可以通过 DBA_OB_TABLE_STAT_STALE_INFOLAST_ANALYZED 字段,判定一个计划生成过程中使用的统计信息是否被更新。
  • 如果统计信息被更新,OceanBase 默认会淘汰对应表在 Plan Cache 中包含的查询计划,强制在下一次执行时生成新的执行计划。
📌 统计信息最佳实践
  • 开启自动收集:默认开启,确保核心业务表的统计信息定期更新。根据业务特点,可通过修改维护窗口时间或将 STALE_PERCENT 调低(如 5%)来提高关键表统计信息的更新频率。
  • 大表使用并行收集:对于大表,可通过设置 DEGREE 并行收集,显著提升收集速度。
  • 倾斜列收集直方图:对于数据分布倾斜的列,务必收集直方图,帮助优化器进行更精确的行数估算。
  • 业务低峰期收集:手动收集统计信息建议安排在业务低峰期进行,避免对在线业务造成影响。
  • 使用采样优化大表收集:对于超大表(如十亿级以上),可通过 ESTIMATE_PERCENT 设置合理采样比例,在保证统计信息基本准确的前提下降低收集成本。
  • 批量收集 Schema 统计信息:对于需要同时更新多个表统计信息的场景,使用 GATHER_SCHEMA_STATS 统一收集,比逐个表手动收集更高效。
🏆 统计信息核心变化总结
统计信息与合并解耦:V4.x 版本将统计信息收集与每日合并解耦,每日合并不再收集统计信息,因此需要特别关注统计信息的收集情况。
自动收集基于 DBMS_SCHEDULER:以周为单位的维护窗口自动运行,收集缺失或过期的统计信息。
过期标准:分区数据增删改量超过 10%(默认)时自动标记为过期,等待下次维护窗口自动收集。
直方图支持完整:V4.x 版本完整支持频率直方图、TopK 直方图和混合直方图三种类型,并可自动选择。
推荐使用 DBMS_STATS:相比 ANALYZE 命令行,DBMS_STATS 系统包功能更完善,是 V4.x 版本收集统计信息的首选方式。
5.3 Outline 执行计划绑定

在 SQL 中添加 Hint 可以控制优化器按 Hint 指定的行为进行计划生成,该方法适用于系统上线前直接将 Hint 加入 SQL 中。但对于已上线的业务,如果出现优化器选择的计划不够优时,则需要在线进行计划绑定,即无需业务进行 SQL 更改,而是通过 DDL 操作将一组 Hint 加入到 SQL 中,从而使得优化器根据指定的一组 Hint 对该 SQL 生成更优计划。该组 Hint 称为 Outline(执行计划纲要)。通过对某条 SQL 创建 Outline,可保证该 SQL 在后续执行时始终使用指定的执行计划,避免因优化器选择不当导致性能波动。

🎯 Outline 的典型应用场景
  • 线上应急计划绑定:生产环境出现性能问题,优化器选择了次优计划,但业务代码无法立即变更,通过 DDL 方式在线绑定执行计划。
  • 避免计划跳变:在 V4.x 之前版本中,Outline 是主要的计划固定手段,用于快速稳定性能,避免因统计信息变化导致计划从优变劣。
  • 无需修改代码的优化:相较于 Hint 需要嵌入 SQL 并修改应用代码,Outline 通过 DDL 操作绑定,适合线上紧急修复场景。

创建 Outline

OceanBase 数据库支持通过两种方式创建 Outline:一种是通过 SQL_TEXT(用户执行的带参数的原始语句),另一种是通过 SQL_ID 创建。

📌 前置条件

创建 Outline 需要进入对应的数据库下执行。建 Outline 前需要先执行 USE database_name; 进入目标数据库。

📝 使用 SQL_TEXT 创建 Outline

使用 SQL_TEXT 创建 Outline 后,会生成一个 Key-Value 对存储在 Map 中,其中 Key 为绑定的 SQL 参数化后的文本,Value 为绑定的 Hint。语法如下:

CREATE [OR REPLACE] OUTLINE <outline_name> ON <stmt> [ TO <target_stmt> ];

参数说明:

  • OR REPLACE:指定后,如果同名的 Outline 已存在,则会进行替换。
  • stmt:一般为一个带有 Hint 和原始参数的 DML 语句。
  • TO target_stmt:如果不指定,则表示如果数据库接受的 SQL 参数化后与 stmt 去掉 Hint 参数化文本相同,则将该 SQL 绑定 stmt 中 Hint 生成执行计划;如果期望对含有 Hint 的语句进行固定计划,则需要 TO target_stmt 来指明原始的 SQL。

⚠️ 重要约束:在使用 target_stmt 时,严格要求 stmt 与 target_stmt 在去掉 Hint 后完全匹配。

💡 示例说明:如下示例中,优化器选择了走主键扫描,如果数据量增大后走索引 idx_c2 更优,此时可以通过创建 Outline 将该 SQL 绑定索引计划。
-- 创建测试表
        CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT, c3 INT, INDEX idx_c2(c2));
        INSERT INTO t1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3);

        -- 查看默认执行计划(优化器选择全表扫描)
        EXPLAIN SELECT * FROM t1 WHERE c2 = 1;

        -- 创建 Outline 绑定索引计划
        CREATE OUTLINE otl_idx_c2 ON SELECT/*+ index(t1 idx_c2)*/ * FROM t1 WHERE c2 = 1;

        -- 使用 TO target_stmt 创建 Outline(针对含有 Hint 的原始 SQL 进行固定)
        CREATE OUTLINE outline1 ON SELECT /*+NO_REWRITE*/ * FROM tbl1 WHERE col1 = 4 AND col2 = 6 ORDER BY 2
            TO SELECT * FROM tbl1 WHERE col1 = 4 AND col2 = 6 ORDER BY 2;

上述示例来源:计划绑定-V4.3.5

🔢 使用 SQL_ID 创建 Outline

使用 SQL_ID 创建 Outline 的语法如下:

CREATE OUTLINE outline_name ON sql_id USING HINT hint;

参数说明:

  • sql_id:需要绑定的 SQL 对应的 SQL_ID,可通过查询 GV$OB_PLAN_CACHE_PLAN_STATGV$OB_SQL_AUDIT 表获取,也可通过 MD5 生成。
  • hint:格式为 /*+ xxx */,用于指定优化器行为。
  • 如果 sql_id 对应的 SQL 语句已有 Hint,则创建 Outline 指定的 Hint 会覆盖原始语句中所有 Hint。
-- 示例:使用 SQL_ID 创建 Outline
        CREATE OUTLINE otl_idx_c2 ON 'ED570339F2C856BA96008A29EDF04C74' USING HINT /*+ index(t1 idx_c2)*/ ;

上述示例来源:CREATE OUTLINE-V4.5.0

💡 生产环境推荐:在实际生产系统中,推荐通过 SQL_ID 进行 Outline 绑定。因为 SQL_ID 是由 SQL_TEXT 通过 MD5 加密得到,相同的 SQL 文本即使多一个空格、换行或制表符,MD5 得到的 SQL_ID 都会不同,使用 SQL_ID 方式可以有效避免因格式差异导致的匹配失败问题。

Outline 的优先级与覆盖规则

当同一个 SQL 同时满足多种 Outline 绑定方式时,遵循以下优先级规则:

  • 优先级高低:当 SQL_ID 相同时,使用 SQL_TEXT 方式创建的 Outline 会覆盖 SQL_ID 方式创建的 Outline,SQL_TEXT 方式创建的优先级更高。
  • Outline vs SPM:Outline(通过 CREATE OUTLINE 显式绑定)优先级最高,其次是 SPM 基线中的已接受计划,最后才是优化器自动生成的非基线计划。Outline 主要用于关键业务的计划锁定,SPM 则用于自动管理计划基线。当两者同时存在时,Outline 明确指定的计划具有最高优先级,系统不会考虑 SPM 中的其他计划。

验证 Outline 是否生效

创建 Outline 后,需要通过以下步骤确认 Outline 是否生效:

  1. 确认 Outline 创建成功:通过查看 GV$OUTLINE 表,确认是否成功创建了对应名称的 Outline。
    -- 查询 Outline 是否创建成功
            SELECT * FROM oceanbase.GV$OUTLINE WHERE outline_name = 'otl_idx_c2'\G;
  2. 确认 SQL 执行使用了绑定计划:当绑定了 Outline 的 SQL 有新的流量执行后,查询 GV$OB_PLAN_CACHE_PLAN_STAT 表中该 SQL 对应的计划信息,确认 OUTLINE_ID 字段是否为绑定的 Outline ID。
    -- 查看 SQL 实际使用的执行计划及 Outline 信息
            SELECT SQL_ID, PLAN_ID, OUTLINE_ID, OUTLINE_DATA 
            FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT 
            WHERE SQL_ID = 'ED570339F2C856BA96008A29EDF04C74';

管理 Outline

✏️ 修改 Outline

OceanBase 支持通过 ALTER OUTLINE 语句修改 Outline,仅支持修改使用 SQL_TEXT 创建的 Outline。语法如下:

ALTER OUTLINE outline_name ADD stmt [ TO target_stmt ];
⚠️ 注意事项:同一个 outline_name 只能指定一个执行计划。如果通过 CREATE OUTLINE 语句已经指定了执行计划,则无法通过执行 ALTER OUTLINE 时再添加。与 CREATE OUTLINE 类似,在 ALTER OUTLINE 时不能同时指定限流规则和执行计划。
🗑️ 删除 Outline

删除 Outline 后,对应 SQL 重新生成计划时将不再依据绑定的 Outline 生成。删除 Outline 需要在 outline_name 中指定数据库名称,或者先执行 USE database_name 后再删除。

-- 删除 Outline(需在对应数据库下执行,或指定完整名称)
        DROP OUTLINE otl_idx_c2;

        -- 指定数据库名称删除
        DROP OUTLINE test.otl_idx_c2;

Outline 监控视图

OceanBase 提供了完整的 Outline 监控视图体系,从 V4.0.0 版本开始引入,帮助 DBA 查看和管理 Outline 信息。

📊 核心视图说明
视图名称引入版本功能描述
DBA_OB_OUTLINES / CDB_OB_OUTLINES V4.0.0 展示当前租户/所有租户的执行计划 Outline 信息
GV$OUTLINE V3.x 开始引入 展示当前租户的 Outline 详细信息,用于快速确认 Outline 创建结果
DBA_OB_OUTLINE_CONCURRENT_HISTORY V4.0.0 展示当前租户的执行计划和限流规则 Outline 历史信息,可用于排查 Outline 的变更历史
📋 查询示例
-- 查看当前租户的执行计划 Outline 信息
        SELECT OUTLINE_NAME, SQL_TEXT, OUTLINE_TARGET, SQL_ID, OUTLINE_CONTENT
        FROM oceanbase.DBA_OB_OUTLINES;

        -- 查看 Outline 历史信息(用于排查变更记录)
        SELECT * FROM oceanbase.DBA_OB_OUTLINE_CONCURRENT_HISTORY\G;
📖 视图字段说明
  • OUTLINE_NAME:Outline 名称
  • SQL_TEXT:创建 Outline 时,在 ON 子句中指定的 SQL
  • OUTLINE_TARGET:创建 Outline 时,在 TO 子句中指定的 SQL
  • SQL_ID:SQL 标识符
  • OUTLINE_CONTENT:完整的执行计划 Outline 信息(包含 Outline Data)

字段定义来源:oceanbase.DBA_OB_OUTLINES-V4.3.3

Hint、Outline 与 SPM 的定位对比

在 OceanBase 中,Hint、Outline 和 SPM 虽然都用于管理执行计划,但它们的定位和使用场景有明显区别。了解三者的区别有助于在合适场景选择正确的计划管理手段。

维度HintOutlineSPM
介入时机 开发/测试阶段 线上应急 长期稳定运行
是否修改代码 需要 不需要(DDL) 不需要(自动)
自动化程度 手动 手动 自动捕获 + 演进
持久化 随 SQL 语句 持久化到系统表 持久化到 Plan Baseline
适用版本 全版本 全版本(V4.x 前主要手段) V4.x 及以上推荐
💡 选型建议
SPM 是 OceanBase V4.x 及以上版本推荐的长期计划管理方案,提供系统级的自动捕获、验证和演进能力,能够有效防止计划退化。
Outline 适用于线上突发性能问题的快速止血,当业务代码无法立即变更时,通过 DDL 在线绑定执行计划。
Hint 适用于开发阶段的主动调优和性能测试验证,直接在 SQL 语句中指定优化策略。
🏆 4.x 版本演进总结
Outline 视图体系完善:从 V4.0.0 版本开始引入 DBA_OB_OUTLINESDBA_OB_OUTLINE_CONCURRENT_HISTORY 等视图,方便查看和管理 Outline。
SPM 机制增强:V4.x 版本正式推荐使用 SPM 作为长期计划管理方案,支持自动计划捕获、验证和演进。
优先级明确:Outline 仍作为最高优先级的计划固定手段,确保关键业务 SQL 的计划稳定性。
V4.x 之前的版本缺陷:V4.0 之前的版本 SPM 存在 Schema 内存占用大、影响 DDL 和重启等问题,禁止使用;Outline 是当时主要的计划固定手段。
📌 最佳实践建议
  • 生产环境推荐 SQL_ID 方式:优先使用 SQL_ID 创建 Outline,可有效避免因 SQL_TEXT 格式差异(如空格、换行)导致的匹配失败问题。
  • 验证 Outline 效果:创建 Outline 后,建议先通过 EXPLAIN 确认计划符合预期,再观察业务低峰期流量,确保绑定生效且性能提升。
  • 限制 Outline 数量:一个 SQL 只绑定一个 Outline,避免因多个 Outline 匹配同一 SQL 导致行为不确定。
  • 定期清理无效 Outline:对于已删除或结构变更的表,关联的 Outline 会自动失效,建议定期查询 DBA_OB_OUTLINES 清理无效 Outline。
  • 优先采用 SPM 替代 Outline:在 V4.x 版本中,对于需要长期稳定的 SQL,建议优先使用 SPM 机制,以获得自动化的计划管理能力。
5.4 Sysbench 性能压测与调优

Sysbench 是一个基于 LuaJIT 的可编写脚本的多线程基准测试工具,可以执行 CPU、内存、线程、IO 和数据库等方面的性能测试,常用于评估测试各种不同系统参数下的数据库负载情况,不需要修改源码,通过自定义 lua 脚本就可以实现不同业务类型的测试。通过 Sysbench 压测,可以验证 OceanBase 在 OLTP 场景下的性能上限、定位系统瓶颈,并为生产环境的容量规划提供数据支撑。

📌 Sysbench 压测的核心目标
  • 确定吞吐上限:找到租户在不同规格下的稳定 QPS/TPS 峰值。
  • 验证延迟稳定性:关注高并发下的 P95/P99 延迟是否满足业务 SLA。
  • 定位性能瓶颈:通过压测发现 CPU、内存、网络或 I/O 方面的瓶颈点。

测试前准备

在开始 Sysbench 压测之前,建议完成以下准备工作,以确保测试结果的准确性和可复现性。

软硬件环境
组件版本/规格建议说明
OceanBase 数据库V4.2.0 及以上版本建议单独部署 ODP
ODP(OBProxy)单独部署在一台机器上高并发测试中,ODP 的 CPU 消耗显著,需预留足够资源
Sysbench1.0.20 或更高版本建议优先使用 1.0 及以上版本
OBClient2.2.0 及以上版本当 OBClient 版本 >= 2.2.0 时,需关闭 ob20 协议以提升性能
磁盘建议磁盘 IOPS 在 10000 以上使用高性能 SSD 或 NVMe 磁盘
机器部署建议当 OBServer 总核数 ≤ 47 核时,压测客户端至少 8 核;超出时配置更高详见 OBServer 核数对压测客户端的要求
租户参数调优

创建专门用于压测的业务租户,并完成以下基础参数配置,以发挥最佳性能:

  • 资源单元:根据测试目标创建合适的 RESOURCE UNIT,确保分配足够的 CPU 和内存资源,建议参考官方性能测试报告中的规格配置。
  • PRIMARY_ZONE:建议设置为 RANDOM,使新建表分区的 Leader 均匀分布在 Zone 之间,避免单节点成为瓶颈。
  • 租户规格查看:压测前可通过 SHOW CREATE TENANT xxx;DBA_OB_UNITS 等视图确认租户的 CPU 和内存分配是否符合预期。
-- 创建压测专用资源单元和租户的示例(硬性规格需根据实际硬件调整)
        CREATE RESOURCE UNIT sysbench_unit MAX_CPU 26, MIN_CPU 26, MEMORY_SIZE '100G';
        CREATE RESOURCE POOL sysbench_pool UNIT='sysbench_unit', UNIT_NUM=1, ZONE_LIST=('zone1','zone2','zone3');
        CREATE TENANT sysbench_tenant resource_pool_list=('sysbench_pool'), PRIMARY_ZONE='RANDOM',
        LOCALITY='F@zone1,F@zone2,F@zone3' SET ob_compatibility_mode='mysql', ob_tcp_invited_nodes='%';
⚠️ 以上租户规格基于官方 Sysbench 性能测试报告中的硬件配置设定,请根据您的实际硬件配置动态调整。部署集群时,建议不要使用 obd cluster autodeploy 命令,因为它为了保障稳定性不会最大化资源利用率,建议单独对配置文件进行调优,最大化资源利用率。
数据库参数调优

下表基于 Sysbench 压测场景总结了部分关键参数的最佳实践:

参数默认值压测推荐值/说明
memstore_limit_percentage0(自适应)高写入场景建议调高至 50%~70%,提升 MemStore 容量,配合 freeze_trigger_percentage 尽快触发转储,避免内存写满时触发写限流,导致性能下降。
freeze_trigger_percentage20(V4.x)系统默认值已可满足多数压测场景。若发现内存压力过大导致性能下降,可尝试降低此值,尽早触发转储。
writing_throttling_trigger_percentage60高并发压测时建议禁用或调高阈值(例如设为 100),以避免触发限流导致 TPS 下降。
_ob_compaction_throttling1隐藏配置项,控制合并限速:0.2(半速),1(全速),2(关闭)。使用 SSD 盘时建议设为 0.2,降低 Compaction 对压测的影响。
memory_limit_percentage80建议设置为较大值(如 80~90),让 OBServer 可使用更多物理内存。
sys_bkgd_net_percentage60%后台网络任务(如备份恢复)带宽占比。压测期间若带宽紧张,可适当降低此值,腾出带宽给业务流量。
net_thread_count0(自适应)若观察到 RpcIO 网络线程 CPU 打满,可能是网络线程数偏少导致网络通信成为瓶颈,可适当增加该值(注意修改后需重启 OBServer)。

注意:压测场景通常以追求极致性能为目标,与生产环境侧重稳定性的调优方向可能不同。生产环境下应谨慎调整限流和合并相关参数。

OBClient 与 ODP 调优
  • 当 OBClient 版本 ≥ 2.2.0 时,会默认开启 ob20 协议及全链路追踪能力,在 Sysbench 测试中会影响性能,建议通过设置环境变量手动关闭:export ENABLE_PROTOCOL_OB20=0
  • ODP 的高并发处理能力需提前验证,建议预先测试 ODP 在目标并发下的 CPU 消耗,避免代理层成为瓶颈。
  • 应用侧应启用连接池复用,并限制单实例最大连接数,防止在高并发下因频繁建连/断连消耗 ODP 资源。

执行压测

OceanBase 支持两种方式进行 Sysbench 测试,可根据实际需求灵活选择。

方式一:OBD test 命令一键压测(推荐)

OBD(OceanBase Deployer)提供了 test 命令,可一键完成 Sysbench 测试,是最快捷的方式。

# 运行 Sysbench 测试(示例)
        obd test sysbench obcluster --tenant=sysbench_tenant --user=root@sysbench_tenant \
        --password=123456 --script=point_select --tables=30 --table-size=1000000 \
        --threads=32 --time=60

OBD test 命令中 --tenant 参数只能指定一个租户名,--user 仅需指定用户名,无需重复指定数据库名。

方式二:基于官方 Sysbench 工具手动逐步测试

这种方式提供了更高的灵活性,支持自定义测试场景和参数组合。

# 1. 准备测试数据(prepare)
        sysbench /usr/local/share/sysbench/oltp_read_write.lua \
        --mysql-host=127.0.0.1 --mysql-port=2883 \
        --mysql-user=root@sysbench_tenant --mysql-password=123456 \
        --mysql-db=test --tables=30 --table-size=1000000 \
        --threads=32 --db-ps-mode=disable --rand-type=uniform prepare

        # 2. 执行压测(run)
        sysbench /usr/local/share/sysbench/oltp_read_write.lua \
        --mysql-host=127.0.0.1 --mysql-port=2883 \
        --mysql-user=root@sysbench_tenant --mysql-password=123456 \
        --mysql-db=test --tables=30 --table-size=1000000 \
        --threads=32 --time=60 --report-interval=10 \
        --db-ps-mode=disable --rand-type=uniform run

        # 3. 清理测试数据(cleanup)
        sysbench /usr/local/share/sysbench/oltp_read_write.lua \
        --mysql-host=127.0.0.1 --mysql-port=2883 \
        --mysql-user=root@sysbench_tenant --mysql-password=123456 \
        --mysql-db=test cleanup

参数说明:--db-ps-mode=disable 可关闭 prepare statement,避免在测试中产生额外的 prepare 开销;--rand-type=uniform 建议设置为 uniform,以减少数据间的锁竞争,避免因随机分布导致的热点问题。

📊 Sysbench 常用压测模型
  • point_select.lua:点查场景,最基础的读性能测试。
  • read_only.lua:只读场景,模拟纯读负载。
  • write_only.lua:只写场景,测试写入吞吐能力。
  • read_write.lua:混合读写场景,更贴近真实业务负载。
  • 如果仅需进行性能对比,可只执行 run 命令;如需模拟每次测试都有独立数据集的模式,则需按 clean → prepare → run 的完整流程进行。

压测结果分析

完成压测后,应关注以下核心性能指标,并与预期目标进行对比。

性能基线参考(V4.2.2,3 节点 1-1-1 规格)

以下为官方 Sysbench 性能测试报告中的部分结果,可作为基准参考(实际性能取决于硬件配置):

测试场景线程数QPS(V4.2.2)95% 延迟(ms)
Point Select(点查)32150,002.690.24
Point Select(点查)256791,088.580.46
Point Select(点查)5121,041,150.670.83
Read Only(只读)32131,753.274.18
Read Only(只读)256638,432.407.30
Write Only(只写)3246,505.456.09
Write Only(只写)256227,430.998.74
Write Only(只写)512292,273.1014.46

数据来源:OceanBase 官方性能测试报告。

关键指标解读
  • QPS / TPS:吞吐量指标。若随并发增加不再线性增长,说明系统可能已达到处理瓶颈。
  • 延迟(Latency):重点关注 P95 和 P99 延迟。如果延迟突然陡升,可能由锁等待、内存不足或后台任务(合并/转储)干扰导致。
  • CPU 使用率:通过 top -H -p pidof observer 观察各业务线程(如 T1002_L0_G2)的 CPU 占用情况,判断瓶颈类型。
  • 成功率/错误数:若出现大量错误,需检查是否存在主键冲突、死锁或超时等异常情况。

问题排查与优化

Sysbench 的 TPS 低于预期

当压测结果达不到预期时,可以按照以下步骤进行系统化的排查:

  1. 分析 Sysbench 参数:检查 --rand-type 是否为 uniform,--db-ps-mode 是否已 disable。表数量和大小过小时可适当调大(如 tables=100, table_size=1,000,000)。
  2. 确认连接方式PRIMARY_ZONE = RANDOM 时必须连接 ODP 进行测试;若 Primary Zone 为单 Zone 且直连 OBServer,需确认连接的是 Leader 节点,否则读请求可能被转发到 Follower。
  3. 观察 CPU 开销top -H -p pidof observer
    • 若业务线程基本打满一个 CPU 核且线程数量接近 CPU 总核数 → 机器性能已达极限,需使用更高配置的服务器。
    • 若业务线程数量远低于 CPU 核数且 CPU 总开销远未达上限 → cpu_count 可能分配过少,需检查租户创建命令。
    • 若业务线程 CPU 占用仅为 20%~50% → 其他地方(网络/磁盘/锁)存在瓶颈。
  4. 分析硬件原因:排除 CPU 因素后,排查磁盘和网络瓶颈——确认网卡带宽上限(ethtool),用 ping 测试网络延迟,或用 FIO 测试日志盘的 16K 写入带宽。
  5. 检查后台任务干扰:查看压测期间是否触发了转储/合并(可通过 GV$OB_COMPACTION_PROGRESS 或 OCP 监控确认),导致 CPU 飙升并拉低 TPS。转储合并的影响程度与租户规格相关,4C8G 租户下 TPS 可能下降 80%,而 32C64G 租户下降幅度可控制在 10% 左右。
常见报错与解决
  • ERROR 6002(死锁相关):可能因并发写入同一行导致。建议调大数据集、增加 tables 数量(如 tables=100),或将 --rand-type 设为 uniform 减少锁冲突,也可在 Sysbench 命令中添加 --mysql-ignore-errors=1062,1213,6002 忽略部分可预期错误。
  • 主键冲突(Duplicate entry):通常由 Sysbench 生成的随机数据碰撞导致。可增加 tables 数量降低碰撞概率,或使用新版 OBD test 命令进行压测以避免部分问题。
  • ERROR 4012(超时):检查压测租户的 ob_query_timeoutob_trx_timeout 是否设置过小,或是压测 SQL 执行效率低下触发超时。
  • 网络线程(RpcIO)打满 CPU:可能因网络通信成为瓶颈,可尝试增加 net_thread_count(修改后需重启 OBServer)。
⚠️ 特殊场景案例:kprobe 内核探针导致性能下降

在压测环境中,发现 %sys 内核态 CPU 利用率高达 25%~30%,压测指标比期望值低 30% 以上。排查发现,操作系统底层的一些频繁调用的系统内核函数(如 kretprobe_hash_unlock)被主机安全产品注册到了 kprobe 列表中。执行 echo 0 > /sys/kernel/debug/kprobes/enabled 关闭 kprobe 后,%sys CPU 大幅下降,压测指标回升。

结果验证与后续优化
  • 稳定性验证:建议执行 4 小时以上的长稳压测(--time=14400),重点关注长时间运行下是否出现性能衰减、内存泄漏或服务不稳定。
  • 反哺生产配置:将压测中验证有效的最佳实践参数同步到生产环境配置模板中,并在重大版本升级后重新执行压测,验证性能是否符合预期。
  • 若 QPS 波动呈现规律性下降,可通过查询视图 GV$OB_COMPACTION_PROGRESS 判断是否为转储合并操作导致,转储合并会占 CPU 资源,导致业务线程受影响。
🏆 4.x 版本 Sysbench 测试关键变化总结
一键压测支持:V4.0.0 版本之后提供 OBD test 命令,极大简化了压测流程。
OBClient 协议兼容:OBClient 2.2.0 及以上版本默认开启 ob20 协议,压测时需手动关闭以保证性能。
租户级参数演进freeze_trigger_percentage 默认值从 V3.x 的 70 调整为 20,memstore_limit_percentage 默认值从 50 调整为自适应。
路由与连接方式优化:V4.x 推荐使用 ODP 连接并进行压测,尤其当租户 PRIMARY_ZONE 为 RANDOM 时,必须通过 ODP 保证请求的均匀分发。
5.5 SQL 计划管理(SPM)

在 OceanBase 数据库中,优化器通常会选择最优的执行计划。然而,由于统计信息的滞后、数据分布的变化或系统版本的升级,SQL 的执行计划有时会发生意料之外的“跳变”(plan regression),从高效计划退化为低效计划,严重影响业务性能。SQL 计划管理(SQL Plan Management,以下简称 SPM)正是为此而设计的一套自动化计划治理机制。它通过建立和演进 “SQL 计划基线”,确保只有经过实际验证的新计划才能被使用,从而有效防止计划回退。

🎯 SPM 的核心价值

SPM 在 OceanBase 4.x 及以上版本中,是推荐的、系统级的长期计划管理方案。与需要手动介入的 Outline(执行计划纲要)不同,SPM 的主要价值在于自动化和演进能力:它能在业务无感知的情况下,自动捕获新生成的高效计划,并在验证其性能优于当前基线后,平稳地替换旧计划,实现“计划越用越好”。

具体而言,SPM 在以下典型场景中发挥着关键作用:

  • 大/小账号数据倾斜的“计划走偏”:业务表中数据存在倾斜时,优化器可能因初始查询使用特定账号而生成错误的索引选择。SPM 可以通过基线机制保证已验证的优计划被使用,并及时验证新计划的性能,避免回退。
  • 分页查询因代价估算不准导致计划失效:在数据分布不均或索引选择不佳的场景下,优化器容易选错索引导致性能下降,SPM 可避免因计划走偏引起的性能问题。
  • 升级后优化器行为变化引起计划劣化:当数据库做版本升级后,新版本的优化器行为可能产生细微差异,选错索引或连接算法。SPM 能有效验证新旧计划的性能差异,避免使用走偏的计划引起性能回退。

需要注意的是,SPM 本身并不会主动去产生新的执行计划,因此该机制是一种“防回退”的机制,而不是一种用来“产生更优计划”的机制。

核心工作原理

SPM 基于 SQL Plan Baseline 实现。每个 Plan Baseline 持久化存储一个已经验证过的执行计划信息(即 `outline_data`),通过这些信息可以精确复现该计划。

SPM 的工作流程遵循“先捕获,再演进”的自动化循环:

  1. 计划捕获:当系统为某个 SQL 生成新计划时,如果 SPM 中被开启(`optimizer_use_sql_plan_baselines=true`),新计划将自动被捕获并加入演进队列;否则仅做基线匹配。
  2. 计划演进:当新计划与基线计划不同时,系统会自动启动一个演进任务,利用随后的真实业务流量进行“金丝雀(Canary)测试”,持续监控新计划的性能表现。这包括执行时间、物理/逻辑读、并发度等实际指标。
  3. 计划选择:在演进完成后,如果验证发现新计划性能优于基线,则自动将新计划标记为 `ACCEPTED` 并替代原有基线;如果性能不及预期,则忽略该计划,后续执行继续使用基线。

值得注意的是,对于一条没有基线计划的 SQL,其第一次执行成功的计划会被系统默认标记为 `ACCEPTED` 状态,成为该 SQL 的初始计划基线。因此,在 SQL 上线前确保其使用了最优的执行计划至关重要。

开启与关闭 SPM

⚠️ 关键版本说明

在 OceanBase 数据库 V4.0 之前版本,SPM 的实现机制会由 SQL 自动生成 `plan_baseline` 的 schema 对象并通过 DDL 同步给整个集群。这可能导致以下严重问题,因此 V4.0 之前的版本须强制禁用 SPM 功能

  • 影响业务 DDL 执行:由于 DDL 是集群内串行执行,自动触发的大量 DDL 可能阻塞业务正常的 DDL 操作。
  • schema 内存占用巨大且只增不减:`plan_baseline` schema 对象缺乏自动清理机制,随着 SQL 数量增多,schema 内存占用会持续膨胀。
  • 影响 OBServer 重启稳定性:schema 内存占用过大会导致 OBServer 重启全量刷新 schema 耗时过长,甚至超时。

V4.x 及之后版本,SPM 的功能已经过重构,不再走 DDL 及依赖 schema,是推荐长期使用的计划管理方案。

SPM 通过两个核心租户级系统变量来控制其行为,正确的组合设置非常重要。下表列出了所有组合下系统的实际行为:

optimizer_capture_sql_plan_baselines optimizer_use_sql_plan_baselines 组合效果
true true ✅ 计划捕获 + 演进 + 基线使用(生产环境推荐组合)。系统会自动捕获新计划、进行金丝雀验证,并在验证通过后自动替换旧基线。
true false ⚠️ 计划捕获 + 演进 关闭。新计划在首次无基线时会自动添加至基线,但后续不进行演进验证,优化器直接使用新计划。此组合下优化器不会考虑基线中的已有计划。
false true ⚠️ 仅使用现有基线,不会捕获新计划或进行演进。若某 SQL 无计划基线,则优化器按默认方式生成计划并直接执行。
false false ❌ SPM 功能完全关闭。优化器使用默认方式生成计划,不会读取或保存任何 Plan Baseline 信息。
-- 1. 查看当前 SPM 状态(建议在业务租户下执行)
        SHOW VARIABLES LIKE 'optimizer_use_sql_plan_baselines';
        SHOW VARIABLES LIKE 'optimizer_capture_sql_plan_baselines';

        -- 2. 完整开启 SPM 功能(生产环境推荐配置)
        SET GLOBAL optimizer_capture_sql_plan_baselines = 'true';
        SET GLOBAL optimizer_use_sql_plan_baselines = 'true';

        -- 3. 完全关闭 SPM 功能(V4.x 版本请谨慎使用,关闭后基线不生效)
        SET GLOBAL optimizer_use_sql_plan_baselines = 'false';
        SET GLOBAL optimizer_capture_sql_plan_baselines = 'false';

        -- 4. 查看当前租户的 SPM 配置参数(可通过视图查询)
        SELECT * FROM oceanbase.DBA_SQL_MANAGEMENT_CONFIG;

手动干预与演进验证

虽然 SPM 设计上主张自动化,但在某些需要人为确认或批量导入的场景下,OceanBase 提供了 DBMS_SPM 系统包进行精细控制:

  • DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE:从计划缓存中批量加载 SQL 计划,构建基线。
  • DBMS_SPM.ACCEPT_SQL_PLAN_BASELINE:手动接受某个演进中的计划,将其状态从 `REJECTED` 或演进队列中改为 `ACCEPTED`,从而使该计划成为新的基线。
  • DBMS_SPM.DROP_SQL_PLAN_BASELINE:从基线中删除指定的计划,返回值为 `PLS_INTEGER`,表示删除的计划数量。
-- 使用 DBMS_SPM 手动接受一个演进计划
        DECLARE
            v_accept_plans NUMBER;
        BEGIN
            v_accept_plans := DBMS_SPM.ACCEPT_SQL_PLAN_BASELINE(
                sql_handle  => '529F6E6454EF579C7CC265D1F6131D70',
                plan_name   => '3388268709115914355'
            );
            DBMS_OUTPUT.PUT_LINE('Number of accepted plans: ' || v_accept_plans);
        END;
        /
⚠️ 演进结果的节点同步延迟:在 OceanBase 的多节点集群中,演进结果会先暂存在每个 OBServer 节点的本地缓存中,然后周期性地同步到内部系统表。这意味着某个节点上刚刚完成的演进结果,无法立即被其他节点感知。如果遇到这种“计划偶尔不对”的情况,请耐心等待缓存同步完成,通常为秒级到分钟级。

SPM 核心状态枚举与基线选择逻辑

通过 `DBA_SQL_PLAN_BASELINES` 视图,可以详细查看每个 Plan Baseline 的状态,不同状态决定了计划的最终选择和演进行为:

  • ENABLED = 'YES' / `'NO'`:计划是否启用,只有 `YES` 时才会被纳入基线管理和选择考虑。
  • ACCEPTED = 'YES' / `'NO'`:计划是否被验证通过,`YES` 表示该计划已被验证为优,`NO` 表示计划尚在演进中或未被确认。
  • FIXED = 'YES' / `'NO'`:计划是否被用户“锁定”。当基线中的某个计划被标记为 `FIXED` 后,它将拥有最高优先级,系统将无条件优先使用该计划,并且不会触发任何演进任务,直到该计划被解除锁定。

此外,部分失效的 Plan Baseline 会被系统自动设置为 `REPRODUCED = 'NO'`,表示因依赖对象(如下线索引)的删除而无法成功复现执行计划。

-- 查看当前租户的计划基线详情
        SELECT SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED, REPRODUCED 
        FROM oceanbase.DBA_SQL_PLAN_BASELINES
        WHERE SIGNATURE = 'your_sql_signature';

        -- 查看基线中所有计划的信息
        SELECT * FROM oceanbase.DBA_SQL_PLAN_BASELINES\G

SPM 的使用限制

在 OceanBase 4.x 版本中,SPM 功能存在以下使用限制,设计和验证时需注意:

  • 恢复中的租户(under recovery)和主备部署中的备租户(standby cluster),无法执行计划演进。
  • `sys` 租户相关的 SQL 语句,以及数据库内部的 SQL 语句,均不参与计划演进。
  • 包含 `INSERT INTO VALUES` 子句的 SQL 语句,不参与计划演进。

监控 SPM 运行状态

OceanBase 提供了系统视图用于监控 SPM 的运行状态和配置,从 V4.0.0 版本开始引入:

  • oceanbase.DBA_SQL_PLAN_BASELINES:展示当前租户中所有的 SQL 计划基线。包含该计划基线的创建方式(`ORIGIN` 值:`AUTO-CAPTURE` 自动捕获,`MANUAL-LOAD` 手动加载等)、创建者、启用/采纳/固定状态等信息。
  • oceanbase.DBA_SQL_MANAGEMENT_CONFIG:展示当前租户的 SPM 配置参数,如基线自动保留策略、演进任务开关、演进间隔等。其中 PLAN_RETENTION_WEEKS 控制 Plan Baseline 的保留周期,SPACE_BUDGET_PERCENT 控制可用内存占比,AUTO_SPM_EVOLVE_TASK 表示自动演进开关。
-- 查看当前租户的所有计划基线信息
        SELECT * FROM oceanbase.DBA_SQL_PLAN_BASELINES\G

        -- 查看当前租户的 SPM 相关配置(基线保留策略等)
        SELECT PARAMETER_NAME, PARAMETER_VALUE FROM oceanbase.DBA_SQL_MANAGEMENT_CONFIG;

        -- 调整 Plan Baseline 的保留周期(默认为 53 周)
        UPDATE oceanbase.DBA_SQL_MANAGEMENT_CONFIG 
        SET PARAMETER_VALUE = 52 WHERE PARAMETER_NAME = 'PLAN_RETENTION_WEEKS';

        -- 关闭自动演进功能
        UPDATE oceanbase.DBA_SQL_MANAGEMENT_CONFIG 
        SET PARAMETER_VALUE = 'OFF' WHERE PARAMETER_NAME = 'AUTO_SPM_EVOLVE_TASK';
📌 SPM 使用最佳实践
  • 优先选择 V4.x 版本:SPM 在 V4.0 版本后经过重构,推荐在 V4.x 及以上版本中长期使用。V4.0 之前的版本因存在严重的稳定性风险,必须禁用。
  • 确保首次计划最优:一条 SQL 首次执行成功的计划会被自动标记为 `ACCEPTED`,成为计划基线。在执行重要 SQL 上线前,应确保其执行计划是最优的,或在 SPM 完全开启前手动导入已知优计划。
  • 防止基线污染:在业务低峰期且 SQL 性能稳定时,建议先手动导入 Baseline,再开启自动捕获。避免在早期(如空表或小数据量)的不具代表性的计划被错误地固化。
  • 利用 `FIXED` 强制锁定核心计划:对于关键或高频的“核心 SQL”,可以在确认其计划性能最优后,手动将其标记为 `FIXED` 状态以完全锁定计划。标记为 `FIXED` 后,系统将无视后续任何新计划,也不会进行演进,直至解除锁定。
  • 定期审视低效基线:建议建立定期巡检机制,利用 `DBA_SQL_PLAN_BASELINES` 视图发现那些 `ACCEPTED` 但成本较高,或 `LAST_EXECUTED` 很久未变化的“僵尸”基线,并考虑将其 `ENABLED` 设为 `'NO'` 或删除,以释放资源。
🏆 计划管理方案定位对比

在 OceanBase 中,Hint、Outline 和 SPM 共同构成了完备的“开发态 → 运维态 → 治理态”计划治理体系:

  • Hint:适用于开发阶段的主动调优和性能测试验证,直接嵌入 SQL 语句中,需修改代码。适合快速验证特定执行路径,不影响线上。
  • Outline:适用于线上突发性能问题的“快速止血”,在不修改业务代码的前提下,由 DBA 手动通过 DDL 为某个 SQL 绑定一组 Hint 紧急固定计划。是 V4.x 之前版本的主要计划固定手段。
  • SPM:适用于系统的、自动化的长期计划治理。开启后,系统自动捕获和演进计划,是 V4.x 版本推荐的长效方案。

三者可根据从“开发”到“线上应急”再到“长期治理”的不同阶段组合使用,但需注意当 Outline 和 SPM 同时存在时,Outline 明确指定的计划具有最高优先级。

第6章:资源隔离与内部机制

6.1 租户间资源隔离原理

OceanBase 数据库是一个原生支持多租户架构的分布式数据库系统。为了确保不同租户间的业务稳定运行、避免资源争抢,OceanBase 在数据库内部实现了完善的租户间资源隔离机制,对 CPU、内存、IOPS 等关键资源进行精细化管理。

🎯 资源隔离的核心价值
  • 性能保障:确保关键业务租户始终获得预设的资源配额,避免因其他租户的突发负载导致性能波动。
  • 成本控制:通过资源隔离实现多业务系统共享同一物理集群,提高硬件资源利用率,降低总体拥有成本(TCO)。
  • 稳定性增强:单个租户的异常负载不会扩散到其他租户,保障集群整体稳定性。
  • 弹性伸缩:支持租户规格的动态调整,适应业务负载的变化。

资源隔离的基本单位:Unit

在 OceanBase 数据库中,资源单元(Unit)是给租户分配资源的基本单位。一个 Unit 可以类比于一个 Docker 容器,它定义了 CPU、内存、日志磁盘空间和 IOPS 等资源的规格。Unit 具有节点、Zone、Region 等位置属性,节点是服务器的抽象,Zone 是机房的抽象,Region 则是地域的抽象。

Unit 的关键特性

  • 一个节点上可以创建多个 Unit,每创建一个 Unit 都会占用一部分该节点的物理资源。
  • 一个租户可以在多个节点上放置多个 Unit,但一个租户在某个节点上只能有一个 Unit。
  • 租户的多个 Unit 相互独立,资源隔离是节点本地的行为——如果一个租户在某个节点上的资源未得到满足,它不会在另一个节点上抢占其他租户的资源。
  • 资源单元是资源调度的基本单位,节点的资源分配情况会记录在内部表中以便 DBA 查看。
Unit 的创建与管理
-- 创建资源单元配置(Unit Config)
        CREATE RESOURCE UNIT unit1 
            MAX_CPU=8, MIN_CPU=4, 
            MEMORY_SIZE='32G', 
            MAX_IOPS=128000, MIN_IOPS=128000, 
            LOG_DISK_SIZE='2T';

        -- 查看集群中所有 Unit 配置
        SELECT * FROM oceanbase.DBA_OB_UNIT_CONFIGS;

        -- 查看当前集群中各个节点上的 Unit 分布
        SELECT TENANT_ID, UNIT_ID, SVR_IP, SVR_PORT, MAX_CPU, MEMORY_SIZE
        FROM oceanbase.GV$OB_UNITS;

OceanBase 数据库并没有依赖 Docker 或虚拟机技术来实现资源隔离,而是在数据库内核中直接实现,相比 Docker 和虚拟机更加轻量,并且便于实现优先级等高级特性。

租户内存隔离

OceanBase 实现了内存完全隔离,内存被视为“刚性资源”,分配后无法被其他租户使用,必须显式释放。一个租户的内存耗尽可能导致该租户的写入操作受阻,但不会影响到其他租户的正常运行。

租户内存的组成结构

每个租户的内存由用户租户本身及其对应的 Meta 租户的内存共同组成,可划分为两大类别:

  • 不可动态伸缩内存(Non-auto-scaling Memory):主要包括 MemStore(存储增量数据)和 SQL Work Area(SQL 排序等阻塞算子使用的工作区内存)。
  • 可动态伸缩内存(Auto-scaling Memory / KVCache):由 KVCache 统一管理,包括 Row Cache(数据行缓存)、Block Cache(数据块缓存)、Schema Cache(表结构元数据缓存)等。KVCache 会尽量使用除去不可动态伸缩内存后的全部租户内存,当租户内存紧张时,会优先从中淘汰未被引用的内存。
💡 租户与 Meta 租户的内存隔离:每个用户租户都有一个对应的 Meta 租户,两者内存资源需要隔离,不支持共享。默认 Meta 租户占整体租户规格的 10%。为了保证 Meta 租户正常运行,Meta 租户内存资源规格最小为 512M,不设最大值。整体租户规格最小值调整为 1G。
内存分配的具体规则为:
- 租户规格 ≥ 10G 时,Meta 租户和用户租户内存比例为 1:9。
- 租户规格 ≥ 2G 时,Meta 租户的内存规格固定为 1G,剩余资源给用户租户。
- 租户规格 < 2G 时,Meta 租户固定分配 512M,剩余资源给用户租户。
核心内存配置参数
参数名称默认值作用说明
memstore_limit_percentage V4.2.3 之前为 50%;V4.2.3 起默认值为 0,表示系统根据租户内存规格自适应调整:8G 以下规格为 40%,8G 及以上为 50% 控制租户 MemStore 占租户总内存上限的百分比,用于存储增量数据。
ob_sql_work_area_percentage 5% 控制 SQL 阻塞算子(如排序、Group By、Union 等)工作区内存占用上限。如果请求并发较大且每个请求占用工作区内存较多,可通过调大该值来规避内存不足报错。
freeze_trigger_percentage 20% 当 MemStore 使用率达到该阈值时触发冻结(Minor Freeze),将内存数据转储到磁盘,避免内存写满导致写入阻塞。
writing_throttling_trigger_percentage 60% 当 MemStore 使用率达到该阈值时启用写入限流,控制写入速度,防止 MemStore 写满后触发不必要的转储操作。
⚠️ V4.x 版本演进说明:从 V4.2.3 版本开始,memstore_limit_percentage 默认值从 50 调整为 0,表示系统采用自适应策略,不再需要手动配置。从 V4.3.0 版本开始,还支持通过租户级隐藏配置项 _memstore_limit_percentage 来配置租户 MemStore 占租户总内存的百分比。当同时配置了租户级和集群级参数时,以租户级隐藏配置项的值为准。

租户 CPU 隔离

OceanBase 从 V4.0.0 版本开始支持租户间 CPU、内存、IOPS 隔离,一个 Unit 可以指定 CPU、内存、IOPS、日志磁盘空间四种资源。一个租户能使用的 CPU 资源由 Unit 规格决定。CPU 被视为弹性资源,可通过调度算法在不同租户间动态分配。

基于线程数的软隔离(基础方案)

这是 OceanBase 最基础的 CPU 隔离机制,通过控制每个租户的活跃工作线程数量来实现 CPU 资源限制。核心原理是:每个租户有独立的线程池,线程池大小由 Unit 规格中的 MAX_CPUMIN_CPU 决定,通过控制并发执行的线程数间接限制 CPU 使用。

OBServer 采用以下公式计算租户的工作线程数:

-- 活跃工作线程数 = cpu_count * cpu_quota_concurrency(默认为 4)
        -- 线程总数上限 = cpu_count * workers_per_cpu_quota

重要说明cpu_quota_concurrency 默认为 4,表示每个物理 CPU 核对应 4 个工作线程。这种设计是因为 SQL 执行过程中可能存在 IO 等待、锁等待等情况,单个线程无法完全利用一个 CPU 核心。

-- 创建资源单元时指定 CPU 规格
        CREATE RESOURCE UNIT web_unit 
            MAX_CPU=8,   -- 最大可使用的 CPU 核数,决定线程池上限
            MIN_CPU=4,   -- 保障的最低 CPU 核数(开启超卖时有效)
            MEMORY_SIZE='16G', 
            LOG_DISK_SIZE='20G';
基于 cgroup 的硬隔离(增强方案)

为了达到更好的隔离效果,从 V4.0.0 版本开始,OceanBase 支持配置 cgroup(Control Groups)来实现更精确的 CPU 隔离。cgroup 是 Linux 内核提供的机制,能够对线程的 CPU 使用率进行精准限制,达到租户间 CPU 强隔离的效果。

cgroup 的使用限制与注意事项

  • 要求 Linux 内核版本必须为 Linux Kernel 4.19 及以上版本
  • 以下场景下不建议使用 cgroup:单租户场景、租户之间有业务关联的场景(如上下游微服务)、小规格租户场景(CPU 为 2C 或 4C)。
  • OceanBase 数据库升级后,需要重新配置 cgroup。
  • 如果 OBServer 服务器重启,创建好的 cgroup 子目录会被清除,需要重新配置 cgroup。
  • 配置 User 级资源隔离和 Function 级资源隔离时,如果不需要控制 CPU 的资源隔离,则不需要配置 cgroup;配置 SQL 级资源隔离时,无论是否需要控制 CPU 的资源隔离,均需要配置 cgroup。
📌 CPU 隔离的效果体现
  • Plan Cache 租户分离:一个租户的 Plan Cache 淘汰不会影响另一个租户。
  • Audit 表分离:一个租户的 QPS 太高,不会冲洗掉另一个租户的 SQL Audit 信息。
  • 事务模块隔离:一个租户的行锁挂起、事务挂起、日志回放异常等问题,均不会影响到其他租户。
  • 日志模块隔离:日志流(Log Stream)的目录以租户为单位组织,独立存放。
  • 大查询 CPU 限制:通过 large_query_threshold(默认 5s)定义大查询阈值,并通过 large_query_worker_percentage(默认 30%)限制大查询最多可使用的租户活跃工作线程数,保证系统有足够 CPU 资源执行小查询(OLTP)。

租户 IOPS 隔离

OceanBase 从 V4.0.0 版本开始支持租户间 IOPS 隔离,通过 Unit 规格中的 MAX_IOPSMIN_IOPS 参数来限制租户的磁盘读写次数,并支持通过权重(IOPS_WEIGHT)进行资源分配。IOPS 被视为弹性资源,可根据权重动态调整带宽分配。

IOPS 隔离的关键配置
  • Unit Config 中的 MAX_IOPS(最小值 1024)和 MIN_IOPS 参数控制租户 IOPS 配额。
  • IOPS 配置前建议进行磁盘性能校准,确保 MAX_IOPSMIN_IOPS 不高于以 16KB 随机读测试得出的基线 IOPS 值。
  • 配置 SQL 级资源隔离时,无论是否需要控制 CPU 的资源隔离,均需要配置 cgroup。
-- 在 Unit Config 中配置 IOPS 参数
        CREATE RESOURCE UNIT io_unit 
            MAX_CPU=8, MIN_CPU=4,
            MEMORY_SIZE='32G',
            MAX_IOPS=128000, MIN_IOPS=128000,   -- IOPS 配置
            LOG_DISK_SIZE='2T';

        -- 查看当前租户的 IOPS 使用情况
        SELECT TENANT_ID, NAME, MAX_IOPS, MIN_IOPS, IOPS_WEIGHT 
        FROM oceanbase.DBA_OB_UNIT_CONFIGS;
⚠️ 版本演进说明io_category_config 配置项在 V4.x 版本中因对应逻辑已改变,不再使用,从 V4.2.1 Beta 版本开始已删除。租户 IOPS 隔离主要通过 Unit Config 中的 MAX_IOPSMIN_IOPSIOPS_WEIGHT 来实现。

多租户资源隔离的隔离效果

从普通租户用户的角度,可以看到以下隔离效果:

  • 内存完全隔离:SQL 执行过程中各种算子使用的内存分离,Block Cache 和 MemStore 分离,一个租户的内存耗尽不会影响到另一个租户的写入和读取。
  • SQL 模块隔离:不同租户的 Plan Cache 和 Audit 表完全分离,互不影响。
  • 事务模块隔离:一个租户的行锁挂起、事务挂起、日志回放异常等均不会影响其他租户。
  • 日志模块隔离:日志流的目录以租户为单位组织:tenant_id/unit_id_*/log 等目录结构独立存放。

租户内资源隔离:三种粒度

除了租户间资源隔离,OceanBase 还通过 DBMS_RESOURCE_MANAGER 系统包支持租户内资源隔离,分为三种粒度:

1️⃣ 用户级资源隔离

指定用户与资源组的映射关系,使该用户执行的所有 SQL 使用的资源即为对应资源组分配的资源。通过该特性,可以让不同用户执行不同类型的任务,实现资源隔离。

2️⃣ 后台任务级资源隔离

指定后台任务与资源组的映射关系,隔离各任务使用的资源。当前支持 8 种后台任务:

  • compaction_high:Mini Merge 和 DDL KV Merge 任务
  • ha_high:复制、Rebuild 和恢复等高优先级高可靠任务
  • compaction_mid:Minor Merge 任务
  • ha_mid:迁移等中优先级高可靠任务
  • compaction_low:Major Merge 任务
  • ha_low:备份和备份清理等低优先级高可靠任务
  • ddl:唯一索引校验、删列补数据等场景的任务
  • ddl_high:DDL MemTable 的转储任务
3️⃣ SQL 级资源隔离

通过将满足某个条件的 SQL 绑定到指定资源组上执行来实现资源隔离。通常适用于业务中存在大账号和小账号的场景——将处理不同订单的 SQL 绑定到不同的资源组,避免大账号把 CPU 资源用完导致小账号的订单无法得到处理。

📌 租户内资源隔离的使用前提
  • 如果需要实现 CPU 资源的隔离,必须确保 cgroup 目录已配置且 cgroup 功能已启用,因为 CPU 资源的隔离依赖 cgroup。
  • User 级和 Function 级资源隔离,如果不需要控制 CPU 资源隔离,则不需要配置 cgroup。
  • SQL 级资源隔离时,无论是否需要控制 CPU 资源隔离,均需要配置 cgroup。
  • 配置 IOPS 资源隔离前,建议进行磁盘性能校准,确保 Unit Config 中的 MAX_IOPSMIN_IOPS 不大于 16KB 随机读的基线 IOPS 值。
🏆 4.x 版本资源隔离演进总结
V4.0.0 起:正式支持租户间 CPU、内存、IOPS 隔离,一个 Unit 可指定 CPU、内存、IOPS、日志磁盘空间四种资源。
cgroup 支持:V4.0.0 版本开始支持配置 cgroup 来增强 CPU 隔离效果。
租户级内存自适应:V4.2.3 版本起,memstore_limit_percentage 默认值调整为 0,启用自适应内存分配策略。
后台任务隔离增强:V4.x 版本持续增强后台任务资源隔离能力,支持 8 种后台任务的资源组映射。
6.2 资源组与后台任务隔离

OceanBase 数据库支持对前台 SQL 和后台任务进行资源隔离,以保证关键业务的稳定运行。通过资源组(Resource Group)和资源管理计划(Resource Plan),可以灵活限制不同后台任务对 CPU、IOPS 等资源的占用,避免后台任务(如转储、合并、备份等)占用过多系统资源而影响正常的业务请求。

🎯 资源隔离的核心价值
  • 前台业务保活:通过限制后台任务资源消耗,确保在线业务(OLTP)始终获得稳定的 CPU 和 IOPS 配额,避免因后台任务突发负载导致性能波动。
  • 成本控制:在保证业务 SLA 的前提下,允许后台任务使用剩余资源,实现资源利用最大化。
  • 灵活可控:针对不同类型后台任务(转储、备份、迁移、统计信息收集等)分别设定资源组,实现精细化资源管理。

资源隔离的基本概念

OceanBase 数据库通过 DBMS_RESOURCE_MANAGER 系统包来管理数据库中资源的分配,从而实现资源隔离。其中涉及三个核心组件:

  • 资源组(Resource Consumer Group):根据资源要求组合在一起的一组会话。系统将资源分配给资源组,而不是单个会话。前台 SQL 可以按用户、SQL 条件绑定到不同资源组,后台任务也通过预定义的 FUNCTION 映射到资源组。
  • 资源管理计划(Resource Plan):资源管理计划内容的容器,指定如何将资源分配给资源组。您可以通过激活特定的资源管理计划来控制资源的分配。
  • 资源管理计划内容(Resource Plan Directive):用于将资源组与资源管理计划相关联,并指定如何将资源分配给该资源组。一条资源管理计划可以对应多条资源管理计划内容,但一条资源管理计划中不能包含两条相同的资源管理计划内容。
💡 适用版本说明:该内容仅适用于 OceanBase 数据库企业版。OceanBase 数据库社区版仅提供 MySQL 模式。
调用 DBMS_RESOURCE_MANAGER 系统包需要 ADMINISTER_RESOURCE_MANAGER 系统权限。在业务租户创建时,默认会授予该权限。

后台任务资源隔离(Function 级资源隔离)

Function 级的资源隔离是通过指定后台任务与资源组的映射关系,隔离各任务使用的资源。由于 OceanBase 数据库后台执行的诸如合并转储、备份恢复和数据补全等任务,不在 User 范畴之内,且执行时可能挤占前台用户任务的 CPU 和 IO 资源。因此,针对不同后台任务可以指定 FUNCTION 映射关系,后台任务的资源可以被限制在其对应的资源组内。

支持的后台任务类型

OceanBase 数据库支持将不同类型后台任务映射到资源组,当前支持以下 8 种类型:

任务类型定义适用场景
COMPACTION_HIGHMini Merge 和 DDL KV Merge 任务高频转储,优先级较高
COMPACTION_MIDMinor Merge 任务定期合并中级操作
COMPACTION_LOWMajor Merge 任务每日合并,资源消耗大但优先级低
HA_HIGH复制、Rebuild、恢复等高优先级高可靠任务节点故障恢复
HA_MID迁移任务等中优先级高可靠任务数据再平衡
HA_LOW备份、备份清理等低优先级高可靠任务数据备份
DDL唯一索引校验、删列补数据操作Schema 变更
DDL_HIGHDDL MemTable 的转储操作DDL 相关任务
📌 自 V4.3.5 版本起新增任务类型
以下任务类型通过 CLOG_HIGH_GROUPCOMPACTION_HIGH_GROUPBACKGROUND_GROUP 分组方式统一管理,均支持资源隔离:
- CLOG_HIGH:clog 日志提交任务
- OPT_STATS:统计信息收集任务
- CLOG_LOW / CLOG_MID:clog 相关任务
- GC_MACRO_BLOCK:宏块回收任务
- IMPORT:数据导入任务
- SQL_AUDIT:SQL 审计任务
- MVIEW:物化视图刷新任务
- REPLAY_HIGH:日志回放任务
关于各资源请求类型(后台任务)对应的详细说明,参见官方文档 资源隔离概述

简化配置:官方推荐的分组策略

由于自由配置后台任务的资源隔离方法较为复杂,OceanBase 官方将资源请求类型分为三组,推荐使用以下分组策略进行配置。

资源分组详情
资源组包含的资源请求类型说明
CLOG_HIGH_GROUPCLOG_HIGHclog 日志提交资源组,直接影响业务请求延迟
COMPACTION_HIGH_GROUPCOMPACTION_HIGH转储资源组
BACKGROUND_GROUPCOMPACTION_LOW、COMPACTION_MID、HA_LOW、HA_MID、HA_HIGH、DDL、DDL_HIGH、CLOG_LOW、CLOG_MID、OPT_STATS、GC_MACRO_BLOCK、IMPORT、SQL_AUDIT、MVIEW、REPLAY_HIGH 等通用后台任务资源组
官方推荐资源隔离策略

根据分组情况,OceanBase 推荐以下资源隔离配置。

资源组MIN_IOPSMAX_IOPSIOPS_WEIGHTMAX_CPUCPU_WEIGHT
CLOG_HIGH_GROUP4010010010030
COMPACTION_HIGH_GROUP301005010070
BACKGROUND_GROUP101003010070
⚠️ 策略说明:CLOG_HIGH 直接影响业务请求延迟,对 IOPS 请求要求较高,因此 MIN_IOPS 配置为 40;同时该任务对 CPU 要求不高,故 CPU_WEIGHT 配置为 30。后台任务如备份、清理等对 IO 和 CPU 资源要求相对较低,因此 MIN_IOPS 配置为 10。

配置后台任务资源隔离的完整步骤

前置条件
  • 如果需要控制 CPU 的资源隔离,在配置后台任务资源隔离前,必须配置 cgroup 目录并开启 cgroup 功能。配置 cgroup 目录并开启 cgroup 功能的相关操作,参见官方文档 配置 cgroup
  • 仅进行 IOPS 资源隔离(不控制 CPU)时,不需要配置 cgroup。
  • 当前版本中,CPU 和 IOPS 资源隔离均不再强制依赖磁盘性能校准操作,可根据需要选择是否执行。
步骤一:创建资源组
-- 创建前台任务资源组和后台任务资源组
        -- 使用 root 用户登录 sys 租户

        -- 创建前台业务资源组(默认定于高优先级用户 SQL)
        CALL DBMS_RESOURCE_MANAGER.CREATE_CONSUMER_GROUP(
            CONSUMER_GROUP => 'online_group',
            COMMENT => '前台业务资源组'
        );

        -- 创建后台任务资源组(低优先级)
        CALL DBMS_RESOURCE_MANAGER.CREATE_CONSUMER_GROUP(
            CONSUMER_GROUP => 'background_group',
            COMMENT => '后台任务资源组'
        );
⚠️ 权限说明:调用 DBMS_RESOURCE_MANAGER 系统包需要 ADMINISTER_RESOURCE_MANAGER 系统权限。在业务租户创建时,默认会授予该权限。
步骤二:创建资源管理计划
-- 创建资源管理计划
        CALL DBMS_RESOURCE_MANAGER.CREATE_PLAN(
            PLAN => 'resource_plan',
            COMMENT => '资源管理计划(前台+后台隔离)'
        );
步骤三:配置资源计划指令(为资源组分配资源)
-- 配置资源计划指令,为资源组分配 CPU 和 IOPS 资源
        -- 前台业务资源组:高优先级,分配更多的 CPU 资源
        CALL DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(
            PLAN => 'resource_plan',
            GROUP_OR_SUBPLAN => 'online_group',
            COMMENT => '前台业务资源组:高优先级',
            MGMT_P1 => 80,  -- CPU 总资源分配80(权重方式)
            MIN_IOPS => 50,
            MAX_IOPS => 100,
            WEIGHT_IOPS => 80,
            CPU_WEIGHT => 80   -- CPU 资源分配权重
        );

        -- 后台任务资源组:低优先级,分配较少的 CPU 资源
        CALL DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(
            PLAN => 'resource_plan',
            GROUP_OR_SUBPLAN => 'background_group',
            COMMENT => '后台任务资源组:低优先级',
            MGMT_P1 => 20,
            MIN_IOPS => 10,
            MAX_IOPS => 100,
            WEIGHT_IOPS => 20,
            CPU_WEIGHT => 20   -- CPU 资源分配权重
        );
步骤四:建立后台任务与资源组的映射关系
-- 将不同后台任务 FUNCTION 映射到指定资源组
        CALL DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING(
            ATTRIBUTE => 'FUNCTION',
            VALUE => 'COMPACTION_HIGH',
            CONSUMER_GROUP => 'background_group'
        );

        CALL DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING(
            ATTRIBUTE => 'FUNCTION',
            VALUE => 'COMPACTION_LOW',
            CONSUMER_GROUP => 'background_group'
        );

        CALL DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING(
            ATTRIBUTE => 'FUNCTION',
            VALUE => 'HA_LOW',
            CONSUMER_GROUP => 'background_group'
        );

        CALL DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING(
            ATTRIBUTE => 'FUNCTION',
            VALUE => 'HA_MID',
            CONSUMER_GROUP => 'background_group'
        );

        CALL DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING(
            ATTRIBUTE => 'FUNCTION',
            VALUE => 'DDL',
            CONSUMER_GROUP => 'background_group'
        );

        CALL DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING(
            ATTRIBUTE => 'FUNCTION',
            VALUE => 'CLOG_HIGH',
            CONSUMER_GROUP => 'background_group'
        );

上述是构建后台任务资源隔离的核心环节。针对不同的后台任务 FUNCTION,可以通过 SET_CONSUMER_GROUP_MAPPING 过程进行精细化管理。

🔧 简化配置建议
推荐使用官方提供的资源请求类型分组来简化配置:
COMPACTION_HIGH 映射到 COMPACTION_HIGH_GROUP 资源组,CLOG_HIGH 映射到 CLOG_HIGH_GROUP 资源组,其余后台任务统一映射到 BACKGROUND_GROUP 资源组。这样可以快速实现后台任务资源隔离,而无需对每种任务类型逐一配置。
步骤五:激活资源管理计划
-- 设置 resource_manager_plan 激活资源管理计划
        -- 必须在业务租户下执行
        ALTER SYSTEM SET resource_manager_plan = 'resource_plan';

        -- 验证资源管理计划已激活
        SHOW PARAMETERS LIKE 'resource_manager_plan';

        -- 停用资源管理计划
        ALTER SYSTEM SET resource_manager_plan = '';
可选:磁盘性能校准(如需要 IOPS 精准隔离)

如果需要精确配置 IOPS 隔离,可执行以下磁盘校准操作(注意:当前版本中,CPU 和 IOPS 资源隔离均不再强制依赖磁盘性能校准,可根据需要选择是否执行)。

-- 在 sys 租户中执行磁盘校准
        ALTER SYSTEM RUN JOB "io_calibration";

        -- 查看校准进度
        SELECT * FROM oceanbase.GV$OB_IO_CALIBRATION_STATUS;

        -- 查看校准值(校准完成后执行)
        SELECT * FROM oceanbase.GV$OB_IO_BENCHMARK;

监控资源隔离状态

OceanBase 数据库提供了多个视图用于监控资源隔离的配置和运行状态。

资源组监控视图
视图名称查看范围引入版本主要字段说明
DBA_OB_RSRC_GROUPS当前租户内所有资源组信息V4.0.0RESOURCE_GROUP_IDRESOURCE_GROUP_NAMECOMMENT
DBA_OB_RSRC_PLANS当前租户内所有资源管理计划V4.0.0PLAN_IDPLAN_NAMECOMMENTSTATUS(当前激活的计划标记为 ACTIVE)
DBA_OB_RSRC_DIRECTIVES当前租户内各资源计划的指令配置V4.0.0PLAN、GROUP_OR_SUBPLAN、MGMT_P1、MIN_IOPS、MAX_IOPS、WEIGHT_IOPS、CPU_WEIGHT 等
⚠️ 相比 V3.x 版本中使用的 DBA_RSRC_PLAN_DIRECTIVES,从 V4.0.0 版本开始,建议使用 DBA_OB_RSRC_DIRECTIVES 视图 来查看租户在 Resource Manager 中的各类资源配置参数信息。
-- 查看当前租户的所有资源组
        SELECT RESOURCE_GROUP_ID, RESOURCE_GROUP_NAME, COMMENT 
        FROM oceanbase.DBA_OB_RSRC_GROUPS;

        -- 查看资源管理计划内容(资源配置详情)
        SELECT PLAN, GROUP_OR_SUBPLAN, MGMT_P1, MIN_IOPS, MAX_IOPS, WEIGHT_IOPS, CPU_WEIGHT 
        FROM oceanbase.DBA_OB_RSRC_DIRECTIVES;

        -- 查看资源管理计划状态(是否激活)
        SELECT PLAN_NAME, STATUS FROM oceanbase.DBA_OB_RSRC_PLANS;

        -- 查看后台任务与资源组的映射关系
        -- 可通过 DBA_OB_RSRC_MAPPING 视图查看
        SELECT ATTRIBUTE, VALUE, CONSUMER_GROUP 
        FROM oceanbase.DBA_OB_RSRC_MAPPING 
        WHERE ATTRIBUTE = 'FUNCTION';

资源管理计划的优先级与校验规则

当激活某个资源管理计划后,该计划中的所有资源组会按照以下规则获得资源:

  • 在系统满负载时,资源组按照 MGMT_P1 参数按比例分配 CPU 资源(总和不超过 100)。MGMT_P1 指定系统满负载情况下,相对可用的最大 CPU 占比。
  • 在系统未达满负载时,资源组可无限制使用 CPU,MGMT_P1 仅作为满负载下的保障比例。
  • IOPS 资源按照 MIN_IOPSMAX_IOPSWEIGHT_IOPS 联合控制。
  • 一个资源管理计划中不能包含两条相同的资源管理计划内容(即同一个资源组在同一个计划中只能出现一次)。
📌 cgroup 使用限制与注意事项
  • 适用版本:v4.0.0 及后续版本
  • 单租户场景:集群中只有一个租户时,不建议使用 cgroup 进行资源隔离(因隔离前后资源一样,无需额外隔离)
  • 业务关联场景:租户之间有业务上下游关联时(如微服务架构),依赖这些租户之间的实时性能保障,cgroup 强隔离会切断资源借用通道,可能导致整体业务性能降低
  • 小规格租户:CPU 为 2C 或 4C 的小规格租户,cgroup 的资源隔离会进一步压缩租户可用 CPU,不建议使用
  • 不影响系统租户:资源隔离配置不会对 sys 租户生效
  • 重启持久性:OBServer 重启后,cgroup 子目录会被清除,需要重新配置 cgroup
  • 升级兼容性:OceanBase 数据库升级后,需要重新配置 cgroup
  • SQL 级资源隔离:配置 SQL 级资源隔离时,无论是否需要控制 CPU 资源隔离,均需要配置 cgroup
🏆 4.x 版本资源隔离演进总结
8 种基础任务类型:V4.x 支持 COMPACTION_HIGH、COMPACTION_MID、COMPACTION_LOW、HA_HIGH、HA_MID、HA_LOW、DDL、DDL_HIGH 共 8 种后台任务类型的资源隔离
分组配置简化:V4.2.5 版本引入官方推荐的分组配置策略,通过 CLOG_HIGH_GROUP、COMPACTION_HIGH_GROUP、BACKGROUND_GROUP 三个资源组统一管理十几种任务类型
视图体系完善:从 V4.0.0 版本开始引入 DBA_OB_RSRC_* 系列视图,方便查看和管理资源隔离配置
功能增强:V4.3.5 版本新增 CLOG_HIGH、OPT_STATS、MVIEW、IMPORT 等多种后台任务类型的资源隔离支持
6.3 Meta 租户与负载均衡

在 OceanBase 4.x 架构中,集群管理、租户管理、资源调度等核心职责均由 RootService 承担。而 RootService 本身运行于系统租户(sys tenant)之上,集群早期的元数据膨胀和调度瓶颈问题对集群稳定性构成挑战。为此,OceanBase V4.0 引入了 Meta 租户,将每个用户租户的私有元数据(如配置项、位置信息、副本信息、日志流状态、备份恢复信息、合并信息等)迁移至专属的 Meta 租户管理,实现了更细粒度的资源隔离,减轻了系统租户的负担。

🎯 Meta 租户的核心价值
  • 缓解系统租户瓶颈:V4.x 以前版本中,系统租户因存储所有租户的元数据而逐渐臃肿,资源隔离粒度粗,稳定性受挑战。Meta 租户的引入有效拆分了系统租户的压力。
  • 资源隔离更干净:每个用户租户对应独立的 Meta 租户,租户私有数据不再共用系统租户的存储和计算资源,实现了元数据层面的资源隔离。
  • 生命周期同步:Meta 租户的生命周期与其对应的用户租户严格保持一致,用户租户创建时自动创建,删除时自动销毁,无需人工管理。

Meta 租户详解

定位与数据属性

Meta 租户用于存储和管理用户租户的集群私有数据,这部分数据不需要进行跨库物理同步以及物理备份恢复,例如:配置项、位置信息、副本信息、日志流状态、备份恢复相关信息、合并信息等。Meta 租户的数据存储格式与用户租户一致,但不可直接登录访问,普通用户只能通过系统租户的视图来查询 Meta 租户下的数据。

ID 规则与命名规范

Meta 租户与用户租户采用固定的 ID 编码映射关系:

  • 用户租户 ID:最小值 1002,最低位编码为 0(偶数)。
  • Meta 租户 ID:最小值 1001,最低位编码为 1(奇数),且 Meta 租户 ID + 1 = 用户租户 ID
  • 命名规范:Meta 租户的默认名称为 META${user_tenant_id}。例如,用户租户 ID 为 1002 时,其对应 Meta 租户名为 META$1002
资源分配与运维限制

Meta 租户没有独立的 Unit,创建租户时系统默认为 Meta 租户预留资源,各项资源从用户租户的资源中扣除。资源分配规则如下:

用户租户规格Meta 租户内存分配说明
≥ 10G租户规格的 10%(1:9 比例)Meta 租户和用户租户按 1:9 比例分配内存
≥ 2G 且 < 10G固定 1G剩余资源分配给用户租户
≥ 1G 且 < 2G固定 512MB剩余资源分配给用户租户

Meta 租户的运维操作存在明确限制:

  • 不支持创建、删除、重命名 Meta 租户(生命周期与用户租户自动绑定)。
  • 不支持用户登录访问 Meta 租户。
  • 不支持修改 Meta 租户的 Locality 和 Primary Zone。
  • Meta 租户数据不可水平扩展,只有一个日志流,不具备扩展能力。
💡 Meta 租户的数据可见性:普通用户不能直接登录 Meta 租户,但可以通过系统租户的视图(如 CDB_OB_TENANTS)查询 Meta 租户的信息,也可以通过用户租户自身的视图间接访问 Meta 租户的部分元数据。

负载均衡机制:RootService 与资源调度

RootService 作为集群的中心控制服务,基于系统租户承担着 OceanBase 数据库的大量管理工作,包括集群管理、租户管理、资源管理、负载均衡、每日合并调度、迁移复制等。RootService 通过心跳机制检测节点的健康状态,定期生成均衡计划,确保资源在节点间分布均衡。

负载均衡的层次

OceanBase 的负载均衡机制涉及两个层次:

  • 租户间资源均衡(Unit 均衡):通过在 OBServer 节点间迁移 Unit,使 Zone 内各节点的 CPU、内存资源占用率趋于一致。
  • 租户内副本均衡(Leader 均衡):Unit 迁移后,RootService 还会执行 Leader 副本的再平衡,将 Leader 尽可能分散到所有节点,避免单个节点承载全部写请求。
🔧 负载均衡的总开关与核心参数
  • enable_rebalance:负载均衡机制的总开关,默认值为 True。当该参数为 False 时,资源单元均衡和分区副本均衡均关闭。
  • server_balance_cpu_mem_tolerance_percent:设置 Zone 内各节点 CPU 和内存资源占用率差异的容忍阈值,默认值为 5%,取值范围为 [1, 100]。当节点资源占用率与 Zone 平均值的差值超过该阈值时,触发资源再均衡调度,直至差值降至阈值以下。
  • resource_soft_limit:辅助开关,仅在 enable_rebalanceTrue 时生效。默认值为 50,决定资源单元均衡的启用与否。
资源单元均衡的技术原理

RootService 在进行资源单元调度时,会计算每个 OBServer 节点的资源占用率,并通过迁移 Unit 的方式使各节点资源占用率趋于平衡。当节点间差异降至阈值以下时,调度停止。

针对多种类型资源(如 CPU 和内存),系统会为每种资源分配权重:资源占用率越高,该资源在综合计算中所占权重越高。计算节点综合资源使用率时,按权重进行归一化处理,从而使调度策略更贴近实际资源利用情况。例如,假设集群有 50 核 CPU 和 1000GB 内存,Unit 已占用 20 核 CPU 和 100GB 内存,则 CPU 使用率为 40%,内存使用率为 10%,归一化后 CPU 权重为 80%,内存权重为 20%。后续调度将基于综合使用率进行。

-- 查询资源单元均衡任务状态(sys 租户查看)
        SELECT * FROM oceanbase.DBA_OB_UNIT_JOBS WHERE JOB_TYPE = 'MIGRATE_UNIT';

        -- 查询 RootService 最近的事件历史
        SELECT * FROM oceanbase.DBA_OB_ROOTSERVICE_EVENT_HISTORY LIMIT 10;
Leader 均衡与路由优化

Unit 迁移后,Leader 副本分布可能出现不均衡。RootService 还会执行 Leader 的再平衡,将 Leader 副本尽可能分散到所有节点,避免单个节点承载全部写请求。在 V4.x 版本中,用户可以通过调整租户的 PRIMARY_ZONE 属性影响 Leader 的分布策略。

-- 修改租户的 Primary Zone 分布(影响 Leader 调度)
        ALTER TENANT tenant_name SET PRIMARY_ZONE = 'zone1;zone2,zone3';

        -- 开启或关闭 Leader 自动均衡(集群级)
        ALTER SYSTEM SET enable_auto_leader_switch = true;

系统租户的角色与限制

尽管引入了 Meta 租户分担压力,系统租户在 V4.x 架构中仍承担不可替代的核心职责,但它也存在明显的容量限制和单点风险。了解系统租户的定位和限制,有助于更好地理解整个资源调度体系。

系统租户的核心职责
  • 管理集群和所有租户的生命周期。
  • RootService 基于系统租户运行,负责资源调度和集群管理。
  • 作为应用系统访问 OceanBase 数据库的入口,客户端通过 Config Server 获取系统租户 IP 列表,访问系统租户获取元数据后建立与目标租户的连接。
系统租户的限制与风险
  • 扩展性受限:系统租户仅有一个 1 号日志流,只支持单点写入,不具备水平扩展能力。
  • 单点风险:虽然有多副本机制保证少数节点故障时可容忍,但系统租户仍是集群的单点。因内核 Bug 导致系统租户异常会影响集群服务可用性。
  • 容量挑战:大量应用同时重启时会产生连接流量尖峰,可能短时间耗尽系统租户的工作线程,导致连接建立失败。
  • 不推荐业务使用:系统租户定位于集群管理和租户管理,不提供完整的数据库功能,不推荐在生产或业务测试等场合使用。
⚠️ 运维警示:系统租户的稳定性对 OceanBase 集群的稳定性至关重要。若系统租户出现异常,可通过运维命令强制切主恢复:ALTER SYSTEM SWITCH ROOT SERVICE LEADER TO zone_name。也可以通过外部 admin 工具强制切换服务,新主上任后,再隔离原来的异常机器。

负载均衡监控视图

OceanBase 提供了丰富的视图用于监控负载均衡配置和运行状态。

核心监控视图
视图名称查看范围引入版本主要用途 DBA_OB_UNITS 查询所有 Unit 信息(系统租户) V4.0.0 查看集群中所有 Unit 的 ID、所属租户、所在节点、资源规格及状态 GV$OB_UNITS 动态性能视图,用户租户内查询本租户 Unit 信息,系统租户内查看全部 Unit V4.0.0 查看 Unit 资源使用情况及状态迁移进度 DBA_OB_UNIT_CONFIGS 查询所有资源单元配置 V4.0.0 查看 MAX_CPUMIN_CPUMEMORY_SIZEMAX_IOPSMIN_IOPS 等规格定义 DBA_OB_UNIT_JOBS 查询 Unit 相关任务(迁移、GC 等) V4.0.0 监控 Unit 迁移进度和状态 DBA_OB_SERVERS 查询集群节点信息 V4.0.0 查看各节点的资源总量和当前资源使用情况 DBA_OB_ROOTSERVICE_EVENT_HISTORY 查询 RootService 事件历史 V4.0.0 排查负载均衡调度记录和异常事件
-- 查看集群中所有 Unit 的分布情况(sys 租户执行)
        SELECT UNIT_ID, TENANT_ID, ZONE, SVR_IP, SVR_PORT, MAX_CPU, MEMORY_SIZE, STATUS
        FROM oceanbase.DBA_OB_UNITS
        ORDER BY ZONE, SVR_IP;

        -- 查看各节点资源均衡程度(计算资源占用率差异)
        SELECT ZONE, SVR_IP, 
            MAX_CPU AS total_cpu,
            (SELECT SUM(MAX_CPU) FROM oceanbase.DBA_OB_UNITS u2 
                WHERE u2.SVR_IP = u1.SVR_IP AND u2.SVR_PORT = u1.SVR_PORT) AS used_cpu
        FROM oceanbase.DBA_OB_UNITS u1;

        -- 查看当前用户租户的 Unit 资源使用情况(用户租户执行)
        SELECT UNIT_ID, ZONE, MAX_CPU, MEMORY_SIZE/1024/1024/1024 AS memory_gb,
            STATUS, CREATE_TIME
        FROM oceanbase.GV$OB_UNITS
        WHERE TENANT_ID = (SELECT TENANT_ID FROM DBA_OB_TENANTS WHERE TENANT_NAME = USER());

        -- 查看 RootService 的近期负载均衡事件
        SELECT EVENT, VALUE, TIME 
        FROM oceanbase.DBA_OB_ROOTSERVICE_EVENT_HISTORY 
        WHERE EVENT IN ('unit_balance_finished', 'unit_balance_start')
        ORDER BY TIME DESC
        LIMIT 20;
📌 负载均衡运维最佳实践
  • 合理设置容忍阈值server_balance_cpu_mem_tolerance_percent 默认值 5% 可满足多数场景。若系统资源宽裕且希望减少迁移次数,可适当调高(如 10%);若希望节点资源严格均衡,可适当调低(如 2%),但需注意过低阈值可能导致频繁迁移。
  • 监控 Unit 状态:重点关注 GV$OB_UNITS 中的 STATUS 字段。若出现长时间处于 MIGRATING IN/OUT 状态的 Unit,需排查迁移任务是否阻塞。
  • 扩缩容后验证均衡:添加新节点或删除节点后,应通过 DBA_OB_UNITSDBA_OB_SERVERS 视图检查资源分布是否达到预期均衡状态。
  • 关注系统租户容量:系统租户不具备水平扩展能力,大量应用同时重启可能引发连接风暴。建议通过 OCP 监控系统租户工作线程使用率,并在应用端配置合理的连接重试策略和启动间隔。
  • 手动迁移场景:当业务需要控制特定节点上的 Unit 分布(如计划缩容或硬件维护),可通过 OCP 平台手动触发 Unit 迁移。迁移目标必须在同一 Zone 内且有足够资源承载。
🏆 4.x 版本架构演进总结
Meta 租户引入:V4.0 起为每个用户租户创建专属 Meta 租户,独立存储租户私有数据(配置项、副本信息、日志流状态等),实现元数据资源隔离,减轻系统租户负担。
ID 编码规则:用户租户为偶数 ID(最小 1002),Meta 租户为奇数 ID(最小 1001),映射关系为 Meta 租户 ID + 1 = 用户租户 ID
负载均衡精细化:V4.x 版本在多资源类型(CPU、内存、IOPS)均衡计算中引入权重归一化策略,使调度决策更贴近实际资源利用情况。
监控视图完善:从 V4.0.0 起引入 DBA_OB_UNITSDBA_OB_UNIT_JOBSDBA_OB_ROOTSERVICE_EVENT_HISTORY 等视图,方便 DBA 全面追踪负载均衡状态。
系统租户仍是关键:尽管引入了 Meta 租户,系统租户仍是 OceanBase 集群的核心调度和服务入口,其稳定性和容量需要特别关注。
6.4 RootService 负载均衡机制深度解析

RootService(RS)是 OceanBase 集群的总控服务,运行在系统租户的 1 号日志流 Leader 所在的 OBServer 节点上,负责集群元数据管理、资源调度、负载均衡、每日合并调度、故障检测与恢复等核心职能。在 OceanBase 4.x 架构中,RootService 通过多层次的负载均衡机制,动态调整资源和数据在节点间的分布,确保集群资源利用率最大化、系统性能最优化。

🎯 RootService 负载均衡的核心目标
  • 资源均衡:通过迁移资源单元(Unit),使 Zone 内各 OBServer 节点的 CPU、内存等资源占用率趋于一致,避免单节点过载。
  • 数据均衡:通过迁移 Tablet 副本,使各 Unit 的负载(请求量、数据量)尽量均衡,避免热点。
  • 流量均衡:通过调整 Leader 副本分布,使读写流量在各节点间合理分布,配合 Primary Zone 实现流量调度。
  • 高可用保障:节点故障时自动迁移数据副本和 Leader,保障服务的持续可用。

RootService 的组成架构

RootService 的负载均衡由 ObRootBalancer 模块驱动,该模块包含多个策略组件,协同完成负载均衡的全流程:

核心组件说明
  • ObRootBalancer:负载均衡模块的入口类,作为后台线程周期性地运行,驱动其他策略模块执行调度。
  • TenantBalanceInfo:数据结构类,记录 Zone、Server、Unit、Partition、Replica 之间的交叉引用关系,为负载均衡算法提供查询接口。gather_stat() 方法负责初始化数据结构,采集当前集群的实时状态。
  • ObUnitBalance:资源单元均衡的主策略类,负责决策哪些 Unit 需要迁移以及迁移到哪个目标节点。
  • ObPartitionGroupCoordinator:分区组和 Unit 组调度策略的实现,负责分区(Tablet)级的负载均衡。
  • ObRereplication:副本补充策略类,当分区副本数量不足时,自动生成添加副本的任务,保障数据冗余度。
  • ObServerChecker:节点状态检查类,负责检测节点是否可以安全下线(如计划缩容场景),以及节点故障时的副本迁移任务。
-- 查看集群中 RootService 相关任务列表(系统租户)
        SELECT * FROM oceanbase.DBA_OB_ROOTSERVICE_EVENT_HISTORY
        WHERE EVENT LIKE '%balance%' OR EVENT LIKE '%migrate%'
        ORDER BY TIME DESC
        LIMIT 20;

负载均衡的层次结构

OceanBase 的负载均衡机制分为三个层次,相互独立又协同配合,共同保障集群资源的合理利用和系统的稳定运行。

层次一:租户间资源均衡(Unit 均衡)

Unit 均衡是指在 Zone 内的多个 OBServer 节点之间调度 Unit,使各节点的资源占用率(CPU、内存等)尽可能接近,避免资源闲置或单节点过载。

  • 触发条件:RootService 定期检测 Zone 内 OBServer 节点的资源占用率,当节点间的资源占用率差异超过 server_balance_cpu_mem_tolerance_percent 配置的阈值(默认 5%)时,自动触发均衡调度。
  • 调度单位:资源单元(Unit)是资源调度的最小单位。Unit 的分配和均衡决策由 RootService 基于 Unit 规格进行,核心目标是优化多种资源(CPU、内存等)在 Zone 内的均衡分布。
🔧 Unit 均衡的核心配置参数
  • enable_rebalance:负载均衡机制的总开关,默认值为 True。当该参数为 False 时,资源单元均衡和分区副本均衡均关闭。如需手动迁移 Unit 时,可将其设置为 False 关闭自动调度。
  • server_balance_cpu_mem_tolerance_percent:Zone 内节点间 CPU 和内存资源占用率差异的容忍阈值,默认值为 5,取值范围为 [1, 100]。当节点与 Zone 平均值的差异超过此值时触发 Unit 均衡,直至差异降至阈值以下。
层次二:租户内数据均衡(Tablet 均衡)

Tablet 均衡是指在租户的多个 Unit 之间调整 Tablet(数据分片)的分布,使得各 Unit 的负载比较均衡。Tablet 是数据迁移、负载均衡和故障恢复的最小单位,大小通常建议控制在 2GB 左右(基于运维经验的推荐值,并非绝对限制)。

  • 触发条件:当 Tablet 上的请求负载(CPU 耗时、IO 次数、网络流量)出现不均衡时,RootService 会触发 Tablet 的跨 Unit 迁移。
  • 调度单位:Tablet 是数据负载均衡的最小单位。与 V3.x 版本相比,V4.x 以日志流为单元的架构使得 Tablet 均衡的粒度更细、资源开销更小。
层次三:流量均衡(Leader 均衡)

Leader 均衡是指在同 Zone 内的多个 Unit 之间调整日志流 Leader 副本的分布,使读写流量在各节点间合理分布,避免单个节点承载全部写请求。

  • 触发条件:Unit 迁移后,Leader 分布可能出现不均衡。RootService 的 ObLeaderCoordinator 组件负责执行 Leader 的再平衡,将 Leader 副本尽可能分散到所有节点。
  • 流量分布模型:Primary Zone 描述了 Leader 副本的偏好位置,决定了流量分布。例如,假设表 t1 的 primary_zone="Zone1",则 RootService 会尽量将 t1 表的 Leader 调度到 Zone1 上来。
⚠️ V4.x 版本限制:OceanBase 当前版本仅支持租户级别的 Primary Zone。创建租户时若未指定 primary_zone,默认为 RANDOM,表示各个 Zone 优先级相同。

多资源类型均衡的计算模型

当集群中同时需要均衡多种类型的资源(如 CPU 和内存)时,仅依据单一资源使用率进行调度会不够精确。OceanBase 采用权重归一化算法,为每种资源分配权重,以计算节点的综合资源使用率。

权重分配策略

每种资源的权重基于其在集群总资源中的占比动态分配,资源使用越多,其权重越高。以下为一个典型计算示例:

  • 集群包含:50 核 CPU、1000 GB 内存。
  • 当前 Unit 已占用:20 核 CPU → CPU 使用率 40%;100 GB 内存 → 内存使用率 10%。
  • 归一化计算:CPU 权重 = 40% / (40%+10%) = 80%;内存权重 = 10% / (40%+10%) = 20%。

基于权重计算每个 OBServer 节点的综合资源使用率,RootService 依据此综合指标进行 Unit 迁移调度,使均衡决策更贴近实际资源利用情况,避免单类型资源密集使用场景下的调度偏差。

单资源类型均衡示例:CPU

假设两个 OBServer 节点各有 10 个 CPU 核心:

  • OBS0 包含 6 个 Unit(每个 Unit 占 1 核)→ CPU 使用率 60%。
  • OBS1 包含 4 个 Unit → CPU 使用率 40%。
  • 两者差异为 20%,超过默认 5% 的阈值,RootService 触发调度。
  • 将 1 个 Unit 从 OBS0 迁移到 OBS1 后,两节点 CPU 使用率均为 50%,集群资源更加均衡。

Unit 迁移机制:先建后删的流程

为保证迁移过程中读写不中断,Unit 迁移采用“先建后删”的流程。迁移目标必须与源在同一个 Zone 内,且目标节点有足够的资源承载。

迁移流程
  • 创建目标 Unit:在目标节点上创建新的 Unit 副本。
  • 同步数据:将源 Unit 中的数据同步到目标 Unit(通过日志同步)。
  • 切换租户 Unit 列表:将目标 Unit 加入租户的 Unit 列表,同时将源 Unit 标记为待迁移状态。
  • 删除源 Unit:数据同步完成且服务切换后,删除源 Unit,释放资源。
-- 查询 Unit 迁移任务(系统租户)
        SELECT * FROM oceanbase.DBA_OB_UNIT_JOBS WHERE JOB_TYPE = 'MIGRATE_UNIT';

        -- 查询 RootService 调度事件(近期负载均衡记录)
        SELECT EVENT, VALUE, TIME 
        FROM oceanbase.DBA_OB_ROOTSERVICE_EVENT_HISTORY 
        WHERE EVENT IN ('unit_balance_finished', 'unit_balance_start', 'tablet_balance_finished')
        ORDER BY TIME DESC
        LIMIT 20;

Primary Zone 与 Leader 调度

Leader 副本承载了业务的强一致读写流量,其分布决定了流量分布。Primary Zone 通过 Zone 优先级列表来指定 Leader 的偏好位置,RootService 会优先将 Leader 调度到优先级最高的 Zone 上。

Primary Zone 优先级规则
  • 分号(;):表示不同的优先级等级,排在左侧的优先级更高。例如:'hz1,hz2;sh1,sh2;sz1' 表示 hz1/hz2 优先级高于 sh1/sh2,sh1/sh2 优先级高于 sz1。
  • 逗号(,):表示相同优先级,流量均匀打散在同优先级的 Zone 上。
  • Leader 调度时,RootService 会按照优先级顺序尝试在 Zone 上设置 Leader,只有高优先级 Zone 的副本不可用时才会调度到低优先级 Zone。
Primary Zone 的继承与层级

Primary Zone 支持多种粒度的配置。在 OceanBase V4.x 中,仅支持租户级别的 Primary Zone。V3.x 曾支持表级、表组级、数据库级等更细粒度的配置,但 V4.x 将管理粒度提升到租户级以简化运维。

切主动能(Leader Switch)

除了负载均衡驱动的 Leader 自动调度,RootService 还支持按需主动切主。当需要调整 Leader 分布时(如 Primary Zone 变更、节点维护),RootService 会根据新的 Primary Zone 优先级平滑地迁移 Leader,切主过程对业务透明,不会造成中断。

💡 Leader 均衡与容灾:Zone 的地域(Region)属性也会影响 Leader 调度。当高优先级 Zone 的所有副本均不可用时,RootService 会优先选择同一 Region 内的其他 Zone 作为替代,以减少跨 Region 的网络延迟。

故障检测与自动处理

RootService 通过心跳机制实时监测各 OBServer 的健康状态,OBServer 默认每 2 秒向 RootService 发送一次心跳包。RootService 依据心跳包的接收状态判断节点状态,并自动执行对应的故障处理。

心跳超时检测与故障处理流程
检测指标阈值参数对应故障状态RootService 处理
心跳丢失时间短 累计超过 lease_time(默认 10 秒) lease_expired:心跳租约到期,进程可能短暂断线 将 OBServer 标记为 inactive,暂时不迁移数据
心跳丢失时间长 累计超过 server_permanent_offline_time permanent_offline:进程彻底断线 将该 OBServer 上的所有数据副本从 Paxos 成员组中删除,并在其他可用 OBServer 上补充副本
心跳存在但磁盘异常 心跳包中携带磁盘状态 磁盘故障 尝试将该 OBServer 上的全部 Leader 副本切走
📌 RootService 高可用机制

RootService 本身也基于 Paxos 协议实现高可用。RootService 服务副本数量可通过集群配置指定,各副本基于 Paxos 协议选举 Leader,Leader 上任后为集群提供 RootService 服务。当当前 Leader 发生故障卸任时,其他 RootService 副本重新选举产生新的 Leader,继续提供服务。RootService 的各副本不是一个单独的进程,而是某些 OBServer 上启动的服务。

RootService 状态监控与运维

OceanBase 提供了多个视图用于监控 RootService 的运行状态和负载均衡任务进度,从 V4.0.0 版本开始引入。

核心监控视图
视图名称功能描述适用租户
DBA_OB_UNIT_JOBS 查询 Unit 相关任务(迁移、GC 等)的进度和状态 系统租户
DBA_OB_ROOTSERVICE_EVENT_HISTORY 记录 RootService 的关键事件历史,包括负载均衡开始/完成、切主等 系统租户
GV$OB_UNITS 动态性能视图,用户租户内查询本租户 Unit 资源使用情况,系统租户内查看全部 Unit 系统租户/用户租户
DBA_OB_UNITS 查询所有 Unit 的 ID、所属租户、所在节点、资源规格及状态 系统租户
DBA_OB_SERVERS 查询集群节点信息,包括各节点的资源总量和当前资源使用情况 系统租户
-- 系统租户查看集群中所有 Unit 的分布情况
        SELECT UNIT_ID, TENANT_ID, ZONE, SVR_IP, SVR_PORT, MAX_CPU, MEMORY_SIZE, STATUS
        FROM oceanbase.DBA_OB_UNITS
        ORDER BY ZONE, SVR_IP;

        -- 用户租户查看本租户 Unit 资源使用情况
        SELECT UNIT_ID, ZONE, MAX_CPU, MEMORY_SIZE/1024/1024/1024 AS memory_gb,
            STATUS, CREATE_TIME
        FROM oceanbase.GV$OB_UNITS
        WHERE TENANT_ID = (SELECT TENANT_ID FROM DBA_OB_TENANTS WHERE TENANT_NAME = USER());

        -- 查看 RootService 的近期负载均衡事件(排查调度问题)
        SELECT EVENT, VALUE, TIME 
        FROM oceanbase.DBA_OB_ROOTSERVICE_EVENT_HISTORY 
        WHERE EVENT IN ('unit_balance_finished', 'unit_balance_start', 'tablet_balance_finished')
        ORDER BY TIME DESC
        LIMIT 30;

        -- 查询 Unit 迁移任务进度
        SELECT * FROM oceanbase.DBA_OB_UNIT_JOBS WHERE JOB_TYPE = 'MIGRATE_UNIT';
⚙️ 当需要主动控制 Unit 分布(如计划缩容或硬件维护)时,可以在 OCP 平台手动触发 Unit 迁移。迁移目标必须与源在同一个 Zone 内,且目标节点有足够的资源承载。

负载均衡运维最佳实践

📌 推荐实践与常见问题
  • 合理设置容忍阈值server_balance_cpu_mem_tolerance_percent 默认值 5% 可满足多数场景。若系统资源宽裕且希望减少迁移次数,可适当调高;若希望节点资源严格均衡,可适当调低,但需注意过低阈值可能导致频繁迁移,增加系统开销。
  • 监控 Unit 迁移状态:重点关注 GV$OB_UNITS 中 Unit 的 STATUS 字段。若出现长时间处于 MIGRATING 状态的 Unit,可通过 DBA_OB_UNIT_JOBS 查看迁移任务详情,排查迁移是否阻塞。
  • 扩缩容后验证均衡:添加新节点或删除节点后,建议通过 DBA_OB_UNITSDBA_OB_SERVERS 视图检查资源分布是否达到预期均衡状态。
  • 手动调度场景:当需要主动控制特定节点上的 Unit 分布(如缩容或硬件维护)时,可通过 OCP 平台关闭自动迁移,手动迁移 Unit。迁移目标必须在同一 Zone 内且资源充足。
  • 关注系统租户容量:RootService 运行在系统租户下,系统租户不具备水平扩展能力。大量应用同时重启可能引发连接风暴,耗尽系统租户工作线程。建议在应用端配置合理的连接重试策略和启动间隔,并通过 OCP 监控系统租户工作线程使用率。
🏆 4.x 版本负载均衡演进总结
调度粒度优化:V4.x 以 Tablet(数据分片)为数据均衡的最小单位,相比 V3.x 的分区级调度粒度更细、调度效率更高。
日志流架构支撑:V4.x 以日志流为事务提交和 Paxos 复制的基本单元,取代了旧版本中以分区为单位的模式,海量分区场景下的负载均衡开销大幅降低。
权重归一化算法:在多种资源同时均衡的场景下,采用基于资源使用率的权重归一化策略,使调度决策更贴近实际负载。
Primary Zone 简化:V4.x 仅支持租户级 Primary Zone,不再支持表级/表组级配置,简化了流量调度的管理复杂度。
监控视图完善:从 V4.0.0 版本开始引入 DBA_OB_UNIT_JOBSDBA_OB_ROOTSERVICE_EVENT_HISTORY 等视图,为负载均衡的监控和问题排查提供了全面支持。
6.5 租户参数模板与最佳实践

在 OceanBase 4.x 架构中,租户的参数配置是保障数据库高性能、高可用的关键环节。通过合理的参数设置,可以使数据库行为更贴合业务需求,充分发挥硬件性能。OceanBase 提供了多层次的参数体系和场景化的推荐配置模板,帮助 DBA 高效完成租户的参数配置与管理。

🎯 租户参数配置的核心价值
  • 场景化调优:针对不同的业务场景(OLTP、OLAP、HTAP 等),推荐差异化的参数配置,实现最佳性能。
  • 标准化管理:通过参数模板机制,实现大批量租户的参数标准化配置,避免重复手动调整。
  • 精细化控制:区分配置项(运维级)和系统变量(会话级),满足不同粒度的配置需求。
  • 版本兼容保障:基于版本自带的推荐模板,确保配置的兼容性和可靠性。

OceanBase 配置管理概览

OceanBase 数据库的配置管理通过系统配置项(Parameters)系统变量(Variables)两个维度来实现。理解二者的区别是正确配置租户的基础。

系统配置项与系统变量的对比
对比维度系统配置项(Parameters)系统变量(Variables)
生效范围 集群级、租户级、Zone 级、机器级 租户级的 Global 级别或 Session 级别
生效方式 支持动态生效(dynamic_effective)或重启生效(static_effective) Global 变量需重新建立 Session 才能生效;Session 变量仅对当前会话生效
修改方式 支持 SQL 语句修改(ALTER SYSTEM)和启动参数修改(-o) 仅支持 SQL 语句修改,MySQL 模式用 SET,Oracle 模式可用 ALTER SYSTEM
持久化 持久化到内部表和配置文件 observer.config.bin 仅 Global 级别的变量会持久化,Session 变量不持久化
适用场景 运维级配置,适用于控制机器级别的系统行为 业务级配置,适用于控制租户内部 SQL 执行行为
-- 查询集群级配置项(系统租户)
        SHOW PARAMETERS LIKE 'enable_rebalance';

        -- 查询租户级配置项(系统租户或用户租户)
        SHOW PARAMETERS LIKE 'ob_tcp_invited_nodes';

        -- 查询租户级 Global 变量(推荐在用户租户下执行)
        SHOW GLOBAL VARIABLES LIKE 'ob_query_timeout';

        -- 修改租户级 Global 变量(推荐在用户租户下执行)
        SET GLOBAL ob_query_timeout = 20000000;   -- 设置为 20 秒
运维提示:系统配置项通常用于控制基础设施行为(如内存、网络、合并策略等),影响范围广泛,建议生产环境的配置项变更在充分测试后进行;系统变量更贴近业务 SQL 执行,灵活性更高。

租户参数模板管理

从 V4.3.2 版本开始,OceanBase 在安装包的 /home/admin/oceanbase/etc 目录下提供了多场景的参数推荐配置模板。参数模板会预先将租户的参数设置好,在需要创建配置相似的一系列租户的场景下,可直接应用参数模板,如此可不必反复配置租户参数。从 V4.3.1 版本开始,OCP 平台也支持可视化的租户参数模板管理。

内置参数模板类型

OceanBase 提供了五种预置的参数模板,覆盖主流业务场景:

模板名称适用场景核心特征
express_oltp 事务支付、核心交易系统、高吞吐互联网应用 无外键约束、无存储过程、无长事务和大事务、无复杂 Join 和复杂子查询
complex_oltp 银行和保险系统、复杂关联子查询、PL 批处理作业 涉及复杂 Join、复杂关联子查询、长/大事务,可能使用并行执行加速短查询
olap 实时数据仓库分析应用 适合大规模数据扫描、聚合查询、复杂分析负载
htap 混合 OLAP 和 OLTP 负载、实时经营数据分析、欺诈检测、个性化推荐 兼顾事务处理的高并发和分析查询的扫描效率,需平衡两类负载的性能要求
kv HBase 兼容和 Key-Value 表模型访问模式、单分区访问、无 SQL 层 极高的数据吞吐量要求,对延迟极度敏感
📌 OCP 租户参数模板管理功能
OCP 平台支持参数模板的完整生命周期管理,包括:
  • 新建参数模板:定义模板名称并设置相关参数,可通过「添加参数」按钮新增参数项并配置参数值。
  • 编辑参数模板:修改已有模板的参数配置。
  • 复制参数模板:将已有复杂模板复制并重命名,在 A 的基础上做简单修改,无需重新配置大量参数。
  • 删除参数模板:清理不再使用的模板,保持管理清单整洁。
  • 应用参数模板:在创建租户时直接选择参数模板,快速完成配置。

配置路径:OCP 控制台 → 租户 → 租户参数模板列表 → 租户参数模板管理。

场景化参数最佳实践

以下针对 V4.3.x 版本的推荐参数配置,综合了 OceanBase 在各行业客户的大量实战调优经验。具体配置值可能因版本迭代有所差异,建议以版本自带模板为准。

一、OLTP 场景推荐参数

OLTP 场景追求高并发、低延迟的事务处理,参数配置重点在于保障小事务的响应速度。

参数名称推荐值说明生效范围是否重启生效
_enable_defensive_check 0(False) 关闭查询执行的防御性检查,生产环境关闭可提升 DML 操作性能约 10% 集群级
enable_syslog_recycle True 开启系统日志自动回收,防止日志文件占满磁盘空间 集群级
max_syslog_file_count 300 配合 enable_syslog_recycle 使用,设置保留的日志文件最大数量 集群级
_rowsets_max_rows 256(V4.3.3+) SQL 引擎向量化执行时处理的最大行数,对于早期版本建议设为 1 租户级
log_transport_compress_all True 开启日志传输压缩,在带宽受限场景下可节省网络带宽,CPU 开销较低 租户级
-- OLTP 场景参数配置示例(sys 租户执行)
        ALTER SYSTEM SET _enable_defensive_check = 0;
        ALTER SYSTEM SET enable_syslog_recycle = True;
        ALTER SYSTEM SET max_syslog_file_count = 300;

        -- 租户级参数配置(用户租户执行)
        SET GLOBAL _rowsets_max_rows = 256;
        SET GLOBAL log_transport_compress_all = True;
二、复杂 OLTP 场景推荐参数

复杂 OLTP 场景涉及复杂查询、长事务、PL 批处理等,需要适当放宽大查询阈值,平衡复杂操作与小事务的性能。

参数名称推荐值说明
large_query_threshold 600s 复杂 OLTP 负载中复杂查询可能执行较长时间,需调大阈值
_enable_defensive_check 0 关闭防御性检查以提升性能
log_transport_compress_all True 带宽受限场景下启用 RPC 压缩,减少网络开销
三、AP/HTAP 场景推荐参数

AP 和 HTAP 场景追求分析查询的快速响应,参数配置重点在于保障大规模扫描和并行执行的能力。

参数名称推荐值说明生效范围
large_query_threshold 600s AP 查询通常执行时间较长,需调大阈值避免被误判为大查询并限流 集群级
enable_record_trace_log false 关闭 SQL 和事务跟踪日志记录,可提升 AP 查询性能 集群级
enable_syslog_recycle true 开启系统日志自动回收,防止日志文件占满磁盘空间 集群级
max_syslog_file_count 300 保留日志文件的最大数量 集群级
log_transport_compress_all true 带宽受限场景下启用 RPC 压缩 租户级
-- AP/HTAP 场景参数配置示例(sys 租户执行)
        ALTER SYSTEM SET large_query_threshold = '600s';
        ALTER SYSTEM SET enable_record_trace_log = false;
        ALTER SYSTEM SET enable_syslog_recycle = True;
        ALTER SYSTEM SET max_syslog_file_count = 300;

        -- 租户级配置(用户租户执行)
        SET GLOBAL log_transport_compress_all = True;

租户核心系统变量推荐配置

租户级系统变量直接控制 SQL 执行行为,以下为生产环境常用的配置建议:

常用租户系统变量配置表
变量名称默认值推荐配置说明
ob_query_timeout 10 秒 20 ~ 60 秒 单条 SQL 语句执行超时时间(微秒),建议根据业务 SQL 平均执行时间调整
ob_trx_timeout 1 天(V4.x) 按业务需求调整 事务总超时时间,V4.x 默认值较大,可根据业务场景适当调整
ob_trx_idle_timeout 24 小时 按业务需求调整 事务空闲超时时间,建议保持较大值,与 ob_trx_timeout 配合使用
ob_tcp_invited_nodes '%' 或指定白名单 租户 IP 白名单,生产环境建议设置为具体 IP 段,避免安全风险
ob_compatibility_mode MySQL MySQL 或 Oracle 租户兼容模式,创建租户时指定,创建后不可修改
read_consistency STRONG STRONG / WEAK 读一致性级别,弱一致性读可结合 LDC 路由,显著降低读延迟
parallel_degree_policy MANUAL AUTO / MANUAL 并行度策略,AUTO 模式下优化器自动决定并行度(Auto DOP)
runtime_filter_type ''(空字符串) BLOOM_FILTER,RANGE,IN Runtime Filter 类型,用于优化 Hash Join 性能。V4.2.0 版本引入,默认关闭。开启时可设置为 BLOOM_FILTER,RANGE,IN,支持 Bloom Filter、Range 和 In 三种过滤方式
-- 租户变量配置示例(用户租户下执行)
        -- 调整 SQL 超时时间为 30 秒
        SET GLOBAL ob_query_timeout = 30000000;

        -- 设置 IP 白名单(允许任意 IP 访问,生产环境请按需收紧)
        SET GLOBAL ob_tcp_invited_nodes = '%';

        -- 开启弱一致性读(需配合 LDC 使用)
        SET GLOBAL read_consistency = 'WEAK';

        -- 开启 Auto DOP(自动并行度)
        SET GLOBAL parallel_degree_policy = 'AUTO';

        -- 开启 Runtime Filter 优化复杂 Join(系统变量默认值为空字符串)
        SET GLOBAL runtime_filter_type = 'BLOOM_FILTER,RANGE,IN';

租户资源参数优化

租户的资源参数在创建资源单元(Unit Config)时定义,决定了租户获得的物理资源上限。合理的资源分配是性能保障的基础。

资源单元配置建议
参数推荐实践说明
MAX_CPU / MIN_CPU 核心业务租户建议 MIN_CPU = MAX_CPU 避免 CPU 超卖带来的性能抖动,保障租户 CPU 资源确定性
MEMORY_SIZE 根据业务数据量和并发估算,建议预留 20% 余量 内存是刚性资源,分配后不可被其他租户使用,需合理规划
MAX_IOPS / MIN_IOPS 建议基于磁盘性能校准结果配置,最小值不低于 1024 IOPS 隔离需要磁盘校准支持,配置前建议进行基准测试
LOG_DISK_SIZE 建议为 MEMORY_SIZE 的 2-4 倍 日志磁盘用于存放 Redo 日志,需要预留足够空间应对写入高峰
-- 创建生产环境资源单元示例
        CREATE RESOURCE UNIT prod_unit 
            MAX_CPU=16, MIN_CPU=16,        -- CPU 不超卖
            MEMORY_SIZE='64G', 
            MAX_IOPS=128000, MIN_IOPS=128000, 
            LOG_DISK_SIZE='128G';          -- 日志盘为内存 2 倍

        -- 创建资源池
        CREATE RESOURCE POOL prod_pool 
            UNIT='prod_unit', 
            UNIT_NUM=1, 
            ZONE_LIST=('zone1','zone2','zone3');

        -- 创建租户并应用参数模板
        CREATE TENANT prod_tenant 
            RESOURCE_POOL_LIST=('prod_pool')
            PRIMARY_ZONE='RANDOM'
            CHARSET='utf8mb4'
            SET ob_compatibility_mode='mysql', 
                ob_tcp_invited_nodes='10.0.0.0/8';

        -- 查看租户创建结果
        SELECT TENANT_ID, TENANT_NAME, PRIMARY_ZONE, COMPATIBILITY_MODE 
        FROM oceanbase.DBA_OB_TENANTS 
        WHERE TENANT_NAME = 'prod_tenant';
⚠️ 资源分配注意事项:创建租户时 RESOURCE_POOL_LIST 为必选项。租户创建后租户类型(MySQL/Oracle 模式)无法修改。多个资源池必须满足 UNIT_NUM 相同,且 Zone 不能重叠。系统租户不对外提供完整数据库能力,建议不要在生产或业务测试等场合使用系统租户运行业务负载。

租户参数模板与最佳实践汇总

V4.3.x 版本模板配置文件位置

OceanBase 安装包的 RPM 包安装后,可在以下目录找到场景化的参数推荐模板:

/home/admin/oceanbase/etc/
        ├── default_parameter.json          # 参数推荐配置(通用)
        └── default_system_variable.json    # 系统变量推荐配置(通用)

各场景的具体配置值分布在模板文件中,供 OCP 和 OBD 等运维工具读取使用。

📌 参数配置最佳实践总结
  • 区分配置层级:集群级配置项影响整个集群的行为,修改需谨慎;租户级配置项/变量仅影响指定租户,可单独调优。
  • 善用参数模板:创建大批量租户时优先使用参数模板,确保配置一致性,减少手工操作错误。
  • 按场景选型:根据业务负载特征选择对应的模板类型(express_oltp / complex_oltp / olap / htap / kv),而非自行随意配置。
  • 充分验证后再上线:参数变更应在测试环境充分验证性能影响后再应用到生产环境,尤其对于超时类和资源类参数。
  • 定期审视配置:随业务演进和数据量变化,定期评估现有参数配置是否仍适应当前负载特征,及时调整优化。
  • 善用视图监控:通过 DBA_OB_TENANTS 查看租户基本信息,通过 SHOW PARAMETERSSHOW GLOBAL VARIABLES 验证配置生效状态。
🏆 4.x 版本参数配置演进总结
V4.3.2:正式提供场景化参数推荐模板(express_oltp / complex_oltp / olap / htap / kv)
V4.3.1:OCP 平台支持可视化的租户参数模板管理,支持参数模板的创建、编辑、复制、删除等完整生命周期管理
参数体系成熟:配置项和系统变量的分层体系逐步完善,覆盖 OLTP、OLAP、HTAP 等多种业务场景
实战经验沉淀:基于大量真实场景的调优经验沉淀为标准配置,显著降低用户调优门槛

第7章:向量化执行引擎与 Auto DOP

7.1 向量化执行引擎

向量化执行(Vectorized Execution)是一种高效的按批处理数据的技术,是数据库系统从传统行式执行(火山模型)向批量处理演进的关键里程碑。在分析型查询中,向量化执行可以大幅提升执行性能,充分发挥现代 CPU 的并行计算能力。OceanBase 从 3.2 版本引入向量化执行引擎,从 4.0 版本开始默认开启,并在 4.3 版本推出了向量化引擎 2.0,通过对数据格式、算子实现和存储层的全面优化,实现了执行性能的又一次飞跃。

🎯 向量化执行的核心价值
  • 大幅减少函数调用开销:从逐行调用 next() 转变为批量调用 next_batch(),显著降低 CPU 资源浪费。
  • 提升 CPU 缓存命中率:批量数据紧凑存放,充分利用 L1/L2/L3 缓存,减少 Cache Miss。
  • 充分利用 SIMD 指令集:向量化的数据组织方式对 AVX、SSE 等 SIMD 指令更友好,实现指令级并行。
  • 降低分支预测失败率:批量处理使得算子内部的执行路径更加规律,减少 CPU 分支预测错误带来的性能损失。

从火山模型到向量化执行

要理解向量化引擎,首先需要了解传统的查询执行模型——火山模型(Volcano Model)。火山模型自 1990 年在论文《Volcano, an Extensible and Parallel Query Evaluation System》中被提出以来,被 Oracle、MySQL、DB2、SQL Server 等主流关系型数据库广泛采用。

🌋 火山模型(Volcano Model)

火山模型也被称为迭代模型(Iterator Model)。在火山模型中,一个查询计划会被分解为多个算子(Operator),每个算子都是一个迭代器,需要实现 next() 接口,通常包括以下步骤:

  1. 调用子算子的 next() 方法,获取子算子返回的一行计算结果;
  2. 对子算子返回的行执行当前算子的计算逻辑;
  3. 返回一行结果给父算子。

在 OceanBase 的代码中,算子迭代接口名为 ObOperator::get_next_row()

火山模型通过解耦算子逻辑,能够优雅地将任意算子组装成执行树,但该模型在现代 CPU 架构下存在明显的性能瓶颈:

  • 框架函数调用开销巨大:每处理一行数据都需要一次虚函数调用,处理 1 亿行数据就需要调用 1 亿次 get_next_row(),CPU 资源浪费严重。
  • CPU 缓存利用率低:频繁的函数调用和分支判断导致指令缓存(ICache)命中率下降。
  • 数据依赖严重:单行处理限制了指令级并行(ILP)和超标量执行能力。
  • 内存带宽浪费:逐行访问导致大量随机内存访问,无法有效利用 CPU 预取机制。

向量化执行的核心原理:从逐行到批量

向量化引擎通过在火山模型基础上引入批量处理能力,将一次迭代处理一行数据转变为一次处理一批数据,从根本上突破火山模型的性能瓶颈。

批次处理的收益分析
对比维度传统火山模型(逐行)向量化引擎(批量)
函数调用方式 get_next_row() 每次返回一行 get_next_batch() 一次返回一批数据
函数调用次数 处理 1 亿行需调用 1 亿次 以批次大小 1024 行为例,仅需调用约 9.7 万次
CPU 缓存利用 数据逐行访问,缓存命中率低 批次数据紧凑存放,Cache 命中率显著提升
SIMD 支持 数据不连续存放,SIMD 利用困难 数据连续,对 SIMD 指令友好
分支预测 每行处理路径不确定,分支预测失败率高 批次内执行路径规整,分支预测更准确

执行计划中通过 rowset=N 标识向量化处理时的批次大小。例如,在执行计划中看到如下信息:

|0 - output([T_FUN_COUNT_SUM(T_FUN_COUNT(*))]), filter(nil), rowset=16

其中 rowset=16 表示该算子一次处理 16 行数据。该参数会依据系统资源(CPU 核心数、内存大小)和 SQL 类型(TP/AP)自适应调整,最佳实践是不手动修改。

OceanBase 向量化引擎的技术演进

OceanBase 向量化引擎经历了从 V3.2 引入到 V4.3 引擎 2.0 的持续演进,每一阶段都有明确的技术目标和性能提升。

版本演进路线图
版本状态核心特点 V3.2 默认关闭 首次引入向量化执行引擎,基于 Uniform 数据描述方式,性能较非向量化已有明显提升。但因 V3.x 架构限制,部分场景(如索引回表)不支持向量化。 V4.0 默认开启 向量化执行引擎默认启用,支持大规模 AP 查询加速。V4.x 架构以日志流为事务单元,为向量化引擎的全面铺开奠定了基础。 V4.3(引擎 2.0) 全面升级 实现按列的新数据格式,将数据描述信息(null、len、ptr)分别按列连续存放,彻底解决向量化 1.0 的数据访问效率问题。核心算子(Sort、Hash Join、Hash Group By)按新格式重新设计与实现。
向量化引擎 1.0 的局限性

在向量化引擎 1.0 实现中,存储层数据投影后,某列表达式的一批数据在内存中的组织格式是由多个连续的数据描述单元(包含 null 描述、数据长度 len 及数据指针 ptr)及实际数据组成,存在以下问题:

  • 读写访问不够高效:每次访问都需要先获取数据描述单元,然后通过 ptr 间接访问数据,不能直接访问。
  • 内存使用更多:以 N 行 int32_t 数据为例,数据描述单元占 12 字节,总内存为 N × (12+4) 字节,而实际数据仅需 N × 4 字节,空间放大 4 倍。
  • SIMD 计算不够友好:一批数据对应的实际数据不一定连续存放,难以利用 SIMD 指令并行计算。
  • 序列化/物化开销更多:需要进行指针的 swizzling 操作,即将指针转换为相对偏移,效率低下。
向量化引擎 2.0 的关键优化:数据格式革新

为优化向量化 1.0 数据格式带来的不足,向量化引擎 2.0 实现了新的按列的数据格式。将数据描述信息(null、len、ptr)按列分开连续存放,避免数据信息冗余存储。针对不同数据类型和使用场景,实现了三种数据格式:

  • 定长数据格式:仅需 null bitmap 和连续的数据,length 信息存放一份即可。相比引擎 1.0,数据无冗余存放、节省空间,直接访问性能更好,数据连续对 SIMD 更友好,物化/序列化无需指针 swizzling。
  • 变长离散格式:每个数据在内存中可能不连续存放,使用数据地址指针和长度描述,长度和指针信息按列连续存放。适合存储层为编码数据、需要投影但不需要深拷贝数据的场景,支持短路计算。
  • 变长连续格式:数据连续存放在内存中,长度信息和偏移地址使用 offset 数组描述。数据访问和按批复制效率更高,但短路计算和列存编码投影不友好,主要用于按列物化场景。
算子及表达式全面优化

向量化引擎 2.0 不仅优化了数据格式,还对算子及表达式实现进行了全面重构,主要优化思路包括:

  • 利用 Batch 数据属性信息:批次数据的元信息(如是否全部为 NULL、是否全部满足某个条件)可用于跳过不必要的计算逻辑。
  • 算法数据结构优化:Sort、Hash Join、Hash Group By、数据 Shuffle、聚合计算等核心算子按新格式重新设计与实现。
  • 表达式特化实现:针对常用表达式提供特化版本,减少通用的分支判断开销。

这些优化共同减少了 CPU 数据 Cache Miss,降低了 CPU 分支预测错误及 CPU 指令开销,显著提升了整体执行性能。

存储层向量化读取

向量化引擎的优化不仅仅在 SQL 层,更深入到存储引擎层。通过存储层向量化读取,实现了从存储到计算的全链路向量化处理。

  • 存储向量化:存储引擎在读取 SSTable 时,可以一批次读取多个微块(Microblock),并以列存格式组织返回给 SQL 层。
  • 谓词下压向量化:过滤条件可以下压至存储层,在读取数据时完成向量化的过滤判断,减少向上层传递无效数据。
  • 行列存统一支持:在行存模式下,OceanBase 已实现向量化存储引擎的无缝对接,无需任何修改即可支持向量化执行。

对现代 CPU 的深度优化

OceanBase 向量化引擎通过三层优化深度适配现代 CPU 架构,最大化发挥 CPU 性能:

  • 数据紧凑排列,提升 CPU Cache 友好性:批量数据按列组织并紧凑存储,CPU 在顺序读取数据时可充分发挥硬件预取机制,L1/L2/L3 缓存命中率显著提高。
  • SIMD 指令加速:向量化 2.0 将数据存储格式调整为列式,使连续内存的批量计算更易利用 SIMD 指令(如 AVX512)实现并行处理。
  • Batch 级别属性判断:利用批次数据的整体特征(如全 NULL、全满足某条件)跳过逐行判断逻辑,减少无效计算开销。

性能测试与收益

向量化引擎的性能提升在多个基准测试和生产场景中得到了充分验证:

历史成绩(V3.2 时代)

2021 年,OceanBase 凭借向量化引擎的贡献,以 1526 万 QphH@30000GB 的性能成绩,创造了 TPC-H 分析型基准测试新的世界纪录,排名第二。

V4.3 版本对比测试

在 TPC-H 30TB 数据集测试中:

  • 向量化引擎整体性能是非向量化的 2.48 倍
  • 对于 Q1 这种聚合分析且计算密集的 SQL 查询,性能提升约 10 倍

从 V4.3.0 到 V4.3.3 GA 版本,TPC-H 1TB 数据集的查询性能从 99 秒提升至 60 秒,实现了 64% 的性能提升

跨版本持续优化

相比 V4.2.1 版本,V4.3 版本的 TPC-H 和 TPC-DS 性能分别提升 25%111%

向量化引擎与 AI 预处理

向量化引擎不仅服务于传统 AP 场景,还在 AI 生态中扮演着重要角色。AI 模型的训练与推理依赖大规模高质量数据的高效预处理(如特征工程、数据清洗、统计分析等 AP 场景任务)。OceanBase 向量化引擎通过大幅提升大规模数据的分析处理能力,可快速完成 AI 所需的海量数据预处理工作,缩短数据到模型的流转周期,为 AI 应用提供稳定、高效的底层数据存储与计算底座。

📌 向量化引擎的使用与监控
  • 默认开启:从 OceanBase 4.0 版本开始,向量化执行引擎默认开启,无需手动配置。
  • 执行计划标识:向量化执行计划中会通过 rowset=N 标识批次大小。该参数建议由系统自适应调整,不推荐手动修改。
  • 监控视图:可通过 GV$OB_SQL_AUDIT 等视图观察 SQL 执行耗时变化,验证向量化引擎的效果。
  • 版本注意:V3.2 等早期版本向量化引擎默认关闭,且因架构限制部分场景不支持,强烈建议升级到 V4.0 及以上版本。
🏆 向量化引擎未来演进方向
编译器技术支持:探索利用 LLVM JIT 等技术生成更高效的批处理代码(ob_enable_jit 参数,从 V4.3.1 版本开始引入)。
更多算子向量化覆盖:持续扩大向量化算子的覆盖面,进一步提升复杂查询的执行效率。
AI 融合深化:与向量检索等 AI 能力深度融合(V4.3.3 起支持向量数据类型和向量索引),为 AI 应用提供更强的基础设施支撑。
执行效率精益求精:继续优化内存布局和指令级并行,挖掘现代 CPU 的极限性能。
7.2 Auto DOP(自动并行度)

并行执行(Parallel Execution)通过利用多个 CPU 核心来加速大规模数据处理的查询,是数据库系统用于处理分析型查询的核心技术之一。然而,如何为不同的查询合理地设置并行度(Degree of Parallelism,DOP),以达到加速查询和避免资源过度消耗之间的平衡,是一个实践难题。针对手动指定并行度的痛点,Auto DOP 功能应运而生,它让优化器能够根据查询的实际需求,自动判断是否开启并行执行,并选择合适的并行度,从而实现并行度的“自动驾驶”。

🎯 Auto DOP 的核心价值
  • 自适应与智能化:优化器在生成执行计划时会评估查询的执行代价,自动决定是否开启并行执行,并为当前查询确定一个合适的并行度。
  • 降低调优门槛:数据库管理员不再需要深入了解每条 SQL 的复杂度和每个表的统计信息,只需一键开启,即可获得相比手动调优“过得去”甚至更优的性能。
  • 避免“过度并行”:自动模式可以有效避免为那些数据量小的表或本不需要并行加速的查询,开启不必要的并行执行,从而节省系统资源。
  • 自动化 PDML:开启 Auto DOP 后,优化器可以对 DELETE/UPDATE/INSERT 等多种操作自动开启并行执行,并自动启用并行数据操作语言(PDML),无需额外配置。

从手动指定到自动选择

OceanBase 优化器的并行度 DOP 选择策略目前分为AUTO 模式MANUAL 模式,选择 AUTO 模式即为 Auto DOP。[reference:0]

您可以通过 MANUAL 模式手动指定并行度,一种方式是使用系统变量对当前会话中的所有执行查询开启并行执行并指定并行度,但这可能会给会话中本不需要并行加速,或者不需要使用较高 DOP 的查询带来额外的开销,从而导致性能下降;另一种方式是通过 Hint 来指定特定查询的并行度,但这需要对每一条业务查询进行单独考量,对于存在大量业务查询的场景是不可行的。[reference:1]

基于手动指定并行度所带来的不便和限制,AUTO 模式应运而生。OceanBase 数据库查询优化器的 Auto DOP 功能,在生成查询计划时会评估查询所需要执行的时间,自动确定是否开启并行以及确定对当前查询合适的并行度,避免了由于手动指定并行度而导致的性能下降问题。[reference:2]

如何开启 Auto DOP 策略

OceanBase 数据库优化器通过系统变量 parallel_degree_policy 进行 GLOBAL 和 SESSION 级别的 Auto DOP 选择策略配置,也可通过更高优先级的 Hint 进行查询级别的 DOP 选择策略配置。

🔧 parallel_degree_policy 系统变量
  • 默认值MANUAL[reference:3]
  • 取值范围["MANUAL", "AUTO"][reference:4]
  • 引入版本:从 V4.2.0 版本开始引入[reference:5]
  • AUTO: 启用 Auto DOP 策略[reference:6]
  • MANUAL: 禁用 Auto DOP 策略[reference:7]
  • 生效范围: Global 和 Session[reference:8]
-- 在 GLOBAL 级别启用 Auto DOP
        SET GLOBAL parallel_degree_policy = AUTO;

        -- 在 SESSION 级别启用 Auto DOP
        SET SESSION parallel_degree_policy = AUTO;

        -- 在 GLOBAL 级别禁止 Auto DOP
        SET GLOBAL parallel_degree_policy = MANUAL;

        -- 使用 Hint 在查询级别启用 Auto DOP
        SELECT /*+PARALLEL(AUTO)*/ * FROM ...;

        -- 使用 Hint 在查询级别禁止 Auto DOP
        SELECT /*+PARALLEL(MANUAL)*/ * FROM ...;
        -- 或者直接指定一个固定的 DOP(例如 8)
        SELECT /*+PARALLEL(8)*/ * FROM ...;

以上示例参考自 OceanBase 官方文档。[reference:9]

并行开启方式及优先级

OceanBase 数据库支持通过 Auto DOP、Hint、系统变量、Schema DOP 等多种方式对查询开启并行执行。在查询支持并行执行的前提下,多种并行开启方式的优先级(由高到低)如下:表级并行 Hint 配置 DOP > 全局并行 Hint 配置 DOP > 系统变量配置 DOP > Schema DOP。[reference:10]

其中,全局并行 Hint 可以通过设置 PARALLEL(AUTO) 来指定查询使用 Auto DOP 策略,或通过设置 PARALLEL(MANUAL) 来禁止 Auto DOP,也可以直接使用 PARALLEL(dop_value) 来为一个查询中的所有表指定一个固定的 DOP。由于全局并行 Hint 优先级低于表级并行 Hint,当同时存在全局和表级并行 Hint 时,全局 Hint 仅对没有表级 Hint 生效的表生效。[reference:11]

-- 全局并行 Hint:指定查询中所有表使用 DOP = 8
        SELECT /*+PARALLEL(8)*/ * FROM t1, t2 WHERE t1.c1 = t2.c1;

        -- 全局并行 Hint:指定查询中所有表使用 Auto DOP 策略
        SELECT /*+PARALLEL(AUTO)*/ * FROM t1, t2 WHERE t1.c1 = t2.c1;

        -- 表级并行 Hint 与全局并行 Hint 组合使用
        -- 表 t1 因表级 Hint 使用 DOP = 8,表 t2 因全局 Hint 使用 DOP = 4
        SELECT /*+PARALLEL(t1 8) PARALLEL(4)*/ * FROM t1, t2 WHERE t1.c1 = t2.c1;

        -- 表级并行 Hint 与 Auto DOP 组合使用
        -- 表 t1 因表级 Hint 使用 DOP = 8,表 t2 根据 Auto DOP 策略自动获取 DOP
        SELECT /*+PARALLEL(t1 8) PARALLEL(AUTO)*/ * FROM t1, t2 WHERE t1.c1 = t2.c1;

以上示例参考自 OceanBase 官方文档。[reference:12]

⚠️ **不支持并行的场景**:当查询涉及 PL 用户自定义函数(UDF)或数据库链接(DBLinks)时,禁止查询开启并行执行。对于使用 DAS 扫描的基表,不支持指定并行度,在计划形态上显示并行度为 1。[reference:13]

Auto DOP 核心参数

为了在生产环境中安全且高效地使用 Auto DOP,需要了解并合理配置以下几个关键参数,它们共同构成了 Auto DOP 的资源管控与执行优化体系。

  • parallel_servers_target:租户级别的系统变量,它明确规定了租户在每个节点上可申请的并行执行线程数量。其默认值是 MIN CPU * px_workers_per_cpu_quota。当这个参数指定的线程资源耗尽时,后续发起的并行执行请求需要进入排队等待的状态,直至有可用资源释放。[reference:14]
  • parallel_degree_limit:用于设置在使用 Auto DOP 策略时,优化器选择并行度的上限值。该变量从 V4.2.0 版本开始引入。默认值为 0,表示对并行度不进行限制。当 parallel_degree_limit = 0 时,系统会自动借助 CPU 资源状况和 parallel_servers_target 的设定,共同限制最大 DOP 值。[reference:15]
  • parallel_min_scan_time_threshold:以毫秒(ms)为单位,默认值设置为 1000 ms,该参数对并行度的调整有着直接影响。具体来说,当基表的扫描代价高于该参数设定的值时,系统就会自动开启并行处理模式。假如适当调小该值,可以降低基表开启并行处理的门槛,使得评估执行时间更短的基表扫描也能被纳入并行处理范畴。[reference:16]
-- 设置租户级 Auto DOP 并行度上限(例如 32)
        SET GLOBAL parallel_degree_limit = 32;

        -- 开启 Auto DOP
        SET GLOBAL parallel_degree_policy = AUTO;

Auto DOP 适用场景与风险

Auto DOP 仅基于查询执行成本进行评估,以减小响应时间(RT)为目标来决定是否启用并行处理及计算 DOP。若业务负载中包含对用户不关心的慢查询,则功能开启后这些查询可能会以并行方式执行,并获得较高的并发度。这些查询的并行执行将占用系统并行资源,对其他需要正常并发处理、优化响应时间的查询造成干扰。若这些查询占用过多资源,系统 CPU 负载将急剧上升,严重影响正常业务查询。[reference:17]

Auto DOP 只能优化针对当前查询响应时间(RT)的目标,而不会考虑系统中存在大量需要并发处理的查询场景。因此,在实际业务系统中使用 Auto DOP 功能,需要根据实际优化目标,有针对性地调整相关系统变量。特别是当 AP 慢查询开启 Auto DOP 占用大量线程并行执行时,会在一定程度上影响分配给小查询的资源。[reference:18]

为此,OceanBase 官方文档提供了两种使用场景:

  • 场景 1:查询性能极致优化:追求单条查询的极致性能优化,使用 Auto DOP 功能。此场景下,建议在使用 Auto DOP 的基础上配置适当的资源组或资源配置(比如 parallel_servers_targetparallel_degree_limit 等),作为兜底策略,以避免单个查询占用过多并行执行资源,对系统造成负面影响。[reference:19]
  • 场景 2:系统性能优化:追求系统整体性能最优,不使用 Auto DOP,而是使用资源隔离策略,控制不同负载对资源的占用。[reference:20]
📌 使用建议
  • 善用限流参数:使用 Auto DOP 时,务必同时设置 parallel_servers_targetparallel_degree_limit,这对避免系统过载至关重要。
  • 关注资源使用:可通过系统视图监控并行执行线程的使用情况和排队情况,以便及时调整限流参数。
  • 优先推荐使用:对于不熟悉并行度设置规则的用户,Auto DOP 提供了智能便捷的解决方案。
  • 资源隔离兜底:应严格遵循资源隔离规范(如配置 cgroup 或使用资源组),避免自动并行执行影响在线 TP 业务,让 AP/TP 业务在共用集群时隔离得当。
🏆 4.x 版本 Auto DOP 演进总结
V4.2.0:Auto DOP 相关系统变量 parallel_degree_policyparallel_degree_limitparallel_min_scan_time_threshold 正式引入,为实现自动化、智能化的并行度选择提供了关键工具。[reference:21][reference:22][reference:23]
V4.2.0+:Auto DOP 作为关键特性持续演进,其智能化程度不断提高,调优效果得到进一步加强和验证。
V4.x 官方推荐:在 OceanBase 4.x 版本中,Auto DOP 已成为官方推荐使用的 SQL 性能调优方案之一,尤其适合对并行度规则不太熟悉的用户。

第8章:分区表优化与分区裁剪实战

8.1 分区裁剪原理与价值

当用户访问分区表时,往往只需要访问其中的部分分区。优化器根据 SQL 中的 WHERE 条件,自动识别需要访问的分区,直接跳过不需要的分区,只扫描目标数据分区——这一优化过程称为分区裁剪(Partition Pruning)。它是分区表最重要的优化手段,通过分区裁剪,SQL 的执行效率可以得到大幅度提升。

🎯 分区裁剪的核心价值
  • 大幅减少磁盘 I/O:只扫描必要分区,跳过无关数据,显著降低数据扫描量。
  • 提升查询响应速度:对于大表查询,裁剪后的效率往往是全分区扫描的数倍甚至数十倍。
  • 降低计算资源消耗:减少无效数据的读取和处理,节约 CPU 和内存资源。
  • 支撑海量数据管理:时序日志、交易流水等大数据量场景,分区裁剪是保证查询性能的关键。例如,一张按天分区的日志表保存了近30天数据,如果查询只查当天数据,正常情况下仅需扫描1个分区;若裁剪失效,则需扫描全部30个分区,查询效率差距极为明显。

分区裁剪的基本原理

分区裁剪由 OceanBase 数据库优化器的 Query Range 子模块完成。优化器根据表的分区信息和 SQL 中指定的条件,抽取出相关的分区信息,计算出需要访问的分区集合。核心触发条件是:WHERE 子句中必须包含分区键的过滤条件。

HASH / LIST 分区裁剪

对于 HASH 分区和 LIST 分区,分区裁剪的基本原理是:根据 WHERE 子句中的条件计算分区列的值,通过结果判断需要访问哪些分区。HASH 和 LIST 分区仅支持等值条件裁剪,不支持范围查询裁剪。此外,如果分区条件为表达式,且该表达式作为一个整体出现在等值条件里,也可以进行分区裁剪。

-- 示例:HASH 分区,WHERE 条件为分区键等值,可触发裁剪
        CREATE TABLE tbl1(col1 INT, col2 INT) PARTITION BY HASH(col1) PARTITIONS 5;

        EXPLAIN SELECT * FROM tbl1 WHERE col1 = 1;

        -- 执行计划结果(仅访问分区 p1)
        -- 关键信息:partitions(p1)
        -- |0 |TABLE FULL SCAN|tbl1|1|4|
        -- Outputs & filters: 0 - output([tbl1.col1], [tbl1.col2]),
        --   filter([tbl1.col1 = 1]), access([tbl1.col1], [tbl1.col2]), partitions(p1)

        -- 示例:分区条件为表达式,整体出现在等值条件中,也可裁剪
        CREATE TABLE t1_h (c1 INT, c2 INT) PARTITION BY HASH(c1 + c2) PARTITIONS 5;

        EXPLAIN SELECT * FROM t1_h WHERE c1 + c2 = 1;

        -- 执行计划结果(同样仅访问一个分区)
        -- 关键信息:partitions(p1)
💡 HASH 分区的定位:HASH 分区主要用于数据打散和负载均衡,不适用于数据归档和范围查询场景。其裁剪能力仅限于精准等值查询。
RANGE 分区裁剪

对于 RANGE 分区,通过 WHERE 子句中分区键的范围与表定义的分区范围取交集来确定需要访问的分区。RANGE 分区支持等值、大于、小于、区间范围等多种裁剪方式,是业务中最稳定、最常用的分区类型,特别适用于时序日志、交易流水、订单数据等场景。

-- 示例:RANGE 分区支持范围查询裁剪
        -- 分区定义(假设 c1 为分区键)
        -- PARTITION p0 VALUES LESS THAN (100), p1 VALUES LESS THAN (200), p2 VALUES LESS THAN (MAXVALUE)

        -- 查询条件为范围区间
        SELECT * FROM t1 WHERE c1 >= 120 AND c1 < 180;

        -- 优化器计算交集后,仅需要访问 p1 分区
⚠️ 重要限制:对于 RANGE 分区,如果分区条件是一个函数(即表达式分区)且查询条件是一个范围,则不支持分区裁剪。例如分区键定义为 c1 + 1,查询条件为 c1 > 100 AND c1 < 150,此时优化器无法进行裁剪,会扫描全部分区。等值条件则可正常裁剪。
二级分区裁剪原理

对于二级分区(子分区)表,分区裁剪需要同时满足一级分区键和二级分区键的条件,才能实现精准裁剪。

  • 裁剪流程:首先根据一级分区键条件确定需要访问的一级分区,然后根据二级分区键条件进一步确定需要访问的二级分区,最终取两者的笛卡尔积得出需要访问的物理分区集合。
  • 裁剪不彻底的风险:如果查询条件中只包含一级分区键条件而未包含二级分区键条件,则只能裁剪到一级分区层级,二级分区层面仍需扫描全部子分区,裁剪不完全。

优化器的执行机制

OceanBase 数据库的分区裁剪由 Query Range 子模块执行,其核心工作流程如下:

  1. 条件解析:从 WHERE 子句中提取与分区键相关的过滤条件。
  2. 分区范围计算:将过滤条件与表的分区定义进行匹配计算,生成需要访问的分区列表。
  3. 指定分区与裁剪结果的交集:如果 SQL 中直接指定了分区(通过 PARTITION 语法),优化器会取“指定分区”和“裁剪结果”的交集作为最终访问的分区集合。
  4. 裁剪失效时的退化处理:若 WHERE 条件中不包含分区键,优化器将无法进行分区裁剪,此时会进行全分区扫描,导致 I/O 和 CPU 消耗显著增加。

执行计划验证

通过 EXPLAIN 命令可以查看执行计划并确认分区裁剪是否生效。执行计划输出中的关键信息有两个来源:

  • access() 行中的 partitions 字段:明确列出当前查询实际需要访问的分区集合。例如 partitions(p1) 表示仅访问 p1 分区;partitions(p[0-1]) 表示访问 p0 和 p1 两个分区。
  • 算子层级的信息:在执行计划树中,分区裁剪的信息也会通过 PARTITION RANGE ITERATOR 等算子的形式显示,表明系统正在通过分区范围迭代器访问限定分区。
-- 通过 EXPLAIN 查看分区裁剪结果
        EXPLAIN SELECT * FROM tbl1 WHERE col1 = 1;

        -- 输出中的关键行:
        -- 0 - output(...), filter(...), access(...), partitions(p1)
        -- 
        -- partitions(p1) 表示分区裁剪成功生效,仅访问 p1 分区;
        -- 若显示 partitions(p[0-4]) 或未显示 partitions 字段,则表示裁剪未生效或全分区扫描。

性能收益分析

分区裁剪对查询性能的提升在数据量大的场景下尤为显著,主要体现在以下维度:

  • I/O 减少:仅扫描目标分区数据,磁盘读取量大幅降低。
  • 扫描数据量对比:如上文日志表示例(30 个分区),裁剪生效时仅扫描 1/30 的数据;裁剪失效时扫描全量数据,I/O 放大 30 倍。
  • CPU 和内存节省:减少无效数据的读取和处理,节约计算资源。
  • 支撑业务报表实时性:在数仓、报表等业务场景中,分区裁剪是保障查询实时性的关键技术。客如云智享版报表通过分区化改造,基于分区裁剪大幅提升了报表查询性能。
📌 分区裁剪最佳实践
  • 查询必须包含分区键条件:WHERE 子句中必须带有分区键过滤条件,否则裁剪无法触发,将退化为全分区扫描。
  • RANGE 分区裁剪最稳定:时序日志、交易流水等场景推荐使用 RANGE 分区,支持等值、范围、区间等多种裁剪方式。
  • HASH/LIST 分区仅支持等值裁剪:范围查询无法裁剪,HASH 分区主要用于打散数据而非范围查询。
  • 避免分区键使用函数运算:对分区键进行函数处理会直接导致裁剪失效——优化器无法解析分区范围,只能全分区扫描。
  • 分区键设计需结合业务查询模式:分区键应尽可能与业务的主要查询条件对齐,以实现最大裁剪收益。
  • 可配合分区交换技术做数据归档:在时序数据管理中,结合分区裁剪查询与分区交换归档,可同时保障查询效率和存储成本优化。
🏆 小结
分区裁剪是 OceanBase 分区表的核心优化能力。合理设计分区键、选择合适的分区类型、确保查询条件包含分区键并避免对分区键进行函数运算,是实现分区裁剪、充分发挥分区表性能优势的关键。

⏩ 下一节预告8.2 分区裁剪失效的常见场景 将详细分析分区裁剪失效的高频陷阱,并提供对应的优化方案和开发规范。
8.2 分区裁剪失效的常见场景

分区裁剪失效是导致分区表查询性能下降的最常见原因。很多开发者误以为“建了分区就一定会变快”,实际上分区表能否提速,核心取决于能否有效触发分区裁剪。一旦分区裁剪失效,查询不仅无法获得性能提升,反而会因全分区扫描和额外的元数据开销,导致查询效率甚至低于普通单表。本节从日常开发、数仓查询、业务报表等真实场景出发,整理高频失效陷阱及对应的排查方法与修复策略。

🔧 常用排查方法
  • 查询不包含分区键条件:检查 WHERE 子句是否包含分区键过滤条件,这是触发分区裁剪的基础。
  • 使用 EXPLAIN 查看执行计划:重点关注 partitions 字段,如果列出所有分区(如 partitions(p[0-30]))则表示裁剪未生效。
  • 统计信息及时更新:定期执行 ANALYZE TABLE 或使用 DBMS_STATS 收集最新统计信息,避免优化器因统计信息过时而误判。

5 类高频失效陷阱

以下是日常开发、数仓及报表场景中高频遇到的真实踩坑点,覆盖约 90% 的裁剪失效问题。

场景 1:分区键进行了函数运算或类型转换

对分区键进行函数处理(如日期格式化、截取、数值运算等)或触发隐式类型转换,会直接导致优化器无法识别分区范围,只能执行全分区扫描。核心原则:分区键禁止做函数、运算、类型转换。

-- ❌ 错误写法:对分区键包裹函数、运算
        SELECT * FROM orders WHERE DATE(created_at) = '2025-01-01';
        SELECT * FROM orders WHERE created_at + 1 = 20250101;

        -- ✅ 正确写法:通过区间范围匹配,不对分区键做任何函数包裹
        SELECT * FROM orders WHERE created_at >= '2025-01-01' AND created_at < '2025-01-02';

        -- ❌ 错误示例:分区键是时间类型,却传入字符串类型,触发隐式转换
        SELECT * FROM orders WHERE created_at = '2025-01-01 00:00:00';
        -- ✅ 正确示例:保证查询条件字段类型与分区键类型完全一致
        SELECT * FROM orders WHERE created_at = TIMESTAMP '2025-01-01 00:00:00';
📘 分区键使用函数会破坏分区字段原生索引,导致分区裁剪失效;等值、范围、IN 常量均可正常裁剪。
场景 2:查询条件中缺少分区键或使用了 OR 连接

查询条件中不包含分区键是最基础也最常见的裁剪失效场景。时序日志表分区时业务开发规范必须强制携带分区键条件,否则优化器无法定位目标分区。同时 OR 条件中只要有一个分支缺少分区键或导致裁剪逻辑无法合并,整体裁剪就会失效。

-- ❌ 错误写法:缺少分区键过滤条件,导致全分区扫描
        SELECT * FROM orders WHERE order_status = 'PAID';

        -- ❌ 错误写法:OR 条件中部分条件无法定位分区
        SELECT * FROM orders WHERE id = 10086 OR created_at = '2025-01-01';

        -- ✅ 正确写法:WHERE 子句中必须包含分区键过滤条件
        SELECT * FROM orders WHERE created_at >= '2025-01-01' AND created_at < '2025-01-02';
场景 3:分区键为 NULL 值或使用了非确定性函数

当分区键字段允许 NULL 值且查询条件包含 IS NULLIS NOT NULL 时,优化器无法精准定位分区,可能触发大部分分区扫描。同理,使用 CURRENT_DATENOW() 等非确定性函数进行分区键过滤,优化器无法在计划生成阶段确定具体分区范围,也会导致分区裁剪失效。

-- ❌ 错误示例:分区键使用 IS NULL 条件,优化器无法精准定位
        SELECT * FROM orders WHERE partition_key IS NULL;

        -- ✅ 改进方案:避免分区键为 NULL,或与业务设计结合将 NULL 映射为特殊值
        SELECT * FROM orders WHERE partition_key = 0;  -- 以 0 代表空值,迁移数据时统一处理

        -- ❌ 错误示例:分区键使用 CURRENT_DATE 函数,优化器无法确定具体分区
        SELECT * FROM orders WHERE created_at = CURRENT_DATE;
        -- ✅ 正确示例:使用具体常量值
        SELECT * FROM orders WHERE created_at = '2025-01-01';
场景 4:子查询或关联查询中的连接条件无法推导分区键

当分区键的过滤条件来自子查询或 JOIN 关联条件,且优化器无法推导出具体分区范围时,分区裁剪可能失效。

-- ❌ 错误示例:分区键在 IN 子句中依赖子查询结果
        SELECT * FROM orders WHERE created_at IN (SELECT max_date FROM date_config);

        -- ✅ 改进方案:先执行子查询,再将结果传递给外层查询(例如通过临时表)
        CREATE TEMPORARY TABLE temp_date AS SELECT max_date FROM date_config;
        SELECT * FROM orders WHERE created_at IN (SELECT max_date FROM temp_date);
Join 裁剪的特殊说明:在分区表与非分区表进行 Join 时,如果分区键与小表的连接列相关但优化器无法提前确定分区范围,OceanBase 会在执行引擎的 Probe 阶段,根据小表实际返回的值动态计算需要扫描的分区。这种动态分区剪枝(Dynamic Partition Pruning)默认开启,能在编译期无法确定分区时实现运行时剪枝,进一步优化查询性能。但对于普通等值或范围查询,分区裁剪在计划生成阶段(优化器)完成,属于静态剪枝。
场景 5:统计信息过时导致优化器误判

即便查询条件包含了分区键且未做函数运算,优化器也可能因统计信息过时,在成本估算时误判全分区扫描“代价更低”,从而在部分节点“突然”放弃分区裁剪。

-- 定期收集统计信息,确保优化器获得准确的行数和分布数据
        ANALYZE TABLE orders COMPUTE STATISTICS;
        -- 或使用 DBMS_STATS 系统包(推荐)
        CALL DBMS_STATS.GATHER_TABLE_STATS('TEST', 'ORDERS');

        -- 查看统计信息最后更新时间
        SELECT TABLE_NAME, LAST_ANALYZED, NUM_ROWS 
        FROM oceanbase.DBA_TAB_STATISTICS 
        WHERE TABLE_NAME = 'ORDERS';

二级分区裁剪的特殊性

二级分区的裁剪机制遵循“先一级、后二级”的逻辑[reference:13],其裁剪效果取决于条件组合,需要特别注意:

过滤条件组合裁剪效果
仅包含一级分区键 剪枝到一级分区层级,该一级分区下的所有二级分区仍需扫描(裁剪不彻底)
仅包含二级分区键 需遍历所有一级分区再匹配二级分区,分区裁剪效果大打折扣,性能影响较大[reference:14]
同时包含一级分区键和二级分区键 精准裁剪到具体二级分区,实现完全剪枝
-- ❌ 裁剪不彻底:只包含一级分区键
        -- 一级分区条件可定位到一级分区,但该分区下所有二级分区仍需扫描
        SELECT * FROM orders_by_day_and_category 
        WHERE order_date = '2025-01-01';

        -- ✅ 完全剪枝:同时包含两级分区键条件
        SELECT * FROM orders_by_day_and_category 
        WHERE order_date = '2025-01-01' AND category = 'ELECTRONICS';

开发规范与优化建议

📌 分区裁剪实践规范
  • 强制携带分区键条件:时序分区表(RANGE)的查询必须携带分区键的过滤条件,避免全分区扫描。业务规范建议:所有涉及分区表的核心查询 SQL,在 Code Review 阶段必须检查 WHERE 子句是否包含分区键。
  • 分区键原生字段过滤:禁止在分区键上包裹函数、进行运算或触发隐式类型转换,确保查询条件与原字段类型完全一致。
  • HASH/LIST 分区仅等值裁剪:HASH 分区主要用于打散数据负载均衡,不适合数据归档和范围查询业务;LIST 分区仅支持等值裁剪,范围查询会失效。
  • 二级分区同时包含两级键:对于二级分区表,尽量同时携带一级分区键和二级分区键条件;若无法提供完整条件,优先保证一级分区键存在,避免裁剪效果大幅下降。
  • 定期更新统计信息:统计信息过时是导致优化器误判的常见原因,建议在业务数据量发生显著变化后及时执行统计信息收集。
  • 利用动态分区剪枝:在分区表与非分区表进行 Join 且优化器无法提前确定分区范围时,DPP 会根据小表实际返回的值动态计算需要扫描的分区。该特性默认开启,可通过 ob_enable_dynamic_partition_pruning 参数控制。
  • 使用 EXPLAIN 验证:每次变更 SQL 或分区定义后,使用 EXPLAIN 验证裁剪效果。执行计划中 partitions(p) 字段若仅列出目标分区表示裁剪生效,若包含全部分区则表示裁剪失效。
🏆 小结
分区裁剪失效是分区表性能退化的首要原因。通过掌握上述 5 类高频失效陷阱,理解二级分区裁剪的特殊性,并严格遵循开发规范,可以确保分区裁剪稳定生效,充分发挥分区表的性能优势。

第9章:OBProxy 高可用与运维配置

9.1 OBProxy 高可用架构

OBProxy(OceanBase Database Proxy,也称 ODP)是 OceanBase 数据库专用的反向代理服务。在分布式的 OBServer 集群与客户端之间,OBProxy 通过 SQL 路由与容灾机制,屏蔽了底层分布式架构的复杂性,让应用访问 OceanBase 如同访问单机数据库。OBProxy 本身具备高可用(HA)设计,是 OceanBase 整体高可用体系的关键组成部分。

🎯 OBProxy 在高可用架构中的定位
  • 屏蔽分布式复杂度:OBProxy 将用户请求转发到最佳的 OBServer 节点,应用无需感知底层数据分布和多副本机制。
  • 无状态设计:OBProxy 不存储任何会话状态信息,任何实例故障均可快速被其他健康实例替换,不存在单点故障。
  • 无限水平扩展:无状态特性使 OBProxy 实例数量的增加不受状态同步制约,可随业务流量线性扩容。
  • 松耦合:OBProxy 与 OBServer 之间分工明确,ODP 只承担路由和容灾功能,数据库的全部功能由 OBServer 实现。

无状态:OBProxy 高可用的核心基础

OBProxy 的高可用能力根植于其无状态的设计理念。与数据库引擎不同,OBProxy 不存储任何会话状态、路由表或事务上下文信息。这一设计带来了以下关键优势:

  • 极致的故障恢复能力:任意一个 OBProxy 实例故障时,客户端的请求可以被负载均衡器(如 F5、SLB、HAProxy)自动调度到其他健康的 OBProxy 实例上,故障节点对整个业务链路完全透明。同时,OBProxy 在部署时会带有一个守护进程 obproxyd.sh,周期性检查 OBProxy 的健康程度,一旦发现宕机就立即重启。
  • 弹性水平扩展:添加或删除 OBProxy 实例无需数据迁移或状态同步,系统管理员可按业务流量变化灵活调整实例数量。
  • 简化运维:支持通过丰富的内部命令对 OBProxy 状态进行实时监控,运维人员可以从容应对版本升级和节点替换。

OBProxy 的部署方式

OBProxy 作为 OceanBase 的接入层,支持多种部署模式,以适应不同环境的需求。

合并部署

在合并部署模式下,ODP 与 OBServer 部署在同一台服务器上。该模式适用于私有云或资源受限的场景,通常以 OCP 方式启动,可以访问多个集群。当 OCP 控制台意外宕机时,ODP 依然能够正常访问已访问过的集群,但无法访问尚未访问过的集群。启动时基于 RootService 列表时,ODP 只能访问固定的单个集群,独立于 OCP。如果本地服务器发生故障,服务器负载均衡器(SLB)会自动检测故障并进行流量切换,ODP 无需执行额外操作。

独立部署

在独立部署模式下,ODP 作为独立组件部署在专属服务器上(可与 OBServer 节点同机器,通常建议独立部署),实现计算与接入层的资源隔离。这种模式既支持以 OCP 方式启动(可访问多个集群),也支持基于 RootService 列表启动(固定访问单个集群,独立于 OCP)。

🔧 部署模式版本适配说明
  • ODP 软件包分为 V1.8.x(适配 OceanBase V3.x)和 V4.x 两个系列。
  • 若下游对接的 OceanBase 集群为 V4.0.0 及以上版本,必须选择 ODP V4.0.0 及以上版本的软件包,以确保协议兼容性。
  • 推荐在单台服务器上仅部署一个 ODP 实例并使用默认端口 2883。如需在同一台服务器部署多个 ODP,须使用不同的端口和配置路径加以区分。

接入层高可用:负载均衡器 + OBProxy 集群

在生产环境中,OBProxy 的高可用并非依靠 OBProxy 本身,而是采用负载均衡器(LB)+ OBProxy 集群的经典架构。该架构通过引入负载均衡器将客户端请求分发到多个 OBProxy 实例,确保在任意一台 OBProxy 故障时业务无感知。

典型接入架构

构建接入层高可用的标准模式如下:

  • 集群化部署:至少部署两个 OBProxy 实例形成集群,消除单点风险。
  • 负载均衡器:在 OBProxy 集群前端部署四层或七层负载均衡器(如 F5、SLB、NLB、HAProxy、Nginx 等),将客户端请求分发至集群中健康的实例。
  • 高可用客户端:业务应用程序通过负载均衡器的 VIP(虚拟 IP)地址或域名连接数据库,而非直接连接单个 OBProxy IP,从而实现故障自动切换。
-- 高可用架构下的连接串示例(通过 SLB VIP 访问)
        jdbc:mysql://10.0.0.100:2883/test?user=root@tenant_name&password=****
⚠️ 负载均衡器配置必须包含健康检查功能,定时探测后端 OBProxy 实例的 TCP 端口连通性及业务存活状态,以自动屏蔽异常节点。
OBProxy 集群的常用管理方式

通过 OceanBase Cloud Platform(OCP)可以高效管理 OBProxy 集群的全生命周期。登录 OCP 控制台后,在左侧导航栏点击 OBProxy 即可进入管理页面,在页面上方点击"创建 OBProxy 集群"按钮开始配置。创建过程中需指定集群名称、proxyro 用户密码(用于 ODP 访问 OceanBase 集群的专用账号)以及 ODP 软件包的版本。此外,OCP 还提供接管外部 OBProxy、删除/重启/升级 OBProxy 节点、向集群中添加新 OBProxy 等丰富的管理功能。

故障检测与自动容灾处理

OBProxy 通过多层次的主动检测机制,及时发现 OBServer 节点的状态变化,并自动采取流量切换和故障隔离措施,确保服务持续可用。

故障检测
  • 后端节点状态刷新:通过调度任务定期刷新 OBServer 节点、Zone 以及主备集群的状态。OBProxy 还实现了服务端持久连接和 KeepAlive 机制,主动探测与后端 OBServer 的连接状况,帮助识别 idle 连接被异常关闭等场景。
  • 客户端 KeepAlive 探活:通过客户端 KeepAlive 机制探测 OBProxy 与客户端连接的状况,同时可以避免 SLB 等负载均衡的空闲超时。
黑名单机制

OBProxy 使用黑名单机制实现后端 OBServer 的自适应访问控制。该机制在 OBServer 错峰合并、升级、Leader 切换、宕机、启动和停止等过程中,引导流量避开故障节点。

  • 宕机黑名单:一旦检测到某台 OBServer 宕机或因执行 stop server 命令暂停服务,该节点会被加入宕机黑名单。此时应用不再访问该节点,直到该节点恢复正常后才会从黑名单中释放。
  • 活着不可用黑名单:与宕机黑名单不同,活着不可用黑名单中 OBServer 的状态是暂时的或不确定的(如资源限制、内存超限、正在合并)。此类节点会周期性重试,一旦恢复正常即被放出黑名单。
  • 优先级规则:宕机黑名单优先级高于活着不可用黑名单。
核心配置参数

通过 SHOW PROXYCONFIGALTER PROXYCONFIG SET 命令可查看和修改 OBProxy 的配置参数。与黑名单相关的核心参数如下:

参数名称默认值说明
enable_congestion True 是否启用黑名单机制。若运行异常可暂时关闭,以便 OBProxy 恢复正常工作。
congestion_failure_threshold 5 设置在 congestion_fail_window 秒内,OBServer 出错多少次则将其加入黑名单。
congestion_fail_window 120 错误统计周期(秒)。若在该周期内错误次数超过 congestion_failure_threshold,节点加入黑名单。
congestion_retry_interval 20 被加入活着不可用黑名单的 Server 的重试周期(秒)。
min_keep_congestion_interval 0 节点被加入黑名单后,至少停留多长时间后才允许被放出黑名单。
请求级故障处理
  • 处理中的请求:OBProxy 实现了异步中止机制。当检测到 OBServer 节点故障后,主动中止正在处理的连接请求,避免长时间等待导致业务连接池耗尽。
  • 新请求处理:利用黑名单机制,避免将新的请求发送到已故障的节点。
  • 副本位置感知:当 OBServer 节点因故障发生副本切换时,该节点会主动通知 ODP 路由信息的变化。ODP 据此感知副本的新位置,将后续请求路由到新副本。但如果 OBServer 节点完全不可响应,ODP 则需依赖探测机制发现异常,此时副本位置信息的更新可能会有延迟。
-- 查看 OBProxy 配置(需通过 root@proxysys 账号连接 2883 端口)
        SHOW PROXYCONFIG;

        -- 修改配置参数
        ALTER PROXYCONFIG SET congestion_failure_threshold = 3;

        -- 查看 OBProxy 集群中的实例信息
        SHOW PROXYINFO;

主备集群架构下的容灾支持

OBProxy 支持两地两中心的物理备库(Physical Standby Database)解决方案。当主集群发生变化时,OBProxy 能够高效地切换至新的主集群,实现跨数据中心的高可用容灾能力。RTO 取决于 OBServer 节点的检测时间和状态刷新任务的调度间隔,默认检测间隔为 20 秒。此外,OBProxy 也支持配置弱一致性读策略(通过 obproxy_read_consistency 参数),将部分读流量转发至 Follower 副本,进一步实现读写分离并分摊主副本负载。

📌 OBProxy 高可用最佳实践总结
  • 规避单点故障:生产环境至少部署 2~3 个 OBProxy 实例,严禁单实例直连。使用负载均衡器(SLB/F5/HAProxy)提供统一 VIP 作为业务访问入口。
  • 独立部署优先:在资源允许的条件下,优先将 OBProxy 与 OBServer 分开部署,避免资源争用和故障排查困难。
  • 版本匹配:OBProxy 版本必须与 OceanBase 集群的大版本保持兼容。V4.x 集群必须使用 V4.x 及以上的 ODP 软件包。
  • 参数调优:根据业务并发特征,调整 client_max_connectionswork_thread_num 等参数,避免 OBProxy 自身成为瓶颈。
  • 连接池配合:在应用端使用数据库连接池,结合 OBProxy 的会话保持能力,实现高效的连接复用。
  • 监控告警:通过 OCP 平台实时监控 OBProxy 节点的健康状态、QPS、连接数及响应延迟,设置合理告警阈值,提前发现性能瓶颈。
  • 规避开源误区:部分使用者认为 OBProxy 不具备高可用能力,单挂一个 ODP 后担心代理层成为单点。实际上,OBProxy 自身无状态,通过"集群化部署 + 负载均衡器"即可实现接入层高可用。这种高可用架构与使用一个 VIP 绑定多个 ODP 实例属于两种不同方案,各有适用范围,实践中可根据环境灵活选择。
🏆 4.x 版本 OBProxy 高可用架构演进总结
无状态架构持续强化:4.x 版本进一步强化了 OBProxy 的无状态特性,确保水平扩展能力,支持动态扩缩容,提升运维自动化水平。
OCP 管理能力增强:4.x 版本中 OCP 对 OBProxy 集群的管理能力持续增强,支持从创建到下线的全生命周期自动化运维。
容灾能力提升:4.x 版本优化了主备集群切换时的路由更新效率,缩短了 RTO,提升了跨数据中心的高可用能力。
协议与性能持续优化:4.x 版本持续优化 OBProxy 与 OBServer 之间的自研专有协议,提升数据转发效率与链路可靠性。
9.2 核心配置参数与最佳实践

OBProxy 作为 OceanBase 集群的统一接入层,其核心配置参数直接影响着系统的性能、稳定性和高可用能力。本章基于 OceanBase 4.x 版本的最新特性,系统梳理 OBProxy 的核心配置参数体系,提供生产环境的最佳实践配置建议,并介绍常见问题定位与优化策略,帮助 DBA 高效完成 OBProxy 的配置与运维。

🔧 配置管理通用方法

OBProxy 的配置可通过 SQL 命令动态管理。必须使用 root@proxysys 账号连接到 OBProxy 的 2883 端口后执行:

-- 查看 OBProxy 的所有配置参数
        SHOW PROXYCONFIG;

        -- 查看特定配置项(使用 LIKE 模糊匹配)
        SHOW PROXYCONFIG LIKE 'work_thread_num';

        -- 修改配置参数(部分可动态生效)
        ALTER PROXYCONFIG SET work_thread_num = 64;

        -- 修改需要重启 OBProxy 才能生效的参数
        -- 应在修改参数后重启 OBProxy 进程

V4.3.x 版本中,大部分配置项可动态生效(无需重启 OBProxy),少数参数(如 work_thread_numtask_thread_num 等)需要重启后生效。

一、CPU 与线程配置

核心线程参数说明
参数名称描述默认值取值范围是否需重启生产建议
work_thread_num OBProxy 的工作线程数,对 CPU 占用影响最大[reference:0] 128 (V4.3.0+);历史版本为 8[reference:1] [1, 128][reference:2] [reference:3] 单独部署时建议根据机器 CPU 核数设置 2~4 倍;混合部署时建议保持默认并开启 automatic_match_work_thread 自动适配。
automatic_match_work_thread 是否根据 CPU 核数自动调整工作线程数 true[reference:4] true / false[reference:5] [reference:6] 推荐开启。单独部署时可设为 false 并手动指定 work_thread_num;混合部署时保持 true 以避免过度占用 CPU[reference:7]。
task_thread_num 后台任务线程数,用于拉取 rslist、刷新路由信息等后台任务[reference:8] 2[reference:9] [1, 4][reference:10] [reference:11] 保持默认值 2,配置过高可能造成资源浪费[reference:12]。
net_accept_threads 执行 accept 的线程数,负责接收新的客户端连接请求 2[reference:13] [0, 8][reference:14] [reference:15] 高并发场景(>10000 QPS)可考虑适当调至 4~8。
grpc_thread_num gRPC 线程数,用于 OBProxy 与 OCP 等管控组件通信[reference:16] 8[reference:17] [8, 16][reference:18] [reference:19] 保持默认值 8
💡 线程参数调优建议work_thread_num 是影响 OBProxy CPU 占用的核心参数。建议通过 OCP 或 SHOW PROXYCONFIG 监控当前线程使用情况,结合 QPS 峰值逐步调整。

二、内存配置

核心内存参数说明
参数名称描述默认值取值范围是否需重启生产建议
proxy_mem_limited OBProxy 可占用的系统内存上限,超过则进程主动退出[reference:20] OCP 部署时默认 2 GB;命令行安装未显式指定时为 800 MB[reference:21] [100 MB, 100 GB][reference:22] (动态生效)[reference:23] 生产环境建议调整为 4 GB~8 GB,避免内存紧张导致进程被 Kill。配置后需验证实际内存使用情况,监控是否接近上限[reference:24]。
client_max_connections 每个 OBProxy 允许的最大客户端连接数[reference:25] 8192[reference:26] [0, 65535] (早期文档范围为 [0, 65535])[reference:27] [reference:28] 根据业务并发预估配置,建议设置上限 20000~50000,同时检查系统 ulimit -n 是否足够(建议 ≥65535)。不建议设置过大,以免导致 OBProxy 内存不足[reference:29]。
⚠️ 内存不足应急处理

当 OBProxy 日志中出现 obproxy's memory is out of limit's 90% 时,应立即调整 proxy_mem_limited 为更高值(如 4GB 或 8GB),无需重启即可生效[reference:30]。

三、连接与超时配置

核心连接参数说明
参数名称描述默认值生产建议
listen_port OBProxy 对客端监听端口[reference:31] 2883 保持默认。若需多实例共存,可改用 28830、28831… 等。
client_idle_timeout 客户端空闲断开阈值 600 s 应用使用连接池时建议 ≥1800 s,长连接场景可适当调大。
observer_query_timeout_delta 等待 OBServer 回包的超时增量(在 OBServer 的 ob_query_timeout 基础上叠加)[reference:32] 20 s (取值 [1, 39] s)[reference:33] 跨城或高延迟场景可适当增大,但不可无限放大,建议结合业务延迟 SLO 设定。

四、路由策略配置

OBProxy 提供丰富的路由功能,用户可根据业务场景设置对应的配置项来控制路由策略,实现最佳路由效果。

核心路由参数说明
参数名称描述默认值生产建议
enable_primary_zone 对于无法准确计算 Leader 节点的 SQL,是否优先路由到 Primary Zone[reference:34] true 生产环境建议设为 false。与 enable_cached_server 同时开启时,可能导致复杂查询总是路由到 Primary Zone,造成负载不均[reference:35]。
enable_cached_server 对于无法确定 Leader 的强一致性读,是否优先复用上一次使用的服务器会话 true 生产环境建议设为 false。与 enable_primary_zone 同时开启会导致 SQL 路由到固定节点,引发负载不均[reference:36]。
obproxy_read_consistency 弱一致性读开关,用于配置是否允许弱一致性读请求路由到 Follower 副本 0(强读) 取值 0(强读)或 1(弱读)。启用弱一致性读可大幅降低读延迟,但需配合 LDC 配置和业务侧设置 ob_read_consistency='weak'
proxy_primary_zone_name 强制将请求路由到指定 Zone,优先级高于自动路由,属于强制性路由 适用 Zone 跨地域且网络延迟较大、固定 proxy 无法准确路由至分区 Leader 的场景。设置后该 OBProxy 所有请求将路由到指定 Zone,可能导致远程访问,需谨慎评估。
proxy_idc_name 指定 OBProxy 所属 IDC,用于判断与 OBServer 是否为同一 IDC,影响弱一致性读优先路径 配置弱一致性读场景必须设置,配合 LDC 实现同机房就近路由。需与集群中某 Zone 的 idc 属性匹配。
enable_full_username / proxy_tenant_name 简化用户名格式,允许仅使用用户名访问指定租户[reference:37] true / 空 enable_full_username 设为 false,并配置 proxy_tenant_name 为目标租户名。注意:该 OBProxy 只能访问这一特定租户[reference:38]。
📌 路由策略特别说明
  • 核心权衡项enable_primary_zoneenable_cached_server 同时为 true 是生产环境负载不均的常见根因,强烈建议生产环境将这两个参数设为 false,让 OBProxy 对无法确定 Leader 的 SQL 使用随机路由,避免流量集中[reference:39]。
  • 路由策略(弱读)proxy_route_policy 参数默认值为 '',表示使用 ODP 默认的 MERGE_IDC_ORDER 策略[reference:40]。

五、日志与监控配置

核心日志参数说明
参数名称描述默认值生产建议
syslog_level OBProxy 日志级别,从低到高可选 DEBUG、TRACE、INFO、WARN、USER_ERR、ERROR INFO 生产环境建议设为 WARNERROR,仅在排查问题时临时调整为 DEBUG。
slow_query_time_threshold 慢查询阈值(毫秒),超过该值的 SQL 会被记录到日志中[reference:41] 500 ms (取值范围 [0s, 30d])[reference:42] 根据业务对慢查询的定义调整,一般推荐 500ms~1000ms,用于定位性能问题。
slow_proxy_process_time_threshold OBProxy 内部处理慢阈值(毫秒),用于诊断 OBProxy 自身处理逻辑的性能瓶颈 2 ms 保持默认值,用于定位 OBProxy 内部性能瓶颈。
enable_prometheus 是否暴露 Prometheus 格式的监控指标[reference:43] true (官方文档未见固定默认值,常用为 true) 必须开启,配合 Prometheus + Grafana 构建监控大盘。
prometheus_listen_port Prometheus 指标拉取端口[reference:44] 2884 (配置文件常用值) 建议与业务端口分离(2883 vs 2884)。
📊 监控与运维命令参考
-- 查看 OBProxy 的内存使用情况
        SHOW PROXYMEMORY;

        -- 查看 LDC 部署情况(需配置 proxy_idc_name)
        SHOW PROXYINFO IDC;

        -- 查看内部路由表状态(table entry)
        SHOW PROXYROUTE;

        -- 查看 OBProxy 配置(特定项)
        SHOW PROXYCONFIG LIKE 'proxy_idc_name';

六、TCP KeepAlive 与探活配置

配置合理的 TCP KeepAlive 参数可以有效避免因网络设备空闲超时而意外中断连接。

服务端探活配置(OBProxy → OBServer)
参数名称描述推荐值说明
sock_option_flag_out 二进制位参数:bit 0 表示是否启用 no_delay,bit 1 表示是否启用 keepalive 3 3 的二进制是 11,表示同时启用 no_delay 和 keepalive。
server_tcp_keepidle 启动 keepalive 探活前的空闲等待时间(秒) 5 经过 5 秒无数据交互后开始发送探活包。
server_tcp_keepintvl 两个 keepalive 探活包之间的间隔(秒) 5 探活失败时,每 5 秒重试一次。
server_tcp_keepcnt 最多发送的 keepalive 包数量 2 最长 5+5*2=15 秒可发现 dead_socket。
server_tcp_user_timeout 等待 TCP 层 ACK 确认消息的超时时长(毫秒) 0 强烈建议设为 0,避免网络拥堵导致 TCP 超时断链问题。
配置说明:设置 keepalive 参数后,最长可在 15 秒内发现坏死的连接,有效防止应用连接池因长期等待而耗尽连接。但参数值不宜过小,避免因瞬时网络抖动导致误判。

七、生产上线前检查清单

  • 端口冲突扫描ss -lntp | grep -E '288[1-4]',确认无其他进程抢占 OBProxy 的关键端口(2883/2884)。
  • IPv6 双栈验证(若开启):同时使用 IPv4 和 IPv6 的 obclient 连接并执行 SELECT 1;,确保返回一致。
  • 连接风暴压测:使用 sysbench 或自研脚本模拟 5k 并发短连接,观察 OBProxy 是否出现 accept queue dropped(ss -s 查看)。若出现,需调大 backlog 并检查 ulimit -n 是否 ≥65535。
  • 高可用验证:部署至少 2~3 个 OBProxy 实例,并使用负载均衡器(F5/SLB/HAProxy)提供统一 VIP,测试任意一台 OBProxy 故障时业务是否无感知切换。
  • 配置备份与版本管理:将 OBProxy 的配置文件(obproxy_config.yaml / obproxy.conf)纳入版本管理,便于追踪变更和快速回滚。
🏆 4.x 版本配置参数演进总结
work_thread_num 默认值提升:V4.3.0 版本起,默认值从 8 提升至 128,更好地适配多核 CPU 环境。[reference:45]
参数管理更灵活:V4.x 版本中,大部分配置参数通过 ALTER PROXYCONFIG SET 修改后可动态生效,无需频繁重启 OBProxy。[reference:46]
监控能力增强:V4.x 版本丰富了 SHOW PROXYMEMORYSHOW PROXYROUTE 等内部命令,可快速定位内存使用和路由问题。
社区最佳实践沉淀:基于大量社区案例,enable_primary_zoneenable_cached_server 建议设为 false 逐步成为生产环境共识。[reference:47]
9.3 上线前检查建议

OBProxy(ODP)作为 OceanBase 集群的统一接入层,其配置和部署状态直接影响业务的稳定性和高可用能力。本章基于 OceanBase 4.x 版本的官方推荐,结合生产环境的实践经验,系统梳理 OBProxy 上线前的完整检查清单,帮助 DBA 在业务流量接入前完成全面验证,避免上线后出现性能瓶颈或可用性风险。

📌 上线前检查的整体框架

OBProxy 上线前的验证遵循“环境准备 → 部署配置 → 连通性验证 → 高可用验证 → 性能压测 → 监控配置 → 巡检验证”的完整流程。建议使用 obdiag check run 一键执行自动巡检,在手动检查前即发现版本、配置、状态等基础问题。

一、环境与软件版本检查

在部署 OBProxy 之前,需要确认服务器软硬件环境满足要求,并选择合适的 OBProxy 版本。

  • 硬件资源:ODP 服务器推荐配置为 4 核 CPU、8 GB 内存、200 GB 磁盘,可使用 OBServer 节点作为 ODP 部署服务器。ODP 启动后常驻 CPU 占用约 0.7 核,内存约 100 MB,但峰值负载时仍需预留充足资源。
  • 操作系统:建议使用 CentOS 7.x 及以上版本,内核版本不低于 3.10。务必确认服务器防火墙已开放 ODP 所需的端口:2883(业务端口)、2884(Prometheus 端口)、2885(内部通信端口)
  • 版本选型:OBProxy 必须与 OceanBase 集群的版本系列匹配,V4.x 集群必须使用 ODP V4.0.0 及以上版本。建议使用 GitHub Releases 中最新稳定版本,生产环境需关注每个版本的功能迭代和缺陷修复情况。
  • obdiag 自动检查:执行 obdiag check run --obproxy_cases=proxy 可自动检查 OBProxy 版本是否为不建议使用的缺陷版本(version.bad_version)或已不再支持的旧版本(version.old_version)。

二、部署与配置检查

完成 ODP 部署后,需验证其配置是否与生产预期一致,并确认与 OceanBase 集群的连通性。

  • 部署方式验证:确认 ODP 已正确接入 OceanBase 集群。使用 OCP 部署时,可在 OCP 控制台查看 OBProxy 集群列表,确认 ODP 实例状态正常。通过 obd 部署时,确保配置文件中的 rs_list 或 OceanBase 集群信息正确。
  • 配置参数检查:使用 root@proxysys 账号登录 ODP(默认 2883 端口),执行 SHOW PROXYCONFIG; 查看当前配置,重点关注以下核心参数:
    • listen_port:业务监听端口,默认为 2883。
    • prometheus_listen_port:Prometheus 指标拉取端口,默认为 2884,应与业务端口分离。
    • proxy_mem_limited:进程内存上限,OCP 部署默认为 2 GB,命令行安装未指定时为 800 MB,生产环境建议调整为 4 GB~8 GB。
    • client_max_connections:最大客户端连接数,默认为 8192。
  • 热参数动态验证:部分配置(如 proxy_mem_limited)修改后可动态生效(need_reboot = false),修改后无需重启。建议在上线前确认所有需要重启的参数均已按需配置。
  • 连通性测试:使用 MySQL 或 OBClient 客户端通过 ODP 的 IP 和端口(如 mysql -h 10.10.10.1 -P 2883 -u root@tenant_name -p)登录目标租户,并执行 SELECT 1; 验证数据链路畅通。

三、高可用与负载均衡验证

生产环境必须确保接入层的高可用能力,避免单个 ODP 实例故障导致业务中断。

  • 多实例部署:建议至少部署 2~3 个 ODP 实例,避免单点故障。可使用 obd 或 OCP 向集群中添加多个 ODP 节点。
  • 负载均衡器配置:在 ODP 集群前端部署 SLB/F5/HAProxy 等负载均衡器,配置健康检查,将客户端请求分发至健康 ODP 实例。应用程序通过负载均衡器的 VIP 或域名访问数据库,而非直连单 ODP IP。
  • 高可用模拟测试:通过 kill -9kill -19 模拟 ODP 进程异常退出,通过 kill -18 模拟进程暂停,观察负载均衡器是否能自动将流量切到健康 ODP,业务侧是否出现明显抖动。
  • 守护进程验证:确认 ODP 安装目录下的 obproxyd.sh 守护进程已正确运行,其通过 nc 命令周期性探测监听端口(默认 2883)。若检测到 ODP 进程不存在,守护进程会立即拉起 ODP,恢复时间约 1 秒。
  • 主机故障模拟:在业务压测期间,对部署 ODP 的服务器模拟网络中断或关机,验证 RTO 是否满足业务 SLA 要求。

四、性能压测与容量评估

通过压测验证 ODP 在当前硬件配置下的性能上限,确保其不会成为系统的瓶颈。

  • ODP 独占 QPS 基线:在独占场景下,单 ODP 实例的 QPS 通常在 2 万~3.5 万 之间,具体取决于 CPU 型号和配置。
  • 水平扩展策略:如果 QPS 不满足业务需求,可以通过水平扩容(增加 ODP 实例数量)线性提升集群整体吞吐量。若无法扩容,可单独优化单实例性能。
  • 吞吐量指标:重点关注 ODP 实例的 CPU 使用率(top -H -p pidof obproxy)和网络带宽占用。若某个 ODP 实例的 CPU 接近打满,说明已达性能上限,需增加实例或提升硬件配置。
  • 延迟指标:检查 ODP 内部处理耗时(slow_proxy_process_time_threshold),该值通常在 μs(微秒)级。如果出现异常延迟,需排查网络链路或后端 OBServer 响应时间。
  • 资源规格评估:根据预估的 QPS 峰值,提前规划 ODP 实例数量和配置。若 CPU 资源充足但 QPS 偏低,需进一步定位组件瓶颈(网络、磁盘 I/O、内存等)。

五、安全与访问控制检查

确保 ODP 和 OceanBase 集群的网络安全策略已正确配置,防止未授权访问。

  • 租户白名单:在 OceanBase 租户侧配置 ob_tcp_invited_nodes,限制仅允许 ODP 的 IP 地址或业务应用的 IP 段访问租户。
  • 网络安全组/防火墙:确认服务器防火墙只开放 ODP 所需的 2883、2884 端口,对 2885 等内部端口进行白名单限制。生产环境建议将 ODP 与管理网络隔离,限制 root@proxysys 账号的访问来源。
  • 账号权限管理:严格管理 root@proxysys 的密码,避免泄露。业务应用应使用普通租户账号(用户名@租户名)连接 ODP。
  • 访问日志审计:开启 ODP 的审计日志功能,定期分析异常访问模式,及时发现潜在安全风险。

六、监控与告警配置

在上线前完成监控和告警的配置,以便业务接入后实时掌握 ODP 的运行状态。

  • OCP 监控大盘:在 OCP 控制台中查看 ODP 的关键指标,包括 QPS、连接数、内存使用率、慢查询数量等。
  • Prometheus 集成:确认 enable_prometheus = true,并验证 http://odp_ip:2884/metrics 是否可正常拉取指标数据。
  • 告警规则配置:设置合理的告警阈值,重点关注以下指标:
    • ODP 进程内存使用超过 proxy_mem_limited 的 90%,此时 ODP 会在日志中打印 obproxy's memory is out of limit's 90%
    • SQL 执行失败次数(sql_execute_failed_count)突增。
    • ODP 与 OBServer 的连接异常或频繁断开。
📊 ODP 关键监控指标(OCP / Prometheus)
  • 系统指标:CPU 使用率、内存使用量(单位:GB)、磁盘使用率
  • 业务指标:QPS、活跃连接数、活跃会话数
  • 延迟指标:ODP 内部处理耗时(μs)、SQL 平均响应时间(ms)
  • 错误指标:SQL 执行失败次数、超时次数、连接拒绝次数

指标采集依赖 OCP-Agent 中的 ODPExporter 组件,通过 Prometheus 协议暴露。

七、obdiag 自动巡检检查

使用 OceanBase 官方提供的敏捷诊断工具 obdiag 对 OBProxy 进行一键巡检,高效发现配置和潜在风险。

  • 安装 obdiag:在管理节点上安装 obdiag,建议使用最新版本。
  • 执行巡检命令
    # 对 OBProxy 执行专项巡检(检查版本、参数配置等)
            obdiag check run --obproxy_cases=proxy
    
            # 对 OceanBase 数据库执行全量巡检(含 OBProxy)
            obdiag check run
    
            # 指定报告输出格式(HTML/JSON/XML/YAML/TABLE)
            obdiag check run --report_type=html --store_dir=./check_report
  • 巡检结果解读:巡检报告会包含每个检查项的详细结果和运维建议,重点关注 “WARNING” 和 “ERROR” 级别的异常项。例如:
    • version.bad_version:提示当前 OBProxy 版本存在缺陷,建议立即升级。
    • parameter.work_thread_num:检查工作线程数配置,防止线程耗尽问题。
    • parameter.enable_ob_protocol_v2_with_client:若已启用则告警。
  • 部署环境检查obdiag check run --cases=build_before 可在部署前对服务器环境进行预检,检查内核参数、docker0 网络接口等潜在问题。
💡 obdiag 增强功能:自 V3.3.0 起,obdiag check 支持 Python 脚本形式的巡检任务,可编写复杂逻辑的巡检场景。自 V4.2.0 起引入 SSH 连接管理,支持多任务共享每节点 SSH 连接,提升大规模集群巡检效率。

八、上线检查清单汇总(Checklist)

上线前,建议按照以下清单逐项确认,确保 OBProxy 部署稳定可靠:

检查项验证方法预期结果 服务器硬件资源充足且满足最低配置 lscpu, free -h, df -h CPU ≥4 核,内存 ≥8 GB,磁盘 ≥200 GB 服务器防火墙端口 2883/2884/2885 已开放 ss -lntp \| grep -E '288[3-5]' 显示 ODP 进程监听在 2883、2884、2885 端口 OBProxy 版本与 OceanBase 集群版本匹配 obdiag check run --obproxy_cases=proxy 版本检查项无 WARNING 或 ERROR 核心配置参数(内存、线程)符合生产预期 SHOW PROXYCONFIG LIKE 'proxy_mem_limited'; 内存限制 ≥4 GB,线程数配置合理 OBProxy 与 OceanBase 租户连通性正常 mysql -h odip -P 2883 -u root@tenant -p -e "SELECT 1;" 返回 1,连接成功 至少部署 2~3 个 ODP 实例 SHOW PROXYINFO; 或 OCP 平台查看 ODP 集群包含多个节点,状态均为运行中 前端负载均衡器配置健康检查且可自动切流 模拟 ODP 进程 kill -9,观察流量切换 业务 TPS 短暂下降后恢复,无长时间中断 守护进程 obproxyd.sh 正常运行 ps aux \| grep obproxyd 存在 obproxyd.sh 守护进程 性能压测达到预期 QPS,ODP 未成为瓶颈 使用 sysbench 等工具进行压测 QPS 符合预估,ODP CPU 使用率未打满 租户白名单已配置,仅允许授权 IP 访问 SHOW VARIABLES LIKE 'ob_tcp_invited_nodes'; 显示允许的 IP 地址或 IP 段 OCP 监控大盘和告警规则已配置并生效 OCP 平台查看监控图表 关键指标(QPS、内存、连接数)均有监控图表和告警规则 obdiag 巡检命令执行通过,无 WARNING/ERROR obdiag check run --obproxy_cases=proxy 所有检查项状态均为 PASS 或 INFO
🏆 上线前检查最佳实践总结
自动化优先:优先使用 obdiag 巡检工具完成基础检查,再针对业务特性进行手动验证。
高可用是核心:OBProxy 无状态,但需要“集群化 + 负载均衡器”才能实现接入层高可用。
持续监控:监控体系应在业务接入前就绪,而非在出现问题后临时配置。
版本同步:OBProxy 版本与 OceanBase 集群版本保持匹配,避免协议不兼容问题。
定期巡检:业务上线后,建议每日/每周执行自动化巡检,及时发现潜在风险。
应急预案:提前准备 ODP 故障后的切换预案,包括负载均衡器的切换策略和 ODP 重启/替换操作步骤。

第10章:租户克隆

10.1 租户克隆概述

从 OceanBase V4.3.0 版本开始,新增了 租户克隆 功能。通过该功能,您可以在系统租户中基于一个源租户,快速克隆出一个全新的、数据与资源相互隔离的新租户。新租户在初始时刻包含的数据,就是源租户在执行克隆语句那个时刻的一个快照。与传统的主备租户(基于日志同步)和复制租户(仅复制配置信息)不同,租户克隆是一种 基于快照和共享存储 的快速创建机制,因此具备极高的效率。

技术原理解析

租户克隆能实现“秒级”快速创建,其核心原理在于:创建时仅复制元数据,不复制用户数据,数据存放在共享存储上。初始化数据由源租户和新租户共享物理宏块实现。随着数据分别写入两个租户,共享的宏块会逐渐减少,独占宏块会逐渐增多。

🎯 租户克隆的核心优势
  • 💨 快速高效:因为只复制元数据而非用户数据,克隆操作可以在极短的时间内完成(在一分钟左右完成绝大多数场景),相比传统数据拷贝方式效率显著提升。
  • 🔒 严格数据隔离:新租户和源租户是相互独立的个体。任何一方对数据的修改(增、删、改)都不会影响另一方,确保了业务的独立性和安全性。
  • ⚖️ 独立资源隔离:新租户拥有自己独占的 CPU、内存和 IOPS 资源池,与源租户的资源不会相互抢占,能够保障各自的性能稳定。

六种核心应用场景

基于租户克隆的高效、隔离特性,它可以在以下六大场景中发挥关键价值。

  1. 报表任务:当核心业务租户处于负载高峰期时,可以快速克隆一个专门用于跑报表的租户,避免复杂的报表查询拖慢核心业务。
  2. 快弹只读备租户:利用克隆功能创建一个新租户并将其转换为一个只读备租户,主动从生产租户拉取日志,以准实时的方式分摊内部运营等复杂查询负载。
  3. 应用版本发布:在版本发布前,克隆一个源租户作为验证环境。若新版本出现问题,可以快速丢弃克隆租户,保障源租户安全。
  4. 数据库变更验证:在需要执行 DDL 等高风险变更操作前,先在克隆租户上模拟测试,验证其影响和可行性,有效降低变更风险。
  5. 开发测试环境搭建:利用租户克隆,可以在短时间内为开发或测试人员准备一个与生产数据一致的隔离环境,提升开发测试效率。
  6. 快速数据导出:需要对大量数据进行导出或处理时,克隆出一个新租户,在克隆租户上执行导出操作,既能减轻生产租户的负担,也能保护数据的安全。

架构版本限制与历史记录查询

  • 最低版本要求:克隆源租户必须是 OceanBase V4.3.0 或更高版本的租户,不支持对 V4.3.0 之前版本的租户进行克隆。
  • 克隆任务历史查询:从 V4.3.0 版本开始,您可以通过查询 DBA_OB_CLONE_HISTORY 视图来获取所有已完成的克隆任务记录,进行审计与排查。
📋 租户克隆 vs. 租户复制 vs. 主备租户
  • 复制租户:仅复制源租户的配置信息,不复制任何数据,用于快速创建具备相同规格的新租户。
  • 主备租户:用于容灾,数据通过日志实时同步,提供高可用的只读服务,并能在主备间执行故障切换。
  • 克隆租户:基于快照和共享存储技术,快速复制元数据并共享初始数据,新租户拥有完整的独立读写能力。适用于需要快速构建与源租户数据一致的隔离测试或分析环境的场景。
🏆 小结
租户克隆是 OceanBase 4.3 版本引入的一项创新功能。它通过独特的“快照+共享存储”技术,实现了租户的极速创建和严格的数据、资源隔离,为企业在报表、快弹只读备租户、应用验证、DDL 变更、开发测试和数据导出等场景中,提供了一种高效、安全、灵活的解决方案。
10.2 使用限制

租户克隆功能在提供高效、灵活的克隆能力的同时,也存在一系列重要的使用限制。深刻理解这些限制,是确保克隆操作顺利执行并维护系统稳定性的前提。本章将这些限制分为技术前提系统环境限制任务并发冲突执行中禁止操作四类,并针对每一类提供相应的状态检查方法,供 DBA 在克隆前进行自动化排查。

🔧 状态预检通用指南

在执行克隆操作前,建议使用以下方法主动检查系统状态,确保各项条件满足:

  • 查任务锁:通过 CDB_OB_BALANCE_JOBS 视图检查是否存在 Transfer 等任务;通过 DBA_OB_UNITS 视图检查是否存在 Unit 迁移任务。
  • 查容灾:查询 CDB_OB_LS_REPLICA_TASKSDBA_OB_LS_REPLICA_TASKS 视图,确认无容灾任务。
  • 查状态:检查所有 Unit 所在的 OBServer 节点均可用,确认租户状态正常。
  • 查归档:通过 SHOW PARAMETERS LIKE 'log_archive_dest' 确认日志归档已开启且路径正常。

一、技术前提

  • 源租户版本要求:克隆源租户必须是 OceanBase 数据库 V4.3.0 或更高版本 的租户。不支持对 V4.3.0 之前版本的租户执行克隆租户的操作。
  • 日志归档必须开启:租户克隆功能强依赖于租户的日志归档功能。在执行租户克隆操作前,必须确保源租户已开启日志归档模式,并且在租户克隆过程中,源租户不能关闭日志归档模式。为租户开启归档模式后,如果希望立即执行克隆租户的操作,需要执行 ALTER SYSTEM MINOR FREEZE TENANT = tenant_name; 语句对该租户发起一轮转储。
  • 克隆源必须为主租户:不支持对备租户(Standby Tenant)执行克隆租户的操作。您只能克隆 OceanBase 集群中的主租户(Primary Tenant)。
-- 检查租户版本(需 ≥ V4.3.0)
        SELECT TENANT_ID, TENANT_NAME, VERSION FROM oceanbase.DBA_OB_TENANTS;

        -- 检查租户日志归档状态(需确认归档已开启且路径正确)
        SHOW PARAMETERS LIKE 'log_archive_dest';

        -- 检查租户是否为主租户(role=PRIMARY)
        SELECT TENANT_ID, TENANT_NAME, ROLE FROM oceanbase.DBA_OB_TENANTS;
⚙️ 对于 V4.3.x 版本,不支持对 V4.3.0 之前版本的租户执行克隆操作。此外,如果开启归档后立即克隆,请确保执行一次 Minor Freeze 以保证归档位点已准备就绪。

二、系统环境限制

  • Unit 所在 OBServer 节点必须可用:当源租户中有 Unit 所在的 OBServer 节点不可用时,不支持执行租户克隆的操作。
  • 无正在进行的升级任务:不允许在数据库或租户升级过程中执行克隆租户的操作。
  • 日志流无容灾任务:不允许在日志流副本有容灾任务时执行克隆租户的操作。确认方法:系统租户通过指定当前租户的 tenant_id 查询 CDB_OB_LS_REPLICA_TASKS 视图,如果查询结果为非空,则表示该租户有容灾任务正在执行;用户租户可通过查询 DBA_OB_LS_REPLICA_TASKS 进行确认。
-- 检查 Unit 所在的 OBServer 节点是否可用
        SELECT UNIT_ID, TENANT_ID, SVR_IP, STATUS FROM oceanbase.GV$OB_UNITS;

        -- 检查是否存在容灾任务
        SELECT * FROM oceanbase.CDB_OB_LS_REPLICA_TASKS WHERE tenant_id = xxx;
        SELECT * FROM oceanbase.DBA_OB_LS_REPLICA_TASKS;

三、任务并发冲突

在执行租户克隆操作前,必须确保源租户上没有以下正在执行的变更任务:

  • Switchover:不允许在 Switchover(无损切换)过程中执行克隆租户的操作。
  • 日志流(LS)变更:不允许在增加 LS、删除 LS 或 LS 属性变化的过程中执行克隆租户的操作。
  • Transfer:不允许在 Transfer(分区转移)过程中执行克隆租户的操作。
  • Unit 迁移:不允许在 Unit 迁移过程中执行克隆租户的操作。
  • 租户缩容:在通过减少资源池中的 UNIT_NUM 个数来对租户进行缩容时,不允许执行克隆租户的操作。
-- 检查是否存在 Transfer 任务(CDB_OB_BALANCE_JOBS)
        SELECT * FROM oceanbase.CDB_OB_BALANCE_JOBS WHERE tenant_id = xxx;

        -- 检查是否存在 Unit 迁移任务
        SELECT * FROM oceanbase.DBA_OB_UNITS WHERE MIGRATE_FROM_SVR_PORT IS NOT NULL;
⚠️ 上述各项任务均可能通过自动调度产生,或在 DBA 主动执行变更操作时触发。克隆操作会因系统处于上述状态而失败,建议在确认所有冲突任务均已完成后再执行克隆。

四、克隆任务执行期间禁止的操作

在租户克隆任务执行过程中,不能对源租户进行以下操作。如果需要执行,必须先取消当前正在进行的克隆任务。

  • 对源租户执行 Switchover 操作;
  • 通过修改源租户的 UNIT_NUM 个数或 PRIMARY_ZONE 第一优先级进行租户扩缩容操作;
  • 对源租户执行增加副本、减少副本、调整副本分布等操作;
  • 对源租户手动执行 Transfer 操作;
  • 对源租户手动执行 Unit 迁移操作;
  • 对源租户执行升级操作。

如果必须执行上述操作,您可以取消克隆任务后再执行,取消克隆任务的详细操作可参考官方文档。

📋 克隆任务状态监控

OceanBase 提供了以下系统视图,供您监控克隆任务的状态:

  • oceanbase.DBA_OB_CLONE_PROGRESS:用于查看正在执行的克隆任务的进度信息。
  • oceanbase.DBA_OB_CLONE_HISTORY:用于查看已完成克隆任务的历史记录,该视图从 V4.3.0 版本开始引入。
-- 查看正在执行的克隆任务进度
        SELECT * FROM oceanbase.DBA_OB_CLONE_PROGRESS\G

        -- 查看已完成的克隆任务历史
        SELECT * FROM oceanbase.DBA_OB_CLONE_HISTORY\G
🏆 小结
租户克隆功能虽然强大,但受限于底层的分布式调度机制和任务互斥原则,执行前必须确保系统处于“静默”状态。通过使用CDB_OB_BALANCE_JOBSDBA_OB_UNITSCDB_OB_LS_REPLICA_TASKS等视图进行主动检查,可以有效规避任务冲突。
此外,克隆操作强依赖日志归档,必须保证归档已开启且持续可用。只有在版本满足、系统无冲突任务、归档状态正常的前提下,租户克隆才能顺畅执行。
10.3 克隆后的存储空间注意事项

租户克隆功能在实现“秒级”快速创建的同时,其共享存储的机制也带来了一个必须重点关注的问题:存储空间随时间自然膨胀。本章深入剖析克隆租户存储空间变化的内在规律,提供全方位的监控、评估与优化策略,帮助 DBA 有效管理克隆租户的存储成本,避免因空间不足引发系统异常。

🎯 本章核心问题:克隆租户的存储空间为什么会不断增长?

当源租户被克隆后,新租户初始时刻与源租户共享完全相同的物理宏块(Macroblock)。宏块是 OceanBase 中基础的数据存储单元。随着两个租户各自独立地写入新数据,共享的宏块逐渐减少,独占宏块逐渐增多,存储空间的占用也会逐步增加,需要及时关注 OBServer 节点上存储空间的占用情况。[reference:14]

一、存储空间膨胀原理详解

租户克隆借鉴了类似操作系统“进程 fork”的设计理念。在克隆时,只拷贝源租户的元数据(表结构、索引定义等),物理数据块则以只读方式共享,正是这种“Copy-on-Write”机制赋予了克隆秒级完成的极高效率。克隆过程中,将源租户的元数据拷贝一份给到新克隆租户,使得新克隆租户在初始时刻访问的是和源租户完全相同的物理宏块。[reference:15]

  • 初始共享阶段:克隆刚完成时,所有物理数据块均为共享状态,几乎不产生额外的存储开销。
  • 分岔点:当源租户或克隆租户首次修改某个共享数据页时,系统会为该数据页创建一个独立的副本,这一过程被称为“写时复制”。修改方将基于这个新副本进行后续操作。此后,该数据页在源租户和克隆租户中分别独立,不再共享,占用双份存储空间。
  • 膨胀累积:克隆租户使用时间越长、写入或更新的数据越多,需要单独占用的物理宏块就越多,存储空间的占用也随之逐步增加,需要及时关注 OBServer 节点上存储空间的占用情况。[reference:16]
🎯 核心结论:克隆租户的数据与源租户严格隔离,初始时刻共享数据,后续各自独立写入的数据会逐步私有化。克隆租户的存储开销会随着其上数据的变化而动态增长,其最终占用的存储空间,与其业务数据的活跃度和数据量呈正相关。

二、存储空间监控与用量评估

准确评估克隆租户的存储成本,是进行成本核算和资源规划的关键。OceanBase 提供了以下核心视图和方法。

1. 租户数据空间监控
  • GV$OB_UNITS:通过该视图中的 DATA_DISK_IN_USELOG_DISK_IN_USE 字段,可直接查看每个 Unit 当前使用的数据磁盘和日志磁盘大小。[reference:17]同时可通过 MEMORY_SIZELOG_DISK_SIZE 字段获得该 Unit 的内存和日志磁盘规格上限,帮助判断资源是否充足。
  • CDB_OB_TABLET_REPLICAS / DBA_OB_TABLET_REPLICAS:查看各租户 Tablet 副本的物理存储详情,可用于分析空间占用的明细分布。
-- 查询集群中各节点、各租户 Unit 的实际存储占用(系统租户下执行)
        SELECT TENANT_ID, SVR_IP, DATA_DISK_IN_USE / 1024 / 1024 / 1024 AS data_disk_used_gb,
            LOG_DISK_IN_USE / 1024 / 1024 / 1024 AS log_disk_used_gb,
            MEMORY_SIZE / 1024 / 1024 / 1024 AS memory_limit_gb,
            LOG_DISK_SIZE / 1024 / 1024 / 1024 AS log_disk_limit_gb,
            STATUS
        FROM oceanbase.GV$OB_UNITS
        WHERE TENANT_ID = 1002;   -- 替换为实际租户 ID

        -- 查看集群中所有租户 Unit 的资源规格和状态
        SELECT UNIT_ID, TENANT_ID, STATUS, MAX_CPU, MEMORY_SIZE, MAX_IOPS, LOG_DISK_SIZE
        FROM oceanbase.DBA_OB_UNITS;
2. 会话级内存监控

若克隆租户主要用于分析查询,其 SQL 执行期间可能消耗大量工作区内存。可通过 GV$OB_SQL_AUDITWORK_AREA_MEMORY_USED 列定位大内存消耗的 SQL,结合资源组做好资源隔离。

3. 利用 OCP 进行可视化监控

通过 OceanBase Cloud Platform(OCP)平台进入具体的租户概览页面,可以直观地查看数据盘和日志盘的使用趋势。此外,OCP 还支持对部署在 Kubernetes 上的 OBProxy 进行白屏化创建和管理。

三、存储空间回收与优化策略

1. 执行 Major Compaction

在执行大量的 DML 操作(尤其是 DELETE 和 UPDATE)后,存储空间可能存在“空洞”。合并可以对数据进行重新整理,清理过期的多版本数据,有效回收存储空间,提高存储效率。在克隆租户使用一段时间后,建议根据其数据变化量,定期执行合并操作。

-- 手动触发租户级合并(系统租户下执行)
        ALTER SYSTEM MAJOR FREEZE TENANT = clone_tenant_name;

        -- 查看合并任务进度(包含合并状态和 GLOBAL_BROADCAST_SCN 等详细信息)
        SELECT * FROM oceanbase.DBA_OB_MAJOR_COMPACTION;
2. 定期清理过期克隆租户

删除不再使用的克隆租户是释放存储空间最直接有效的方式。DBA 应建立克隆租户的全生命周期管理策略,设计合理的过期清理机制,定期扫描并删除超过保留期限的克隆租户。删除租户时,系统会自动回收其占用的元数据和用户数据空间。此外,删除大租户可能触发大量的磁盘操作,导致系统瞬时 IO 负载增加,建议在业务低峰期执行。

3. 合理规划克隆租户的资源规格

在创建克隆租户时,应进行充分的容量评估。不同于源租户,克隆租户通常用于临时或特定场景(如报表、验证),其资源需求可能更低。在克隆命令中,可以为其指定更小的 MAX_CPUMEMORY_SIZEMAX_IOPS,以实现更精细的成本控制。但是,Unit 数量及 Unit 在各个 OBServer 节点上的分布,均与源租户保持一致。[reference:18]

-- 在克隆租户时为其指定一个新的、更小的 Unit Config
        CREATE RESOURCE UNIT small_unit 
            MAX_CPU=4, MIN_CPU=4, 
            MEMORY_SIZE='16G', 
            MAX_IOPS=64000, MIN_IOPS=64000;

        CREATE TENANT clone_tenant FROM source_tenant 
            WITH RESOURCE_POOL clone_pool, UNIT small_unit;
⚠️ 常见警示与FAQ
  • 空间不足错误(err = -4184):若在合并时遇到该错误,通常是磁盘空间不足所致,需立即清理空间或扩容。
  • 频繁克隆与删除的影响:克隆操作会消耗 CPU 和内存资源进行数据验证和传输,删除大租户可能触发大量磁盘操作,加剧 IO 负载。但租户删除后系统会立刻释放资源,无其他副作用。
  • 克隆期间禁止操作及取消克隆:租户克隆执行期间禁止对源租户执行 Switchover、扩缩容、增删副本、手动 Transfer、手动 Unit 迁移及升级。如需取消正在执行的克隆任务,可通过以下命令取消:
    ALTER SYSTEM CANCEL CLONE TENANT clone_tenant_name;
🏆 小结
正视膨胀:克隆租户的存储开销会随时间逐步增加,这是由写时复制技术带来的正常现象,在资源规划时需充分考虑这一点。
持续监控:利用 GV$OB_UNITSDBA_OB_UNITS 等系统视图,建立对租户存储用量的常态化监控。
主动优化:将定期合并、生命周期管理和资源规格适配相结合,形成闭环的存储优化策略。
成本控制:租户克隆虽然带来了极大的灵活性和速度,但不应忽视其背后的存储成本。DBA 需权衡“敏捷”与“成本”,确保业务价值最大化。

第11章:Oracle 兼容性深度解析

11.1 兼容性总体概述

OceanBase 数据库从 V2.x.x 版本开始正式支持 Oracle 兼容模式(即 Oracle 租户),旨在为从 Oracle 数据库迁移至 OceanBase 的业务系统降低改造成本,同时也让数据库设计开发人员、数据库管理员等能够复用积累的 Oracle 技术知识,快速上手 OceanBase 数据库。经过多个大版本的持续演进,Oracle 兼容模式已成为 OceanBase 的关键功能之一。

🎯 OceanBase Oracle 模式的核心价值
  • 高度兼容,平滑迁移:在数据类型、SQL 语法、数据库对象以及 PL 等基本功能上与原生 Oracle 数据库高度兼容。整体兼容 90% 以上的 Oracle 语法,覆盖大部分标准 SQL 和常用 PL/SQL 语法。在数据类型方面,Oracle 原生 24 种数据类型中,OceanBase 已支持 20 种。
  • 大多数场景可“平迁”:在数据库安全、备份恢复、高可用和优化器等高级特性上,OceanBase 不仅提供了良好的兼容性,甚至超越原生 Oracle。这意味着在从 Oracle 数据库迁移到 OceanBase 的过程中,用户不需要消耗大量时间去学习新知识,即可流畅地实现迁移。
  • PL 全面兼容,降低迁移壁垒:OceanBase 已基本兼容 Oracle 全部的研发功能,涵盖存储过程、函数、包、触发器、游标、异常处理、动态 SQL 等核心 PL 能力。同时已兼容多个 Oracle 常用系统包(如 DBMS_LOB、DBMS_OUTPUT、DBMS_SQL、UTL_RAW、DBMS_RANDOM 等),进一步降低迁移壁垒。
  • 完整的生态工具链:提供 OMA(OceanBase Migration Assessment,自动化评估工具)、OCP(OceanBase Cloud Platform,云管理平台)、OMS(OceanBase Migration Service,数据迁移服务)等整迁移套件,覆盖从源库评估、数据同步到性能监控的全生命周期,帮助企业顺利完成数据库国产化替代。

OceanBase 4.x 系列版本关键里程碑

兼容性的持续增强是 OceanBase 4.x 系列版本的核心演进方向,以下为几个关键的里程碑版本:

  • V4.1.0:Oracle 模式兼容性基础版本,提供较完善的数据类型、SQL 语法、内置函数和系统视图兼容,已适配多种 Oracle 密码策略,支持用户锁定和解锁功能,并开放 OMS 迁移能力。
  • V4.2.0:增加 Auto DOP(自动并行度)功能,支持并行查询智能化调度,同时引入 SPM(SQL Plan Management),大幅提升复杂查询场景的计划稳定性与性能。
  • V4.2.1 LTS:Oracle 兼容性的里程碑版本,是首个 Oracle 兼容模式推荐长期支持(LTS)版本。它汇集了 OceanBase 在银行、证券、保险、运营商、电力、人社等多个行业实际业务场景中打磨出的各项兼容能力,可让更多业务场景平滑地完成应用迁移。
  • V4.3.0:正式支持列存引擎与物化视图,并引入向量化执行引擎 2.0,全面加速 AP 分析负载。
  • V4.3.3:正式支持向量检索(ANN)能力,支持向量数据类型和向量索引,同时新增列存副本功能,为 HTAP 混合负载下的性能隔离提供更优方案。
  • V4.3.5:OLAP 能力进一步强化,包括基于物化视图的查询改写、物化视图实时更新、旁路导入等,大幅增强复杂分析场景的易用性与实时性。

更多功能变动信息,建议参考对应版本的官方文档。

数据类型兼容

OceanBase 数据库兼容大部分 Oracle 数据库中支持的数据类型。Oracle 数据库中有 24 种数据类型,OceanBase 数据库目前支持其中的 20 种。基于优化考虑,LONGLONG RAW 数据类型已过于陈旧,OceanBase 暂不计划支持;BFILENCLOB 目前也尚未兼容。对于大对象(LOB)数据类型,OceanBase 支持 BLOBCLOB,存储上限为 48 MB。

-- 查看当前租户的字符集(Oracle 模式)
        SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');

系统视图兼容

OceanBase 已兼容 Oracle 大量的字典视图(Data Dictionary Views)与性能视图(Performance Views)。其中,字典视图已兼容大量(如 ALL_*DBA_*USER_* 三类权限视角),为日常运维和性能诊断提供了完整的兼容视图生态。

-- 在 Oracle 租户中可直接查询兼容的系统视图
        SELECT * FROM ALL_TABLES WHERE OWNER = 'SCHEMA_NAME';
        SELECT * FROM DBA_OB_TABLET_REPLICAS;

PL 全面兼容(Oracle 模式)

OceanBase 已经基本能够兼容 Oracle 全部的研发功能。已支持的 PL 语法及功能涵盖数据类型、流程控制、集合与记录(多维集合暂不支持)、静态/动态 SQL 语句、子程序与包、触发器、异常处理、系统包(如 DBMS_LOB、DBMS_OUTPUT、DBMS_SQL 等)以及自定义数据类型等多个核心维度。这极大降低了将大规模 PL/SQL 迁移至 OceanBase 数据库的难度与成本。

-- Oracle 模式示例:创建存储过程
        CREATE OR REPLACE PROCEDURE example_proc (p_id IN NUMBER, p_name OUT VARCHAR2) IS
        BEGIN
            SELECT name INTO p_name FROM users WHERE id = p_id;
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                p_name := NULL;
        END example_proc;
        /

备份恢复与安全特性

兼容 Oracle 模式的多数备份恢复与安全特性,包括租户级物理备份、日志归档、PITR(基于时间点的即时恢复)以及备份数据自动过期清理,保证企业级数据安全与合规需求。安全特性方面,支持与 Oracle 高度一致的密码策略(密码生命周期、密码复杂度、密码重用限制等),同时支持 TDE 透明加密、SSL 链路加密与基于标签的安全(Label Security)机制。

📌 关键版本里程碑总结
  • V4.2.1 LTS:Oracle 兼容模式的第一个长期支持版本,适合在关键生产系统中部署。
  • V4.3.5+:列存引擎与物化视图等 AP 能力已趋于成熟,适合 HTAP 混合负载场景。
🏆 小结
OceanBase 的 Oracle 兼容模式经过多版本迭代,已发展为一个高度成熟、性能卓越的数据库解决方案。它不仅提供了常见应用场景“平滑迁移”的兼容能力,在 AP、高可用、工具链等层面甚至实现了超越,为企业构建新一代分布式数据基础设施提供了坚实保障。
11.2 不兼容特性与迁移注意事项

OceanBase 数据库在数据类型、SQL 语法、PL 对象等基础功能上与 Oracle 数据库高度兼容,但受限于底层分布式架构和产品形态的差异,部分 Oracle 原生特性暂不支持或存在行为差异。本章系统梳理 Oracle 迁移至 OceanBase(Oracle 模式)过程中的主要不兼容特性,并提供详细的迁移注意事项和最佳实践指南,帮助 DBA 在迁移前有效规避风险,确保迁移过程平稳顺畅。

🔧 迁移前必做:兼容性评估

在进行 Oracle 到 OceanBase 的迁移之前,强烈建议先进行充分的兼容性评估:

  • 使用 OMA 进行全面评估:OceanBase Migration Assessment(OMA)是官方提供的数据库兼容性评估工具,通过连接源端数据库自动采集对象和 SQL 进行兼容性分析,精准输出评估报告,包括不兼容对象清单和改造建议。
  • 验证核心业务场景:在测试环境中进行完整的业务验证,包括结构迁移、全量迁移和增量同步,确保核心业务 SQL 和存储过程在 OceanBase 中正常运行。
  • 关注 LOB 字段数据:检查 Oracle 中 LOB 字段(CLOB/BLOB)的最大长度,若超过 48M 则需要提前处理,避免迁移后数据丢失。
  • 提前识别不兼容对象:通过上述工具和手段提前发现不兼容的数据库对象和 SQL,能有效避免迁移过程中的报错。

一、数据类型不兼容

不兼容的数据类型

Oracle 原生支持 24 种数据类型,OceanBase 目前支持其中的 20 种。以下为暂不兼容的数据类型:

数据类型兼容状态迁移建议
LONG / LONG RAW 不兼容 必须转换:这两种数据类型已过于陈旧,OceanBase 目前无支持计划,迁移前需转换为 CLOBBLOB
BFILE 不兼容 需改造应用逻辑,将外部文件存储改为使用 BLOB 或对象存储方案。
NCLOB 不兼容 建议将数据转换为 CLOBVARCHAR2(注意字符集适配)。
大对象(LOB)数据类型限制

OceanBase 支持 BLOBCLOB 类型,但存储上限为 48 MB(Oracle 可达 4 GB),且在大数据量场景下性能可能不如预期,建议复杂场景中尽量避免使用或改用普通表存储。迁移时需注意检查源端 LOB 字段最大值,防止数据丢失。

字符集注意事项

Oracle 模式租户创建时,字符集由租户属性决定。建议在创建租户前与源端对齐字符集,避免迁移后出现字符乱码问题。

二、SQL 语法不兼容

DDL 语法差异

在 Oracle 迁移至 OceanBase 的过程中,部分 DDL 语法存在不兼容或行为差异,尤其在约束、索引和分区定义方面。

1. 约束类(Constraint)
  • 不支持 references_clause 子句定义外键:外键关联的 REFERENCES 子句会被忽略。
  • 不支持 constraint_state 约束状态属性:如 DEFERRABLEINITIALLY DEFERRED/IMMEDIATE 等属性会被忽略。
  • 不支持 ENABLE/DISABLE 约束状态:相关约束控制子句不兼容。
2. 索引类(Index)
  • 不支持 bitmap_join_index_clause 子句定义位图索引:位图连接索引定义会报错,系统会忽略 BITMAP 关键字,创建普通索引而非位图索引。
  • 全局索引分区定义部分属性被忽略:同步 DDL 中包含 index_partitioning_clause 中的 segment_attributes_clause(如 TABLESPACE 子句)会被忽略。
3. 表分区(Table Partition)
  • 不支持 set_subpartition_template 子句:为每个表分区创建默认 RANGE/LIST/HASH 子分区的子句不兼容,会报错。
  • 不支持 exchange_partition_subpart 子句:交换数据和索引 segment 的分区操作不兼容。
  • 不支持 enable_disable_other_clause 子句:启用/禁用表锁或触发器的 DDL 控制子句不兼容,会报错。
物化视图刷新模式差异

OceanBase 支持创建物化视图和物化视图日志,但 不支持 ON COMMITON STATEMENT 刷新模式,这两种实时刷新语法不兼容。

数据类型变更限制

OceanBase 与 Oracle 在修改列数据类型时的限制存在差异。使用 ALTER TABLE ... MODIFY 修改带约束(如主键、唯一键、CHECK)的列时,目标类型的兼容范围与 Oracle 不同,迁移前需详细对照目标类型映射表。

支持的分区操作说明

OceanBase 在分区 DDL 方面支持新增、删除、清空、重命名等常用操作,但不支持 Oracle 的部分高级分区 DDL 语法(如 EXCHANGE PARTITIONMERGE PARTITIONSSPLIT PARTITION),迁移时需改为手动重建分区表。

三、PL 与系统包不兼容

复杂嵌套类型不支持

OceanBase V4.x table 嵌套类型暂不支持复杂嵌套类型(如 table(collection(collection(record(basic type))))),仅支持简单嵌套类型 table(collection(record(basic type))),迁移时建议采用多层 FOR LOOP 手动遍历。

外部 Java 调用不兼容

OceanBase 内核暂不支持调用外部 Java,迁移过程中若遇到 AS LANGUAGE JAVA NAME 声明,需重写 PL/SQL 逻辑以适配 OceanBase。

系统包能力差异

部分系统包如 UTL_FILEDBMS_SCHEDULER 等高级功能在 OceanBase 中能力有限或行为存在差异,需在评估阶段重点排查。

四、性能与优化器差异

Oracle Hint 不完全适用

OceanBase 优化器采用基于代价(CBO)的优化模型,与 Oracle 优化器存在差异。Oracle 中的部分 Hint 在 OceanBase 中可能无效甚至报错,迁移时需逐一验证并删除无效的 Legacy Hint,必要时改用 OceanBase 支持的 Hint(如 /*+ PARALLEL(8) *//*+ LEADING(...) */ 等)。

游标循环 + 单条 DML 是性能杀手

OceanBase 的强项在于分布式并行计算与批处理(Set-based)。“游标循环 + 单条 DML”在分布式架构下是绝对的性能瓶颈,迁移时应重写为批量操作(如 BULK COLLECT + FORALL),充分利用 OceanBase 的批处理能力。

索引类型差异:主键索引迁移注意事项

Oracle 上的主键默认是全局索引。使用 DBCAT 迁移到 OceanBase 时会变成本地索引,而使用 OMS 迁移则仍是全局索引。两者在性能表现上存在差异,迁移前需根据业务查询模式选择合适的索引类型。

五、迁移最佳实践与工具链

迁移评估工具链
  • OMA(OceanBase Migration Assessment):官方兼容性评估工具,提供精准的兼容性评估、性能评估和分布模式建议,输出详细的评估报告和改造建议。
  • DBCAT:轻量级命令行工具,支持 Oracle 与 OceanBase 之间的 DDL 转换和 Schema 比对,适用于结构迁移和 Schema 验证。
数据迁移工具链
  • OMS(OceanBase Migration Service):官方一站式数据迁移服务,支持结构迁移、全量迁移和增量同步,提供多维度数据校验和反向回流能力,是 Oracle 迁移至 OceanBase 的首选工具。
  • DataX:开源数据同步工具,适用于特定场景的异构数据迁移。
迁移流程建议
  1. 兼容性评估:使用 OMA 完成完整的兼容性评估,生成评估报告,梳理不兼容对象清单。
  2. 环境准备:创建 OceanBase Oracle 模式租户,对齐字符集,配置合适的资源规格。
  3. 结构迁移:使用 DBCAT 或 OMS 完成 Schema 迁移,验证表结构、索引、约束的正确性。
  4. 全量迁移:使用 OMS 完成全量数据迁移,关注大表和 LOB 字段的迁移性能,必要时分批迁移。
  5. 增量同步:配置 OMS 增量同步链路,确保源端与目标端数据实时一致。
  6. 业务验证:在目标端完成核心业务场景的功能验证和性能测试。
  7. 反向回流:配置 OMS 反向链路,确保紧急回退能力。
常见问题快速定位
  • LOB 数据迁移:Oracle 中 LOB 数据可能超过 48M,迁移后会导致数据丢失。迁移前需使用 DBMS_LOB.GETLENGTH 检查最大长度。
  • 复杂嵌套类型错误(ORA-00600):因 OceanBase 不支持复杂嵌套类型。
  • 存储过程报错:检查是否使用了 AS LANGUAGE JAVA NAME 或复杂嵌套类型。
  • 优化器性能差异:检查 Hint 是否无效,验证计划并修改 SQL。
⚡ Oracle 迁移到 OceanBase 的典型问题与优化要点
  • 优化 Hint 适配:Oracle 迁移过来的 DBA 最容易犯的错误是过于依赖原厂的 Legacy Hint。OceanBase 优化器基于代价(CBO)与 Oracle 不同,应删除无效 Hint,必要时改用 OceanBase 支持的 Hint。
  • 放弃逐行处理,拥抱批处理:OceanBase 在并行计算和批处理(Set-based)方面有天然优势,应重写游标循环为 FORALL 批量操作。
  • SQL 语法适配:迁移过程中需关注 Oracle 特有 PL/SQL、序列处理、分析函数等语法在 OceanBase 中的适配。
  • 事务模型适配:OceanBase 采用 Paxos 协议保证强一致性,与 Oracle 的单机事务模型存在差异,需合理调整事务隔离级别。
  • 存储引擎差异:OceanBase 的 LSM-Tree 存储引擎与 Oracle 的 B+Tree 不同,需优化索引设计和查询计划。
  • 分布式事务模型适配:OceanBase 采用 Paxos 协议保证强一致性,与 Oracle 的单机事务模型存在差异,在涉及跨分区事务时需关注性能表现。
  • 特殊列处理:从 Oracle 迁移来带有 `UNUSED` 标记的列会直接报错,需先通过 `ALTER TABLE ... DROP UNUSED COLUMNS` 清理,或手动改造 DDL 再迁移。同时,Oracle 支持在一个 `DROP COLUMN` 语句中删除多个列,而 OceanBase 要求串行执行,需分多次 `DROP`。
  • 定时任务方案选型:`DBMS_SCHEDULER` 在 OceanBase 中能力有限,若迁移中遇到调度器代码报错,可考虑使用数据库原生的 `CREATE JOB` 定时任务或存储过程逻辑替代,避免系统包绑定。
🏆 小结
OceanBase 在 Oracle 兼容性方面已取得显著进展,大部分业务场景可实现“平滑迁移”。但由于底层分布式架构的差异,部分数据类型、SQL 语法、DDL 操作和 PL 功能仍存在不兼容行为。迁移前使用 OMA 进行全面评估是保障迁移成功的关键第一步,迁移过程中应将“批处理思想”贯穿全流程,充分释放 OceanBase 分布式架构的性能潜力。

第12章:常见问题与故障排查

12.1 常见错误码及解决

OceanBase 数据库在 MySQL 模式和 Oracle 模式下提供了完善的错误码体系,覆盖 SQL 执行、事务处理、资源管理、系统内部等各个层面。当应用出现异常时,快速识别错误码及其含义是高效排查问题的关键。本章根据 OceanBase V4.5.0 官方错误码文档,整理了生产环境中最高频的错误码,并给出详细的排查思路和解决方法。

📌 错误码格式说明
  • MySQL 模式:格式为 ERROR <err_num> (<sql_state>): err_msg
    例如:ERROR 4012 (HY000): Timeout, query has reached the maximum query timeout...
  • Oracle 模式:错误信息以“前缀 + 数字编码”的形式返回。源于数据库的错误以 ORA- 开头,源于存储过程的错误以 PLS- 开头,OceanBase 特有内部错误以 OBE- 开头。
    例如:ORA-00942: table or view does not existPLS-00201: identifier '...' must be declaredOBE-00600: internal error code, arguments: -4012, Timeout

错误码分类概览

了解错误码的数值范围有助于快速定位问题所属领域:

错误码范围类别典型场景
0001 - 3999 MySQL 兼容错误 与原生 MySQL 相同的错误(如 1045 密码错误、1062 唯一键冲突)
4000 - 4499 通用运行时错误 超时(4012)、内存不足(4013/4030)、Leader 切换(4038)等
4500 - 4999 Root Service 错误 资源池、租户、Zone 管理等集群运维错误
6000 - 6999 事务层错误 事务空闲超时(6002)、锁冲突(6005)、死锁(1213/6005)等
8000 - 8999 致命错误 服务正在启动/停止(8001/8002)、租户ID不匹配(8005)等,需重建连接

ob_error:命令行错误码查询工具

OceanBase 自带了轻量级命令行工具 ob_error,可根据错误码快速查询错误含义和解决建议,无需翻阅文档。该工具在 OceanBase 安装包的 bin 目录下,通常在 /home/admin/oceanbase/bin/ob_error(也可能是 ob_error 在 PATH 中)。

基本用法
# 查询单个错误码
        ob_error <error_code>

        # 查询 OceanBase 内部错误码(带负号)
        ob_error -4012

        # 查询 MySQL 兼容错误码
        ob_error 1062

        # 查询 Oracle 模式错误码(支持 ORA- 前缀和 OBE- 前缀)
        ob_error ORA-00942
        ob_error OBE-00600
示例输出
$ ob_error -4012
        OceanBase Error:
            Code: 4012, SQLState: HY000, Cause: Timeout, query has reached the maximum query timeout: 10000000(us), maybe you can adjust the session variable ob_query_timeout or query_timeout hint, and try again.
            Solution: 1. Optimize SQL to reduce execution time. 2. Increase ob_query_timeout or ob_trx_timeout. 3. Check network latency or cluster load.

        $ ob_error 1045
        MySQL Compatible Error:
            Code: 1045, SQLState: 42000, Cause: Access denied for user '%.*s'@'%.*s' (using password: %s)
            Solution: Verify username/password, or grant the required privileges using GRANT statement.
集成到故障排查流程中

当您遇到未知错误码或需要快速获取解决思路时,优先使用 ob_error 工具查询,可大幅提升效率。同时结合 observer.log 中的详细堆栈和本章后续的错误码列表,形成“工具查码 → 日志定位 → 列表对照”的三步诊断法。

💡 提示:ob_error 工具不需要连接数据库,只要安装 OceanBase 软件包即可使用,非常适合在运维脚本或紧急排查时快速调用。

一、超时类错误

🔸 ERROR 4012 (HY000) : Timeout

现象:SQL 执行失败,返回 “Timeout, query has reached the maximum query timeout: ...”。
常见原因

  • 单条 SQL 执行时间超过了租户变量 ob_query_timeout 的值(默认 10,000,000 微秒 = 10 秒)。
  • 事务总持续时间超过了 ob_trx_timeout(默认 86,400,000,000 微秒 = 24 小时)。
  • RPC 请求超时或网络延迟过高。

排查步骤

  1. 首先判断是哪种超时:若错误信息中包含 “query timeout” 则是 SQL 超时;若包含 “transaction timeout” 则是事务超时。
  2. 如果是 SQL 超时,通过 EXPLAIN 查看执行计划,检查是否存在全表扫描、缺失索引或分区裁剪失效等问题。
  3. 如果是正常的大数据量操作(如报表、批处理),可适当调大超时参数:
    SET SESSION ob_query_timeout = 60000000; -- 调整为 60 秒
    SET SESSION ob_trx_timeout = 86400000000; -- 24 小时(默认值已较大)
  4. 若频繁出现且 SQL 本身很快,应排查网络或负载情况。
🔸 ERROR 6002 (25000) : Transaction idle timeout

现象:事务内两条 SQL 之间的间隔时间超过了 ob_trx_idle_timeout 阈值(默认 24 小时),系统自动回滚事务。
常见原因:应用程序在事务中进行了长时间的用户交互或业务逻辑处理,未及时提交或回滚。
解决方法

  • 回滚当前事务:ROLLBACK; 然后重新开始事务。
  • 如果业务确实需要长时间空闲,可调大参数:SET SESSION ob_trx_idle_timeout = 172800000000; (48 小时)
  • 检查应用代码,避免在事务中执行非数据库操作,尽量缩短事务生命周期。
🔸 ERROR 4038 (HY000) : The observer or zone is not the master

现象:请求发送到了 OBServer 的 Follower 副本,而不是当前 Leader 副本。
常见原因:Leader 切换后客户端 Location Cache 未及时更新,或 OBProxy 路由错误。
解决方法

  • OceanBase 内部会自动重试或重新获取路由,应用层通常无需处理。若频繁出现,检查集群是否存在频繁切主(网络分区、时钟不同步等)。
  • 直连场景下,应通过 OBProxy 访问,避免直接连接 OBServer 节点。

二、资源与内存类错误

🔸 ERROR 4030 (HY000) : Over tenant memory limits

现象:租户整体内存使用量超过了上限,无法分配新内存。
常见原因

  • MemStore 写满(最常见):写入压力大,转储速度跟不上。
  • SQL 工作区(Work Area)内存占用过高,例如大型排序、Hash Join 等。
  • KVCache(如 Block Cache、Row Cache)占用过高且无法及时淘汰。

排查与解决

  1. 查询 MemStore 使用情况:
    SELECT tenant_id, svr_ip, round(memstore_used/1024/1024/1024,2) used_gb, round(memstore_limit/1024/1024/1024,2) limit_gb, round(memstore_used/memstore_limit*100,2) pct FROM oceanbase.GV$OB_MEMSTORE WHERE tenant_id=1002;
  2. 若 MemStore 占用过高且转储不及时,可手动触发转储:
    ALTER SYSTEM MINOR FREEZE TENANT = tenant_name;
  3. 若 SQL 工作区占用高,可调整 ob_sql_work_area_percentage(默认 5%),或在 SQL 中使用 /*+ PARALLEL(8) */ 将部分计算下压。
  4. 如果内存普遍紧张,需增加租户内存规格:
    ALTER RESOURCE UNIT unit_name MEMORY_SIZE = '32G';
🔸 ERROR 4013 (HY001) : No memory or reach tenant memory limit

现象:租户内存不足,但并非 MemStore 写满,而是其他模块(如 SQL 算子、Plan Cache 等)内存分配失败。
排查方法:查询内存详细分布:
SELECT tenant_id, svr_ip, ctx_name, round(hold/1024/1024/1024,2) hold_gb FROM oceanbase.GV$OB_MEMORY WHERE tenant_id=1002 ORDER BY hold DESC LIMIT 10;
定位到占用最高的 ctx_name,根据模块名称调整对应的参数(如 plan_cache_high_water_marklarge_query_worker_percentage)。

🔸 ERROR 5150 (HY000) : Tenant not in this server

现象:通过直连 IP 访问时,该 OBServer 节点上没有目标租户的资源单元(Unit)。
解决方法

  • 推荐使用 OBProxy 访问集群,OBProxy 会自动路由到正确的节点。
  • 若必须直连,先查询租户 Unit 分布:SELECT * FROM oceanbase.GV$OB_UNITS WHERE TENANT_ID = xxx;,然后连接到对应的 SVR_IP 和端口。
🔸 ERROR 1040 (08004) : Too many connections

现象:客户端连接数超过了租户或 OBServer 设置的 max_connections 限制。
解决方法

  • 检查应用连接池配置,限制最大连接数,并确保连接池能正确回收闲置连接。
  • 若确实需要更多连接,可调大参数:SET GLOBAL max_connections = 10000;(注意对应租户和 OBServer 资源配置)。
  • 排查是否存在连接泄漏(例如应用程序未关闭连接)。

三、连接与通信类错误

🔸 ERROR 8001 (08004) : Server is initializing

现象:向正在启动的 OBServer 发送了请求。
解决方法:等待 OBServer 启动完成(通常几十秒到几分钟),无需其他操作。

🔸 ERROR 8002 (08004) : Server is stopping

现象:向正在关闭的 OBServer 发送了请求。
解决方法:等待 OBServer 正常退出,或若确需立即终止可执行 kill -9,但应确保业务已切走。

🔸 ERROR 8005 (08005) : Tenant id not match

现象:通过 OBProxy 连接时,租户名称发生了改变(如重命名),导致 OBProxy 缓存中的租户 ID 与新的不一致。
解决方法:断开当前连接,重新通过 OBProxy 建立连接即可。建议租户改名操作提前断开所有相关连接。

🔸 ERROR 2006 (HY000) : MySQL server has gone away

现象:客户端与 OceanBase 之间的连接被意外关闭。
常见原因wait_timeoutinteractive_timeout 导致连接空闲超时被服务端关闭;网络设备断开;服务端重启。
解决方法:在连接池中配置心跳检测(如 testOnBorrowvalidationQuery),或适当调大 wait_timeoutSET GLOBAL wait_timeout = 28800;(8 小时)。

四、SQL 语法与对象类错误

🔸 ERROR 1062 (23000) : Duplicate entry for key

原因:违反唯一约束(主键或唯一索引),插入或更新的键值已存在。
解决:使用 INSERT IGNORE 忽略冲突,或 ON DUPLICATE KEY UPDATE 处理更新,或提前检查数据。

🔸 ERROR 1146 (42S02) : Table doesn't exist

原因:表名错误、数据库名错误、或当前租户下无该表。
解决:检查 SHOW TABLES; 确认表存在,注意大小写(取决于 lower_case_table_names 设置)。

🔸 ERROR 1045 (42000) : Access denied for user

原因:用户名、密码错误,或该用户无权限访问指定的数据库。
解决:确认用户名密码,检查 SHOW GRANTS FOR username@host;,使用 GRANT 授权。

🔸 ERROR 1054 (42S22) : Unknown column

原因:SQL 中引用的列不存在。
解决:检查列名拼写,或确认表结构是否发生过变更但 Schema 未刷新(可等待几秒重试)。

🔸 ERROR 1366 (HY000) : Incorrect string/integer value

原因:字段类型与插入值不匹配,例如向 INT 列插入了字符串。
解决:检查数据类型,使用 CAST 转换或修改插入值。

🔸 ERROR 1406 (22001) : Data too long for column

原因:插入的字符串长度超过列定义的最大长度。
解决:截断数据,或使用 ALTER TABLE MODIFY 扩大列长度。

🔸 ERROR 1235 (0A000) : %s not supported

原因:使用了当前 OceanBase 版本暂不支持的功能或语法(例如某些 Oracle 特有函数)。
解决:查阅对应版本的官方文档,使用支持的语法替代。

🔸 ERROR 4179 (HY000) : %s not allowed

原因:执行了与当前状态冲突的操作,例如在合并过程中修改租户 Locality,或在升级过程中执行 DDL。
解决:等待合并/升级等后台任务完成后再重试。

🔸 ERROR 4000 (HY000) : Common error

原因:通用内部错误,通常伴随更详细的错误信息。
解决:查看 observer.log 中对应时间的堆栈,联系技术支持或根据上下文自行分析。

🔸 ERROR 4664 (HY000) : Timeout OB_GET_LOCATION_TIME_OUT

原因:SQL 执行过程中获取分区位置信息超时。
解决:调大超时参数(ob_trx_timeoutob_query_timeout),或检查网络延迟和集群负载。

🔸 ERROR 4209 (HY000) : partition has not leader

原因:分区没有 Leader 副本,常见于选举未完成或节点宕机。
解决:检查集群健康状态,等待选举完成;若长期无主,需排查网络或磁盘故障。

五、事务与锁类错误

🔸 ERROR 6005 (HY000) : Try lock row conflict

原因:并发事务更新同一行数据,加锁冲突。OceanBase 内部会自动重试,若重试仍失败则报此错误。
解决:检查应用是否存在热点行更新,考虑减少冲突(如使用乐观锁、拆分更新、排队等)。

🔸 ERROR 1213 (HY000) : Deadlock

原因:事务间形成了循环等待,OceanBase 会自动选择一个事务回滚打破死锁。
解决:应用程序捕获该错误后重试事务。可通过 DBA_OB_DEADLOCK_EVENT_HISTORY 视图分析死锁原因。

🔸 ERROR 6002 (HY000) : Transaction is killed

原因:事务被手动或系统终止(如通过 KILL SESSION 或达到资源限制)。
解决:重新执行事务,并检查是否被运维操作干预。

六、Oracle 模式特有错误码

🔸 ORA-00942: table or view does not exist

原因:表或视图不存在,或当前用户无权限访问。
解决:确认表名拼写,检查 user_tablesall_tables,并授予 SELECT 权限。

🔸 ORA-01017: invalid username/password; logon denied

原因:用户名或密码错误。
解决:确认凭证,或使用 ALTER USER username IDENTIFIED BY new_password; 重置密码。

🔸 ORA-01403: no data found

原因:PL/SQL 中的 SELECT INTO 语句未返回任何行。
解决:使用 EXCEPTION WHEN NO_DATA_FOUND THEN ... 处理异常,或改用游标查询。

🔸 ORA-01438: value larger than specified precision allowed for this column

原因:数值超出列定义的精度(如 NUMBER(5,2) 存储 1234.5)。
解决:扩大列精度或调整数值。

🔸 ORA-01722: invalid number

原因:字符串无法转换为数字。
解决:检查数据,使用 TO_NUMBER 时确保格式匹配,或先用正则过滤非法字符。

🔸 ORA-01843: not a valid month

原因:日期格式字符串与格式掩码不匹配,例如 ‘13’ 作为月份。
解决:使用 TO_DATE 时指定正确的 fmt 参数,如 TO_DATE('2025-13-01','YYYY-MM-DD') 会报此错误。

🔸 ORA-02291: integrity constraint violated - parent key not found

原因:插入或更新子表外键列时,对应的父表主键不存在。
解决:先插入父表记录,或暂时禁用外键约束(生产环境慎用)。

🔸 ORA-02292: integrity constraint violated - child record found

原因:删除父表记录时,子表仍有引用该记录的外键数据。
解决:先删除子表相关数据,或使用 DELETE FROM parent WHERE ... CASCADE CONSTRAINTS;(需权限)。

🔸 ORA-04091: table is mutating, trigger/function may not see it

原因:行级触发器中试图读取或修改触发表本身,导致变异表错误。
解决:重写触发器,改用复合触发器(Compound Trigger)或临时表来绕过。

🔸 ORA-06530: Reference to uninitialized composite

原因:使用了未初始化的对象或集合。
解决:使用构造函数初始化,如 l_obj := my_obj_type(NULL, ...);l_col := my_collection_type();

🔸 ORA-06531: Reference to uninitialized collection

原因:嵌套表或 VARRAY 未初始化。
解决:调用集合的构造函数:l_table := my_table_type();

🔸 ORA-06550: PL/SQL: numeric or value error

原因:PL/SQL 中算术运算、类型转换或约束违反。
解决:检查变量声明、赋值和运算,确保值不超出范围。

🔸 ORA-00904: invalid identifier

原因:标识符(列名、别名、变量名)无效或拼写错误。
解决:检查名称是否在作用域内,注意大小写(双引号内区分大小写)。

🔸 ORA-00933: SQL command not properly ended

原因:SQL 语法错误,如缺少分号、关键字错位、括号不匹配等。
解决:逐段检查 SQL,可借助 SQL 格式化工具辅助排查。

🔸 ORA-01031: insufficient privileges

原因:当前用户缺少执行该操作所需的系统权限或对象权限。
解决:联系 DBA 授予对应权限,例如 GRANT CREATE SESSION TO user;GRANT SELECT ON table TO user;

🔸 OBE-00600: internal error code, arguments: %d, %s

现象:OceanBase Oracle 模式下的内部错误,其中第一个参数是 OceanBase 内部错误码,第二个参数是错误描述。
解决:根据内部错误码查阅本文档或其他官方资料,若无法自行解决则联系技术支持。

🔸 PLS-00103: Encountered the symbol ... when expecting one of the following

原因:PL/SQL 语法错误,例如缺少分号、关键字写错、声明顺序错误等。
解决:检查代码,参照 Oracle PL/SQL 语法规范修正。

🔸 PLS-00201: identifier '%.*s' must be declared

原因:引用的变量、游标、子程序等未声明,或声明位置错误。
解决:检查标识符拼写,确保声明在使用之前,且作用域正确。

🔸 PLS-00306: wrong number or types of arguments in call

原因:调用过程或函数时传递的参数个数或类型与定义不匹配。
解决:检查函数/过程的参数列表,调整调用语句。

🔸 PLS-00428: an INTO clause is expected in this SELECT statement

原因:在 PL/SQL 块中使用的 SELECT 语句没有 INTO 子句来接收返回值。
解决:添加 INTO 变量列表,例如 SELECT ... INTO v_var1, v_var2 FROM ...;

七、错误排查通用流程

当遇到未知或罕见错误码时,可遵循以下标准流程进行排查:

  1. 快速查询错误码含义:使用 ob_error <错误码> 获取官方解释和解决建议。
  2. 捕获完整错误信息:包括错误码、SQLSTATE、错误消息、发生时间、执行 SQL 语句及参数。
  3. 查阅官方文档:OceanBase 官网提供按错误码分类的详细说明和常见案例。
  4. 检查相关日志
    • observer.log:存储 OBServer 节点运行信息,包含大量调试和错误详情。
    • rootservice.log:Root Service 相关日志,涉及租户、资源管理等。
    • election.log:选举模块日志,用于排查 Leader 切换、无主等问题。
  5. 分析触发条件:结合业务场景(并发量、数据量、操作类型)复现问题,缩小范围。
  6. 尝试应急措施:如调整超时参数、增加内存、重启节点等,但应在业务低峰期操作。
  7. 升级版本:确认是否为已知 Bug,可参照社区或官方发布的修复版本进行升级。
  8. 联系技术支持:对于内部错误码(如 4000、4001、4016、4388 等)或无法定位的问题,提供完整日志和复现步骤给 OceanBase 技术团队。
🏆 小结
错误码排查是数据库运维人员的必备技能。本章汇总了 OceanBase 4.x 生产环境中最常见的错误码及其详细处理方法,覆盖了超时、内存、连接、SQL 语法、事务、锁以及 Oracle 模式特有错误等多个方面。善用 ob_error 命令行工具可大幅提升排查效率。建议将本章内容与“12.2 性能抖动排查方法论”结合使用,形成“定点错误 → 系统性能”的完整诊断链路。
12.2 性能抖动排查方法论

性能抖动(Performance Jitter)是指数据库系统在正常负载下突然出现响应时间增大、吞吐量下降、CPU 飙升或连接堆积等现象,通常持续数秒至数分钟,随后自行恢复。与持续的性能瓶颈不同,抖动往往由后台任务、突发流量、锁冲突或资源竞争等瞬时因素触发。本章提供一套系统化的排查方法论,帮助 DBA 快速定位抖动根因并采取有效应对措施。

🎯 性能抖动的常见现象与根因分类
  • 响应时间突增:通常由 SQL 执行计划突变、锁等待、资源争抢(CPU/IO)或网络延迟导致。
  • CPU 使用率飙升:可能来源于不合预期的并行执行、低效 SQL(全表扫描)、后台 Compaction 任务或频繁的 SQL 硬解析。
  • 连接堆积/会话积压:常由慢查询阻塞、事务未及时提交、连接池配置不当或服务端线程资源耗尽引起。
  • 内存使用率持续攀升:可能存在内存泄漏(如 Plan Cache 膨胀)、MemStore 积压或 KVCache 异常。
  • 磁盘 I/O 突增:通常与转储(Minor Compaction)、合并(Major Compaction)、备份或迁移任务相关。

一、排查前置准备:构建可观测性基线

在发生抖动时,能够快速获取历史数据是定位问题的关键。建议提前部署以下监控手段:

  • OCP 监控大盘:配置租户级 QPS、RT、CPU 使用率、内存使用率、磁盘 IO 等关键指标的实时监控和告警。
  • 慢查询采集:开启 slow_query_log 并设置合理的 long_query_time(如 100ms),定期采集慢 SQL。
  • 系统日志保留策略:确保 observer.logrootservice.logelection.log 保留足够时长(建议至少 7 天),并配置日志轮转。
  • 定期快照:使用 GV$OB_SQL_AUDIT 定期采集 Top SQL 快照,便于事后对比。
  • 部署 obdiag 工具:通过 obdiag check 定期执行巡检,一键采集诊断信息。

二、排查流程(从整体到局部)

当收到抖动告警或用户反馈时,可按以下步骤系统化排查:

步骤 1:确认抖动的时间范围和影响范围
  • 询问业务方具体的抖动时间点(精确到分钟),收集应用日志中的错误和超时记录。
  • 通过 OCP 或系统视图查看该时间段的集群全局指标,判断是单个租户、单个节点还是整体集群问题。
  • 关键查询:SELECT * FROM oceanbase.GV$OB_SERVERS 查看节点状态;SELECT * FROM oceanbase.GV$OB_UNITS 查看租户资源分布。
步骤 2:关联后台任务(合并、转储、备份、迁移)

性能抖动最常见的诱因是后台任务的突增资源消耗。使用以下视图快速排查:

-- 查看合并任务(Major Compaction)进度
        SELECT * FROM oceanbase.DBA_OB_MAJOR_COMPACTION;

        -- 查看转储任务(Minor Compaction)状态
        SELECT * FROM oceanbase.GV$OB_COMPACTION_PROGRESS;

        -- 查看数据备份任务
        SELECT * FROM oceanbase.CDB_OB_BACKUP_JOBS;

        -- 查看 Unit 迁移任务
        SELECT * FROM oceanbase.DBA_OB_UNIT_JOBS;

判断标准:若抖动时间点与后台任务的开始/结束时间高度重合,则根因为后台任务干扰。可采取以下措施:

  • 调整合并时间窗口,将 major_freeze_duty_time 设置在业务低峰期。
  • 适当降低合并并发度(compaction_low_thread_score)或使用限流参数。
  • 若备份任务影响过大,可错峰调度或限制备份带宽(sys_bkgd_net_percentage)。
步骤 3:分析慢查询与执行计划突变

若抖动期间 QPS 并未明显下降,但延迟升高,通常是某些 SQL 执行计划劣化或出现了大查询。

-- 查找抖动时间点附近执行时间最长的 SQL
        SELECT request_time, elapsed_time, queue_time, execute_time, query_sql
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE request_time BETWEEN start_time_micros AND end_time_micros
        ORDER BY elapsed_time DESC
        LIMIT 20;

        -- 查看 SQL 的计划缓存信息,确认是否使用了错误的索引或 Join 顺序
        SELECT plan_id, outline_id, outline_data 
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_EXPLAIN 
        WHERE sql_id = '...';

常见原因与对策

  • 统计信息过旧:执行 ANALYZE TABLEDBMS_STATS.GATHER_TABLE_STATS 更新统计信息。
  • 索引失效:检查是否因数据分布变化导致优化器选择了全表扫描,使用 Outline 绑定执行计划。
  • 并行度不适当:若大查询开启了过高并行度(Auto DOP 评估过高),可通过 parallel_servers_target 限制总并行资源。
步骤 4:排查锁冲突与事务堵塞

长事务或未提交的事务会持有锁,阻塞后续操作,导致响应时间飙升。

-- 查看当前锁等待关系
        SELECT * FROM oceanbase.GV$OB_TRX_LOCK_WAIT;

        -- 查看长时间未提交的事务(例如超过 1 小时)
        SELECT trans_id, tenant_id, svr_ip, trans_start_time, trans_timeout
        FROM oceanbase.GV$OB_TRANSACTION
        WHERE trans_start_time < DATE_SUB(NOW(), INTERVAL 1 HOUR);

处理方式:若发现阻塞源头,可 kill 阻塞会话:KILL session_id;。同时检查应用代码,避免在事务中执行非必要操作,及时提交或回滚。

步骤 5:分析资源使用异常(CPU / 内存 / IO)
  • CPU 飙升:通过 top -H -p pidof observer 查看线程 CPU 占用,结合 observer.log 中的 trace_id 定位具体 SQL 或任务。
  • 内存持续增长:查询 GV$OB_MEMORY 跟踪各模块内存变化,重点关注 Plan CacheKVCacheSQL Work Area。若存在泄漏迹象,可临时执行 ALTER SYSTEM FLUSH PLAN CACHE; 释放内存。
  • 磁盘 IO 飙高:使用 iostat -x 1 观察磁盘 util 和 await,确认是否由合并、转储或备份引起。必要时调整合并限速参数 _ob_compaction_throttling(建议 0.2 或 0.5)。
-- 查看租户 MemStore 使用情况
        SELECT tenant_id, svr_ip, 
            round(ACTIVE_MEMSTORE_USED/1024/1024/1024,2) AS active_gb,
            round(TOTAL_MEMSTORE_USED/1024/1024/1024,2) AS total_gb,
            round(MEMSTORE_LIMIT/1024/1024/1024,2) AS limit_gb,
            round(TOTAL_MEMSTORE_USED/MEMSTORE_LIMIT*100,2) AS pct
        FROM oceanbase.GV$OB_MEMSTORE
        WHERE tenant_id = 1002;
步骤 6:网络抖动排查

跨节点部署的 OceanBase 集群对网络延迟敏感。若怀疑网络问题,可:

  • 检查 observer.log 中是否有大量 RPC timeoutconnection refused 日志。
  • 使用 pingnetstat -s 检测网络丢包和重传率。
  • 确认交换机、防火墙未做限流,且各节点时间同步偏差在 2 秒以内(使用 NTP/chrony)。

三、典型场景速查表

抖动特征优先排查方向常用诊断视图/命令应急措施
定时抖动(如每天凌晨 2 点) 每日合并(Major Compaction) DBA_OB_MAJOR_COMPACTION 调整合并时间窗口 major_freeze_duty_time,或临时手动合并错峰
不定期短时 CPU 飙升 转储(Minor Compaction)或备份 GV$OB_COMPACTION_PROGRESSCDB_OB_BACKUP_JOBS 降低 ha_low_thread_score 限制备份并发,或调整 _ob_compaction_throttling
某类 SQL 突然变慢 统计信息过期 / 执行计划突变 GV$OB_PLAN_CACHE_PLAN_STATGV$OB_SQL_AUDIT 手动收集统计信息,使用 Outline 绑定计划
TPS 下降且出现大量锁等待 长事务未提交 / 热点行更新冲突 GV$OB_TRX_LOCK_WAITGV$OB_TRANSACTION Kill 阻塞会话,优化事务大小和隔离级别
连接数突增且响应缓慢 慢查询堆积 / 连接池异常 SHOW PROCESSLISTGV$OB_SQL_AUDIT Kill 慢查询,重启 OBProxy 或应用连接池

四、自动化诊断工具:obdiag

obdiag 是 OceanBase 官方提供的敏捷诊断工具,可以一键采集系统状态、慢日志、ASH 报告等,大幅提升排查效率。以下命令可在抖动发生后快速获取诊断数据:

# 一键巡检集群(包含 OBServer、OBProxy 状态、参数检查等)
        obdiag check run

        # 采集当前系统状态快照(CPU、内存、磁盘、网络、进程等)
        obdiag gather sysstat

        # 采集性能报告(包含等待事件、Top SQL、锁等)
        obdiag gather perf

        # 采集慢查询日志(自动从 observer.log 和 obproxy_slow.log 提取)
        obdiag gather slowlog --since "1 hour ago"

        # 采集指定 trace_id 的全链路日志
        obdiag gather trace --trace_id "YB420ABF87A4-0005FEA36C9B5B7A"

        # 采集 ASH 报告(活跃会话历史)
        obdiag gather ash --from "2025-06-15 14:00:00" --to "2025-06-15 14:30:00" --report_type=HTML

五、应急处理与长期优化建议

⚡ 紧急止血措施
  • Kill 慢查询SHOW PROCESSLIST;KILL session_id;
  • 终止转储或合并ALTER SYSTEM CANCEL MINOR FREEZE TENANT = tenant_name;ALTER SYSTEM CANCEL MAJOR FREEZE TENANT = tenant_name;(需谨慎,可能导致存储空间膨胀)
  • 清理 Plan CacheALTER SYSTEM FLUSH PLAN CACHE;(会短暂增加硬解析,但能释放缓存内存)
  • 临时限制大查询并行度SET GLOBAL parallel_servers_target = 16;(降低并行执行总资源)
  • 重启问题节点(高危):仅当节点无主且业务已切流时使用,优先通过 ALTER SYSTEM STOP SERVER 优雅下线。
🏆 长期优化建议
  • 建立性能基线与告警:通过 OCP 配置 QPS、RT、CPU 使用率的动态阈值,及时预警。
  • 定期巡检关键配置:检查 major_freeze_duty_timemajor_compact_triggercompaction_low_thread_score 是否适配业务特征。
  • SQL 质量管控:上线前进行 SQL 审核,避免全表扫描、隐式转换、无分区键查询等问题。
  • 事务设计规范:控制单事务大小,避免长事务,合理使用 READ COMMITTED 隔离级别。
  • 定期收集统计信息:使用 DBMS_STATS 或维护窗口自动收集,确保优化器决策准确。
  • 资源隔离:对关键业务租户配置 cgroup 限制 CPU,使用资源组隔离后台任务与前台 SQL。
  • 归档与清理策略:定期清理历史备份、过期日志及回收站对象,避免磁盘空间满。
📌 常用诊断视图速查
  • GV$OB_SQL_AUDIT:SQL 执行历史(需开启审计)
  • GV$OB_PLAN_CACHE_PLAN_STAT:计划缓存命中率与执行统计
  • GV$OB_MEMORY / GV$OB_MEMSTORE:内存使用详情
  • GV$OB_TRANSACTION / GV$OB_TRX_LOCK_WAIT:事务与锁信息
  • GV$OB_COMPACTION_PROGRESS:Compaction 进度
  • DBA_OB_UNITS / GV$OB_SERVERS:资源分配与节点状态
  • GV$OB_SQL_PLAN_MONITOR:实时执行计划监控(并行查询详情)
12.3 日志分析与调试技巧

日志是数据库运维和问题诊断的核心数据源。OceanBase 数据库提供了多层次、多模块的日志体系,覆盖系统运行、选举、Root Service、SQL 审计等各个维度。掌握日志分析方法,能够帮助 DBA 快速定位故障根因、追溯历史事件、分析性能瓶颈。本章系统介绍 OceanBase 4.x 的日志体系、配置方法、分析技巧以及常用调试工具。

📁 OceanBase 日志文件概览

OceanBase 日志位于每个 OBServer 节点的 ${home}/log 目录下,主要日志文件包括:

  • observer.log:主日志文件,记录 SQL 执行、事务、存储、RPC 等各类运行时信息,是排查问题的首要目标。
  • election.log:选举模块日志,记录 Paxos 选举相关事件,用于排查 Leader 切换、无主等问题。
  • rootservice.log:Root Service 日志,记录集群管理、租户管理、负载均衡等操作。
  • trace.log:全链路追踪日志,用于记录分布式调用链路,需要主动开启。
  • slow_query.log:慢查询日志(若开启),记录超过阈值的慢 SQL。
  • obproxy.log:OBProxy 日志(独立组件),记录代理层的连接、路由和错误信息。

一、日志级别与动态调整

OceanBase 支持标准的日志级别(从低到高):DEBUG < TRACE < INFO < WARN < USER_ERR < ERROR。默认级别为 INFO,生产环境不建议调低到 DEBUG,以免产生大量日志影响性能。此外,OceanBase 还提供了 WDIAGEDIAG 两个特殊诊断级别,分别用于记录普通诊断信息和错误诊断信息。

查看当前日志级别
-- 系统租户下查看日志级别配置
        SHOW PARAMETERS LIKE 'syslog_level';

        -- 查看所有模块的日志级别(结果格式:module_name:level)
        SELECT * FROM oceanbase.GV$OB_LOG_LEVEL;
动态调整日志级别(无需重启)

可以针对特定模块调整日志级别,用于临时调试。例如,将 SQL 执行模块的日志级别调整为 DEBUG:

-- 设置 SQL 执行模块日志级别为 DEBUG
        ALTER SYSTEM SET syslog_level = 'sql.executor:DEBUG';

        -- 恢复全局默认 INFO 级别(所有模块)
        ALTER SYSTEM SET syslog_level = 'INFO';

        -- 同时调整多个模块
        ALTER SYSTEM SET syslog_level = 'sql.executor:DEBUG,storage.compaction:WARN';
⚠️ 注意:DEBUG/TRACE 级别会产生大量日志,仅建议在问题复现期间短时开启,使用后立即恢复。

二、日志分析与关键信息提取

1. 使用 trace_id 全链路追踪

OceanBase 为每个 SQL 请求分配一个全局唯一的 trace_id(格式类似 YB42C00031AB-0005E720D5FF1D2A)。该 ID 会贯穿整个分布式执行链路,出现在相关节点的日志中。通过 trace_id 可以在多个节点的日志中关联同一请求的完整执行路径。

-- 在 SQL 审计视图中获取 trace_id
        SELECT trace_id, elapsed_time, query_sql 
        FROM oceanbase.GV$OB_SQL_AUDIT 
        WHERE request_time > UNIX_TIMESTAMP() * 1000000 - 3600000000
        LIMIT 10;

        -- 在 observer.log 中搜索 trace_id(所有节点)
        grep "trace_id=YB42C00031AB-0005E720D5FF1D2A" /home/admin/oceanbase/log/observer.log*
2. 根据错误码快速定位

日志中经常出现形如 ret=-4012ERROR 4012 的错误码。结合错误码查阅文档或本章 12.1 节,可快速了解问题类型。在日志中搜索错误码并查看前后上下文,通常能发现触发条件。

3. 分析合并与转储相关日志

合并和转储相关日志通常包含关键词 compactionminor freezemajor freeze。通过查看这些日志可以判断合并是否卡住、超时或资源不足。

# 查看合并进度相关日志
        grep -E "major freeze|compaction" /home/admin/oceanbase/log/observer.log | tail -100

        # 查找合并超时或失败信息
        grep -E "major freeze.*failed|compaction.*timeout" /home/admin/oceanbase/log/observer.log
4. 定位网络或 RPC 问题

搜索 RPC timeoutconnection refusedsend error 等关键词,可发现节点间通信异常。结合 election.log 排查选举相关网络问题。

三、日志轮转与清理策略

日志文件无限增长可能导致磁盘写满,OceanBase 提供了自动轮转和回收机制。

-- 开启系统日志自动回收
        ALTER SYSTEM SET enable_syslog_recycle = True;

        -- 设置保留的日志文件最大数量(默认 300)
        ALTER SYSTEM SET max_syslog_file_count = 500;

        -- 手动执行日志清理(如删除 7 天前的日志)
        # 在操作系统层面可使用 logrotate 或自定义脚本,但注意不要直接 rm 正在写入的文件
📌 生产环境建议:将 enable_syslog_recycle 设置为 True,并配合 max_syslog_file_count 控制日志数量,同时监控磁盘使用率。

四、自动化诊断工具:obdiag

OceanBase 官方提供的 obdiag 工具能够一键采集诊断信息,自动分析常见问题,大幅提升排查效率。

常用命令
# 一键巡检集群(包含 OBProxy、OBServer 状态、参数检查等)
        obdiag check run

        # 按时间段批量收集日志(核心命令)
        obdiag gather log --from "2025-06-15 14:00:00" --to "2025-06-15 15:00:00"

        # 使用 since 参数收集最近一段时间内的日志
        obdiag gather log --since "30m" --scope observer

        # 根据关键词筛选日志
        obdiag gather log --since "1h" --grep "ERROR" --scope observer

        # 收集集群主机信息(dmesg、CPU、内存等)
        obdiag gather log --scope host --since "5m"

        # 收集指定 trace_id 的全链路日志
        obdiag gather log --trace_id YB42C00031AB-0005E720D5FF1D2A

        # 收集系统状态快照(CPU、内存、磁盘、网络、进程等)
        obdiag gather sysstat

其中 obdiag gather log 支持灵活的收集范围,可通过 --scope 指定日志类型(observer/election/rootservice/all),通过 --since 指定相对时间,通过 --from/--to 指定绝对时间。obdiag 会生成压缩包和 HTML 格式的报告,包含关键日志摘要和优化建议,非常适合快速初筛。

五、高级调试技巧

1. 开启特定模块的 DEBUG 日志

当需要深入了解某个功能(如 SQL 优化器、存储引擎)的内部行为时,可临时调整对应模块的日志级别。注意先在测试环境验证。

-- 开启 SQL 优化器 DEBUG 日志
        ALTER SYSTEM SET syslog_level = 'sql.optimizer:DEBUG';

        -- 开启事务模块 TRACE 日志
        ALTER SYSTEM SET syslog_level = 'transaction:TRACE';
2. 使用 ob_admin 解析底层日志

ob_admin 是 OceanBase 提供的底层运维工具,内置了 clog_tool 模块,可以解析事务提交日志(clog)和 SSTable,用于排查数据不一致、数据丢失等深层次问题。常用命令如下:

# 解析并打印指定的 clog 文件(基础命令)
        ./ob_admin clog_tool dump_log 

        # 以类 SQL 格式解析 clog,提升可读性
        ./ob_admin clog_tool dump_format 

        # 按十六进制格式打印日志(处理不可打印字符)
        ./ob_admin clog_tool dump_hex 

        # 按过滤条件解析 clog(例如按 table_id 过滤)
        ./ob_admin clog_tool dump_filter 'table_id=1100611139457011' 

dump_format 命令为例,它会将日志解析为类 SQL 格式,输出的 trans_id、DML 类型、table_id 以及具体修改内容一目了然,是定位数据正确性问题的利器。使用 dump_hex 则可处理包含不可打印字符的日志。

3. debug_sync 同步点调试

OceanBase 提供了与 MySQL 兼容的 debug_sync 机制,允许在代码关键位置设置同步点,用于复现并发竞争条件。可以通过设置 Session 变量 debug_sync 或系统变量 ob_global_debug_sync 开启。该功能主要用于数据库内核开发调试,生产环境默认关闭。

4. 全链路追踪(Trace Log)

全链路追踪是 OceanBase 4.x 版本引入的强大诊断功能,能够追踪用户 SQL 请求在数据库全链路过程中,在不同组件、不同阶段执行的相关信息,并以可视化方式展现。

OceanBase 数据库企业版支持 Session 级别的全链路追踪,可通过 DBMS_MONITOR.OB_SESSION_TRACE_ENABLE 存储过程开启。该过程支持三个跟踪级别:Level 1 记录关键阶段信息;Level 2 在 Level 1 基础上增加更多细节;Level 3 输出最详细的内部调用栈,适用于深度问题诊断。

在 Oracle 模式下,可通过 DBMS_MONITOR 包控制追踪开关及日志打印细节。全链路追踪的日志会同时记录在 OBProxy 的 obproxy_trace.log 和 OBServer 的 trace.log 中,每个日志文件大小限制为 256MB。

六、日志分析典型案例

案例 1:SQL 超时(ret=-4012)

日志特征WARN [SQL] execute timeout, ret=-4012, query_timeout=... trace_id=...
分析步骤

  1. 根据 trace_id 在 observer.log 中搜索,确认该 SQL 在哪个节点执行。
  2. 查看同一 trace_id 的其他日志,判断是执行计划复杂还是等待锁资源。
  3. 若为执行计划问题,通过 GV$OB_PLAN_CACHE_PLAN_EXPLAIN 查看计划,优化 SQL 或调大超时参数。
案例 2:合并卡住(major freeze 长时间未完成)

日志特征major freeze progress 停滞在某个百分比,或出现 timeout waiting for tablet merge
分析步骤

  1. 查询 DBA_OB_MAJOR_COMPACTION 查看合并状态和卡住的分区。
  2. 在 observer.log 中搜索对应分区 ID 或 compaction 关键词,查看是否有错误(如磁盘满、内存不足)。
  3. 若长期卡住,可尝试手动取消合并:ALTER SYSTEM CANCEL MAJOR FREEZE TENANT = tenant_name;,然后重新触发。
案例 3:Leader 切换频繁(election.log 大量选举日志)

日志特征election.log 中出现 start electionbecome leaderleader lease expired 等高频事件。
分析步骤

  1. 检查网络延迟(ping 测试)和时间同步(ntpstat)。
  2. 查看节点 CPU 负载是否过高导致租约超时。
  3. 检查是否有节点频繁宕机或重启。
💡 日志分析最佳实践
  • 统一日志采集:将各节点的日志集中到日志中心(如 ELK、Splunk),便于跨节点检索。
  • 设置合理日志级别:生产环境保持 INFO/WDIAG,仅在排查问题时临时调低,用完及时恢复。
  • 定期归档清理:结合 enable_syslog_recycle 或外部工具,避免日志占满磁盘。
  • 善用 trace_id:在应用日志中打印 SQL 的 trace_id,出现问题时可将应用日志与数据库日志关联。
  • 使用 obdiag 自动化采集:复杂问题时直接运行 obdiag gather log 获取完整现场,提高沟通效率。
  • 组合使用 ob_admin:当常规日志分析无法定位时,使用 ob_admin clog_tool dump_format 从底层日志中还原操作细节,尤其适合排查数据一致性问题。

第13章:性能监控与诊断

13.1 SQL Audit 审计表详解

SQL Audit(SQL 审计)是 OceanBase 数据库提供的一项核心诊断功能,通过系统视图 GV$OB_SQL_AUDIT(从 V4.0.0 版本开始引入)记录集群中每一次 SQL 请求的完整执行信息,包括来源、执行状态、资源消耗、等待事件及执行各阶段耗时等。该视图是 SQL 性能诊断和问题排查的利器,能够帮助 DBA 快速定位慢查询、分析瓶颈、追踪问题 SQL 的全链路执行过程。

🎯 SQL Audit 的核心价值
  • 全量记录:记录每一条 SQL 请求(无论成功或失败),包括客户端信息、服务端信息、执行耗时、等待事件、返回码等。
  • 多维度关联:通过 TRACE_ID 关联日志,通过 SQL_ID 关联执行计划,通过 SID 关联会话,通过 TX_ID 关联事务,实现全链路追踪。
  • 阶段耗时分解:将 SQL 执行过程分解为网络传输、队列等待、计划生成、执行等多个阶段,精准定位耗时瓶颈。
  • 租户隔离:视图按租户拆分,系统租户可查看所有租户数据,普通租户只能查看本租户数据,保障数据安全。
  • 自动化诊断:结合 obdiag gather sql_audit 命令,可一键采集指定时间段的审计数据,离线分析性能问题。

一、启用与配置 SQL Audit

1. 开启 SQL Audit 功能

SQL Audit 功能默认开启,可通过集群级配置项 enable_sql_audit 全局控制。若需关闭或重新开启,执行以下命令:

-- 开启 SQL Audit(默认开启)
        ALTER SYSTEM SET enable_sql_audit = true;

        -- 关闭 SQL Audit(谨慎操作,关闭后将无法记录 SQL 审计信息)
        ALTER SYSTEM SET enable_sql_audit = false;

该配置项为布尔类型,默认值为 True,修改后无需重启 OBServer 节点,动态生效。

2. 租户级开关

除了集群级开关,还可以通过租户级系统变量 ob_enable_sql_audit 控制当前租户是否开启 SQL Audit 功能:

-- 开启当前租户的 SQL Audit
        SET GLOBAL ob_enable_sql_audit = true;

        -- 查看当前租户的 SQL Audit 状态
        SHOW VARIABLES LIKE 'ob_enable_sql_audit';

该变量默认值为 true,生效范围为 Global,支持通过 SET 语句修改。

3. 内存配额配置

SQL Audit 使用独立的内存空间,通过租户级系统变量 ob_sql_audit_percentage 控制 SQL Audit 功能占用租户内存的百分比。实际可用内存上限为 ob_sql_audit_percentage% × 租户内存,同时受限于约 1GB 的内部硬限制。

-- 设置 SQL Audit 内存占租户内存的百分比(默认 3%,取值范围 [0,80])
        SET GLOBAL ob_sql_audit_percentage = 3;

二、SQL Audit 淘汰机制

SQL Audit 的记录存放在可配置的内存空间中,内存有限,因此需要淘汰机制。每个租户的后台任务每隔 1 秒 检查内存使用情况,决定是否触发淘汰。淘汰机制如下:

SQL Audit 内存上限范围触发淘汰的条件停止淘汰的条件
[0, 64MB] 内存上限 × 50% 0 MB
[64MB, 100MB] 内存上限 − 20MB 内存上限 − 40MB
[100MB, 5GB] 内存上限 × 80% 内存上限 × 60%
[5GB, +∞) 内存上限 − 1GB 内存上限 − 2GB
记录数淘汰 900 万条 800 万条
💡 了解淘汰机制有助于合理设置 ob_sql_audit_percentage,避免因内存不足导致审计数据过早淘汰而丢失关键信息。

三、GV$OB_SQL_AUDIT 核心字段详解

该视图字段丰富,按功能分类介绍如下:

📌 请求标识字段
字段名说明 REQUEST_ID 请求 ID,标识一次请求,随时间递增,可作为游标拉取审计信息。 TRACE_ID SQL 请求的链路追踪 ID,可关联查询日志及其他监控指标。 SQL_ID SQL 的唯一标识,同一条 SQL 的多次执行具有相同的 SQL_ID。 PLAN_ID 执行计划 ID,可关联查询计划的详细信息和统计信息。 SID Session ID,对应 GV$OB_PROCESSLIST 视图中的 ID 字段。 TX_ID 事务 ID,可关联该事务的所有 SQL。 SQL_EXEC_ID 本次执行的 ID,用于区分同一 SQL 的不同执行。
📌 来源与目标字段
字段名说明
SVR_IP / SVR_PORT 接收请求的 OBServer 节点的 IP 和端口。
CLIENT_IP / CLIENT_PORT 客户端 IP 和端口。注意:通过 ODP 连接时,显示的是 ODP 的 IP;直连时,显示的是客户端 IP。
TENANT_ID / TENANT_NAME 发送请求的租户 ID 和名称。
USER_ID / USER_NAME 发送请求的用户 ID 和名称。
DB_ID / DB_NAME 数据库 ID 和名称。
📌 执行结果与性能指标字段
字段名说明
RET_CODE 执行结果返回码:0 表示成功,其他值表示对应的 OceanBase 错误码。
AFFECTED_ROWS 影响行数(如 UPDATE、DELETE 操作)。
RETURN_ROWS 返回行数(如 SELECT 操作)。
PARTITION_CNT 该请求涉及的分区数,反映 SQL 的分布式访问范围。
📌 时间分解字段(性能分析核心)

SQL 执行的完整耗时分解如下,单位为微秒:

  • ELAPSED_TIME:从请求到达到执行结束的总时间,由以下子阶段组成。
  • NET_TIME:发送 RPC 到接收到请求的时间。
  • NET_WAIT_TIME:接收到请求到进入队列的时间。
  • QUEUE_TIME:队列等待时间,反映当前租户的请求积压情况。若该值较大,说明系统负载高或租户线程资源不足。
  • DECODE_TIME:出队列后 Decode 请求的时间。
  • GET_PLAN_TIME:生成执行计划的时间,反映 Plan Cache 的健康状况。若该值较大,可能是 SQL 未命中 Plan Cache 导致硬解析。
  • EXECUTE_TIME:计划的实际执行时间,由 CPU 时间和等待时间组成。
  • TOTAL_WAIT_TIME_MICRO:执行过程中所有等待的总时间。
💡 性能分析技巧:通过对比 QUEUE_TIMEGET_PLAN_TIMEEXECUTE_TIME 等字段,可快速定位耗时瓶颈在哪个阶段——是排队积压?计划生成慢?还是执行本身慢?
📌 等待事件字段
字段名说明
EVENT 最长等待事件的名称。
WAIT_CLASS 等待事件所属的类别名称(如 Network、Concurrency、User IO 等)。
TOTAL_WAIT_TIME_MICRO 执行过程所有等待的总时间(微秒)。
TOTAL_WAITS 执行过程中总的等待次数。
RPC_COUNT 发送的 RPC 个数,反映分布式执行中跨节点交互的次数。
📌 执行计划相关字段
  • PLAN_TYPE:执行计划类型。1 表示本地计划(Local),2 表示远程计划(Remote),3 表示分布式计划(Distribute),0 表示无执行计划(如 COMMIT 语句)。本地计划性能最佳,远程次之,分布式最差。若分布式计划占比过高,需检查 SQL 是否命中分区裁剪或是否需要调整分区策略。
  • IS_HIT_PLAN:是否命中 Plan Cache。命中表示软解析,未命中表示硬解析,硬解析会增加耗时。若租户 Plan Cache 命中率过低,应检查 Plan Cache 空间是否过小导致计划频繁被淘汰。
  • PLAN_HASH:执行计划的 Hash 值,可用于判断同一 SQL_ID 的不同执行是否存在计划变化。

四、典型使用场景与 SQL 示例

场景 1:定位慢查询(按总耗时排序)
SELECT usec_to_time(request_time) AS request_time,
            elapsed_time/1000 AS elapsed_ms,
            execute_time/1000 AS exec_ms,
            queue_time/1000 AS queue_ms,
            plan_type,
            sql_id,
            query_sql
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE request_time > UNIX_TIMESTAMP() * 1000000 - 3600000000  -- 最近 1 小时
        ORDER BY elapsed_time DESC
        LIMIT 20;

通过 ELAPSED_TIME 快速找出执行时间最长的 SQL,并结合 QUEUE_TIMEEXECUTE_TIME 等字段初步判断瓶颈阶段。

场景 2:分析某类 SQL 的聚合统计(按 SQL_ID 分组)
SELECT sql_id,
            COUNT(*) AS exec_count,
            AVG(elapsed_time) AS avg_us,
            MAX(elapsed_time) AS max_us,
            AVG(queue_time) AS avg_queue_us,
            AVG(get_plan_time) AS avg_get_plan_us
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE request_time > UNIX_TIMESTAMP() * 1000000 - 3600000000
        GROUP BY sql_id
        ORDER BY avg_us DESC
        LIMIT 20;

统计各类 SQL 的平均耗时、最大耗时、排队时间等,识别需要优化的 SQL 类型。

场景 3:根据 TRACE_ID 全链路追踪
SELECT * FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE trace_id = 'YB420ABF87A4-0005FEA36C9B5B7A'\G

获取该 SQL 的完整执行信息,并在日志中搜索同一 TRACE_ID,实现应用与数据库端的全链路关联。

场景 4:分析 Plan Cache 命中率
SELECT COUNT(*) AS total,
            SUM(CASE WHEN is_hit_plan = 1 THEN 1 ELSE 0 END) AS hit_count,
            ROUND(SUM(CASE WHEN is_hit_plan = 1 THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS hit_rate
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE request_time > UNIX_TIMESTAMP() * 1000000 - 3600000000;

若命中率偏低,应检查 plan_cache_high_water_mark 等参数,合理分配 Plan Cache 内存空间。

场景 5:分析分布式计划占比
SELECT plan_type,
            COUNT(*) AS cnt,
            ROUND(COUNT(*) / SUM(COUNT(*)) OVER() * 100, 2) AS pct
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE request_time > UNIX_TIMESTAMP() * 1000000 - 3600000000
        AND plan_type IN (1, 2, 3)
        GROUP BY plan_type;

plan_type=3(分布式计划)占比过高,说明大量 SQL 涉及跨分区/跨节点访问,需检查分区键是否合理或分区裁剪是否生效。

场景 6:定位等待事件瓶颈
SELECT event, wait_class, COUNT(*) AS cnt,
            AVG(total_wait_time_micro) AS avg_wait_us,
            SUM(total_wait_time_micro) AS total_wait_us
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE request_time > UNIX_TIMESTAMP() * 1000000 - 3600000000
        AND total_wait_time_micro > 0
        GROUP BY event, wait_class
        ORDER BY total_wait_us DESC
        LIMIT 10;

找出导致总等待时间最长的等待事件类型,针对性地进行优化(如磁盘 IO 慢需调整存储、锁等待需排查事务冲突等)。

五、命令行一键采集 SQL Audit 数据

OceanBase 官方诊断工具 obdiag 支持一键采集 SQL Audit 数据,便于离线分析或留存历史快照。

# 采集最近 1 小时的 SQL Audit 数据
        obdiag gather sql_audit --since "1h"

        # 采集指定时间段的 SQL Audit 数据
        obdiag gather sql_audit --from "2025-06-15 14:00:00" --to "2025-06-15 15:00:00"

        # 仅采集慢查询(elapsed_time > 100ms)
        obdiag gather sql_audit --since "1h" --slow_query_threshold "100ms"

        # 按 SQL_ID 过滤采集
        obdiag gather sql_audit --since "1h" --sql_id "195E74D0ACE24FF470774DF649D62921"

执行后,obdiag 会生成一个压缩包,内含该时间段的 SQL Audit 数据(JSON 格式),可导入到其他分析工具或留存备查。

⚠️ 注意:obdiag gather sql_audit 需要 OceanBase V4.2.0 及以上版本支持,且要求目标租户已开启 SQL Audit 功能。

六、视图兼容性与版本说明

  • 视图名称变化:OceanBase 4.0.0.0 及以上版本使用 GV$OB_SQL_AUDIT,V3.x 及更早版本使用 GV$SQL_AUDIT。查询时需根据版本选择正确的视图名称。
  • 租户隔离:该视图按租户拆分。系统租户(sys)可以查询所有租户的数据;普通租户只能查询本租户的数据。
  • PL 请求的 SQL_ID 展示:从 V4.2.3 版本(V4.2.x)和 V4.3.2 版本(V4.3.x)开始,针对 CALL 语句执行的 PL 请求和通过匿名块执行的 PL 请求,SQL_ID 字段会展示为实际生成的 MD5 编码。
  • SID 字段的含义:在不同连接方式下有所差异——直连模式下为服务端 Session ID;ODP 模式下当 ODP 配置 client_session_id_version = 2 时为客户端 Session ID,否则为服务端 Session ID。

七、最佳实践与注意事项

📌 生产环境配置建议
  • 保持 SQL Audit 默认开启:SQL Audit 是性能诊断的重要工具,默认开启且内存占用可控(默认 3% 租户内存),建议保持开启状态。
  • 根据业务负载调整内存配额:对于高并发场景,若审计数据频繁被淘汰,可适当调大 ob_sql_audit_percentage(建议 5%~10%)。
  • 定期采集审计快照:结合 OCP 监控或 obdiag gather sql_audit 定期采集 SQL Audit 数据,便于事后回溯分析。
  • 关联分析提升效率:将 TRACE_IDSQL_IDPLAN_ID 与日志、执行计划视图关联使用,构建完整的问题诊断链路。
  • 关注淘汰阈值:若发现 SQL 审计数据缺失,应检查淘汰机制是否过于激进,适当调整 ob_sql_audit_percentage 或增加租户内存。
  • 测试环境可关闭以释放资源:在压测或性能测试环境中,如果对审计功能无需求,可临时关闭以释放内存资源。
🏆 小结
GV$OB_SQL_AUDIT 是 OceanBase 数据库 SQL 性能诊断的核心工具。通过该视图,DBA 可以全量记录每一次 SQL 请求的完整执行信息,支持多维度关联分析、阶段耗时分解和等待事件追踪。掌握 SQL Audit 的使用方法,是高效定位 SQL 性能问题、保障数据库稳定运行的关键能力。
13.2 性能视图与实时诊断(GV$OB_*)

OceanBase 数据库提供了一系列动态性能视图(Dynamic Performance Views),用于实时监控数据库的运行状态、资源消耗、会话活动、锁等待以及 SQL 执行情况等关键指标。这些视图是 DBA 进行性能诊断和问题排查的核心工具。本章系统梳理 OceanBase 4.x 中最常用的动态性能视图,介绍各视图的功能定位、关键字段含义以及典型使用场景。

📌 GV$ 与 V$ 视图说明
  • GV$OB_*(全局视图):以 GV$ 为前缀的视图用于显示整个集群中所有 OBServer 节点的运行状态和性能指标信息,提供全局视角。
  • V$OB_*(本地视图):以 V$ 为前缀的视图仅显示当前客户端所连接的 OBServer 节点的状态信息。
  • 租户隔离:绝大多数性能视图按租户拆分。系统租户(sys)可以查询所有租户的数据,普通租户只能查询本租户的数据。
  • 命令行一键采集:使用 obdiag gather sysstat 可一键采集系统状态快照,包含 CPU、内存、磁盘、网络及关键性能视图数据。

一、性能视图体系分类概览

OceanBase 数据库的性能视图覆盖多个维度,以下按功能类别进行划分:

📊 SQL 诊断类视图
视图名称核心用途
GV$OB_SQL_AUDIT SQL 执行审计记录,包含每一条 SQL 的详细执行信息、耗时分解、等待事件等。
GV$OB_PLAN_CACHE_PLAN_STAT 计划缓存中每个执行计划的统计信息,包括执行次数、平均耗时等。
GV$OB_PLAN_CACHE_PLAN_EXPLAIN 计划缓存中物理执行计划的树形结构详情,仅支持 GET 操作。
GV$OB_SQL_PLAN_MONITOR 慢查询的算子级统计信息,展示每个算子的实际执行数据。
GV$OB_PLAN_CACHE_STAT 计划缓存的整体统计信息,如命中率、内存占用等。
🔐 事务与锁相关视图
视图名称核心用途
GV$OB_TRANSACTION 当前运行中的事务信息。
GV$OB_TRANSACTION_PARTICIPANTS 活跃事务的参与者信息,从 V4.0.0 版本开始引入。
GV$OB_LOCKS 当前用户各表持锁或请求锁的情况,从 V4.2.0 版本开始引入。
GV$OB_TRX_LOCK_WAIT 事务锁等待关系,用于定位锁冲突和阻塞源头。
💾 内存与资源监控视图
视图名称核心用途 GV$OB_MEMORY 租户在各节点上的内存使用状况,按内存上下文(CTX)分类统计。 GV$OB_MEMSTORE MemTable 的内存使用状况,包括 Active 和 Frozen MemStore。 GV$OB_KVCACHE KV Cache 统计信息(如 Block Cache、Row Cache 等)。
⚙️ 后台任务与负载均衡视图
视图名称核心用途
GV$OB_COMPACTION_PROGRESS OBServer 节点级合并(Compaction)进度信息,从 V4.0.0 版本开始引入。
GV$OB_TABLET_COMPACTION_PROGRESS Tablet 级别的 Compaction 进度信息,仅显示正在运行的任务。
GV$OB_COMPACTION_DIAGNOSE_INFO Compaction 诊断信息,用于排查合并卡住问题。
GV$OB_UNITS Unit 信息视图,展示 Unit 规格、资源分配及状态。
GV$OB_SERVERS OBServer 节点信息,包括各节点的总资源和已分配资源。
👥 会话与等待事件视图
视图名称核心用途
GV$OB_PROCESSLIST 当前会话信息,与 MySQL 的 SHOW PROCESSLIST 类似。
GV$OB_SESSION_WAIT 会话当前或上一次的等待事件信息。
GV$OB_SESSION_EVENT 会话级别的等待事件统计汇总。
GV$OB_SYSTEM_EVENT 租户级别的等待事件统计汇总。

二、核心视图详解

1. GV$OB_SQL_AUDIT——SQL 审计核心视图

GV$OB_SQL_AUDIT 是 OceanBase 数据库中最常用的 SQL 监控视图,能够记录每一次 SQL 请求的来源、执行状态、资源消耗、等待事件等统计信息。从 V4.0.0 版本开始引入。该视图是按租户拆分的,系统租户可以查询所有租户的数据,普通租户只能查询本租户的数据。

核心字段说明

字段名说明分析价值
TRACE_ID 链路追踪 ID,贯穿整个分布式执行链路 关联日志和多个节点的执行信息,实现全链路追踪
SQL_ID SQL 的唯一标识 用于关联执行计划视图,同一 SQL 多次执行具有相同 SQL_ID
PLAN_ID 执行计划 ID 关联 GV$OB_PLAN_CACHE_PLAN_EXPLAIN 查看详细执行计划
ELAPSED_TIME SQL 执行总耗时(微秒) 定位慢查询的首要指标
QUEUE_TIME 请求在队列中的等待时间 反映租户线程资源是否充足
GET_PLAN_TIME 获取或生成执行计划的耗时 判断是否命中 Plan Cache,硬解析会增加该值
EXECUTE_TIME 计划的实际执行时间 SQL 执行本身耗时的核心指标
RET_CODE 执行结果返回码,0 表示成功 失败时可快速识别错误类型
PLAN_TYPE 计划类型:1=Local,2=Remote,3=Distribute 分布式计划占比过高说明分区裁剪可能失效
IS_HIT_PLAN 是否命中 Plan Cache 命中率偏低需检查 Plan Cache 空间配置
-- 查找最近 1 小时内最慢的 20 条 SQL
        SELECT usec_to_time(request_time) AS request_time,
            elapsed_time/1000 AS elapsed_ms,
            queue_time/1000 AS queue_ms,
            execute_time/1000 AS exec_ms,
            sql_id, plan_type, query_sql
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE request_time > UNIX_TIMESTAMP() * 1000000 - 3600000000
        ORDER BY elapsed_time DESC
        LIMIT 20;

        -- 分析 SQL 的执行耗时构成
        SELECT trace_id, elapsed_time, queue_time, get_plan_time, execute_time
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE sql_id = 'your_sql_id'
        ORDER BY request_time DESC
        LIMIT 10;
2. GV$OB_PLAN_CACHE_PLAN_STAT——计划缓存统计

该视图展示当前租户在所有 OBServer 节点上的计划缓存中缓存的每一个对象的状态,从 V4.0.0 版本开始引入。它不仅缓存了 SQL 计划对象,还缓存了 PL 对象(如匿名块、Package 以及 Function)。

核心字段说明

  • SQL_ID:缓存对象对应的 SQL ID,如果是 PL 对象则为 NULL。
  • TYPE:缓存对象的类型——对于 SQL 计划:1=Local Plan,2=Remote Plan,3=Distribute Plan;对于 PL 对象:1=Procedure,2=Function,3=Package,4=Anonymous Block。
  • EXECUTIONS:执行次数。
  • AVG_EXE_USEC:平均执行时间(微秒)。
  • HIT_COUNT:被命中次数,反映该计划的使用频率。
  • PLAN_SIZE:缓存对象占用的内存大小。
  • SLOW_COUNT:当前 SQL 计划成为慢查询的次数。
-- 查询执行次数最多的 SQL 计划
        SELECT sql_id, type, executions, avg_exe_usec/1000 AS avg_ms, hit_count
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
        WHERE sql_id IS NOT NULL
        ORDER BY executions DESC
        LIMIT 20;

        -- 查看计划缓存内存占用(按租户聚合)
        SELECT tenant_id, SUM(plan_size) AS total_plan_cache_bytes
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
        GROUP BY tenant_id;
3. GV$OB_PLAN_CACHE_PLAN_EXPLAIN——物理执行计划详情

该视图用于展示缓存在所有 OBServer 节点计划缓存中的物理执行计划树形结构,从 V4.0.0 版本开始引入。该视图仅支持 GET 操作,查询时需要指定 PLAN_ID 字段

-- 步骤1:从 PLAN_STAT 视图获取 PLAN_ID
        SELECT PLAN_ID, SQL_ID FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT LIMIT 5;

        -- 步骤2:根据 PLAN_ID 查询物理执行计划树
        SELECT plan_line_id, operator, name, rows, cost, property
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_EXPLAIN
        WHERE plan_id = 605
        ORDER BY plan_line_id;
💡 计划缓存视图的核心差异PLAN_STAT 侧重于计划的执行统计(执行次数、平均耗时等),而 PLAN_EXPLAIN 侧重于计划的形状(算子树结构)。两者配合使用,可以完整分析 SQL 的执行行为和执行路径。
4. GV$OB_TRANSACTION_PARTICIPANTS——活跃事务参与者信息

该视图记录整个集群中未结束事务的参与者上下文信息,包括 Leader 节点和 Follower 节点,从 V4.0.0 版本开始引入。通过该视图可以构造出当前整个事务的执行状态,尤其适用于长事务排查。

核心字段说明

  • TX_ID:事务 ID,用于唯一标识一个事务。
  • SESSION_ID:会话 ID。
  • SCHEDULER_ADDR:Scheduler 所在节点地址。
  • STATE:事务提交状态。
  • ACTION:最后执行的动作。
  • TX_EXPIRED_TIME:事务超时时间点。
-- 查找运行超过 1 小时的未提交事务
        SELECT tx_id, session_id, scheduler_addr, state, action,
            ctx_create_time, tx_expired_time
        FROM oceanbase.GV$OB_TRANSACTION_PARTICIPANTS
        WHERE ctx_create_time < DATE_SUB(NOW(), INTERVAL 1 HOUR);
5. GV$OB_LOCKS——锁持有与等待信息

该视图用于显示当前用户各表持锁或请求锁的情况,从 V4.2.0 版本开始引入。核心字段说明

  • TRANS_ID:持有或请求锁的事务 ID。
  • TYPE:锁类型——TM(表锁)、TX(事务锁)、TR(行锁)。
  • LMODE:持有的锁模式:NONE/SS(行共享)/SX(行独占)/S(共享)/SSX(共享行独占)/X(独占)。
  • REQUEST:请求的锁模式,格式同上。
  • BLOCK:是否被阻塞。0 表示当前事务持有锁;1 表示当前事务正在等待锁。
  • CTIME:持有或等待锁的时间(微秒)。
-- 查找锁阻塞关系
        SELECT wait.trans_id AS blocked_trans,
            holder.trans_id AS blocking_trans,
            wait.ctime AS wait_us,
            wait.lmode, wait.request
        FROM oceanbase.GV$OB_LOCKS wait
        JOIN oceanbase.GV$OB_LOCKS holder
        ON wait.object = holder.object
        WHERE wait.block = 1 AND holder.block = 0;
💡 锁事件与等待事件:OceanBase 提供了独立的锁事件监控体系,已有 100 多个锁事件,可通过 GV$OB_SYSTEM_EVENT(租户级)和 GV$OB_SESSION_EVENT(会话级)查询,在条件中加上 where event like 'latch:%' 即可筛选锁相关等待。
6. GV$OB_MEMORY——内存使用明细

该视图展示当前租户在所有节点上的内存使用状况,按内存上下文(CTX)和模块(MOD)分类统计,从 V4.0.0 版本开始引入。注意:该视图当前仅 MySQL 模式支持,Oracle 模式下查询结果为空。

-- 查看租户内存分布(按 CTX_NAME 聚合)
        SELECT tenant_id, svr_ip, ctx_name,
            ROUND(hold/1024/1024/1024, 2) AS hold_gb
        FROM oceanbase.GV$OB_MEMORY
        WHERE tenant_id = 1002 AND hold > 0
        ORDER BY hold DESC
        LIMIT 20;
7. GV$OB_MEMSTORE——MemStore 内存监控

该视图展示所有服务器上所有租户的 MemTable 内存使用状况,从 V4.0.0 版本开始引入。⚠️ 视图名 GV$SERVER_MEMSTORE 从 V4.0.0 版本开始更名为 GV$OB_MEMSTORE,使用原视图名将会报错。

核心字段说明

  • ACTIVE_SPAN:活跃 MemTable 占用内存的估计值(可能偏大)。
  • MEMSTORE_USED:MemTable 目前使用的总内存大小。
  • MEMSTORE_LIMIT:MemTable 的内存大小限制。
  • FREEZE_TRIGGER:触发 MemTable 冻结的内存大小。
  • FREEZE_CNT:MemTable 的冻结次数。
-- 查询租户 MemStore 使用情况
        SELECT tenant_id, svr_ip,
            ROUND(active_span/1024/1024/1024, 2) AS active_gb,
            ROUND(memstore_used/1024/1024/1024, 2) AS used_gb,
            ROUND(memstore_limit/1024/1024/1024, 2) AS limit_gb,
            ROUND(memstore_used/memstore_limit*100, 2) AS pct
        FROM oceanbase.GV$OB_MEMSTORE
        WHERE tenant_id > 1000
        ORDER BY pct DESC;
8. GV$OB_COMPACTION_PROGRESS——节点级合并进度

该视图展示租户的 OBServer 节点级合并进度信息,从 V4.0.0 版本开始引入。

核心字段说明

  • TYPE:Compaction 类型——MINI_MERGE(转储)、MINOR_MERGE(Minor 合并)、MAJOR_MERGE(租户级合并)、MEDIUM_MERGE(分区级合并)、META_MAJOR_MERGE(元数据合并)。
  • STATUS:合并状态(如 NODE_RUNNING)。
  • TOTAL_TABLET_COUNT:Tablet 总数。
  • UNFINISHED_TABLET_COUNT:未完成的 Tablet 数量。
  • START_TIME/ESTIMATED_FINISH_TIME:开始/预计完成时间。
-- 查看正在进行的合并任务
        SELECT svr_ip, type, status, total_tablet_count,
            unfinished_tablet_count,
            start_time, estimated_finish_time
        FROM oceanbase.GV$OB_COMPACTION_PROGRESS
        WHERE status = 'NODE_RUNNING';
9. GV$OB_UNITS——Unit 资源信息

该视图展示租户所在 OBServer 节点的 Unit 信息,系统租户可查看本集群所有 OBServer 节点的 Unit 信息,从 V4.0.0 版本开始引入。

核心字段说明

  • UNIT_ID:Unit ID。
  • TENANT_ID:租户 ID。
  • ZONE:所在 Zone。
  • MAX_CPU/MIN_CPU:CPU 规格上限和下限。
  • MEMORY_SIZE:内存大小(字节)。
  • MAX_IOPS/MIN_IOPS/IOPS_WEIGHT:IOPS 规格。
  • LOG_DISK_SIZE/LOG_DISK_IN_USE:日志盘规格和已使用。
  • DATA_DISK_IN_USE:已使用的数据盘大小(字节)。
  • STATUS:Unit 状态(NORMAL/MIGRATING IN/MIGRATING OUT/MARK DELETING/WAIT GC/DELETING/ERROR)。
  • REPLICA_TYPE:副本类型,默认为 FULL。
-- 查询租户资源分配情况
        SELECT unit_id, tenant_id, zone, svr_ip,
            max_cpu, min_cpu,
            ROUND(memory_size/1024/1024/1024, 2) AS memory_gb,
            ROUND(log_disk_size/1024/1024/1024, 2) AS log_disk_gb,
            ROUND(log_disk_in_use/1024/1024/1024, 2) AS log_disk_used_gb,
            ROUND(data_disk_in_use/1024/1024/1024, 2) AS data_disk_used_gb,
            status
        FROM oceanbase.GV$OB_UNITS
        WHERE tenant_id = 1002;

三、命令行一键采集性能视图数据

使用 OceanBase 敏捷诊断工具 obdiag 可一键采集系统状态快照,自动收集 CPU、内存、磁盘、网络以及关键性能视图(如 GV$OB_SERVERSGV$OB_UNITSGV$OB_SQL_AUDIT 等)的数据,生成压缩报告。

# 采集系统状态快照(包含性能视图)
        obdiag gather sysstat

        # 采集性能报告(包含等待事件、Top SQL、锁等)
        obdiag gather perf

        # 采集指定时间段的 SQL Audit 数据
        obdiag gather sql_audit --since "1h"

四、实时诊断最佳实践

📌 性能问题排查路径速查
  • 慢 SQL 诊断GV$OB_SQL_AUDITGV$OB_PLAN_CACHE_PLAN_EXPLAIN → 分析耗时阶段和执行计划。
  • 锁冲突定位GV$OB_LOCKS(BLOCK=1)→ 找到阻塞事务 → KILL 或优化逻辑。
  • 内存不足排查GV$OB_MEMORY(按 CTX_NAME 聚合)→ GV$OB_MEMSTORE(MemStore 检查)→ 按需调整参数或扩容。
  • 合并卡住排查GV$OB_COMPACTION_PROGRESS(STATUS 和 UNFINISHED_TABLET_COUNT)→ GV$OB_COMPACTION_DIAGNOSE_INFO → 定位原因。
  • 活跃会话分析GV$OB_PROCESSLIST(查看当前会话)→ 排查长事务或空闲事务。
  • 等待事件分析GV$OB_SYSTEM_EVENT(租户级汇总)→ 找出占比最高的等待事件类别。

五、视图使用注意事项

  • 视图命名变化:OceanBase 4.x 版本中,大量性能视图名增加了 _OB 中缀(如 GV$SQL_AUDITGV$OB_SQL_AUDIT),查询时需使用新视图名。
  • 租户隔离:性能视图按租户拆分。系统租户可以查询所有租户的数据,普通租户只能查询本租户的数据。
  • Oracle 模式限制:部分视图(如 GV$OB_MEMORY)在 Oracle 模式下查询结果为空。
  • 版本差异:不同版本中新视图的引入时间和字段行为存在差异,请以对应版本的官方文档为准。例如 GV$OB_SESSION 从 V4.3.0 开始引入,GV$OB_LOCKS 从 V4.2.0 开始引入。
13.3 等待事件分析与系统统计

等待事件(Wait Event)是 OceanBase 数据库性能诊断的核心工具。工作线程在任何时刻要么处于 CPU 计算状态,要么在等待资源(如磁盘 I/O、网络、锁、临界区等),等待事件正是记录这些等待行为的计数器。通过分析等待事件的类型、频率和耗时,DBA 可以快速定位系统瓶颈,判断性能问题是源于 I/O 瓶颈、锁争用、网络延迟还是其他资源竞争。本章系统介绍 OceanBase 数据库的等待事件体系、核心视图以及系统统计指标的使用方法。

🎯 等待事件分析的三大典型场景
  • CPU 利用率长期偏低:系统负载增加但 CPU 利用率不升,通常由并发瓶颈(如热点临界区争用)导致,可通过等待事件分析定位。
  • SQL 响应时间偶发尖刺:大部分 SQL 响应时间在 1ms 以内,但偶尔出现 >100ms 的尖刺,通常因 SQL 执行过程中被某些等待事件阻塞导致。
  • 系统突然停止执行:通常由锁阻塞或死锁导致,通过查看会话当前等待事件可定位阻塞源头。

一、等待事件的属性与分类

📊 等待事件的核心属性

每个等待事件包含以下关键属性:

  • 等待事件名称:标识具体等待的资源或操作类型。
  • 等待时间:该次等待的持续时间(微秒级)。
  • 参数 1/2/3 (p1/p2/p3):三个额外参数提供等待事件的上下文信息,例如等待的对象地址、等待次数阈值等,有助于深入定位问题原因。

通过查询 V$EVENT_NAME 视图,可以获取所有等待事件的名称及其所属类型。该视图支持 MySQL 和 Oracle 两种兼容模式,从 V1.4 版本开始引入。

-- 查看所有等待事件及其类型(MySQL 模式)
        SELECT name, wait_class FROM V$EVENT_NAME ORDER BY name;

        -- 查看所有等待事件及其类型(Oracle 模式)
        SELECT name, wait_class FROM V$EVENT_NAME ORDER BY name;
💡 OceanBase 数据库目前已有 11 个大类,300 多个等待事件,涵盖网络、调度、临界区、锁、集群、事务提交、用户 I/O 和系统 I/O 等。
📂 等待事件分类体系

每个等待事件都属于一个特定的类别,通过类别可以快速缩小问题分析范围。等待事件分类如下:

类型名称ID描述
OTHER 100 不属于其他类型的等待事件。
APPLICATION 101 由于客户端代码导致的等待事件。
CONFIGURATION 102 由于数据库或实例资源配置不足而导致的等待事件。
ADMINISTRATIVE 103 由于数据库管理员输入命令导致用户等待的等待事件。
CONCURRENCY 104 等待数据库内部资源的等待事件。
COMMIT 105 在日志提交有关的等待事件。
IDLE 106 会话处于非活动状态,等待任务的等待事件(通常可忽略)。
NETWORK 107 与网络通信相关的等待事件。
USER_IO 108 等待用户 I/O 的等待事件(如缓存缺页)。
SYSTEM_IO 109 等待后台进程 I/O 的等待事件(如合并操作)。
CLUSTER 111 与集群相关的等待事件。

当 CPU 使用率长期偏低时,应重点关注 CONCURRENCY 类型的等待事件(如 Latch 锁等待);当 SQL 响应时间偶发尖刺时,应重点关注 COMMIT 和 USER_IO 类型;当系统执行突然停止时,应优先排查 CONCURRENCY 类型的锁等待事件。

二、等待事件核心监控视图

OceanBase 数据库通过以下核心视图展示等待事件的统计信息和明细记录,覆盖了租户级统计、会话级统计、当前等待明细和历史等待明细四个维度,所有视图均从 V1.4 版本开始引入:

1. GV$OB_SYSTEM_EVENT(租户级等待统计)

用于展示租户级别的等待事件统计信息,包括总等待次数、总超时次数、总等待时间和平均等待时间。以租户为粒度进行统计,是分析系统整体瓶颈的首选视图。

核心字段说明

  • EVENT:等待事件名称。
  • WAIT_CLASS:等待事件所属类别。
  • TOTAL_WAITS:该等待事件的总等待次数。
  • TOTAL_TIMEOUTS:该等待事件的总超时次数。
  • TIME_WAITED_MICRO:该等待事件的总等待时间(微秒)。
  • AVERAGE_WAIT:平均等待时间(10 毫秒为单位)。
-- 查看租户级别等待事件统计(按总等待时间降序)
        SELECT EVENT, WAIT_CLASS, TOTAL_WAITS, TIME_WAITED_MICRO/1000 AS total_wait_ms
        FROM oceanbase.GV$OB_SYSTEM_EVENT
        WHERE WAIT_CLASS != 'IDLE'  -- 过滤空闲等待事件
        ORDER BY TIME_WAITED_MICRO DESC
        LIMIT 20;
2. GV$OB_SESSION_EVENT(会话级等待统计)

类似于 GV$OB_SYSTEM_EVENT,但展示每个会话的等待事件统计信息,适用于定位特定会话的性能瓶颈。

-- 查找等待时间最长的会话(最近 1 小时有活动的会话)
        SELECT SID, EVENT, TOTAL_WAITS, TIME_WAITED_MICRO/1000 AS total_wait_ms
        FROM oceanbase.GV$OB_SESSION_EVENT
        WHERE TIME_WAITED_MICRO > 0
        ORDER BY TIME_WAITED_MICRO DESC
        LIMIT 20;
3. GV$OB_SESSION_WAIT(会话当前/上一次等待明细)

用于展示每个会话当前正在等待的事件;若会话当前未在等待,则展示最近一次发生的等待事件。该视图是定位“系统突然停止执行”类问题的首要工具。

STATE 字段说明

  • Waiting:会话正等待该事件。
  • Waited unknown time:由于设置了 timed_statistics 值为 false,无法获得时间信息。
  • Waited short time:等待已完成,等待时间很短,精确时间可用。
  • Waited known time:等待已完成,等待时间精确。
-- 查看当前所有会话的等待明细(过滤空闲等待)
        SELECT SID, EVENT, WAIT_CLASS, STATE, 
            WAIT_TIME_MICRO/1000 AS wait_ms, 
            P1TEXT, P1, P2TEXT, P2, P3TEXT, P3
        FROM oceanbase.GV$OB_SESSION_WAIT
        WHERE WAIT_CLASS != 'IDLE'
        ORDER BY WAIT_TIME_MICRO DESC
        LIMIT 20;
4. GV$OB_SESSION_WAIT_HISTORY(会话最近10次等待历史)

展示每个会话历史上最近发生的 10 次等待事件信息。当问题偶发、难以实时捕获时,该视图提供了事后回溯分析能力,可用于排查偶发性性能毛刺。

-- 查看某会话最近 10 次等待历史
        SELECT SID, EVENT, WAIT_CLASS, WAIT_TIME_MICRO/1000 AS wait_ms, 
            P1TEXT, P1, P2TEXT, P2, P3TEXT, P3
        FROM oceanbase.GV$OB_SESSION_WAIT_HISTORY
        WHERE SID = 1001
        ORDER BY SEQ# DESC
        LIMIT 10;
📌 等待统计与等待明细的区别
  • 等待统计(GV$OB_SYSTEM_EVENT / GV$OB_SESSION_EVENT):展示累计的等待次数和总时间,适合分析历史趋势和整体瓶颈定位。
  • 等待明细(GV$OB_SESSION_WAIT / GV$OB_SESSION_WAIT_HISTORY):展示单次等待的具体细节,包括 3 个参数,适合定位偶发性问题的根因。

三、锁事件(Latch)分析

锁事件(Latch)是 OceanBase 数据库内部用于保护临界区的同步机制(区别于用户事务锁)。当高并发访问热点临界区时,工作线程可能进入等待,从而影响系统性能。锁事件的经典实现是:调用者先尝试加锁,如果加不上,则进入 SPIN 过程反复尝试(避免过早放弃 CPU,对于短临界区非常有效);如果 SPIN 次数达到上限后仍无法获得锁,则进入 WAIT 过程,将自己放入等待队列并让出 CPU。

锁事件本质是等待事件的一种,所有的锁事件都属于同一类等待事件(WAIT_CLASS 为 CONCURRENCY),但是锁事件相比一般的等待事件,具有更多的监控信息。每个锁事件都对应一个临界区,调用者可以通过锁事件的 SPIN 次数和调用次数之比来识别热点临界区,并决定是否采取优化手段。一个设计良好的并发系统,锁事件的 SPIN 次数和调用次数之比应该越小越好。

锁事件相关的视图包括:

  • GV$OB_SYSTEM_EVENT:租户级锁事件统计(过滤 event like 'latch:%')。
  • GV$OB_SESSION_EVENT:会话级锁事件统计。
  • GV$OB_SESSION_WAIT:会话级锁事件明细。
  • GV$OB_SESSION_WAIT_HISTORY:会话级锁事件历史明细。
  • GV$OB_LATCH:锁调用统计,展示加锁次数、SPIN 次数、等待次数等,从 V1.4 版本开始引入。

GV$OB_LATCH 核心字段说明

字段说明
GETS lock 成功次数。
MISSES lock 进入等待的次数。
SPIN_GETS 总共 spin 次数。
SLEEPS 总共 yield 的次数。
WAIT_TIME 等待 sleep 时间。
-- 查询租户级别的锁事件统计(按总等待时间排序)
        SELECT EVENT, WAIT_CLASS, TOTAL_WAITS, TIME_WAITED_MICRO/1000 AS total_wait_ms
        FROM oceanbase.GV$OB_SYSTEM_EVENT
        WHERE EVENT LIKE 'latch:%'
        ORDER BY TIME_WAITED_MICRO DESC
        LIMIT 20;

        -- 查询各临界区的锁调用统计(识别热点临界区)
        -- SPIN_GETS/GETS 比例过高说明锁争用严重
        SELECT NAME, GETS, MISSES, SPIN_GETS, SLEEPS,
            ROUND(SPIN_GETS / NULLIF(GETS, 0) * 100, 2) AS spin_ratio_pct
        FROM oceanbase.GV$OB_LATCH
        WHERE GETS > 0
        ORDER BY SPIN_GETS/GETS DESC
        LIMIT 10;

四、系统统计指标(GV$OB_SYSSTAT)

GV$OB_SYSSTAT 视图用于展示各租户在所有 OBServer 节点上的系统级统计事件信息,是计算核心性能指标(如 QPS、TPS、IOPS、缓存命中率等)的数据源。使用该视图前需要将配置项 enable_perf_event 的值设置为 True,该配置项控制是否开启性能事件统计功能。该视图从 V1.4 版本开始引入。

核心统计指标示例

  • sql select count:SELECT 语句总执行次数。
  • sql insert count:INSERT 语句总执行次数。
  • sql update count:UPDATE 语句总执行次数。
  • sql delete count:DELETE 语句总执行次数。
  • rpc packet in/out:RPC 收发的报文数量与字节数,反映集群网络负载。
  • rpc net delay:RPC 网络延迟统计(微秒)。
  • row cache hit / block cache hit:缓存命中次数,用于计算缓存命中率。
-- 查看租户 QPS(需在 t1 和 t2 分别采样,计算差值)
        SELECT SVR_IP, NAME, VALUE
        FROM oceanbase.GV$OB_SYSSTAT
        WHERE NAME IN ('sql select count', 'sql insert count', 'sql update count', 'sql delete count')
        ORDER BY SVR_IP, NAME;

        -- 计算缓存命中率
        SELECT 
            SUM(CASE WHEN NAME = 'row cache hit' THEN VALUE ELSE 0 END) AS row_cache_hit,
            SUM(CASE WHEN NAME = 'row cache miss' THEN VALUE ELSE 0 END) AS row_cache_miss,
            ROUND(SUM(CASE WHEN NAME = 'row cache hit' THEN VALUE ELSE 0 END) / 
                (SUM(CASE WHEN NAME = 'row cache hit' THEN VALUE ELSE 0 END) + 
                SUM(CASE WHEN NAME = 'row cache miss' THEN VALUE ELSE 0 END)) * 100, 2) AS hit_rate
        FROM oceanbase.GV$OB_SYSSTAT
        WHERE NAME IN ('row cache hit', 'row cache miss');
⚠️ GV$OB_SYSSTAT 视图中的统计值是从 OBServer 启动或配置项 enable_perf_event 开启时开始累计的。若需计算某段时间内的 QPS 或 TPS,需在时间窗口两端采样,计算差值除以时间差。同时,租户各节点聚合时需注意 Unit 分布,确保聚合范围正确。

五、命令行一键采集性能数据

OceanBase 敏捷诊断工具 obdiag 支持一键采集系统状态和等待事件统计信息,大幅提升排查效率。

# 采集系统状态快照(包含 CPU、内存、磁盘、网络、进程信息)
        obdiag gather sysstat

        # 采集性能报告(包含等待事件、Top SQL、锁等)
        obdiag gather perf

        # 按时间段采集日志并分析等待事件
        obdiag gather log --since "1h" --grep "wait event" --scope observer

六、等待事件分析最佳实践

1. 从整体到局部:先看租户级统计

使用 GV$OB_SYSTEM_EVENT 按总等待时间排序,识别占用等待时间最长的 TOP N 等待事件类型,初步判断瓶颈方向。

2. 结合等待类型缩小范围

根据等待事件的所属类别(WAIT_CLASS)进一步聚焦问题:

  • CONCURRENCY:锁或临界区争用,进一步分析锁事件统计(GV$OB_LATCH)。
  • USER_IO / SYSTEM_IO:磁盘 I/O 瓶颈,结合 iostat 等操作系统工具分析。
  • NETWORK:网络瓶颈,检查网络延迟和丢包率。
  • COMMIT:事务提交慢,检查日志同步延迟和磁盘性能。
  • CONFIGURATION:资源配置不足,检查内存、CPU、IOPS 规格。
3. 从统计到明细:定位具体会话

若发现某类等待事件占比异常,可通过 GV$OB_SESSION_WAIT 查看当前正在等待的会话及其参数,锁定阻塞源头。

4. 结合 SQL Audit 关联分析

GV$OB_SESSION_WAIT 中的 SIDGV$OB_SQL_AUDIT 中的 SID 关联,可获取该会话正在执行的 SQL 文本和执行计划,进一步定位问题 SQL。

-- 关联等待事件与正在执行的 SQL
        SELECT sw.SID, sw.EVENT, sw.WAIT_TIME_MICRO/1000 AS wait_ms,
            sa.QUERY_SQL
        FROM oceanbase.GV$OB_SESSION_WAIT sw
        JOIN oceanbase.GV$OB_PROCESSLIST pl ON sw.SID = pl.ID
        LEFT JOIN oceanbase.GV$OB_SQL_AUDIT sa ON sw.SID = sa.SID
        WHERE sw.WAIT_CLASS != 'IDLE'
        ORDER BY sw.WAIT_TIME_MICRO DESC
        LIMIT 10;
5. 善用等待事件的三个参数

许多等待事件的 p1/p2/p3 参数提供了额外的诊断信息:

  • p1 通常表示等待的地址或资源标识。
  • p2 通常表示等待次数或重试次数阈值。
  • p3 通常表示超时时间或其他上下文信息。

例如,latch: default spin lock wait 事件的 p1 为锁地址,p2 为 SPIN 次数,p3 为等待次数,通过这些参数可以关联到具体的临界区和等待特征。

6. 利用 DBA_WR_SYSTEM_EVENT 进行持久化历史分析

从 V4.2.1 版本开始,DBA_WR_SYSTEM_EVENT 视图可用于查询持久化的租户级等待事件历史信息,适用于长期趋势分析和容量规划。

🏆 等待事件分析实践总结
等待统计找方向:使用 GV$OB_SYSTEM_EVENT 快速识别系统瓶颈类型,通过 WAIT_CLASS 判断是 I/O、锁还是网络问题。
等待明细定位根因:使用 GV$OB_SESSION_WAIT 定位具体会话和等待参数,锁定阻塞源头。
关联 SQL Audit 和日志:将 SID、TRACE_ID 与 SQL 执行信息和日志关联,实现全链路诊断。
关注锁事件 SPIN 比例:通过 GV$OB_LATCH 视图分析热点临界区,SPIN_GETS/GETS 比例过高说明锁争用严重。
掌握分类筛选锁事件:锁事件属于 CONCURRENCY 类,查询时使用 where event like 'latch:%' 即可筛选。
通过总等待时间定位瓶颈:一般情况下,如果等待事件总耗时较多,通过查看耗时最多的等待事件名称(EVENT)能够基本确定是什么原因导致较慢。
善用 obdiag 命令行工具:使用 obdiag gather sysstatobdiag gather perf 可一键采集系统状态和性能报告,快速获取诊断数据。
13.4 ASH(Active Session History)分析

在数据库运维中,有时会遇到“CPU 突然飙高几分钟后自动恢复”,或“业务高峰期偶尔出现几次慢查询”等瞬时性能问题。传统监控手段只能看到平均值,难以捕捉这些瞬时的系统波动。ASH(Active Session History,活跃会话历史)正是为解决这类问题而生——它像一位不知疲倦的记录员,每秒给所有活跃会话拍摄一张“状态快照”,帮助 DBA 重现历史时刻的数据库状态,精准定位性能问题根因。

🎯 ASH 的核心价值
  • 瞬时性能问题诊断:精准定位持续几分钟的突发性能问题,如 CPU 突增、I/O 瓶颈、分布式执行阻塞等。
  • 多维度关联分析:支持从时间、会话、SQL、等待事件等多个维度回溯分析,快速锁定问题源头。
  • 事后追溯能力:即使故障已过去,仍可查询历史快照,分析故障发生时的详细现场。
  • 报告自动生成:通过 OCP 平台或 obdiag 命令行工具一键生成 ASH 报告,提供细粒度的诊断信息,比传统性能报告更精细。

一、ASH 核心实现原理

ASH 的实现可概括为“精准筛选,高效采样,环形存储”三大机制:

1. 活跃会话过滤

ASH 只记录活跃会话的状态,空闲会话不会被采样。活跃会话包括:

  • 用户前台会话:正在执行 SQL 请求的用户客户端连接。
  • 内部 RPC 执行:节点间的 RPC 通信任务。
  • 后台线程任务:如转储线程、Clog 线程、定时器线程等。

正在等待资源(如锁、磁盘 I/O)的会话同样属于活跃会话,ASH 会标记其等待事件(如 db file data read)。空闲会话(如处于 Sleep 状态无 SQL 处理)则不会被记录。

2. 周期性采样机制

每个 OBServer 内部有一个专用的 ASH 采样线程,以 1 秒为周期访问数据库内全部活跃会话,并记录其状态快照。视图 GV$OB_ACTIVE_SESSION_HISTORY 中每一行代表一个活跃会话在某个时刻的状态。

⚠️ 采样精度说明:若某个会话的工作时间非常短(不足 1 秒),可能无法被 ASH 捕捉到。对于这类极短时长的操作,建议重复负载并扩大查询时间范围,以获得更可靠的统计结果。
3. 环形缓冲区与持久化设计

ASH 快照数据保存在 30MB 的循环缓冲区中,当存储数据超过 30MB 后,系统会自动覆盖最旧的数据。为了应对历史数据丢失问题,OceanBase 提供了 WR(Workload Repository)持久化机制:

  • 手动持久化:执行 CALL DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT(); 可将当前 ASH 数据以 10:1 的比例采样写入持久化表。
  • 自动归档:从 V4.2.5 BP3 版本开始,系统在 ASH 数据被覆盖前会自动归档到持久化表。
-- 手动触发 WR 快照(将 ASH 数据以 10:1 比例持久化)
        CALL DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT();

二、ASH 视图体系

OceanBase 提供了多层次的 ASH 相关视图,满足实时查询和历史回溯两种分析需求:

1. GV$OB_ACTIVE_SESSION_HISTORY(实时活跃会话历史)

该视图用于展示本租户下所有 OBServer 节点的活跃会话历史记录,是 ASH 分析的核心视图。从 V4.0.0 版本开始引入,从 V4.2.2 版本开始更名为 GV$OB_ACTIVE_SESSION_HISTORY(原名 GV$ACTIVE_SESSION_HISTORY)。

核心字段说明

字段名说明
SAMPLE_TIME 采样时间(微秒精度),按采样周期生成。
SESSION_ID 会话 ID。在 ODP 模式下,当 client_session_id_version = 2 时表示 Client Session ID,否则为 Server Session ID。
SESSION_TYPE 会话类型:FOREGROUND(前台用户会话)或 BACKGROUND(后台会话)。
SESSION_STATE 会话状态:ON CPU(正在执行 SQL 逻辑)或 WAITING(正在等待资源),通过 ON CPU 占比可判断 CPU 瓶颈。
SQL_ID SQL 的唯一标识,可用于关联执行计划视图。
TRACE_ID 链路追踪 ID,用于关联日志和多个节点的执行信息。
EVENT / WAIT_CLASS 等待事件名称及所属类别,是定位瓶颈的核心指标。
P1 / P2 / P3 / P1TEXT 等待事件的三个参数及其名称,提供额外上下文信息(如锁地址、spin 次数等)。
SQL_PLAN_LINE_ID 采样时对应 SQL 算子 ID,可精确定位到执行计划中的具体算子。
IN_* 系列(IN_PARSE、IN_SQL_EXECUTION、IN_PX_EXECUTION 等) 采样时刻会话所处的执行阶段(解析中、执行中、并行执行中等),细粒度刻画 SQL 执行过程。
2. DBA_WR_ACTIVE_SESSION_HISTORY(持久化 ASH 数据)

该视图用于展示本租户持久化后的 ASH 数据,从 V4.2.1 版本开始引入。ASH 快照保存在 30MB 的循环缓冲区中,超过上限后旧数据会被覆盖。对于历史数据的持久化存储和长期分析,需通过 DBA_WR_ACTIVE_SESSION_HISTORY 视图查询。系统租户可使用 CDB_WR_ACTIVE_SESSION_HISTORY 查看所有租户的持久化 ASH 数据。

📌 实时视图与持久化视图的差异
  • GV$OB_ACTIVE_SESSION_HISTORY:数据存储在 30MB 环形缓冲区,可查询近几十分钟的实时 ASH 数据,适合实时故障排查。
  • DBA_WR_ACTIVE_SESSION_HISTORY:数据持久化存储,可查询更长时间的历史记录,适合事后追溯和长期趋势分析。

三、等待事件视图与 ASH 的关联

ASH 与等待事件视图紧密关联,配合使用可以构建完整的性能诊断链路:

  • V$EVENT_NAME:展示当前 OBServer 节点上所有等待事件的定义,从 V1.4 版本开始引入。通过该视图可以了解每个等待事件的名称、所属类别及参数含义。
  • V$SESSION_WAIT:显示会话刚刚完成等待或当前正在等待的事件。
  • V$SYSTEM_EVENT:显示当前租户中所有等待事件的总数。
  • V$SESSION_EVENT:类似于 V$SYSTEM_EVENT,但显示每个会话的所有等待事件。
-- 查看所有等待事件的定义(MySQL 模式)
        SELECT name, wait_class FROM V$EVENT_NAME ORDER BY name;

        -- 查看所有等待事件的定义(Oracle 模式)
        SELECT name, wait_class FROM V$EVENT_NAME ORDER BY name;

四、核心查询场景与 SQL 示例

场景 1:实时监控——最近 10 秒数据库在忙什么
SELECT sample_time,
            session_id,
            CASE WHEN session_state = 'ON CPU' THEN '工作中' ELSE '等待中' END AS 状态,
            event AS 等待原因
        FROM v$ob_active_session_history
        WHERE sample_time > now() - 10
        AND session_type = 'FOREGROUND'
        ORDER BY sample_time DESC
        LIMIT 20;

通过该查询,可以直观地看到每一条活跃会话的实时状态,判断当前系统瓶颈是 CPU 不足还是等待锁/I/O。

场景 2:CPU 使用率突增溯源(ON CPU 占比分析)
SELECT sql_id, COUNT(*) AS sample_cnt,
            ROUND(COUNT(CASE WHEN session_state = 'ON CPU' THEN 1 END) / COUNT(*) * 100, 2) AS on_cpu_pct
        FROM oceanbase.GV$OB_ACTIVE_SESSION_HISTORY
        WHERE sample_time BETWEEN '2025-06-15 14:00:00' AND '2025-06-15 14:05:00'
        AND session_type = 'FOREGROUND'
        GROUP BY sql_id
        HAVING COUNT(CASE WHEN session_state = 'ON CPU' THEN 1 END) > 0
        ORDER BY sample_cnt DESC
        LIMIT 10;

ON CPU 占比越高,说明采样时刻该 SQL 正在消耗 CPU。若 ON CPU 占比远高于 WAITING,则性能问题偏向于 CPU 瓶颈;反之,若 WAITING 占比高,则是等待资源(如锁、I/O)问题。

场景 3:I/O 瓶颈分析——定位存储热点
SELECT sql_id, event, COUNT(*) AS wait_cnt,
            AVG(time_waited) AS avg_wait_us,
            MAX(time_waited) AS max_wait_us
        FROM oceanbase.GV$OB_ACTIVE_SESSION_HISTORY
        WHERE sample_time BETWEEN '2025-06-15 14:00:00' AND '2025-06-15 14:05:00'
        AND session_state = 'WAITING'
        AND event IN ('db file data read', 'data file read', 'log file sync')
        GROUP BY sql_id, event
        ORDER BY wait_cnt DESC
        LIMIT 20;

通过过滤 USER_IO 类的等待事件,可以快速定位 I/O 瓶颈、慢查询或磁盘热点,为索引优化或存储调优提供依据。

场景 4:定位锁阻塞源头
SELECT sample_time, session_id, sql_id, event,
            p1text, p1, p2text, p2, p3text, p3
        FROM oceanbase.GV$OB_ACTIVE_SESSION_HISTORY
        WHERE sample_time BETWEEN '2025-06-15 14:00:00' AND '2025-06-15 14:05:00'
        AND wait_class = 'CONCURRENCY'
        ORDER BY sample_time DESC
        LIMIT 50;

若大量会话在等待 CONCURRENCY 类的锁事件(如 latch: default spin lock wait),则说明存在热点临界区或锁冲突,可通过 GV$OB_LATCH 视图进一步分析。

场景 5:分析 SQL 执行阶段(IN_XXX 状态字段)

GV$OB_ACTIVE_SESSION_HISTORY 提供了细粒度的执行阶段标识字段,帮助快速判断 SQL 卡在哪个环节:

SELECT sample_time, session_id, sql_id,
            IN_PARSE, IN_SQL_OPTIMIZE, IN_SQL_EXECUTION, IN_PX_EXECUTION,
            IN_SEQUENCE_LOAD, IN_COMMITTING, IN_STORAGE_READ
        FROM oceanbase.GV$OB_ACTIVE_SESSION_HISTORY
        WHERE sql_id = 'target_sql_id'
        ORDER BY sample_time DESC
        LIMIT 30;

五、ASH 报告:一键诊断工具

除了直接查询视图,OceanBase 提供了 ASH 报告功能,可一键生成指定时间段的诊断报告,更适合定期巡检和问题复现。ASH 报告能够定位瞬时发生的异常,提供比传统性能报告更细粒度的诊断信息——传统性能报告通常覆盖小时级别的快照,难以诊断秒级或分钟级的性能抖动,而 ASH 报告通过每秒采样,可精准捕捉到这些瞬时异常。

方式一:obdiag 命令行采集(推荐)

敏捷诊断工具 obdiag 支持一键采集 ASH 报告,无需依赖 OCP 图形界面。安装 obdiag 后(兼容 CentOS 7/8/9 等主流 Linux 发行版),使用 obdiag gather ash 命令采集 ASH 报告。

# 采集指定时间段的 ASH 报告(核心命令)
        obdiag gather ash --from "2025-06-15 14:00:00" --to "2025-06-15 14:30:00"

        # 指定报告类型为 HTML(需 OceanBase V4.2.4 及以上版本)
        obdiag gather ash --from "2025-06-15 14:00:00" --to "2025-06-15 14:30:00" --report_type=HTML

        # 按租户、SQL_ID、等待事件过滤采集(需 OceanBase V4.3.5 及以上版本)
        obdiag gather ash --from "2025-06-15 14:00:00" --to "2025-06-15 14:30:00" \
        --tenant_id=1002 --sql_id="195E74D0ACE24FF470774DF649D62921" --wait_class="CONCURRENCY"
💡 如果集群通过 obd 部署,建议升级 obd 到 V2.5.0 或更高版本,然后可直接通过 obd 使用 obdiag 命令,无需额外配置。
方式二:数据库内部 PL 包生成(Oracle 模式)

在 Oracle 模式下,可通过调用 DBMS_WORKLOAD_REPOSITORY.ASH_REPORT 包生成 ASH 报告。该包目前仅支持 Oracle 模式,MySQL 模式暂不支持。

-- 以 SYS 用户登录 Oracle 租户
        obclient -h172.30.xxx.xxx -Pxxxx -usys@oracle -pxxxx -A

        -- 开启 serveroutput
        SET SERVEROUTPUT ON;

        -- 调用 ASH_REPORT 生成报告
        CALL DBMS_WORKLOAD_REPOSITORY.ASH_REPORT(
            BTIME => TO_DATE('2025-06-15 14:00:00', 'yyyy-MM-dd HH24:mi:ss'),
            ETIME => TO_DATE('2025-06-15 14:30:00', 'yyyy-MM-dd HH24:mi:ss')
        );
⚠️ 注意:DBMS_WORKLOAD_REPOSITORY.ASH_REPORT 仅在 Oracle 模式下可用,MySQL 模式暂不支持。
方式三:OCP 企业版图形化界面

通过 OCP 企业版控制台生成 ASH 报告的步骤如下:

  1. 登录 OCP 控制台,在左侧导航栏点击“OceanBase 自治服务”,找到目标集群。
  2. 点击集群名称,进入“报告中心”页面,切换到“活跃会话历史报告”标签页。
  3. 点击“生成报告”,指定分析时间段和报告名称,生成后可在列表中“在线查看”“下载”报告。

六、最佳实践与注意事项

📌 生产环境使用建议
  • 优先使用实时视图排查近期故障GV$OB_ACTIVE_SESSION_HISTORY 存储了近期的实时数据,是定位近期问题的首选数据源。
  • 结合 WR 快照留存长期历史:通过 DBA_WR_ACTIVE_SESSION_HISTORY 持久化历史 ASH 数据,并配合自动归档功能(V4.2.5 BP3+),构建长期可回溯的诊断体系。
  • 关注 SESSION_TYPE 字段区分前台与后台任务:排查用户侧性能问题时,建议过滤 session_type = 'FOREGROUND',避免后台任务干扰分析结论。
  • 合理使用 IN_XXX 状态字段:通过 IN_SQL_OPTIMIZEIN_PX_EXECUTIONIN_COMMITTING 等字段精准定位 SQL 执行阶段,快速判断瓶颈在解析、优化、执行还是提交阶段。
  • 利用环形缓冲区特性设计查询窗口:ASH 数据采用 30MB 环形缓冲区存储,超过容量后旧数据会被覆盖。建议频繁查询的时间窗口不要超过 ASH 历史数据的实际保留时长,避免数据被覆盖后无法查询。
  • 手动触发 WR 快照保留关键数据:在重大变更或压测前后,可执行 CALL DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT(); 手动持久化当前 ASH 快照,为后续回溯分析保留关键现场。
  • 善用 obdiag 命令行工具:对于没有 OCP 企业版的环境,obdiag gather ash 是最便捷的 ASH 报告采集方式。如果 OB 版本 ≥ 4.3.5.0,还可使用 --svr_ip--tenant_id 等参数进行精细化过滤。
🏆 ASH 分析总结
ASH 是 OceanBase 数据库中“事后回溯分析”的有力工具,每秒采样一次活跃会话的完整执行状态。通过 GV$OB_ACTIVE_SESSION_HISTORY 实时视图和 DBA_WR_ACTIVE_SESSION_HISTORY 持久化表,结合 obdiag 命令行工具、OCP 企业版 ASH 报告以及 DBMS_WORKLOAD_REPOSITORY.ASH_REPORT PL 包,DBA 可以:
  • 快速定位 CPU 突增、I/O 瓶颈、锁阻塞等瞬时性能问题。
  • 从会话、SQL、等待事件、执行阶段等多维度回溯分析故障现场。
  • 构建从实时监控到历史追溯的完整性能诊断体系,让数据库运维从“被动救火”转向“主动预防”。
13.5 慢查询日志与全链路追踪

在分布式数据库环境中,一条 SQL 请求的完整执行链路可能跨越客户端、负载均衡器、OBProxy 和多个 OBServer 节点。当出现慢查询时,仅凭单点日志很难快速定位瓶颈。OceanBase 4.x 提供了两套互补的诊断工具:慢查询日志用于持续监控和捕获执行时间较长的 SQL;全链路追踪(Show Trace)则用于事后深入分析某条慢 SQL 在各阶段、各组件上的精确耗时。两者结合,构建起从宏观监控到微观分析的完整诊断体系。

🎯 慢查询日志 vs 全链路追踪
  • 慢查询日志:被动记录,持续运行,适合日常监控和发现潜在的性能问题。通过配置阈值,系统会自动识别并记录慢 SQL。
  • 全链路追踪(Show Trace):主动诊断,按需开启。当发现某条 SQL 慢时,可针对该会话开启 Trace,精确获得该 SQL 从客户端到 OBServer 各阶段的耗时分布,定位瓶颈是发生在网络、排队、解析还是执行。
  • obdiag 自动化采集:通过 obdiag gather log 等命令可一键采集慢查询日志或指定 trace_id 的全链路日志,大幅提升排查效率。

一、慢查询日志体系

OceanBase 的慢查询日志分为 OBServer 层慢查询OBProxy 层慢请求两部分,分别记录存储引擎层的 SQL 执行耗时和代理层的请求处理耗时。两部分日志配合分析,可以判断慢查询是发生在 OBServer 内部还是网络传输/OBProxy 处理环节。

1. OBServer 慢查询日志(observer.log)

OBServer 通过集群级配置项 trace_log_slow_query_watermark 控制慢查询阈值。执行时间超过该阈值的 SQL 会被判定为慢查询,并在 observer.log 中打印包含 [slow query] 字样的详细追踪日志。

配置项/参数默认值说明
trace_log_slow_query_watermark 1s(从 V3.2.3 版本起由 100ms 调整为 1s) 设置慢查询阈值,执行时间超过该值的 SQL 会被记录慢查询日志。单位支持 ms/us,修改后实时生效,无需重启 OBServer。取值范围 [1ms, +∞)。
long_query_time 10s MySQL 模式下的会话级变量,用于设置慢查询阈值。仅 MySQL 模式支持。需配合 slow_query_log 启用。
-- 查看当前慢查询阈值配置(OBServer 级别)
        SHOW PARAMETERS LIKE 'trace_log_slow_query_watermark';

        -- 调整慢查询阈值为 2 秒(动态生效)
        ALTER SYSTEM SET trace_log_slow_query_watermark = '2s';

        -- 在 observer.log 中搜索慢查询日志
        grep "slow query" /home/admin/oceanbase/log/observer.log
💡 版本差异说明:V3.2.3 及之前版本默认值为 100ms;V3.2.3 之后版本默认值调整为 1s。建议根据业务负载特性调整,避免阈值过低产生大量日志或阈值过高遗漏问题 SQL。
2. OBProxy 慢请求日志(obproxy_slow.log)

OBProxy 通过配置项 slow_query_time_threshold 控制慢请求阈值,超过阈值的请求会被记录到 obproxy_slow.log 文件中。OBProxy 慢日志还记录了详细的阶段耗时分解(ODP 处理总时间、预处理时间、获取连接时间、OBServer 执行时间),可用于精准定位瓶颈发生在代理层还是存储层。

-- 查看 OBProxy 慢请求阈值配置(需通过 root@proxysys 账号连接 ODP)
        SHOW PROXYCONFIG LIKE 'slow_query_time_threshold';

        -- 设置慢请求阈值为 500ms
        ALTER PROXYCONFIG SET slow_query_time_threshold = '500ms';
3. 慢查询系统视图:GV$OB_SLOW_QUERY

除了直接查看日志,OceanBase 还提供了 GV$OB_SLOW_QUERY 视图,用于查询集群中记录的所有慢查询信息,方便进行集中分析和统计。该视图包含 SQL 文本、执行时间、返回行数、影响行数等关键字段。

-- 查看最近 10 条慢查询记录
        SELECT request_time, elapsed_time/1000 AS elapsed_ms, 
            sql_id, query_sql
        FROM oceanbase.GV$OB_SLOW_QUERY
        ORDER BY request_time DESC
        LIMIT 10;

        -- 统计近 1 小时内慢查询分布(按 SQL 聚合)
        SELECT sql_id, COUNT(*) AS slow_cnt,
            AVG(elapsed_time)/1000 AS avg_ms,
            MAX(elapsed_time)/1000 AS max_ms
        FROM oceanbase.GV$OB_SLOW_QUERY
        WHERE request_time > UNIX_TIMESTAMP() * 1000000 - 3600000000
        GROUP BY sql_id
        ORDER BY slow_cnt DESC
        LIMIT 20;
📌 注意:部分版本中 GV$OB_SLOW_QUERY 视图可能不可用,此时可通过 GV$OB_SQL_AUDIT 视图配合 ELAPSED_TIME > trace_log_slow_query_watermark 条件筛选慢查询。

二、全链路追踪(Show Trace)

全链路追踪是 OceanBase 4.x 版本新增的强大诊断功能。当开启 SQL Trace 后,系统会记录 SQL 请求从客户端到 OBProxy 再到 OBServer 各个阶段的详细耗时,以树形结构展示,帮助运维人员精确判断慢 SQL 的瓶颈所在。

核心概念:Trace、Span、Tag、Log
  • Trace:一次完整的请求追踪,可以理解为 OceanBase 数据库中的一个事务。一个事务可以有多个 Span。
  • Span:某个具体的请求流程,一个 Trace 可以有多个 Span。Span 可以是语句(Statement)、函数(Function)、匿名块(Anonymous Block)等。Span 之间有父子关系,通过 parent_idid 关联。
  • Tag:键值对,属于某个 Span,用于记录当前操作的详细信息。
  • Log:带时间戳的键值对,属于某个 Span,用于记录操作过程中的事件。
启用全链路追踪(SQL Trace)

SQL Trace 功能默认关闭,可通过会话级系统变量 ob_enable_trace_log 开启。开启后,后续执行的 SQL 会被记录详细追踪信息,通过 SHOW TRACE 命令查看。V4.x 版本中,ob_enable_show_trace 变量与 ob_enable_trace_log 互为别名,均可使用。

-- 开启 SQL Trace(会话级别)
        SET ob_enable_trace_log = 1;

        -- 使用 Hint 方式开启(仅针对单条 SQL)
        SELECT /*+trace_log=on*/ * FROM orders WHERE order_id = 12345;

        -- 执行目标 SQL
        SELECT * FROM t1 WHERE id = 1;

        -- 查看上一条 SQL 的全链路追踪信息
        SHOW TRACE;

        -- 关闭 SQL Trace
        SET ob_enable_trace_log = 0;
⚠️ SQL Trace 会记录大量执行细节,对性能有一定影响,建议仅在问题诊断时按需开启,使用后及时关闭。
SHOW TRACE 输出解读

SHOW TRACE 输出以树形结构展示 SQL 执行的全链路信息,包含以下三列:

  • Title:执行阶段的名称,如 parse beginresolve beginoptimize begin 等。树形缩进展示了阶段之间的父子关系,缩进层级越深表示越内层的执行细节。
  • KeyValue:该阶段的详细信息,如 trace_idsvr_ipplan_idstmt 等,是问题定位的关键信息。
  • Time:当前阶段相对于上一阶段的耗时(微秒),通过累加可以计算各阶段的总耗时。
obclient [test]> SET ob_enable_trace_log = 1;
        obclient [test]> SELECT * FROM t1 WHERE id = 1;
        obclient [test]> SHOW TRACE;

        +----------------------------------------+-----------------------------------------+------+
        | Title                                  | KeyValue                                | Time |
        +----------------------------------------+-----------------------------------------+------+
        | process begin                          | trace_id:YB42AC1E87E6-0005B8AB2D57844F  | 0    |
        | query begin                            |                                         | 1    |
        | parse begin                            | stmt:"SELECT * FROM t1 WHERE id = 1"    | 62   |
        | resolve begin                          |                                         | 21   |
        | resolve end                            |                                         | 33   |
        | execution begin                        | plan_id:78                              | 14   |
        | table scan begin                       |                                         | 25   |
        | table scan end                         |                                         | 86   |
        | execution end                          |                                         | 11   |
        | query end                              |                                         | 39   |
        +----------------------------------------+-----------------------------------------+------+
跨组件追踪(OBProxy + OBServer)

全链路追踪信息会记录在对应组件的日志中。通过 OBProxy 访问时,追踪信息会同时记录在 OBProxy 的 obproxy_trace.log 和 OBServer 的 trace.log 中。SHOW TRACE 命令会自动聚合 OBProxy 和 OBServer 两端的 Span 信息,形成完整的树形链路。

SHOW TRACE 的输出包含以下常见的 Span:

  • obclient/JDBC:客户端层的 Span,记录 SQL 发送起始时间。
  • ob_proxy:OBProxy 层 Span,记录 OBProxy 对 SQL 请求的完整处理耗时。
  • ob_proxy_partition_location_lookup:OBProxy 路由阶段,获取分区位置信息耗时。
  • ob_proxy_do_observer_open:OBProxy 选择 OBServer 并建立连接的耗时。
  • mpquery_single_stmt:OBServer 层执行 SQL 的总 Span。
  • sql_compile / hard_parse:SQL 编译相关 Span,细分为 parse(语法解析)、resolve(语义解析)、rewrite(查询重写)、optimize(优化器生成执行计划)。
  • sql_execute:SQL 执行阶段的 Span,包含 table scanpx_task 等子操作。
关联业务系统:App Trace ID

用户可以通过 JDBC 接口或 SQL 接口,在业务数据库连接上设置调用请求对应的 App Trace ID。该 App Trace ID 会记录到 OceanBase 全链路追踪的信息中,并在 Show Trace 中展示出来。当用户发现某个 App Trace ID 对应的请求或服务数据库调用有报错时,可以使用该 App Trace ID 在全链路诊断系统中快速搜索到关联的数据库 Trace,直接查看该请求在数据库链路中各阶段的耗时情况及报错点。

-- 通过 JDBC 连接设置 app_trace_id 示例
        // Java 代码示例
        Properties props = new Properties();
        props.setProperty("user", "root@test_tenant");
        props.setProperty("password", "******");
        props.setProperty("app_trace_id", "my_app_trace_id_12345");
        Connection conn = DriverManager.getConnection("jdbc:oceanbase://...", props);

        -- 通过 SQL 接口设置 app_trace_id(Session 级别)
        SET @app_trace_id = 'my_app_trace_id_12345';

        -- 执行 SQL 后,该 app_trace_id 会出现在 SHOW TRACE 的 KeyValue 中
        SHOW TRACE;

三、命令行一键采集慢查询与全链路数据

使用 OceanBase 敏捷诊断工具 obdiag 可一键采集慢查询日志和全链路追踪数据,替代手工从多节点收集日志的复杂操作。obdiag 适用于独立部署以及通过 OBD 管理的集群场景。

1. 采集慢查询日志
# 采集最近 1 小时的慢查询日志
        obd obdiag gather log --since "1h"

        # 采集指定时间段的慢查询日志
        obd obdiag gather log --from "2025-06-15 14:00:00" --to "2025-06-15 15:00:00"

        # 按节点和关键词过滤采集(指定 OBServer 节点和 grep 关键词)
        obd obdiag gather log --since "1h" --grep "slow query" --scope observer
2. 采集指定 trace_id 的全链路数据
# 使用 obdiag gather plan_monitor 收集指定 trace_id 的 SQL 执行详情
        obd obdiag gather plan_monitor --trace_id "YB42AC1E87E6-0005B8AB2D57844F"
💡 obdiag 采集结果自动打包为 ZIP 文件,包含所有节点的相关日志、系统信息和关键诊断文件,便于离线分析或留存备查。对于独立部署的 obdiag,可使用 obdiag gather log 命令,用法与 obd 集成命令一致。

四、实践案例:从发现慢查询到定位根因

步骤 1:发现慢查询(慢查询日志)

运维人员在监控中发现某条 SQL 执行时间超过 2 秒,通过查询 GV$OB_SLOW_QUERY 获取其 trace_idSQL_ID,或使用 obdiag gather log --grep "slow query" 一键采集慢查询日志。

SELECT request_time, elapsed_time/1000 AS elapsed_ms,
            trace_id, sql_id, query_sql
        FROM oceanbase.GV$OB_SLOW_QUERY
        ORDER BY request_time DESC
        LIMIT 5;
步骤 2:获取实际执行计划(判断是否走错索引)

通过 SQL_ID 查询计划缓存,确认执行计划是否存在问题。

SELECT plan_id, outline_data, is_hit_plan
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
        WHERE sql_id = 'your_sql_id';
步骤 3:按需开启 SQL Trace 深度分析

针对该 SQL 所在会话开启 SQL Trace,重新执行并分析各阶段耗时。

SET ob_enable_trace_log = 1;
        -- 执行目标 SQL
        SELECT ...;
        SHOW TRACE;

分析 SHOW TRACE 输出,若 parse/resolve/optimize 等编译阶段耗时占比过高(例如超过 50%),说明问题在于硬解析或统计信息过旧;若 table scan 等执行阶段耗时过高,则需进一步分析执行计划是否走错索引或全表扫描。

步骤 4:交叉验证 OBProxy 层耗时

若怀疑瓶颈在网络或 OBProxy 层,可分析 obproxy_slow.log 中的阶段耗时分解:

  • ODP 处理总时间:OBProxy 从接收到请求到转发结果的总时间。
  • ODP 预处理时间:请求在 OBProxy 层的预处理耗时(如路由计算)。
  • ODP 获取连接时间:OBProxy 获取与 OBServer 连接的耗时。
  • OBServer 执行时间:OBServer 实际执行 SQL 的时间。若该值接近总耗时,说明瓶颈在 OBServer;若 ODP 处理总时间远大于 OBServer 执行时间,说明瓶颈在 OBProxy 或网络。

五、最佳实践与注意事项

📌 慢查询日志配置建议
  • 生产环境合理设置阈值:建议将 trace_log_slow_query_watermark 设置为 500ms~2s,平衡监控粒度和日志量。阈值过低会产生大量日志,影响系统性能;阈值过高会漏过问题 SQL。
  • 定期归档分析慢日志:结合 OCP 慢 SQL 功能或 obdiag gather log --grep "slow query" 定期采集慢查询数据,发现性能隐患。
  • 关注 OBProxy 慢请求:若 OBServer 执行时间较短但整体响应慢,应重点排查 OBProxy 层和网络延迟。
  • 动态调整阈值trace_log_slow_query_watermark 支持动态修改,无需重启,可根据业务高峰/低峰期灵活调整。
📌 全链路追踪使用建议
  • 按需开启,用完即关SET ob_enable_trace_log = 1; 会为当前会话中的所有 SQL 记录详细追踪信息,对性能有影响,应在诊断完成后立即关闭。
  • 善用 App Trace ID:在业务代码中设置 app_trace_id,实现业务请求与数据库 Trace 的关联,提升问题定位效率。
  • 结合多种视图交叉验证:将 SHOW TRACE 中的 trace_idGV$OB_SQL_AUDITobserver.log 关联,获取更全面的诊断信息。
  • 关注 SHOW TRACE 中的分布式执行信息:对于分布式 SQL,SHOW TRACE 会显示各 OBServer 节点上的执行子任务耗时,帮助定位远程执行瓶颈。
  • 注意版本兼容性ob_enable_trace_log 变量在 V4.x 版本中存在 ob_enable_show_trace 别名,两者均可使用。
  • 善用 obdiag 命令行工具:使用 obd obdiag gather plan_monitor --trace_id 可一键收集执行详情,快速定位算子级性能瓶颈。
🏆 小结
慢查询日志与全链路追踪是 OceanBase 性能诊断的两大支柱。慢查询日志通过持续监控发现潜在问题,全链路追踪则提供按需、深入的执行阶段分析。两者结合使用,形成完整的诊断闭环:“监控发现 → 定位 SQL → 开启 Trace → 分析阶段瓶颈 → 优化解决”。熟练掌握这两项技能,是成为 OceanBase 高级 DBA 的必备能力。

第14章:数据迁移与同步

14.1 OMS 全量+增量迁移架构

OceanBase 迁移服务(OceanBase Migration Service,以下简称 OMS)是 OceanBase 数据库企业版提供的一款一站式数据传输和同步产品。它集数据迁移、实时同步和增量订阅于一体,支持同构或异构数据库向 OceanBase 进行数据迁移和实时同步。在 OMS 4.x 企业版中,全量迁移结合日志解析的增量同步,可以实现在业务几乎无感知的情况下,将数据完整、实时地复制到目标端,这一过程对业务影响极小,并能满足迁移和同步过程中的数据一致性与高可用要求。

🎯 OMS 的核心功能定位
  • 在线不停服迁移:在全量迁移期间,源端业务可以继续写入,OMS 通过增量同步机制确保源端与目标端的数据最终一致,最大限度地减少业务停机时间。
  • 全量+增量一体化:OMS 将数据复制过程分为全量数据复制和增量数据复制两个阶段,分别由 Full-Import 组件和 Incr-Sync 组件负责,两个阶段无缝衔接,形成完整的迁移链路。
  • 企业级数据一致性保障:OMS 提供全量数据校验能力,对迁移前后的数据进行全字段比对,并支持生成订正 SQL 脚本,帮助用户快速发现并修复数据差异。
  • 多种数据源支持:OMS 支持 OceanBase、MySQL、PostgreSQL、TiDB、Oracle、DB2、Kafka、RocketMQ 等多种数据源与 OceanBase 进行数据迁移和同步。
  • 反向回流与一键回切:在业务切换至目标端后,可快速搭建反向数据链路,将目标端的增量数据实时回流到源端,为应对紧急回切提供安全保障。

一、OMS 整体架构

OMS 从功能视角分为三层:服务接入层、流程编排层和组件链路层。

1. 服务接入层

主要包括客户端迁移服务的交互、各种类型数据源的管理、迁移任务的录入和 OMS 各个组件模块的运维、监控等。用户通过 OMS 控制台完成数据源的添加、迁移任务的创建与配置,并对任务进行全生命周期管理。

2. 流程编排层

主要负责实现上层表结构迁移、启动全量数据迁移/同步、增量数据迁移/同步、数据校验和订正,以及链路切换等任务的执行细节。流程编排层负责调度底层组件,协调各组件之间的工作流。

3. 组件链路层

组件链路层是 OMS 的核心执行层,包含以下关键组件:

  • DBCat:结构迁移核心组件,负责数据对象的采集和转换,支持异构数据库之间的 Schema 转换。
  • Store:增量拉取组件,负责对数据库事务日志进行拉取解析,并对解析后的日志记录按统一格式存储。下游可以从 Store 中以一致的方式消费增量数据,无需关心不同数据库间日志格式的差异。
  • Full-Import:全量导入组件,负责源库表对象中存量数据的迁移和部分增量数据的同步。
  • Incr-Sync:增量同步组件,从增量拉取组件中请求增量数据,并根据用户配置的同步对象进行数据过滤,在保证事务时序性及事务一致性的前提下,将日志记录同步至目标实例。
  • Full-Verification:全量校验组件,负责对迁移表中的行记录进行全字段校验,并针对不一致的数据生成订正语句。
  • Supervisor:监控组件,负责对其它组件进行状态监控。
💡 企业版高可用(HA)特性:Store 和 Incr-Sync 组件支持高可用,当组件发生故障时,高可用服务会守护异常进程并尝试重启,或在多节点部署环境下将组件动态切换至其他可用节点。需要注意的是,高可用特性不能自动恢复 Full-Import/Full-Verification 组件,对于生产环境,建议使用多节点部署架构。企业版高可用功能默认开启,社区版需手动开启且功能受限。

二、全量迁移(Full Import)

全量迁移负责将源端数据库中的存量数据一次性迁移至目标端。Full-Import 组件是这一阶段的核心执行单元,每张迁移的表都会经过数据切片、数据读取、数据加工和数据应用等步骤。

1. Full-Import 的数据处理流程

Full-Import 和 Incr-Sync 组件均采用 Source → Coordinator → Sink 的三段式处理模式:

  • Source(数据源):从源端表中提取数据,并发送给 Coordinator。
  • Coordinator(协调器):对数据进行 ETL 加工处理,然后将处理后的数据发送给 Sink。
  • Sink(数据目标):将数据写入目标表。
2. 全量数据读取方式

Full-Import 组件根据源端数据库类型,采用不同的数据读取策略:

  • JDBC Source(适用于 MySQL 等通用数据库):通过查询源端表的元数据(索引、分区等信息),根据主键或唯一键对数据进行分片,然后并发读取各个分片的数据。
  • Oracle 全量迁移(企业版深度优化):OMS 企业版利用 Oracle 数据库中表的块信息,将数据表按照块信息进行切片。每个切片中的块信息最终组装成 Rowid,并以 Rowid 范围作为查询条件进行高效的数据检索。不同切片之间并发查询源库数据,提高数据迁移速度。
⚠️ Oracle 迁移注意事项:OMS 的 Oracle 全量迁移采用无锁操作,不会对源表进行锁定,因此不会影响源系统的正常业务处理。同时,由于使用 Rowid 作为查询条件,数据检索过程中的资源占用较少,可以减少全表扫描带来的性能开销。
3. 数据写入优化

Full-Import 组件采用批量插入方式提升写入效率。对于 OceanBase 数据库的分区表,Full-Import 实现了根据分区写入聚合的逻辑,进一步优化写入性能。

💡 旁路导入(Direct Load)企业版增强:从 OMS 较高版本(如 4.2.0 及以上)开始,企业版支持通过旁路导入方式向 OceanBase 目标端写入数据。旁路导入可以绕过传统的 SQL 层接口,直接向数据库文件写入数据,进一步提高数据导入效率。开启旁路导入时需注意:目标端用户的 host 字段必须为 %,单行数据不能超过 2MB,且不支持断点续传。在创建迁移任务时,可根据需求选择是否开启旁路导入功能。

三、增量同步(Incremental Sync)

增量同步负责将源端数据库的增量变更(包括 INSERT、UPDATE、DELETE 等 DML 操作以及 DDL 操作)实时同步至目标端,确保源端与目标端数据最终一致。增量同步由 Store 组件和 Incr-Sync 组件协同完成。

1. Store:增量日志拉取与解析

Store 组件是 OMS 实现 Change Data Capture(CDC)的核心,采用基于日志(Log-Based)的 CDC 技术,相较于基于触发器或基于时间戳的方式,对数据库的侵入和影响更小,实时性更高。

Store 由 Reader 和本地存储构成。大部分 Store 会启动一个子进程(即 Reader),负责拉取解析各个数据库的日志。Store 在运行时根据待拉取的数据库类型加载对应的 Reader:

  • Oracle Reader(企业版):基于 Oracle LogMiner 获取增量变更日志(读取 V$LOGMNR_CONTENTS 的内容),通过并发拉取多个归档文件的变更来提升拉取速度。Store 在日志解析过程中会进行事务管理,将同一个事务的日志聚合在一起,吐出已提交的事务,丢弃被回滚的事务,同时支持大事务暂存到磁盘以减少内存开销。
  • MySQL Reader:模拟成 MySQL 数据源的一个从库,与源端建立 Binlog Dump 连接,接收二进制日志流。根据 Event 的长度将日志流拆分成完整且独立的 Binlog Event,然后根据黑白名单进行过滤,仅保留需要同步的表的变更。
  • OceanBase Reader:通过 RPC 向 OceanBase 数据库请求各分区的 Clog(Redo)日志,结合各个表和列的 Schema 信息,将 Redo 日志转换为中间数据格式,以事务为单位输出变更数据。
  • DB2 Reader:通过 DB2 数据库提供的 db2ReadLog API 读取日志记录,同时可以查询日志管理器获取当前日志状态信息。
2. Incr-Sync:增量数据应用

Incr-Sync 组件负责将 Store 组件产生的增量数据应用至目标端。OMS 将增量同步的 Record 主要抽象为 DML、DDL 和 HB(Heartbeat)三类,DML 包括 INSERT、UPDATE 和 DELETE。

Incr-Sync 从 Store 中拉取增量数据后,根据用户配置的同步对象进行数据过滤,然后在保证事务时序性及事务一致性的前提下,将日志记录同步至目标实例。

3. 全量迁移与增量同步的衔接

OMS 在全量迁移开始前首先启动增量日志拉取,将上游数据库产生的日志先落盘保存。待全量迁移完成后,启动增量日志回放,即增量同步,增量同步会在下游数据库消费已落盘的数据。

在全量迁移到增量同步的过程中,OMS 支持多次写入,基于 at least once 和幂等性原则,通过主键(PK)或唯一键(UK)保证数据的一致性。对于 Oracle 迁移,OMS 以 Rowid 来保证幂等性,确保数据在多次写入时的正确性。

4. 无主键表的一致性保障

对于无主键表,OMS 通过事务表机制防止事务重放以确保一致性。目前 MySQL 数据库至 OceanBase 数据库 MySQL 租户的无主键表迁移,不保证数据的最终一致性,需特别关注。

四、企业版关键技术特性

1. 高性能指标

OMS 企业版在全量迁移性能方面可达 100 MB/s、20 万 TPS,数据同步性能可达 50000 RPS,能够满足大部分业务场景的数据迁移需求。实际性能受网络、硬件配置等因素影响。

2. 数据校验机制

OMS 提供全量数据校验功能,Full-Verification 组件负责从源端和目标端读取数据,并根据映射关系(依赖索引信息)对两端的数据进行全字段对比,对比结果包括差异数据文件和订正 SQL 文件。Full-Verification 组件能够对大部分数据源实现流式读取,减少读取压力,并支持多种异构数据源之间的数据比对,内部会对数据进行格式化,确保数据比对的性能和一致性。

3. 结构迁移(Schema Migration)

DBCat 作为 OceanBase 原生的 Schema 转换引擎,可以根据源端和目标端的数据源类型及字符编码类型,进行精确的数据类型映射或转换,支持转换表、约束、索引和视图等多种数据库对象。对于 OceanBase 暂时不支持的数据源类型,DBCat 会选择最兼容的数据类型进行转换映射。

4. 反向回流与链路切换

OMS 支持反向回流功能,在业务应用切换至目标端后,可以创建目标端至源端的同步链路,将目标端的增量数据实时同步回源端,确保业务在出现问题时可以快速回切,降低迁移风险。

五、部署模式

OMS 企业版支持三种部署模式:

  • 单节点部署:单个节点即可提供 OMS 的全部能力,适用于非生产环境或测试场景。
  • 单地域多节点部署:在多节点环境中实现高可用。当某个 OMS 节点不可用时,框架会自动将任务调度到可用的节点。多节点部署需要申请 VIP 作为管理控制台的挂载点,并配置 8088 和 8089 端口的端口映射规则。
  • 多地域多节点部署:支持在多个地域部署 OMS 节点,实现机房维度的容灾能力。
⚠️ 部署注意事项:高可用功能默认开启(企业版),生产环境强烈建议使用多节点部署。社区版高可用需手动开启且功能受限。在部署多节点前,需提前为 InfluxDB 时序数据库预留资源以收集和展示历史监控数据。

六、使用限制与版本支持

  • 租户限制:不支持对 sys 租户和 Meta 租户进行数据迁移。
  • 无主键表:MySQL 数据库至 OceanBase 数据库 MySQL 租户的无主键表迁移,不保证数据的最终一致性,需特别关注。
  • 旁路导入限制:开启旁路导入功能时,需确保目标端用户的 host 字段为 %,单行数据不能超过 2MB,且不支持断点续传。
  • 时间同步要求:部署 OMS 前,需确保 OMS MetaDB 和所有 OMS 主机的时钟已通过 NTP 服务同步,数据库时区与主机时区保持一致。

OMS 企业版的数据迁移和数据同步功能对 OceanBase 企业版及其他数据终端有明确的版本要求。OceanBase 企业版支持的迁移/同步版本包括 V3.1.0 ~ V4.5.0。其他数据终端(如 MySQL、PostgreSQL、TiDB、Kafka 等)也均有对应的版本要求,具体请查阅 OMS 官方文档。

🏆 小结
OceanBase 迁移服务(OMS)企业版通过 Full-Import 全量迁移组件和 Incr-Sync 增量同步组件的无缝衔接,实现了数据库在线不停服迁移。Store 组件作为基于日志的 CDC 核心,支持多种数据库的增量日志拉取与解析。OMS 企业版提供了结构迁移、全量+增量一体化迁移、数据校验、反向回流等完整的数据迁移能力,并支持多节点高可用部署,能够满足生产级高可用容灾场景的数据迁移需求。
14.2 数据校验与对比(OMS 校验/checksum)

数据校验是数据迁移过程的“质检员”。在完成全量和增量数据同步后,源端与目标端数据是否完全一致,是业务切换前必须回答的关键问题。OceanBase 迁移服务(OMS)提供了完善的数据校验能力,在不中断服务的前提下对源端和目标端数据进行多维度比对,帮助用户及时发现数据差异并生成订正方案。本章系统介绍 OMS 的三种校验类型、Full-Verification 组件的核心原理与参数调优,以及数据修正与常见问题处理方法。

🎯 OMS 数据校验的核心价值
  • 不中断服务:校验过程对源端和目标端业务无侵入,不影响在线业务运行。
  • 多维度覆盖:支持全量校验、行数校验、增量校验三种粒度,满足不同场景的数据一致性验证需求。
  • 精确可追踪:以主键或唯一键为单位进行逐行全字段比对,精准定位不一致的记录。
  • 差异可订正:针对不一致的数据自动生成 INSERT、UPDATE、DELETE 订正语句,辅助快速修复。

一、OMS 数据校验的三种类型

OMS 企业版支持三种校验类型,可根据数据量和一致性敏感度灵活选择。

📊 行数校验(Row Count Validation)

原理:仅对比源端和目标端表的行数是否一致,不校验实际数据内容。
特点:速度快、资源消耗低,但无法发现数据内容不一致的问题。
适用场景:数据量大、数据一致性不敏感的场景,可作为快速初筛手段。适合在迁移任务启动初期快速验证增量链路是否正常工作。

📋 全量校验(Full Validation)

原理:对源端和目标端的全表数据进行全字段比对,是精度最高的校验方式。
特点:结果最准确,但耗时较长,对 I/O 和网络有一定消耗。
适用场景:适合对静态数据的一次性全量校验,是业务割接前的“最后一道防线”。

⚡ 增量校验(Incremental Validation)

原理:对比源端和目标端的增量变更数据(捕获 DML 操作),验证增量同步链路的正确性。
特点:校验结果准确,但资源消耗较高。
适用场景:在全量校验完成后,对后续变更数据进行补充校验,适用于迁移割接窗口期。

💡 OMS 还提供了混合校验模式,可同时对源端和目标端表记录进行全字段校验,实时判定数据一致性并输出校验报告。

二、Full-Verification 组件的工作原理

全量校验由 Full-Verification 组件执行,核心流程分为四个阶段:分片、读取、归并、校验。

  • 阶段 1:数据分片(Sharding)
    分片线程根据表的主键或唯一键将源表划分为多个数据分片,放入队列 1。每个分片是表的一个子集,分片大小由 limitator.select.batch.max 参数控制,默认值为 600。通过合理的分片粒度,Full-Verification 能够并发处理大型表,提高校验效率。
  • 阶段 2:并发读取(Data Read)
    数据源读取线程从队列 1 中获取分片,并发地从源端和目标端数据库中读取数据。读取线程数由 limitator.platform.threads.numberlimitator.source.max.thread.size 共同控制。
  • 阶段 3:缓存与归并(Cache & Join)
    系统将同一分片的数据分别读入缓存,并基于主键或唯一键进行归并操作,确保源端和目标端的同一行数据在缓存中对应。
  • 阶段 4:数据校验(Verification)
    校验线程从缓存中读取归并后的数据,进行全字段比对。若发现某条记录不一致,会将主键或唯一键发送到复检队列。
  • 阶段 5:复检(Reverification)
    复检线程从复检队列中取出不一致的唯一键,重新从源端和目标端查询完整记录并进行比对。复检轮次由 limitator.reviewer.rounds.max 控制(默认 20 轮),减少因网络抖动或暂时性不一致造成的误判。

三、关键参数与性能调优

参数名称默认值说明调优建议
limitator.platform.threads.number 约 10(实际随资源配置变化) 数据源读取线程总数(源端+目标端) 建议根据 CPU 核心数和网络带宽调整。每增加 1 个线程会多消耗约 1 核 CPU 资源
limitator.source.max.thread.size 6 单个数据源的最大读取线程数 建议与目标端保持对称配置,避免一端成为瓶颈
limitator.platform.split.threads.number 16 分片线程数 大表较多的场景可适当增加,提升分片阶段效率
limitator.select.batch.max 600 单个分片处理的最大记录数 大表场景可适当调大(如 1000),但需评估内存消耗
limitator.reviewer.rounds.max 20 最大复检轮次 若希望快速获取差异结果,可设为 0 直接输出不一致记录
limitator.reviewer.review.batch.max 100 每个复检任务处理的不一致唯一键数量 若差异较多,可适当调大以提高复检效率
⚠️ 在修改 Full-Verification 组件参数前,建议先在测试环境验证效果,并在生产环境变更时联系 OMS 技术支持确认。

四、数据修正机制

当 Full-Verification 发现数据不一致时,可以根据 filter.verify.rectify.type 参数决定是否自动修正。该参数仅在紧急情况下设置,正常校验场景建议保持默认值 no,仅输出差异报告而不自动修改目标端数据。当需要自动修正时,系统会基于不一致记录的主键或唯一键生成 INSERT、UPDATE 或 DELETE 语句,并在目标端执行,以对齐源端数据。

五、内置校验工具:CHECKSUM TABLE

除了 OMS 的 Full-Verification 组件,OceanBase 还提供了 CHECKSUM TABLE 命令,用于快速计算表的校验和,适用于人工快速验证表级一致性。

-- MySQL 模式(功能暂不支持 QUICK/EXTENDED 关键字)
        CHECKSUM TABLE t1;

        -- Oracle 模式(语法兼容,功能暂不完整)
        CHECKSUM TABLE t1;
⚠️ 当前 OceanBase 版本的 CHECKSUM TABLE 仅支持该关键字语法,部分高级功能暂不生效。对于大规模数据校验,建议优先使用 OMS 的 Full-Verification 组件。

注意事项

  • 该命令需要表的 SELECT 权限。
  • 校验和结果依赖于表的行存储格式。
  • 该命令会逐行读取整个表,对大表执行时可能消耗较多 I/O 资源。
  • 使用 CHECKSUM TABLE 可快速确认表级数据差异,但无法定位具体不一致的行记录。

六、常用校验视图与命令行工具

1. CDB_OB_COLUMN_CHECKSUM_ERROR_INFO(索引列校验)

该视图从 V4.0.0 版本开始引入,展示索引表(包括全局索引和局部索引)与主表之间出现的列校验和不一致信息。当索引表的数据与主表不一致时,可通过该视图快速定位具体表和列。

-- 查看当前租户的索引列校验错误信息
        SELECT TENANT_ID, DATA_TABLE_ID, INDEX_TABLE_ID, COLUMN_ID,
            DATA_COLUMN_CHECKSUM, INDEX_COLUMN_CHECKSUM
        FROM oceanbase.CDB_OB_COLUMN_CHECKSUM_ERROR_INFO
        WHERE TENANT_ID = 1002;
2. CDB_OB_TABLET_CHECKSUM_ERROR_INFO(Tablet 副本校验)

该视图从 V4.0.0 版本开始引入,展示 Tablet 副本之间出现的数据不一致的信息,用于发现多副本数据差异。

-- 查看有数据不一致的 Tablet 副本
        SELECT TENANT_ID, TABLET_ID, SVR_IP
        FROM oceanbase.CDB_OB_TABLET_CHECKSUM_ERROR_INFO
        WHERE TENANT_ID = 1002;
3. 基于 SQL Audit 的轻量校验

通过对比源端和目标端执行相同查询的结果,可以手动验证核心表的数据一致性。例如计算关键表的行数、SUM 值等进行快速校验。对于单表快速验证,也可使用 CHECKSUM TABLE 直接获取整表校验和。

-- 对比行数
        SELECT COUNT(*) FROM t1;

        -- 对比关键字段和值(适用于数值型主键表)
        SELECT MIN(id), MAX(id), COUNT(*) FROM t1;

七、常见问题与处理建议

问题 1:全量校验耗时过长

原因:待校验表数据量较大,或分片粒度设置不合理。
建议

  • 适当调整 limitator.platform.threads.number 增加并发读取线程。
  • 对于超大表,可优先使用行数校验快速验证,再针对差异表进行全量校验。
  • 确保源端和目标端数据库负载充足,避免校验任务影响正常业务。
问题 2:因时区不同导致校验失败

原因:源端和目标端的时区设置不一致,导致 DATETIME、TIMESTAMP 等时间类型数据的字符串表示存在差异。
建议:在 Full-Verification 组件配置中添加 "datasource.timezone" : " + 08:00" 参数,确保使用相同时区进行比对。同时在迁移任务配置阶段,确保源端和目标端的时区、字符集等基础环境保持一致。

问题 3:校验结果存在误报差异

原因:网络抖动或暂态不一致可能导致单次校验误判。
建议

  • Full-Verification 默认支持最多 20 轮复检(limitator.reviewer.rounds.max),可减少误报。
  • 对校验报告中标记为差异的记录进行人工抽样复核,确认是否为真实数据不一致。
  • 检查源端是否还有未提交或未同步到目标端的变更。
🏆 最佳实践总结
分层校验策略:全量迁移完成后立即执行一次全量校验,业务割接前再进行第二次全量校验,增量同步期间保持行数校验持续监控,实现完整的数据一致性保障闭环。
分阶段校验:初次迁移建议先进行行数校验快速筛查,再针对差异表执行全量校验;割接窗口内进行全量校验最终确认。
关注性能影响:避免在业务高峰期执行大规模校验,可通过调整线程数控制校验速度。
保留校验报告:校验完成后保存差异报告和订正脚本,便于问题追溯和复盘分析。
合理使用自动订正:仅在紧急修复场景下启用自动订正,常规场景建议使用生成的订正脚本,由 DBA 审查后手动执行。
定期巡检索引一致性:定期查询 CDB_OB_COLUMN_CHECKSUM_ERROR_INFO 视图,及时发现主表与索引表之间的数据差异。
善用 COUNT 快速预检:建议首先通过行数对比快速验证增量链路工作正常,再进行全量校验。
14.3 obdumper / obloader 高效导入导出

OceanBase 数据导入导出工具(OBLOADER / OBDUMPER)是一款使用 Java 语言开发的客户端工具,为 OceanBase 数据库量身打造,同时支持导入导出数据库对象的结构定义与表数据,且具备丰富的 ETL 处理能力。OBDUMPER 用于将 OceanBase 数据库中定义的对象和表数据以指定的文件格式导出到存储介质中,OBLOADER 则负责将存储介质中的数据库对象定义文件和表数据文件导入到 OceanBase 数据库中,通常推荐两者搭配使用。从 V4.2.1 版本开始,OBLOADER / OBDUMPER 不再区分企业版和社区版,统一提供一套软件包。

🎯 OBLOADER & OBDUMPER 的核心优势
  • 高性能与可观测性:OBLOADER 专门优化了数据的导入性能,内置多种数据预处理函数,自动容错保证数据导入的稳定性,并提供较为丰富的监控信息,以便实时观测数据文件导入的性能和进度。OBDUMPER 设计了多种数据查询策略,大幅提升导出性能。
  • 丰富的数据交换能力:支持将表中数据以 CSV、INSERT SQL、Apache ORC、Apache Parquet、Apache Avro 等多种格式导出或导入。同时支持从本地磁盘、Aliyun OSS、Amazon S3、Tencent COS、Huawei OBS、Azure Blob 和 GCS 等多种对象存储介质进行导入导出。需要注意的是,OBDUMPER 主要支持导出到本地磁盘和对象存储,OBLOADER 在此基础上还额外支持从 Apache HDFS 导入数据。
  • 强大的数据处理能力:支持导出前对数据进行压缩、加密、脱敏、预处理等操作。导入时支持配置数据预处理的控制规则以及文件与表之间的字段映射关系,支持导入限速、防导爆、断点恢复和错误自动重试。
  • 灵活的数据导出选项:OBDUMPER 支持指定分区名仅导出指定表分区内的数据,支持指定全局过滤条件仅导出满足条件的数据,支持指定 SCN 或 TIMESTAMP 仅导出有效事务点或者时间点的历史快照数据,并支持从 OceanBase 数据库的备副本中导出数据。

一、安装与配置

1. 前置准备
  • 安装 Java 8+ 并配置 JAVA_HOME 环境变量,强烈建议安装 JDK 1.8.0_3xx 及之后的版本。
  • 从 OceanBase 软件中心下载最新版本的导数工具软件包:ob-loader-dumper-4.3.x-RELEASE.zip
# 下载并解压导数工具软件包
        unzip ob-loader-dumper-4.3.x-RELEASE.zip
        cd ob-loader-dumper-4.3.x-RELEASE
2. 配置文件说明

导数工具配置文件包括连接配置文件(session.config.json)和运行日志文件(log4j2.xml),位于 {ob-loader-dumper}/conf 目录。

  • 连接配置文件(session.config.json:用于配置数据库连接参数,导数工具会通过其中的 JDBC 参数构建 JDBC URL,并在新建的连接下按顺序执行初始化 SQL 语句。默认配置适用于大多数场景,必要时可手动修改以适配不同的 OBServer 版本和 ETL 场景。
  • 日志配置文件(log4j2.xml:用于查看日志输出路径/日志格式,以及问题自查时调整日志级别。

二、OBDUMPER 数据导出

1. 基本导出命令

以下示例将 test 数据库下的 sample_tbl 表以 CSV 格式导出到指定目录。

# 基本导出语法(使用 ODP 连接)
        ./obdumper -h 'IP地址' -P 2883 -u '用户@租户名#集群名' -p '密码' -D '库名' --table '表名' --csv -f '文件路径' --sys-password '系统租户密码' --skip-check-dir

        # 使用说明:
        # -h 和 -P:指定 ODP 服务的地址和端口(也可通过 -c 指定集群名,直连 OBServer)
        # -u:用户名,格式为 <用户>@<租户>#<集群>,例如 'root@mysql#cluster_a'
        # -D:指定数据库名称
        # --table:指定表名(支持 '*' 导出所有表)
        # --csv:指定导出格式为 CSV(支持 csv、sql、orc、parquet 等)
        # -f:指定导出文件的存储路径
        # --sys-password:系统租户密码(导出某些元数据时需要)
2. 高级导出选项
  • 指定分区导出:仅导出指定的表分区内的数据。
  • 指定过滤条件:使用 --where 指定全局过滤条件,仅导出满足条件的数据。需要注意 --query-sql--where 不能搭配使用,条件需要写在 query-sql 语句内。
  • 一致性快照导出:通过指定 --scn--timestamp,导出有效事务点或时间点的历史快照数据,支持从 OceanBase 数据库的备副本中导出数据。
  • 自定义查询导出:通过 --query-sql 指定自定义的查询语句,仅导出该查询语句的结果集。
  • 仅导出表结构:通过 --ddl 选项仅导出表结构定义,不导出表数据。
# 指定分区导出
        ./obdumper -h xx.x.x.x -P 2883 -u 'user@tenant#cluster' -p '******' -D test_db \
        --table 'sample_tbl' --partition 'p0,p1' --csv -f /output

        # 指定过滤条件导出
        ./obdumper -h xx.x.x.x -P 2883 -u 'user@tenant#cluster' -p '******' -D test_db \
        --table 'sample_tbl' --where "created_at >= '2025-01-01'" --csv -f /output

        # 一致性快照导出(SCN 方式)
        ./obdumper -h xx.x.x.x -P 2883 -u 'user@tenant#cluster' -p '******' -D test_db \
        --table 'sample_tbl' --scn 1700000000000000000 --csv -f /output

        # 仅导出表结构
        ./obdumper -h xx.x.x.x -P 2883 -u 'user@tenant#cluster' -p '******' -D test_db \
        --table 'sample_tbl' --ddl -f /output

        # 导出为 ORC 格式(适用于分析场景)
        ./obdumper -h xx.x.x.x -P 2883 -u 'user@tenant#cluster' -p '******' -D test_db \
        --table 'sample_tbl' --orc -f /output

三、OBLOADER 数据导入

1. 基本导入命令

OBLOADER 支持导入 OBDUMPER 导出的 CSV、SQL、ORC、Parquet 等格式文件,也兼容 mysqldump 导出的 SQL 文件。以下示例将导出的 CSV 文件导入到目标表。

# 基本导入语法
        ./obloader -h 'IP地址' -P 2883 -u '用户@租户名#集群名' -p '密码' -D '数据库名' \
        --table '表名' --csv -f '文件路径' --sys-password '系统租户密码'

        # 使用说明:
        # -f:指定存放 CSV 文件的路径
        # 文件名格式应与表名匹配,例如 sample_tbl.0.0.csv
2. 高级导入选项
  • 字段映射与排除:当数据源表的列数与导入目标表的列数不同时,可以使用 ctrl 文件指定映射关系,或通过 --exclude-column-names 将多余的目标表列排除。
  • 多库导入(V4.3.5+):支持在单次导入任务中一次性导入数据库对象至多个数据库/Schema 中,通过对象表达式(如 test1.tbl1,test2.tbl2)指定导入范围。
  • 导入压缩文件:支持解压缩导入 OBDUMPER 压缩导出的 CSV、SQL、ORC、Parquet 等可读文件格式。
  • 并行导入 DDL(V4.2.1+):支持在 OceanBase 数据库 V4.2.1 及之后的版本并行导入 DDL。
  • 批量提交与预处理:支持批量提交(--batch)和丰富的预处理函数(如 SM3 摘要预处理)。
# 多库导入(V4.3.5+)
        ./obloader -D test --table 'test1.tbl1,test2.tbl2' --csv -f /output

        # 排除目标表中的多余列
        ./obloader -h xx.x.x.x -P 2883 -u 'user@tenant#cluster' -p '******' -D test_db \
        --table 'sample_tbl' --columns "id,name,age" --exclude-column-names "updated_at" \
        --csv -f /input

        # 批量提交优化(窄表可适当增大 batch 值)
        ./obloader -h xx.x.x.x -P 2883 -u 'user@tenant#cluster' -p '******' -D test_db \
        --table 'sample_tbl' --csv -f /input --batch 2000

四、性能调优

OBDUMPER 和 OBLOADER 的性能均可从命令行选项、虚拟机参数和数据库内核等三个方面进行调优。

1. 命令行选项调优

OBDUMPER 核心参数:

参数默认值说明
--thread CPU 逻辑核心数 × 2 导出线程的并发数,根据数据库系统资源的利用情况进行调整
--page-size 1000000 任务分页的大小,根据数据库系统资源利用情况进行调整。注意不要与 Oracle 模式的 --fetch-size 混淆
--fetch-size 1000 Oracle 兼容模式下,每次从数据库游标中读取的行数,仅影响 Oracle 模式
--mem(V4.3.2+) 4G 标识虚拟机的内存大小配置,支持的单位包括 K、M、G 或 T

OBLOADER 核心参数:

参数说明
--thread 并发线程数,系统负载较低时可适当增加以提高导入效率
--batch 批量提交大小,宽表或列中数据较长时需调小(默认 200),窄表或短值时可适当增大
--block-size 文件分块大小(MB),用于逻辑切分原始数据文件,充分发挥 CPU 多核性能
--rw 读写线程比例,0~1 之间,例如 0.4 表示 40% 的写入线程、60% 的读取线程
--mem(V4.3.2+) JVM 内存大小,默认 4G,建议设置为可用物理内存的 60%
2. 虚拟机参数调优

建议将导入/导出脚本中的虚拟机参数修改为可用物理内存的 60%,默认值为 -Xms4G -Xmx4G

vim bin/obdumper
        JAVA_OPTS="$JAVA_OPTS -server -Xms4G -Xmx4G -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M -Xss352K"
3. 数据库内核调优
  • 一致性导出:导出数据前建议手动触发一次合并,在合并成功后再重新导出数据。注意 OBDUMPER 一致性导出暂不支持 OceanBase 数据库 V4.0.0。
  • 导入性能优化:增量内存不足可能触发转储或合并,建议将租户限流阈值设置为高于 90,并将 minor_compact_trigger 设置为较大的值(如 16)以避免导入过程中触发合并。
  • 索引影响:索引会影响数据导入的性能,建议导入时仅设置主键和唯一键,待数据导入完成后再创建普通索引。
# 调整租户限流阈值
        SET GLOBAL writing_throttling_trigger_percentage = 90;

        # 调整 minor_compact_trigger
        ALTER SYSTEM SET minor_compact_trigger = 16;

        # 手动触发合并后导出数据
        ALTER SYSTEM MAJOR FREEZE TENANT = tenant_name;
📌 性能调优注意事项
  • 区分 thread 与 parallel 参数--thread 参数是指客户端到服务端的连接池大小(客户端并发连接数),而 --parallel 参数则是 OBServer 可以调用的工作线程数(服务器端的并行处理能力),两者作用不同。
  • 关注资源使用率:调优时需关注 OBDUMPER/OBLOADER 运行的机器、ODP 服务节点以及 OceanBase 集群中各节点的资源使用率。
  • 避免导出过程中执行合并:合并操作会严重影响导出性能,建议在导出前合并一次,导出期间避免再次触发合并。

五、常见问题与解决方案

问题 1:导出/导入时出现 OOM(内存溢出)

原因:JVM 内存不足或数据量过大。
解决方案

  • 通过 --mem 参数调大 JVM 内存,建议设置为可用物理内存的 60%。
  • 对于超大表,可适当调小 --page-size 以减少单次加载的数据量。
  • 对于 OBLOADER,可通过 --batch 调小批量提交大小,减少内存占用。
问题 2:导出时超时导致任务失败

原因:OBDUMPER 本质上是通过向 OBServer 发起 SELECT 语句来导出数据,大量数据导出可能触发超时。
解决方案

  • conf/session.config.jsonconf/session.properties 中设置会话级别的超时参数,单位为小时。
  • 适当调小 --page-size,减少单次查询的数据量。
问题 3:大小写敏感问题导致文件名匹配失败

原因:OBLOADER/OBDUMPER 命令参数指定的对象名、数据文件名要求大小写一致。MySQL 兼容模式默认表名为小写,Oracle 兼容模式默认表名为大写。
解决方案

  • 将表名放入中括号([])中以区分大小写,例如 --table '[TEST]' 表示 TEST 表。
  • 文件名格式为 表名.group.sequence.suffix,其中 group 表示子任务号,sequence 表示文件滚动号,suffix 表示文件拓展名。
🏆 小结
OBLOADER 与 OBDUMPER 是 OceanBase 生态中高效、专业的数据导入导出工具组合,广泛应用于逻辑备份、数据迁移、数据归档等场景。两者支持丰富的文件格式和存储介质,通过合理的参数调优可以充分发挥 OceanBase 分布式架构的性能优势。在实际使用中,建议先导出小批量数据验证,再逐步扩大数据量,同时关注系统资源使用情况和导出日志,确保任务顺利执行。V4.2.1 及之后版本不再区分企业版和社区版,统一提供一套软件包。
14.4 从 Oracle/MySQL 迁移最佳实践

从传统单机数据库(Oracle/MySQL)迁移至 OceanBase 分布式数据库是一个系统性工程,需要贯穿评估→结构迁移→全量迁移→增量同步→数据校验→业务割接→反向回切的全流程管理。本章结合金融、互联网等行业真实迁移案例,系统梳理从 Oracle 和 MySQL 迁移至 OceanBase 的标准化路径、关键决策点与实战经验。

🎯 本章核心价值
  • 全景路线图:覆盖评估→结构迁移→全量迁移→增量同步→校验→割接→回切的全生命周期。
  • 工具链指引:OMA 兼容性评估、DBCat 结构转换、OMS 数据迁移、obdumper/obloader 导入导出的协同使用。
  • 风险规避:提炼金融级迁移中的兼容性陷阱、性能瓶颈与高可用配置要点。

一、迁移全生命周期路线图

完整的数据库迁移应遵循“先评估、再结构、后数据、保一致、切业务、可回退”的原则,分为以下六大阶段:

阶段核心任务关键产出推荐工具
1. 评估与规划 对象兼容性评估、性能基线采集、分片策略设计 迁移评估报告、分片键设计方案 OMA、DBCat、EXPLAIN 分析
2. 结构迁移 Schema 转换与适配(表/索引/约束/视图/存储过程) 目标端 Schema 就绪 DBCat、OBLoader/OBdumper
3. 全量迁移 存量数据迁移,支持并行导出/导入 源端数据完整复制至目标端 OMS(全量迁移)、OBdumper/OBLoader
4. 增量同步 基于日志的变更数据捕获与实时同步 源端与目标端数据保持准实时一致 OMS(增量同步)、Store/Incr-Sync 组件
5. 数据校验与割接 全量/增量数据一致性校验、业务切换准备 校验报告、割接方案 OMS Full-Verification、CHECKSUM TABLE
6. 反向增量与回切 建立目标端→源端的反向同步链路 紧急回切能力 OMS 反向回流链路

二、迁移评估:OMA 兼容性分析

OceanBase Migration Assessment(OMA)是迁移前的“体检中心”,提供精确的兼容性评估、性能评估、分布模式建议和应用改造建议。

🔍 OMA 核心能力
  • 兼容性评估:自动化采集源端数据库对象(表、索引、约束、视图、存储过程等),评估 OceanBase 兼容性并输出不兼容项清单及改造建议[reference:9]。
  • 性能评估:基于源端 SQL 负载评估 OceanBase 的预期性能表现。
  • 分布模式建议:根据数据访问模式推荐合适的分区键和分片策略。
  • 应用改造建议:针对不兼容对象,提供代码改写提示,减少上线后意外报错。
# OMA 运行示例(Linux 环境)
        ./oma -s <源端地址> -u <用户名> -p <密码> -d <数据库名> -t <目标类型>

        # 查看评估报告(生成 oma.sqlite 文件,通过浏览器打开)
        # 选择评估报告文件,查看兼容性详情和不兼容项清单
⚠️ 迁移评估的关键价值
  • OMA 并不能修复所有兼容性问题,其核心作用是提前识别风险,为后续手工适配提供方向。
  • Oracle 迁移需重点评估:PL/SQL 中需改造的系统包调用复杂嵌套类型外部 Java 调用字典视图依赖等。
  • MySQL 迁移需重点评估:存储引擎差异字符集兼容性自增列行为分区语法差异等。

三、对象兼容性适配

结构迁移是决定后续数据迁移能否顺利的基础。以下是 Oracle 和 MySQL 迁移中最常见的不兼容点及改造建议。

1. Oracle → OceanBase(Oracle 模式)
不兼容点改造建议注意事项
LONG/LONG RAW 类型 转换为 CLOB/BLOB 或普通表字段 OceanBase 暂不支持,必须提前转换,迁移前建议使用 OMA 扫描定位[reference:10]
ROWID/UROWID 伪列 改用业务主键进行行定位 迁移后存储路由不同,原 ROWID 无实际意义
外键约束(ON DELETE SET NULL 等) 业务逻辑改造,将一致性校验下沉至应用层或使用对账任务 分布式环境下物理外键可能影响分布式 DDL 和 DML 性能,强烈建议移除
物化视图 ON COMMIT 刷新 改为定时刷新或应用层缓存 OceanBase 不支持实时刷新(ON COMMIT 模式)[reference:11]
分区语法差异 使用 OceanBase 兼容的 PARTITION BY RANGE/LIST/HASH 语法 OceanBase 分区能力远超 Oracle,但语法细节存在差异,需验证
存储过程/函数中的自治事务 使用 PRAGMA AUTONOMOUS_TRANSACTION 需验证或改造 自治事务移植有限制,复杂逻辑建议用独立连接重写
2. MySQL → OceanBase(MySQL 模式)
不兼容点改造建议注意事项
物理外键约束 将数据一致性校验逻辑下沉到应用代码或通过定时对账任务保证 分布式数据库的最佳实践是移除外键,避免跨节点校验开销
MyISAM 存储引擎 转换为 InnoDB 兼容表 OceanBase 存储引擎为自研 LSM-Tree,与 MyISAM 差异大,需验证 SQL 兼容性
GROUP BY 非严格模式依赖 修正 SQL,使其符合 ONLY_FULL_GROUP_BY 语义 OceanBase 与 MySQL 在 GROUP BY 语义上存在差异,不可依赖非标准写法
部分特有函数/语法 使用等价语法或应用层计算替代 GROUP_CONCATJSON_EXTRACT 等可能存在行为差异
字符集差异(如 utf8mb4_0900_ai_ci 排序规则) 建库/建表时显式指定与源端一致的字符集和排序规则 避免因字符集差异导致索引失效或查询报错[reference:12]
💡 架构升级的最佳窗口:迁移不仅是数据库引擎的替换,更是对历史遗留设计进行优化的最佳时机。建议在结构迁移阶段同步推动架构改造,移除物理外键、规范 SQL 写法,为分布式环境下的扩展性铺平道路。

四、数据迁移工具链对比

OceanBase 生态提供多种数据迁移工具,应根据源端类型、数据量、业务停机时间要求选择合适工具链。

工具对比矩阵
工具适用场景增量同步结构迁移数据校验部署复杂度
OMS 异构数据库大规模迁移,需要增量同步和不掉线割接 ✅ 支持 ✅ 自动 ✅ 内置 中(需独立部署)
OBdumper/OBLoader 同构迁移、逻辑备份恢复、离线批量导入导出 ❌ 不支持 ✅ 支持(--ddl 选项) ❌ 需手动 低(仅需 Java 环境)
mysqldump MySQL 小数据量迁移或紧急导出 ❌ 不支持 ✅ 支持 ❌ 需手动 极低
DataX 异构数据源离线批量同步 ❌ 不支持 ❌ 不支持 ❌ 需手动

建议:需要不掉线迁移的正式生产环境,优先选用 OMS;小批量数据或测试环境可选用 OBdumper/OBLoader。

五、OMS 迁移部署与任务配置

1. OMS 部署模式

OMS 企业版支持单节点、单地域多节点、多地域多节点三种部署模式,生产环境建议采用多节点部署以确保高可用。

# OMS 高可用部署建议
        # 1. 至少部署 2 个 OMS 节点,避免单点故障
        # 2. 将 OMS MetaDB 部署在高可用数据库中
        # 3. 配置 VIP 或负载均衡作为控制台入口
        # 4. 提前为 InfluxDB 时序数据库预留资源,以收集和展示历史监控数据
2. 源端和目标端权限要求

迁移前必须为 OMS 创建专用的数据库用户并授予必要权限:

  • Oracle 源端:需要 SELECT ANY TABLESELECT on V$LOGMNR_CONTENTSEXECUTE ON DBMS_LOGMNR 等权限。
  • MySQL 源端:需要 SELECTREPLICATION SLAVEREPLICATION CLIENTSHOW VIEWPROCESS 等权限。
  • OceanBase 目标端:需要 CREATEINSERTUPDATEDELETEALTERINDEXCREATE VIEWSHOW VIEWPROCESS 等权限。建议使用 root@sys 或具有相应权限的普通租户账号。
3. 迁移任务性能优化参数

根据数据量和硬件配置调整以下 OMS 参数可显著提升迁移效率:

参数默认值调优建议
fullImport.threads 约 10(随资源配置变化) 建议与目标端 CPU 核心数匹配,避免线程过载
incrSync.threads 约 6 增量写入线程数,与目标端写入能力匹配,避免激化转储/合并
store.threads 约 6 源端日志拉取并发线程数,根据源端 LogMiner/Binlog 产出能力调整
batch.size 约 600 批量提交行数,宽表场景适当调小、窄表可适当增大
索引后置 N/A 全量迁移前移除普通索引,迁移完成后重建,可大幅提升导入性能
📌 网络与带宽规划
  • 增量同步依赖源端数据库的日志保留时长,建议 MySQL Binlog 保留 7 天 以上,Oracle 归档日志保留足够长时间。
  • 源端和目标端之间的网络延迟应控制在 100ms 以内,否则增量同步延迟会显著增大。
  • 建议源端与目标端处于同一 VPC 或使用专线连接,避免公网传输带来的不稳定性和延迟。

六、高性能迁移实战

1. 全量迁移性能优化
  • 索引后置策略:在全量迁移开始前,删除目标端表的普通索引和二级索引,仅保留主键和唯一键。全量数据导入完成后,再分批重建索引,可以显著提升数据导入效率。
  • 并行度调优:根据源端 I/O 能力和目标端 CPU 核心数,合理设置 fullImport.threads。每增加一个线程会多消耗约 1 核 CPU 和对应网络带宽。
  • 分批迁移:建议按表或按数据范围划分批次,而非一次性迁移所有对象。迁移成功的对象应尽快纳入业务验证,避免因单对象失败影响整体进度。
2. 增量同步优化
  • 目标端写入限流:增量同步可能因瞬间大事务或高负载触发目标端合并(Major Compaction),拉高 CPU 并增加同步延迟。建议将租户限流阈值 writing_throttling_trigger_percentage 调高至 80~90,合并参数 minor_compact_trigger 适当增大(如 16),避免频繁触发合并。
  • 反压机制:当目标端写入能力不足时,OMS 会自动反压源端日志拉取,防止源端日志积压被清理。若发现增量延迟持续增长,优先排查目标端写入性能。
-- 目标端租户参数调优(sys 租户或用户租户执行)
        ALTER SYSTEM SET writing_throttling_trigger_percentage = 85;
        ALTER SYSTEM SET minor_compact_trigger = 16;

        -- 查看目标端合并状态,防止合并阻塞写入
        SELECT * FROM oceanbase.DBA_OB_MAJOR_COMPACTION;

七、数据校验与割接准备

1. OMS 内置数据校验

OMS 支持三种校验类型,应在全量迁移完成后执行全量校验,增量期间持续运行行数校验或增量校验:

  • 全量校验:对源端和目标端全表数据进行全字段比对,精度最高但耗时较长,适合业务割接前的最终确认。
  • 行数校验:仅比对行数,速度快,适合增量期间持续监控。
  • 增量校验:比对增量变更数据,适合验证同步链路的正确性。

当 Full-Verification 组件校验发现数据不一致时,可根据 filter.verify.rectify.type 参数决定是否自动修正。正常场景建议保持默认值 no,仅输出差异报告而不自动修改目标端数据。

2. 手动数据校验:CHECKSUM TABLE

OceanBase 提供 CHECKSUM TABLE 命令,可快速计算表校验和,用于快速验证表级一致性。注意该命令会逐行读取整个表,对超大表执行时可能消耗较多 I/O 资源。

-- 计算源端和目标端同一表的校验和,进行对比
        CHECKSUM TABLE orders;

        -- 返回结果示例
        +--------------+-------------+
        | Table        | Checksum    |
        +--------------+-------------+
        | test.orders  | 1234567890  |
        +--------------+-------------+
3. 割接前检查清单
  • 数据一致性确认:至少完成一次全量校验,所有差异项已分析并解决。
  • 反向链路就绪:已配置并验证反向回流链路的连通性和数据一致性。
  • 应用连接验证:应用侧已完成 OceanBase 连接测试和功能回归验证。
  • 高可用配置:确认目标端租户的 PRIMARY_ZONE 和 Locality 配置符合业务高可用要求。
  • 监控告警就绪:OCP 或自定义监控已配置关键指标告警(延迟、QPS、资源使用率等)。
  • 回退预案:明确割接失败后的回切步骤和预期 RTO。

八、业务割接与反向回流

1. 割接流程
  1. 停止源端写入:短暂停止源端业务写入,或暂停 OMS 增量同步链路(确保源端日志消费完成)。
  2. 最后一次全量校验:执行最终全量校验,确认源端与目标端数据完全一致。
  3. 启动反向回流链路:创建 OMS 反向回流任务,将目标端增量实时同步回源端,确保回切能力就绪。
  4. 切换应用连接:将应用数据库连接切换至 OceanBase(建议通过配置中心、DNS 或连接池动态切换)。
  5. 业务验证:执行核心业务功能验证,监控关键指标,确认无异常。
  6. 保留源端观察期:建议保留源端数据库至少 3 个月,确保紧急回切能力;同时可在回切演练中验证反向链路稳定性。
2. 反向回流配置要点

反向回流的目的是支持紧急回切。在业务切换至 OceanBase 后,将 OceanBase 的增量变更实时同步回源端数据库,当 OceanBase 出现严重问题时可快速回切至源端。需要注意的是,OMS 不支持从低于 12c 版本的 Oracle 数据库进行反向增量迁移[reference:13]。

# 反向回流配置关键点
        # 1. 创建反向迁移任务时,源端为 OceanBase,目标端为原 Oracle/MySQL
        # 2. 结构迁移阶段:目标端(原库)需要提前创建与 OceanBase 兼容的表结构
        # 3. 增量同步阶段:需关注原库的日志保留策略,确保反向链路可持续
        # 4. 建议在割接前完成反向链路的功能验证,确认数据同步正常
⚠️ 反向回流的性能考量:从 OceanBase 回写原库时,UPDATE、DELETE 操作默认采用全列匹配方式,可能影响性能。对于 Oracle 回流,若原库存在主键,性能基本可接受;对于无主键表,延迟可能显著增大,建议在迁移前为表添加主键。

九、回切方案

当 OceanBase 端出现不可恢复问题时,可通过以下步骤快速回切至原数据库:

  1. 停止应用对 OceanBase 的写入,避免割接期间出现新数据。
  2. 确认反向链路延迟已消费完毕,确保源端已包含 OceanBase 端的全部增量数据。
  3. 执行最后一次反向校验,确认源端数据正确性。
  4. 切换应用连接至原数据库,恢复业务。
  5. 分析 OceanBase 故障根因,修复后重新规划迁移窗口。

建议在割接后的观察期内,保留反向链路持续运行,并定期演练回切流程,确保关键时刻可执行。

十、常见问题与处理建议

问题 1:结构迁移时报错对象已存在或不兼容

原因:目标端存在同名对象,或 OMS 自动转换后的 DDL 语法不兼容。
解决方案

  • 在 OMS 控制台迁移任务详情中,筛选失败对象,查看具体报错信息。
  • 手动修改目标端结构或使用 DBCat 单独转换不兼容对象,重新执行结构迁移。
  • 对于 Oracle 迁移中的 PL/SQL 不兼容,可评估是否在迁移过程中跳过,后续在 OceanBase 端用等价逻辑重写。
问题 2:全量迁移耗时过长

原因:线程数配置偏低、网络带宽受限、目标端写入能力不足。
解决方案

  • 适当调大 fullImport.threads,但需监控源端 I/O 和 OBServer CPU 使用率。
  • 使用旁路导入功能(Direct Load)提升数据写入效率。
  • 对于超大表,可采取“按范围导出+分批导入”的策略,分散负载。
问题 3:增量同步延迟持续增大

原因:目标端写入能力不足、网络延迟高、源端日志产出过快。
解决方案

  • 检查目标端是否正在执行合并,合并期间 CPU 占用高,增量同步延迟会自然增大。可通过 OCP 监控或 DBA_OB_MAJOR_COMPACTION 视图确认。
  • 适当调大 writing_throttling_trigger_percentageminor_compact_trigger,降低合并频率。
  • 检查网络延迟,确保源端与目标端之间网络质量稳定。
问题 4:数据校验发现差异

原因:增量同步中的大事务未完整同步、时区/字符集差异、异构数据类型转换误差。
解决方案

  • 首先排查增量链路状态,确认无积压或阻塞。
  • 针对差异记录进行明细比对,定位差异字段和原因。
  • 若为数据同步误差,可手动订正或重建校验任务。
  • 对于因时区不同导致的校验差异,可在 Full-Verification 组件配置中添加 "datasource.timezone" : " +08:00" 参数确保使用相同时区进行比对。
🏆 小结
从传统数据库迁移至 OceanBase 是一条复杂但可标准化的路径。通过 OMA 前置评估、DBCat 结构适配、OMS 全量+增量迁移、内置数据校验以及反向回流能力,企业可以实现低风险、不掉线的数据库升级。迁移不仅是技术替换,更是优化分布式数据库设计、提升系统扩展性的契机。

第15章:OceanBase 运维工具体系

15.1 OCP 集群管理平台

OceanBase 云平台(OceanBase Cloud Platform,OCP)是伴随 OceanBase 数据库而生的企业级数据库管理平台,以 OceanBase 为核心,提供集群、租户、主机等维度的全生命周期统一管理。最新版本的 OCP 不仅能全面支持 OceanBase V1.4 ~ V4.x 等主流版本,还覆盖了常见的主机管理、OceanBase 集群和租户运维、备份恢复等功能,提供了精细的监控告警能力。[reference:0]

🎯 OCP 的核心定位
  • 降低运维复杂度:将数据库管理任务从复杂的命令行操作转变为直观的图形化操作,显著提升运维效率。
  • 全生命周期管理:提供 OceanBase 集群和租户的全生命周期管理功能,包括集群的安装、运维、性能监控、配置、升级和删除等功能。
  • OBProxy 管控:支持 OBProxy 集群的完整生命周期管理,包括创建、升级、扩缩容等操作。
  • 开放生态:提供开放 API,支持与第三方监控、运维系统集成。

一、企业版与社区版功能差异

OCP 分为企业版和社区版两个版本,面向不同用户群体提供了差异化的功能支持。企业版面向有 OceanBase 管理需求的企业级用户,提供强大的全生命周期管理能力;社区版面向有 OceanBase 管理需求的中小型用户,提供了基础的主机管理、OceanBase 集群和租户运维等能力。[reference:1]下表详细对比了两版本的功能差异:

功能分类功能特性企业版社区版
OceanBase 集群管理 集群生命周期管理(创建、删除)
集群升级
集群扩缩容
集群主备库管理
仲裁服务管理
集群参数管理
集群性能报告
OceanBase 租户管理 租户生命周期管理
Oracle 租户兼容
主备租户管理
租户级 SQL/事务诊断
活跃会话历史(ASH)报告
租户备份恢复
OBProxy 集群管理 集群生命周期管理
集群升级/扩缩容
请求分析
主机管理 Region/IDC 管理
OCP-Agent 管理
监控告警 集群/租户/主机多维度监控
备份恢复 数据备份与恢复
高可用架构 基于两 IDC 的高可用冗余架构
开放 API 开放 API 与生态互联
💡 社区版 OCP 面向中小型业务量的用户,为保证您能够在有限的资源上独立部署和使用,我们对 OCP 进行裁剪,保留了基础功能以满足您对 OceanBase 集群和租户运维的基本需求。如您的业务相对复杂,需要对 OceanBase 集群及租户有更强的管理、监控和诊断能力,您可联系 OceanBase 官方人员获取企业版 OCP。

二、OCP Express:集群内置的轻量版

OCP Express 是与 OceanBase 集群集成的轻量级管理工具,允许您查看关键性能指标并对 OceanBase 集群执行基本的数据库管理操作。OCP Express 提供了租户管理功能,允许您在集群中管理租户,例如租户创建、租户删除、数据库管理和参数管理。

当您的环境资源有限、不想部署完整的 OCP 集群时,OCP Express 是一个理想的选择。它天然支持 4.x 架构,适合快速了解集群运行状态和进行基础运维操作。[reference:2]它适用于无需完整平台功能、仅需核心监控与运维的场景,同时也是小规模部署或测试环境的推荐工具。

三、部署架构

1. 部署模式

OCP 支持三种部署路径:一是直接使用 OBD 配置文件形式部署;二是通过 OBD WEB 命令启动一个 Web 安装向导,以更直观的图形化方式引导用户完成 OCP 的部署;三是通过 OAT 进行可视化部署。[reference:3]

  • 单节点模式(社区版):社区版 OCP 采用单机模式,适用于小型应用和测试环境,面向个人开发者、中小型企业和个人用户,提供基本的数据库功能和服务。
  • 分布式模式(企业版):企业版 OCP 采用分布式架构,适用于大规模的企业级应用和生产环境。企业版基于两 IDC 提供高可用冗余架构,保障管理平台的连续性。
2. 资源规划

部署社区版 OCP 需要规划主机数量、CPU 和内存等资源。以下是推荐的资源规划基准(以管理 ≤10 台 OBServer 为例):

部署组件用途说明CPU(核)内存(GiB)
ocp-server OCP 应用服务 4 8
ocp-metadb 租户 存储集群元数据、用户信息等持久化数据 4 8
ocp-monitordb 租户 存储监控指标数据 4 16
sys 租户 OceanBase 系统租户 5 28
📌 资源规划说明
  • MetaDB 用于存储除监控数据外的其它持久化数据,推荐在 OceanBase 中创建独立的租户用于 MetaDB,并最低分配给该租户 4C、8GiB 内存
  • MonitorDB 用于存储采集的各种性能数据(主要是 OceanBase 集群的数据),推荐在 OceanBase 中创建独立的租户用于 MonitorDB,并最低分配给该租户 4C、16GiB 内存
  • OCP-Server 机器要求的最低配置为 4C 8GiB 以及 500GiB 可用磁盘空间
  • 若将 OCP 与目标 OceanBase 集群混部,需额外评估资源预留,避免因资源争抢影响集群稳定性。

四、命令行生态:obshell

obshell(OceanBase Shell)是 OceanBase 为运维人员和开发人员提供的免安装、即插即用的本地集群命令行工具,支持集群和租户的运维、监控、备份恢复、测试以及工具集等常见命令。[reference:4]它支持通过 OBShell-SDK(Python/Go)或 obshell 命令接管 OCP 部署的集群,并允许与第三方平台集成。

1. obshell 与 OCP 的关系

obshell 与 OCP 的关系更像是一种“互补与分层”的关系,而非简单的替代。OCP 提供中心化的可视化管控能力,适用于平台化管理与多集群监控;obshell 则提供本地化的命令行运维能力,在自动化脚本、本地问题诊断等场景中优势明显。

  • 使用定位:当您需要跨多集群、统一的管控体验时,建议以 OCP 企业版 作为主管理平台;当您在本地开发测试、轻量运维或仅有单集群需求时,可选择 obshell + OBD 组合,兼顾易用性与灵活性。
  • 开源免费:obshell 已全面开源,所有用户均可免费下载使用,无需额外授权。
2. 功能定位
  • 轻量级部署:无需额外安装,随 OceanBase 安装包一并提供(位于 /home/admin/oceanbase/bin/obshell),免安装即插即用。
  • 集群运维能力:支持集群启停、节点管理、扩缩容、升级等运维操作。
  • 租户管理:支持租户创建、删除、修改、参数管理等操作。
  • 备份恢复:支持集群级和租户级的物理备份与恢复。
  • 开放 API:基于 OBServer 节点提供 RESTful API,供第三方产品接入。

如果您熟悉 MySQL 或 Oracle 的命令行运维习惯,obshell 可让您在 OceanBase 中获得类似的操作体验。

五、监控与告警体系

OCP 的监控体系设计遵循分层采集、统一存储、灵活告警的原则,为 DBA 提供了从主机到数据库的全方位可观测能力。

1. 监控数据采集链路

OCP 的监控数据采集主要分为三个链路:

  • Metric 链路(常规监控指标):采集 OBServer 节点状态、obproxy 状态、主机指标等。依赖部署在主机上的 OCP-Agent 中的 ocp_exporter 程序,对外提供 Prometheus 规范的监控指标。内部依赖 NodeExporter(主机监控)、OBProxyExporter(obproxy 监控)、OBCollector(OceanBase 监控)来采集多种指标。采集到的指标会汇总转换后保存到监控库(MonitorDB),监控计算引擎根据 Prometheus 表达式查询监控库并计算后返回给客户端展示图表。
  • OB SQL 链路:采集 OceanBase 数据库相关的 SQL 指标和执行计划信息。主要通过动态视图 v$sql_audit(记录 SQL 的执行审计信息)、v$plan_cache_plan_explain(记录执行计划每个算子的信息)和 v$plan_cache_plan_stat(记录执行计划的审计信息)采集数据。考虑到数据量较大,该链路使用高性能、轻量级的 C++ 程序 obstat2 进行采集,并在本地汇总计算后存入 MonitorDB。
  • OB 资源水位链路:采集 OceanBase 集群、租户的资源水位信息,包括 CPU 总核数/已分配核数(GV$OB_SERVERS)、内存总大小/已使用大小(GV$OB_SERVERS)、磁盘总大小/已使用信息以及系统事件信息(DBA_OB_ROOTSERVICE_EVENT_HISTORY)等。
2. 告警体系

OCP 内置了约 60 个告警项,每个告警项描述了告警的基本信息,如告警名称、等级、概述模板、详情模板等,以及告警检测相关的规则信息。

根据告警的风险程度,OCP 划分了 5 个告警等级

  • 停服:最高级别,表示服务已不可用
  • 严重:表示严重故障,需立即处理
  • 警告:表示存在潜在风险,建议关注
  • 注意:提示性信息,可供参考
  • 提醒:低级别通知

告警规则配置支持通过指标表达式触发告警。系统每个检测周期检测一次监控指标,默认为 60 秒。当指标值超过默认阈值并持续达到设定持续时间时,系统上报告警。告警支持多阈值配置,为 OceanBase 集群的性能及稳定提供了更加坚实的保障。

告警通道:支持钉钉、微信、邮件、Webhook 等多告警渠道,确保问题在第一时间被发现与处理。

3. 监控指标概览

OCP 具备 200+ 监控指标与告警规则,涵盖以下维度:

  • 集群级别:CPU 使用率、内存使用率、QPS/TPS、合并状态等
  • 租户级别:SQL 响应时间、活跃会话数、MemStore 使用率、事务提交延迟等
  • 主机级别:CPU/内存/磁盘 IO、网络吞吐、负载等
  • OBProxy 级别:请求量、连接数、路由延迟等

六、管理功能一览

OCP 覆盖了 OceanBase 数据库生态的各个管理维度,具体包括:

  • 集群管理:支持集群的安装、运维、性能监控、配置、升级和删除等全生命周期管理。V4.x 推荐使用 OCP 创建和管理集群,支持创建分布式集群,满足金融级高可用和水平扩展能力。
  • 租户管理:支持租户创建、资源规格配置、租户参数管理、租户备份恢复、租户克隆等全生命周期管理。从 V4.3.0 版本开始,OCP 支持创建克隆租户。
  • OBProxy 管理:支持 OBProxy 集群的创建、配置、监控和升级等全生命周期管理。[reference:5]
  • 备份恢复管理:支持集群级和租户级的全量备份、增量备份及 PITR(时间点恢复)。
  • 主机管理:支持主机添加、删除、标签管理、OCP-Agent 管理等。
  • 诊断调优:提供 SQL 诊断、慢查询分析、性能报告、SQL 审计、等待事件分析、ASH 报告等全面的诊断能力。
📌 OCP 与其他运维工具的关系
  • OCP(企业版):企业级集中化管控平台,核心管理平台
  • OCP 社区版:轻量级管理平台,降低门槛的替代方案
  • OCP Express:集群内置轻量管理,集群自带,免部署[reference:6]
  • OBD:集群命令行部署工具,配合使用,部署或运维底层
  • obshell:本地集群命令行运维,命令行互补,支持 API 集成
  • ODC:Web SQL 开发工具,开发与管控分离

OCP 社区版因面向中小型业务量的用户,保证了您能够在有限的资源上独立部署和使用。如果您需要更强的管理、监控和诊断能力,建议升级到企业版 OCP。

🏆 小结
OCP 作为 OceanBase 生态的核心管理平台,提供了从安装部署、日常运维到监控告警、诊断调优的全方位能力。企业版与社区版的差异化设计,使不同规模的企业都能找到适合自己的管理方案。对于企业级生产环境,建议采用 OCP 企业版,以充分发挥其强大的管理能力和高可用架构;对于开发测试环境或中小规模部署,OCP 社区版或 OCP Express 则是更加轻量、便捷的选择。
15.2 ODC 数据库开发工具

OceanBase 开发者中心(OceanBase Developer Center,ODC)是 OceanBase 数据库官方推出的图形化开发工具与研发变更管控协同平台。ODC 以 SQL 开发为核心,不仅提供数据库对象管理、数据导入导出、PL 编译与调试等基础工具能力,更内置了变更风险管控、数据安全合规、数据生命周期管理等一系列企业级管控能力,实现了数据库开发与生产变更的一站式协同管理。

🎯 ODC 的核心定位
  • 降低开发门槛:提供可视化的对象管理、拖拽生成 SQL、类 Excel 的结果集编辑、语法高亮与自动补全等能力,帮助开发者快速上手。
  • 保障变更安全:内置 SQL 检查、窗口拦截、审批流程等机制,将数据库变更风险控制在生产环境之外。
  • 实现一体化协同:DBA 统一录入数据源并授权,开发者通过工单申请权限和执行变更,所有操作均可审计。

一、产品架构与两种形态

ODC 提供桌面版Web 版两种产品形态,分别满足个人开发和企业协同两类场景需求。

💻 桌面版:轻量化的个人开发工具

桌面版侧重高效的数据库开发能力,支持 Windows、Mac、Linux 操作系统,具有轻量化和易部署的特性。

  • 本地安装即用:无需依赖服务器,适合个人开发者本地调试、测试和日常开发。
  • 聚焦开发效率:提供 SQL 编辑与执行、PL 编译与调试、数据导入导出、对象管理等核心开发功能。
🌐 Web 版:企业级的管控协同平台

Web 版是 ODC 的主力形态,支持个人空间团队空间两种工作模式。

  • 团队空间:面向企业与团队,DBA 统一录入数据源并授予访问权限,开发者仅可见已授权的数据库,避免账号密码泄露。ODC 还通过项目(Project)机制实现最小协同单元的管理,团队成员在项目内共享数据库资源,通过工单(Ticket)完成数据导出、导入、数据库变更等任务,所有操作可追踪和审批。
  • 个人空间:面向个人开发者,提供灵活的数据源管理能力,无需复杂配置即可开箱即用。
🚀 部署方式
  • Web 版 ODC:原生支持基于 Docker 的容器化部署。
  • 桌面版 ODC:下载安装包直接运行,无需额外服务器资源。

ODC 完美支持 OceanBase 的 MySQL 模式和 Oracle 模式数据库,也支持连接原生 MySQL 数据库。

二、核心功能体系

ODC 围绕开发、管控、安全、协同四大维度构建了完整的功能体系。

1. 高效开发
  • 对象管理:以可视化方式管理表、视图、函数、存储过程等数据库对象。
  • SQL 编辑与执行:支持语法高亮、自动补全、代码片段定义与引用、拖拽对象生成 SQL 语句。
  • PL 编译与调试:支持存储过程、函数、程序包的编译与调试,帮助开发者快速排查 PL 逻辑问题。
  • 数据导入导出:支持批量导入导出,支持 SQL 文件、CSV 等多种格式。
  • 结果集编辑:类 Excel 的编辑体验,支持直接修改数据并提交。
  • 测试数据快速生成:一键生成符合表结构的测试数据,简化开发准备流程。
2. 变更风险管控
  • SQL 检查与窗口拦截:DBA 可定义 SQL 检查规则和环境差异的拦截策略。
  • 分级审批流程:根据操作风险等级,提供“建议→审批→拦截”三级管控,自动向 DBA 发起审批工单,杜绝任意 SQL 上线。
  • 无锁结构变更:支持无锁方式修改表结构,避免 DDL 阻塞业务写入。
  • 操作审计:所有功能操作和 SQL 请求均被记录到操作记录中心。
3. 数据安全与合规
  • 动态数据脱敏:内置完善的脱敏算法,在 SQL 窗口访问、数据导出等场景下自动对敏感数据进行脱敏处理。
  • 细粒度审计:支持 SQL 审计和操作审计,所有操作留痕可追溯。
  • 权限隔离:DBA 统一录入数据源,开发者仅可见已授权的数据库;项目 OWNER、DBA、DEVELOPER 三类角色权限清晰隔离。
4. 数据生命周期管理
  • 分区计划:支持自动预创建和删除过期的 RANGE 分区和 RANGE COLUMNS 分区,实现 RANGE 分区的自动维护,无需人工干预。
  • 数据清理:通过定时任务定期清理业务表中的过期数据,实现在线库瘦身。
  • 数据归档:配置灵活的归档条件,实现冷热数据分离,降低在线库的存储压力。
  • 影子表同步:支持根据源表结构自动创建影子表,适用于全链路压测等场景。
5. 开放集成能力
  • 单点登录(SSO):支持 OAuth2、OIDC 协议,与企业统一身份认证系统对接。
  • 工单审批集成:支持对接 BPMS 或企业自定义审批系统,适配企业已有的审批流程。
  • SQL 审核集成:支持对接企业已有的 SQL 审核平台,对 SQL 语句进行二次审核。
  • 堡垒机集成:支持基于一次性 Token 的认证和数据库账密自动填写,保障数据库访问安全。
6. 协同效率
  • 项目协同:以项目为单位组织数据库、成员和敏感数据,协同工作无需复杂权限配置。
  • 自动授权:支持配置自动授权规则,用户满足条件时自动授予相应权限。

三、企业版与社区版功能差异

ODC 分为社区版和企业版两个版本。社区版免费使用,已包含核心的数据库开发和基础管控能力;企业版在社区版基础上提供更完善的企业级集成能力和高级管控特性。

功能分类功能特性社区版企业版
核心开发 SQL 编辑与执行、对象管理、PL 调试、数据导入导出
变更管控 SQL 检查、工单审批、无锁结构变更
数据安全 动态数据脱敏、操作审计
数据生命周期 分区计划、数据清理、数据归档
协同集成 项目协同、自动授权
SSO 集成 OAuth2/OIDC 单点登录
审批流程对接 对接企业 BPMS/自定义审批系统
SQL 审核集成 对接第三方 SQL 审核平台
堡垒机集成 一次性 Token 认证、账密自动填写
💡 社区版 ODC 已满足绝大多数个人开发及中小团队的基础管控需求。如需 LDAP/OAuth 统一登录、对接企业审批流程、堡垒机安全访问等高级企业级集成能力,建议升级到企业版 ODC。

四、最佳实践与生产建议

场景一:DBA 统一管控的团队开发模式

推荐使用 Web 版 ODC,运行以下工作流程:

  • DBA 在 ODC 中创建数据源并录入数据库账号密码。
  • 创建项目,将数据库加入项目。
  • 邀请开发者加入项目并按需分配角色(DBA/DEVELOPER 等)。
  • 开发者通过 ODC 申请数据库权限,DBA 审批后开发者即可在授权范围内进行开发。
  • 所有 SQL 变更通过工单系统提交,经审批后执行。
场景二:个人开发者本地调试

推荐使用 桌面版 ODC,快速安装后连接本地测试环境或开发环境,即可开展开发和调试工作。

生产环境安全守则
  • 生产环境始终使用 Web 版 ODC,确保所有变更经过审批流程。
  • 禁止直接在生产数据库上执行未经审批的 SQL。
  • DBA 统一录入数据源,开发者严禁获取数据库账号密码。
  • 开启 SQL 审计和操作审计,确保所有操作可追溯。
  • 敏感数据查询场景配置数据脱敏策略。
  • 根据业务高峰低谷合理配置分区计划的执行时间。
📌 ODC 与其他工具的关系
  • vs OCP:OCP 负责集群和租户的运维管理(监控、备份、扩容等),ODC 专注于数据库内部的对象开发、SQL 执行和变更管控。两者共同构成 OceanBase 的管控生态。
  • vs 传统 SQL 客户端:ODC 在提供 SQL 编辑执行的基础上,增加了企业级协同管控能力(工单审批、脱敏、生命周期管理),更适合团队协作。
🏆 小结
ODC 是 OceanBase 数据库生态中面向开发者的核心工具。从个人开发的桌面版,到企业级管控协同的 Web 版,ODC 真正打通了数据库开发的“最后一公里”——让开发者专注写 SQL、DBA 专注管安全,通过内置的审批流程确保每一次数据库变更都安全合规。无论是个人开发者还是大型企业团队,都能在 ODC 中找到适配的开发与管控方案。
15.3 obdiag 自动化诊断与巡检

obdiag(OceanBase Diagnostic Tool) 是 OceanBase 社区官方开源的 轻量级、自动化诊断与巡检工具。它专为 OceanBase 4.x 设计,以 CLI 命令行方式提供一键式集群健康巡检、故障场景诊断、日志与堆栈信息采集等功能。在生产环境中,obdiag 能够帮助 DBA 和 SRE 快速完成“健康体检”、缩短平均修复时间(MTTR),是 自动化运维和应急响应体系中不可或缺的辅助工具

🚀 obdiag 的核心价值
  • 一键集群巡检(obdiag check run:支持从系统内核参数、内部表等方式对 OceanBase 集群进行分析,发现已存在或可能导致集群异常的问题原因并提供运维建议。[reference:0]
  • 一键根因分析(obdiag rca run:内置断连诊断、卡合并诊断、内存爆排查、悬挂事务分析等 20+ 常见故障场景,直接输出根因定位。[reference:1]
  • 一键信息收集(obdiag gather:针对特定问题自动采集日志、堆栈、SQL Audit 等诊断数据,形成标准化数据包。
  • 零侵入 · 只读 · 安全:所有操作仅包含查询视图、读取日志文件及系统命令,不会修改任何集群状态或参数。
  • 智能体即将引入:obdiag agent 即将到来,未来将引入 AI 智能辅助诊断能力,进一步提升运维效率。[reference:2]

一、架构原理与工作模式

obdiag 采用 “Python 轻量引擎 + 插件化任务” 的设计,支持 check(巡检)、gather(信息收集)、rca(根因分析)、display(实时信息洞察) 四大核心功能,每类功能均可独立扩展。

  • 接入层:CLI 命令解析,参数校验,配置文件加载。
  • 执行层
    • 巡检引擎:动态加载巡检任务(task),通过 SSH/SQL 等多步骤并行执行并汇聚结果。[reference:3]
    • 根因分析引擎:支持基于内部表数据和日志的场景化根因分析(如断连诊断、锁冲突诊断、内存爆排查等)。[reference:4]
    • 收集引擎:通过数据库会话、SSH 等方式采集日志、堆栈、系统表快照,打包成压缩文件。
  • 数据源适配层:支持直连 OceanBase(通过 MySQL 协议)、SSH 远程主机(支持密码/密钥)等多种数据源。
📌 对于 OceanBase 4.x 的适配增强
obdiag 深度适配 4.x 的“日志流(Log Stream)”、“Tablet”以及“仲裁服务”等新特性,巡检项中包含对日志流健康状态、Tablet 均衡性、仲裁副本健康度等检查,根因分析场景也支持对 4.x 版本特有的问题(如 replay_hold 回放卡)进行诊断。[reference:5]

二、快速部署与配置

obdiag 支持多种部署方式。对于非 OBD 管理的集群,推荐使用以下方式安装:

# YUM 在线部署(适用于 CentOS 7/8/9 系列)
        sudo yum install -y yum-utils
        sudo yum-config-manager --add-repo https://mirrors.aliyun.com/oceanbase/OceanBase.repo
        sudo yum install -y oceanbase-diagnostic-tool
        source /usr/local/oceanbase-diagnostic-tool/init.sh

        # 通过 pip 安装
        pip install oceanbase-diagnostic-tool

        # 验证安装
        obdiag --version
        # 输出示例: obdiag 4.x.x, OceanBase Diagnostic Tool
💡 若集群通过 OBD(OceanBase Deployer)部署且 OBD 版本 >= V2.5.0,可直接通过 obd obdiag 命令使用 obdiag,无需单独配置。
配置文件(~/.obdiag/config.yml)

为访问集群信息和主机指标,需要配置数据库连接及 SSH 信息(至少提供数据库连接)。推荐通过 obdiag config 交互命令快速生成配置:

# 快速配置向导
        obdiag config -h <db_host> -u <sys_user> [-p password] [-P port]

        # 示例:密码非空场景
        obdiag config -h xx.xx.xx.xx -uroot@sys -p***** -P2881

        # 通过 OBProxy 连接场景(集群名为 obtest)
        obdiag config -h xx.xx.xx.xx -uroot@sys#obtest -p***** -P2883

执行 obdiag config 后会进入交互形式,依次填写各节点的 SSH 登录信息及 OceanBase 安装目录,最终在 ~/.obdiag/config.yml 中生成完整配置。[reference:6]

💡 生产环境建议使用 SSH 密钥认证,并控制用于 obdiag 的 OS 账号仅具备读取 observer 日志、执行 vmstat/iostat 等必要权限。

三、核心命令详解与使用精要

obdiag 最常用的命令分组是 obdiag check(巡检)、obdiag gather(信息收集)和 obdiag rca(根因分析)。

📊 1. 一键集群巡检 —— obdiag check

对集群做全面健康检查,涵盖系统、租户、存储、参数、日志流等维度。

# 列出所有巡检套餐
        obdiag check list

        # 全量巡检(最常用)
        obdiag check run

        # 执行 sysbench 前的巡检任务集合
        obdiag check run --cases=sysbench_free

        # 执行 sysbench 压测时的巡检任务集合
        obdiag check run --cases=sysbench_run

        # 列存 POC 检查
        obdiag check run --cases=column_storage_poc

        # OBProxy 版本检查
        obdiag check run --obproxy_cases=proxy

        # 部署环境检查
        obdiag check run --cases=build_before

        # 输出 JSON 格式报告
        obdiag check run --report_type=json --store_dir /tmp/check_report

        # 无配置文件方式(开箱即用),全量巡检
        obdiag check run --config db_host=xx.xx.xx.xx --config db_port=2881 --config tenant_sys.user=root@sys --config tenant_sys.password=***

巡检报告样例(摘要):

======================= obdiag check summary =======================
        +-----------------------------+-------+---------+------------------------------------------------+
        | 巡检项                      | 结果  | 等级    | 详情 / 建议                                    |
        +=============================+=======+=========+================================================+
        | cluster.core_file_find      | PASS  | INFO    | 所有 observer 节点无 core 文件                 |
        | tenant.tenant_threshold     | WARN  | WARNING | 租户 mysql001 线程利用率超过 95%,建议扩容或调整配置 |
        | log_stream_health           | PASS  | INFO    | 所有日志流均有合法 leader                     |
        | tenant_min_resource         | FAIL  | ERROR   | 部分租户 CPU 或内存少于 2C4G,影响集群稳定性   |
        | major.major_merge_aging     | PASS  | INFO    | 合并任务未超过 36 小时                         |
        +-----------------------------+-------+---------+------------------------------------------------+
        ERROR count: 1   WARN count: 1

巡检项覆盖类型:cluster(如 core_file_find、deadlocks)、major(检查合并任务是否超过 36 小时)、system(内核参数、时钟源、cgroup 配置)、tenant(租户日志盘大小、租户数量、表历史记录数)等。[reference:7]

🧠 2. 一键根因分析 —— obdiag rca run

针对特定场景进行深度根因分析,自动关联日志、监控指标和内部视图。以下场景名称均来自官方文档。[reference:8]

# 查看所有支持的根因分析场景
        obdiag rca list

        # 断连诊断(基于 OBProxy 诊断日志,分析物理连接层面的断连)
        obdiag rca run --scene=disconnection

        # 事务断连诊断(基于内部表数据和日志,分析事务层面的断连根因)
        obdiag rca run --scene=transaction_disconnection

        # 卡合并诊断
        obdiag rca run --scene=major_hold

        # 锁冲突诊断
        obdiag rca run --scene=lock_conflict

        # DDL 过程中磁盘空间不足
        obdiag rca run --scene=ddl_disk_full

        # clog 盘满场景根因分析
        obdiag rca run --scene=clog_disk_full

        # 日志流无主场景根因分析
        obdiag rca run --scene=log_error

        # DDL 失败诊断
        obdiag rca run --scene=ddl_failure

        # 建索引报错根因分析
        obdiag rca run --scene=index_ddl_error

        # 事务执行超时
        obdiag rca run --scene=transaction_execute_timeout

        # 事务不结束场景
        obdiag rca run --scene=transaction_not_ending

        # 事务回滚报错
        obdiag rca run --scene=transaction_rollback

        # 悬挂事务根因分析
        obdiag rca run --scene=suspend_transaction

        # unit GC 问题排查
        obdiag rca run --scene=unit_gc

        # 回放卡场景根因分析(4.x 特有)
        obdiag rca run --scene=replay_hold

        # 内存爆场景根因分析
        obdiag rca run --scene=memory_full

        # 删除 OBServer 节点异常
        obdiag rca run --scene=delete_server_error

        # GC 问题排查
        obdiag rca run --scene=gc_troubleshooting

        # 弱读问题排查
        obdiag rca run --scene=weak_read_troubleshooting

        # 指定时间范围分析(分析过去 40 分钟)
        obdiag rca run --scene=suspend_transaction --env since=40m

        # 输出 HTML 报告格式
        obdiag rca run --scene=lock_conflict --report_type=html

        # 无配置文件方式
        obdiag rca run --scene=disconnection --config db_host=xx.xx.xx.xx --config db_port=2881 --config tenant_sys.user=root@sys --config tenant_sys.password=***
📌 注意事项:由于根因分析需要大量的内部表数据进行分析,obdiag 配置文件中的 tenant_sys 用户需要具备 oceanbase 库下相关表及视图的读权限。[reference:9]
📦 3. 一键信息收集 —— obdiag gather

故障现场或事前按需采集各类诊断信息,打包为 zip 文件,方便高级排查或提交技术支持。

# 收集 observer 日志(最近 30 分钟,格式为 <n> <m|h|d>)
        obdiag gather log --scope observer --since 30m

        # 收集指定时间段的日志
        obdiag gather log --from "2022-06-30 16:25:00" --to "2022-06-30 18:30:00"

        # 收集主机信息(dmesg、CPU、内存等)
        obdiag gather sysstat

        # 收集堆栈信息(暂不支持 ARM 版本)
        obdiag gather stack

        # 收集 Core 文件
        obdiag gather core

        # 收集 ASH 报告
        obdiag gather ash

        # 收集指定 trace_id 的 SQL 执行详情(适用于分析 SQL 执行慢问题)
        obdiag gather plan_monitor --trace_id YB420BA2D99B-0005EBBFC45D5A00-0-0 --env "{db_connect='-hxx -Pxx -uxx -pxx -Dxx'}"

        # 收集 ODP 参数信息(收集最近 30 分钟)
        obdiag gather scene run --scene=obproxy.obproxy_parameter

        # 收集全量诊断信息(包括日志、系统表、OS 指标等)
        obdiag gather all

        # 通过 OBD 收集全量诊断信息
        obd obdiag gather all obcluster --bundle_name "prod_cluster_20250218"

收集完成后会输出 .zip 包路径及包含的内容摘要。[reference:10][reference:11][reference:12]

四、核心巡检项解读

obdiag 内置的巡检项覆盖以下关键领域[reference:13]:

  • cluster(集群):检查是否存在 core 文件的 observer、死锁问题、observer 未启动、系统日志级别等。
  • major(合并):检查合并任务是否超过 36 小时。
  • tenant(租户):日志盘是否为内存的三倍及以上、全局索引是否超过 20、租户数量是否过多、表历史记录是否过多、事务参与者是否超过 200、有用户的模块占用是否超过 10G 等。
  • disk(磁盘):磁盘是否满(80/90)、是否存在磁盘空洞。
  • system(系统):AIo 配置是否正常、基础软件依赖配置、内核参数配置、ulimit 参数、时钟源信息、core_pattern 是否正常、swapon 配置、avx2 指令集。
  • sysbench 相关:压测前的系统检查(cpu_count 配置、日志盘大小、内存分配、日志等级等)。
💡 各巡检项的详细说明可参考官方文档 《关于集群巡检指标的详解》

五、生产环境最佳实践

🔁 1. 利用 crontab 实现每日自动巡检与告警
# 每天凌晨 2 点执行巡检,输出 JSON 并邮件通知(通过外部脚本)
        0 2 * * * /usr/local/bin/obdiag check run --report_type=json > /data/obdiag_reports/$(date +\%Y\%m\%d).json && /opt/scripts/alert_if_error.sh

结合企业微信或邮件,解析 JSON 中的 ERROR 和 WARN 计数实现自动化告警。

⚙️ 2. 配合 OCP 形成“主动性运维”闭环

OCP 提供历史监控及趋势分析,而 obdiag 侧重于当前问题的深度诊断和巡检报告的细粒度输出。推荐策略:

  • OCP 监控发现异常事件(如租户内存超限) → 手动或自动触发 obdiag rca run --scene=<scene_name> 诊断场景 → 输出根因定位。
  • 重大变更前(合并、扩容)先执行 obdiag check run 记录基线健康状态,变更后再对比巡检差异。
🗄️ 3. 常用运维 SQL 辅助诊断

obdiag 封装了大部分巡检逻辑,但 DBA 可直接运行以下 SQL 进行复核:

-- 查看集群各节点资源水位(CPU / 内存)
        SELECT zone, svr_ip, cpu_total, cpu_assigned, mem_total, mem_assigned 
        FROM oceanbase.GV$OB_SERVERS;

        -- 检查日志流健康状态
        SELECT tenant_id, ls_id, role, replica_status 
        FROM oceanbase.__all_log_stream_replica 
        WHERE role = 'FOLLOWER' AND replica_status != 'NORMAL';

        -- 查看 major freeze 合并进度
        SELECT * FROM oceanbase.GV$OB_MERGE_INFO ORDER BY START_TIME DESC LIMIT 1;

        -- 定位租户下 memstore 使用过高的表
        SELECT tenant_id, table_id, partition_id, active_memstore_used, total_memstore_used 
        FROM oceanbase.GV$OB_MEMSTORE 
        WHERE active_memstore_used/total_memstore_used > 0.9 LIMIT 10;

        -- 检查仲裁服务心跳及状态
        SELECT * FROM oceanbase.GV$OB_ARBITRATION_SERVICE_STATUS;
🩺 4. 应急诊断 SOP 示例

当业务侧反馈“写入延迟增大”时,一线 DBA 可快速运行:

# 1. 快速全集群巡检
        obdiag check run

        # 2. 定位断连/卡顿问题
        obdiag rca run --scene=disconnection

        # 3. 收集最近 1 小时 observer 日志及 SQL 报错信息
        obdiag gather log --scope observer --since 1h
        obdiag gather scene run --scene=observer.perf_sql --env "db_connect='-h127.0.0.1 -P2881 -uroot@sys -p*** -Doceanbase'"

        # 4. 根据诊断报告中的建议 SQL 手工复核
✅ 生产环境故障排查时务必注意:obdiag 只读操作不会带来额外风险,但 gather 过程可能消耗少量 IO,建议在业务低峰期进行大规模日志采集。

六、与 OceanBase 4.x 生态协同

obdiag 与 OCP、ODC 分工明确,共同构成运维工具矩阵:

  • OCP:图形化平台,管理集群生命周期、备份恢复、监控告警,适用于平台级统一管控。
  • ODC:开发协同、SQL 变更审批,偏向开发者和应用变更。
  • obdiag:命令行专家,轻量级巡检、根因分析、信息收集集成,适合自动化脚本和快速故障定位。

七、常见问题速查表

问题现象推荐 obdiag 命令预估执行时间 集群整体响应慢,不确定原因 obdiag check runobdiag rca run --scene=disconnection < 30 秒 合并一直卡在某进度 obdiag rca run --scene=major_hold 约 20 秒 某租户写入被限流 obdiag check run --cases=tenant_threshold 约 10 秒 需要向 OceanBase 社区提供完整故障现场 obdiag gather all 约 1~3 分钟
🏆 小结
obdiag 作为 OceanBase 官方开源的自动化诊断工具,将 4.x 版本的运维经验代码化、工具化。建议将 obdiag check run 加入每日定时巡检任务,并在运维文档中明确“obdiag 应急三板斧”流程,让 OceanBase 运维从“被动救火”走向“主动预测和自动化”新阶段。随着 obdiag agent 即将到来,未来还将引入 AI 智能辅助诊断能力,进一步提升运维智能化水平。[reference:14]
15.4 ob-operator Kubernetes 部署

ob-operator(OceanBase Kubernetes Operator) 是 OceanBase 社区官方开源的 Kubernetes 集群管理工具。它基于 Kubernetes Operator 框架构建,用于在 Kubernetes 环境中管理 OceanBase 集群,实现了 OceanBase 集群的容器化部署,并以管理原生 Kubernetes 资源的相同方式对 OceanBase 数据库资源进行声明式管理,大幅简化了 OceanBase 集群在云原生环境中的运维复杂度。

🚀 ob-operator 的核心价值
  • 声明式集群管理:通过 OBClusterOBZoneOBTenant 等 CRD 定义集群和租户的期望状态,由 operator 负责调和至目标状态。
  • 全生命周期管理:集群引导启动、拓扑调整、Kubernetes 拓扑配置、集群扩缩容、版本升级和参数管理一站式完成。
  • 租户管理能力:租户创建、租户拓扑调整、资源单元管理和用户密码更新,均支持声明式配置。
  • 高可用保障:内置节点故障恢复、租户数据备份恢复和物理备租户等能力,可基于 OceanBase 分布式架构保障数据高可用。
  • 跨 K8s 集群部署:支持将 OceanBase 集群的多个 Zone 部署在不同的 Kubernetes 集群中,实现集群级别的容灾。

一、工作原理与核心资源

ob-operator 遵循典型的 Kubernetes Operator 设计模式,通过自定义资源定义(CRD)扩展 Kubernetes API,并包含一个控制器管理器(Controller Manager)负责监听 CRD 资源的变化,执行相应的调和逻辑,将集群的实际状态调整到用户定义的期望状态。

核心 CRD 资源
  • OBCluster:定义 OceanBase 集群的整体配置,包括集群名称、集群 ID、拓扑结构、Observer 镜像和资源配置、存储配置等。
  • OBZone:定义集群中每个 Zone 的详细配置,包含副本数量、节点亲和性、容忍度等。
  • OBTenant:定义租户配置,包括租户名称、资源规格、连接信息等。
  • OBTenantBackup/OBTenantRestore:定义租户备份与恢复策略。
  • K8sCluster:跨 Kubernetes 集群部署时,将工作集群(Worker Cluster)注册到主集群。
📌 跨 K8s 集群部署架构
在跨多个 K8s 集群部署 OceanBase 时,运行 ob-operator 的集群被称为主集群(Master Cluster),其他集群为工作集群(Worker Cluster)。在主集群中创建类型为 K8sCluster 的自定义资源来注册工作集群,ob-operator 使用存储在这些资源中的凭证访问工作集群。OceanBase 的工作负载在工作集群中以原生 K8s 资源运行,而 OceanBase 的自定义资源仍保存在主集群中。

二、部署前置条件

在部署 ob-operator 之前,需要确保满足以下条件:

  • 一个可用的 Kubernetes 集群,版本不低于 v1.16,并且至少具备 2 个 CPU 核心、10 GB 内存和 100 GB 存储空间
  • 已安装 cert-manager。cert-manager 用于管理 operator 所需的 Webhook 证书,是 ob-operator 正常运行的必要依赖。
  • 已安装 local-path-provisioner 或其他可用的 StorageClass,并确认目标存储路径有足够的存储空间。
  • 已安装 kubectl 命令行工具,并能够正常访问目标 Kubernetes 集群。
💡 生产环境推荐配置
若需生产级别的故障恢复能力,推荐使用 Calico 网络插件并配置静态 IP 保留能力,集群至少部署 3 个节点,租户副本数量至少 3 个,每个 Zone 的节点尽量分布在不同的物理机器上。

三、部署 ob-operator

ob-operator 支持两种部署方式:使用 YAML 配置文件使用 Helm。推荐使用 Helm 方式,便于版本管理和升级。

方式一:使用 YAML 配置文件部署
# 部署 stable 版本(推荐生产环境)
        kubectl apply -f https://raw.githubusercontent.com/oceanbase/ob-operator/stable/deploy/operator.yaml

        # 部署 developing 版本(适用于测试和开发环境)
        kubectl apply -f https://raw.githubusercontent.com/oceanbase/ob-operator/master/deploy/operator.yaml
方式二:使用 Helm 部署
# 添加 Helm 仓库
        helm repo add ob-operator https://oceanbase.github.io/ob-operator/

        # 部署 ob-operator
        helm install ob-operator ob-operator/ob-operator --namespace=oceanbase-system --create-namespace
验证部署结果
# 查看 CRD 是否已创建
        kubectl get crds

        # 输出中应包含以下 CRD:
        # obclusters.oceanbase.oceanbase.com
        # obzones.oceanbase.oceanbase.com
        # obtenants.oceanbase.oceanbase.com
        # obtenantbackups.oceanbase.oceanbase.com
        # obparameters.oceanbase.oceanbase.com
        # ...

        # 查看控制器 Pod 是否正常运行
        kubectl get pods -n oceanbase-system

        # 预期输出:oceanbase-controller-manager-xxx 2/2 Running

四、创建 OceanBase 集群

完成 ob-operator 部署后,即可通过定义 OBCluster 资源来创建 OceanBase 集群。

步骤 1:创建命名空间
kubectl create namespace oceanbase
步骤 2:创建默认用户 Secret
kubectl create secret -n oceanbase generic root-password --from-literal=password='your_root_password'
        kubectl create secret -n oceanbase generic proxyro-password --from-literal=password='your_proxyro_password'
        kubectl create secret -n oceanbase generic monitor-password --from-literal=password='your_monitor_password'
        kubectl create secret -n oceanbase generic operator-password --from-literal=password='your_operator_password'
步骤 3:定义 OBCluster YAML 文件
apiVersion: oceanbase.oceanbase.com/v1alpha1
        kind: OBCluster
        metadata:
        name: test
        namespace: oceanbase
        annotations:
            "oceanbase.oceanbase.com/independent-pvc-lifecycle": "true"  # 删除 OBCluster 时保留 PVC
        spec:
        clusterName: obcluster
        clusterId: 1
        serviceAccount: "default"
        userSecrets:
            root: root-password
            proxyro: proxyro-password
            monitor: monitor-password
            operator: operator-password
        topology:
            - zone: zone1
            replica: 1
            - zone: zone2
            replica: 1
            - zone: zone3
            replica: 1
        observer:
            image: oceanbase/oceanbase-cloud-native:4.2.1.7-107000162024060611
            resource:
            cpu: 2
            memory: 10Gi
            storage:
            dataStorage:
                storageClass: local-path
                size: 50Gi
            redoLogStorage:
                storageClass: local-path
                size: 50Gi
            logStorage:
                storageClass: local-path
                size: 20Gi
        monitor:
            image: oceanbase/obagent:4.2.1-100000062024060715
            resource:
            cpu: 1
            memory: 1Gi
        parameters:
            - name: system_memory
            value: "2G"
步骤 4:部署集群
kubectl apply -f obcluster.yaml
步骤 5:检查集群状态
kubectl get obclusters.oceanbase.oceanbase.com test -n oceanbase

        # 预期输出:NAME   STATUS    AGE
        #          test   running   6m

        # 查看 Pod 状态
        kubectl get pods -n oceanbase -o wide
连接 OceanBase 集群
# 获取 Pod IP
        kubectl get pods -n oceanbase -o wide

        # 通过 MySQL 客户端连接
        mysql -h{POD_IP} -P2881 -uroot -p'your_root_password' oceanbase -A -c

五、高可用与故障恢复

ob-operator 利用 OceanBase 原生分布式特性,提供多层次的高可用保障。以下为三种故障恢复策略的详细对比:

恢复策略原理适用条件恢复效果 多副本能力发现 Pod 异常后,通过 add server + delete server 方式新建 Observer 加入集群,利用新节点补充数据副本集群≥3节点,租户≥3副本;仅处理少数派节点故障少数派故障自动恢复 Calico 静态 IP使用原 IP 地址启动新 Observer,若原数据仍存在则可直接复用,无需全量复制数据集群使用 Calico 网络插件少数派/多数派故障均可恢复 K8s Service 模式为每个 OBServer Pod 附加 Service,以 Service 的 ClusterIP 作为通信 IP,Pod 重启或重建时保留原 ClusterIPOceanBase≥4.2.3.0;集群配置 mode: servicePod 重启/误删后原地或快速恢复服务
⚡ 恢复策略选择建议
  • 生产环境:推荐使用 Calico 静态 IP 策略,可获得最佳故障恢复能力和灵活的数据复用能力。
  • 新部署环境:若 OceanBase 版本 ≥4.2.3.0,建议开启 K8s Service 模式,可通过注解 "oceanbase.oceanbase.com/mode": "service" 配置。
  • 若集群中多数派节点同时故障,需结合备份恢复功能或主备租户方案进行手动恢复。

六、运维操作实践

1. 集群扩缩容

修改 OBCluster 中的 spec.topology 中对应 Zone 的 replica 数量,通过 kubectl apply 使配置生效即可触发扩缩容操作。

2. 修改租户资源规格
# 编辑租户配置文件中 resource 部分,调整 maxCPU/memorySize 等参数
        # 执行以下命令使改动生效
        kubectl apply -f tenant.yaml

        # 查看租户资源状态确认修改成功
        kubectl get obtenants.oceanbase.oceanbase.com -n oceanbase -o yaml
3. 升级 ob-operator
# 方式一:重新应用新版配置文件
        kubectl apply -f https://raw.githubusercontent.com/oceanbase/ob-operator/stable/deploy/operator.yaml

        # 方式二:使用 Helm 升级
        helm upgrade ob-operator ob-operator/ob-operator --namespace=oceanbase-system
4. 通过注解控制集群行为
  • "oceanbase.oceanbase.com/independent-pvc-lifecycle": "true":OBCluster 删除时保留 PVC。
  • "oceanbase.oceanbase.com/mode": "service":启用 Service 模式,支持 Pod 异常后 IP 不变。
  • "oceanbase.oceanbase.com/single-pvc": "true":创建使用单一 PVC 挂载的节点。

七、生产环境最佳实践

🎯 1. 网络插件与拓扑规划
  • 生产环境推荐使用 Calico 作为网络插件,以支持静态 IP 能力和多数派节点故障恢复。
  • 若无法使用 Calico,且 OceanBase 版本 ≥4.2.3.0,建议开启 Service 模式 以增强 Pod 重启后的网络连通性。
  • 将不同 Zone 的节点分布在不同的物理宿主机上,避免单点故障导致整个 Zone 不可用。
💾 2. 存储与资源规划
  • 为数据存储(dataStorage)、联机日志(redoLogStorage)和运行日志(logStorage)分别配置独立的存储卷,使用 SSD 类存储以获得最佳性能。
  • 根据实际负载配置 Observer 的 CPU 和内存资源,建议至少 2 CPU + 10Gi 内存 起步。
  • 通过 spec.parameters 设置 system_memorymemory_limit 等关键系统参数,优化集群性能。
🔒 3. 备份与主备租户高可用
  • 配置 OBTenantBackupPolicy 定期备份租户数据,将备份数据存储到 NFS 或云存储中。
  • 通过 OBTenantBackup/OBTenantRestore 快速从备份恢复租户数据。
  • 建立 物理备租户,当主租户发生故障时可快速切换,减少业务中断时间。
📊 4. 监控与可观测性集成
  • ob-operator 使用 obagent 采集监控数据,可通过 Prometheus OperatorServiceMonitor 将监控指标接入 Prometheus。
  • 对接 Grafana 可视化展示集群性能指标和健康状态。
  • ob-operator Controller Manager 通过 Prometheus 标准接口暴露指标,可接入告警系统。
✅ 生产环境 Checklist
  • ✅ Kubernetes 集群使用 Calico 网络插件开启静态 IP 能力,或 OceanBase ≥4.2.3.0 时配置 Service 模式
  • ✅ 部署至少 3 个节点,租户副本数至少 3 个
  • ✅ 各 Zone 节点分布在不同宿主机,避免单物理节点故障影响整个 Zone
  • ✅ 配置 OBTenantBackupPolicy 定期备份租户数据
  • ✅ 通过 Prometheus + Grafana 监控集群状态和 ob-operator 运行情况
  • ✅ 生产环境使用 stable 分支版本的 operator.yaml 或 Helm Chart

八、生态协同与社区支持

ob-operator 与 OceanBase 生态工具协同工作,共同构成云原生数据库管理矩阵:

  • OCP:图形化管理平台,负责集群的运维管理和监控告警,ob-operator 适用于声明式集群部署和自动化运维场景。
  • ODC:开发协同工具,负责 SQL 开发和变更审批,ob-operator 则聚焦于数据库资源与集群的生命周期管理。
  • obdiag:自动化诊断工具,用于集群健康巡检和根因分析,可与 ob-operator 共同构成“部署+诊断”的闭环。
  • KubeSphere Marketplace:可通过 KubeSphere 扩展中心快速安装 OB Operator 和 OceanBase Dashboard,实现一站式部署和管理。

ob-operator 已作为 OceanBase 社区云原生生态的重要组成部分,支持多 K8s 集群部署、ARM 架构镜像、动态 PVC 扩容等特性,能够灵活适配混合云和多云架构场景。社区在 GitHub 上持续迭代,版本演进记录在《变更日志》中,鼓励开发者参与贡献与反馈。

🏆 小结
ob-operator 是 OceanBase 拥抱云原生的关键能力,它将 OceanBase 集群的运维经验代码化为 Kubernetes Operator 的调和逻辑,让 DBA 和应用开发者能够像管理无状态应用一样管理有状态的 OceanBase 数据库。通过将 OBClusterOBTenant 等 CRD 纳入 GitOps 工作流,企业可以实现数据库集群的声明式管理、自动化扩缩容和多集群容灾,真正推动数据库运维从“脚本化”走向“云原生声明式”的新阶段。
15.5 OBAgent 监控采集与告警

OBAgent(OceanBase Agent) 是 OceanBase 社区官方开源的 监控采集与运维框架。它采用 Go 语言开发,以插件驱动架构为核心,专为 OceanBase 4.x 设计,提供灵活的数据采集与运维管理能力。OBAgent 支持推(Push)、拉(Pull)两种数据采集模式,能够适配不同的部署环境和应用场景,是构建 OceanBase 监控告警体系的核心组件。

🚀 OBAgent 的核心价值
  • 双模数据采集(Push/Pull):支持推送到 Pushgateway、vmagent 等,也支持通过 Prometheus HTTP 端点被拉取,灵活适配各类监控架构。
  • 插件驱动架构:默认内置主机采集、OceanBase 指标采集、日志采集、标签处理等核心插件;支持通过开发自定义插件接入任意数据源[reference:0]。
  • 多进程协同工作:agentd(守护进程)、mgragent(运维管理)、monagent(监控采集)和 agentctl(命令行工具)四个组件各司其职,稳定可靠。
  • 运维与监控一体化:除监控采集外,提供重启、文件管理、RPM 包安装及配置热更新等运维能力[reference:1]。

一、系统架构与组件详解

OBAgent 采用多进程解耦设计,由 agentd、mgragent、monagent 和 agentctl 四个核心组件构成:

进程名称主要职责关键特性 agentd守护进程,负责启停 mgragent 和 monagent,并为子进程提供资源限制自动重启异常退出的子进程,保证服务可用性 mgragent运维管理模块,提供黑屏运维功能入口支持重启、重装 OBAgent、文件管理、安装包管理及配置管理等运维能力 monagent监控采集模块,负责各类性能数据和应用日志的采集支持 Push/Pull 双模、插件扩展;涵盖主机、数据库、租户、SQL/事务等全方位指标 agentctl命令行工具,为用户提供黑屏操作入口不包含业务逻辑,提供进程启停、配置变更等便捷操作
📌 在 OCP 生态中的位置
在 OceanBase Cloud Platform 监控链路中,OBAgent 作为 ocp_exporter 程序部署在每台 OB Server 节点上,对外提供 Prometheus 规范的监控指标接口。ocp_exporter 内部依赖 NodeExporter(主机监控)、OBProxyExporter(OBProxy 监控)和 OBCollector(OceanBase 监控),联合采集主机、OBProxy 和 OceanBase 数据库的多维度监控数据。

二、部署配置全流程

OBAgent 支持两种部署方式:使用 OBD(推荐)手动部署。推荐使用 OBD 方式进行部署,可实现自动化配置与依赖管理。

📦 前置条件
  • 确认目标节点默认端口 8088(monagent)、8089(mgragent) 未被占用(也可自定义端口)。
  • 若需源码编译安装 OBAgent,需要 Go 1.14 及以上版本;使用 OBD 部署则无需手动安装 Go 环境。
  • 机器可访问公网(以便 OBD 自动从 YUM 源获取 OBAgent 安装包)。
🛠️ 方式一:使用 OBD 部署(推荐)

场景 A:与 OceanBase 集群同时部署
在 OceanBase 数据库的配置文件中添加 OBAgent 的配置信息,注意 servers 字段必须与 oceanbase-ce 的 servers 字段完全一致[reference:2]:

oceanbase-ce:
        servers:
            - 10.10.10.1
            - 10.10.10.2
            - 10.10.10.3
        global:
            # OceanBase 集群配置项

        obagent:
        servers:
            - 10.10.10.1
            - 10.10.10.2
            - 10.10.10.3
        depends:
            - oceanbase-ce
        global:
            home_path: /home/admin/obagent
            monagent_http_port: 8088
            mgragent_http_port: 8089
            http_basic_auth_user: admin
            http_basic_auth_password: ******
            monitor_password: ******
            sql_port: 2881
            rpc_port: 2882
            cluster_name: obcluster
            cluster_id: 1

场景 B:为已部署集群单独配置 OBAgent
⚠️ OBD 不支持为已部署的集群添加新的组件,如需为已部署集群配置 OBAgent,必须使用手动部署方式[reference:3]。

⚙️ 方式二:手动部署(已部署集群必须使用)

手动部署需单独配置 KV(配置存储)、monagent 和 Prometheus。需确保配置文件中的以下字段与 OceanBase 数据库配置保持一致[reference:4]:

# 1. 下载并安装 OBAgent RPM 包
        wget https://github.com/oceanbase/obagent/releases/download/1.1.2/obagent-1.1.2-9.el7.x86_64.rpm
        rpm -ivh obagent-1.1.2-9.el7.x86_64.rpm

        # 2. 配置 OBAgent(修改配置文件 /home/admin/obagent/conf/monagent.yaml)
        #   配置 OceanBase 数据库连接参数、采集插件、输出目标等

        # 3. 启动 OBAgent 进程
        /home/admin/obagent/bin/monagent -c /home/admin/obagent/conf/monagent.yaml
💡 OBAgent RPM 包中自带 Prometheus 配置模板,可复制到 Prometheus 部署主机直接使用[reference:5]。

三、监控数据采集体系

OBAgent 通过内置插件与可扩展架构,实现全方位、多维度的监控数据采集。

📍 默认支持的采集插件
  • 主机数据采集:CPU 使用率、内存占用、磁盘 IO、网卡流量等主机级指标。
  • OceanBase 数据库指标采集:集群/租户/节点的性能、容量、运行状态等核心指标。
  • 日志信息采集:ERROR 级别等关键日志的采集与上报。
  • 监控数据标签处理:为采集数据添加自定义标签,便于数据的分类、筛选和聚合。
  • Prometheus 协议的 HTTP 服务:提供标准 Prometheus 格式的 Metrics 接口,开箱即用[reference:6]。
  • MySQL 监控采集:集成了 Prometheus 的 mysqld exporter,支持 MySQL 性能指标采集(默认未开启,需通过配置项开启)[reference:7]。
🔌 自定义数据采集

OBAgent 采用插件驱动的设计理念,要扩展功能或自定义数据处理流程,只需开发对应的插件,实现插件的基本接口和对应类型插件的接口即可[reference:8]。

四、运维与管理能力

OBAgent 不仅提供强大的监控采集能力,还内置了丰富的运维管理接口。

🖥️ 命令行工具 agentctl
# 进程启停
        agentctl start       # 启动 OBAgent
        agentctl stop        # 停止 OBAgent
        agentctl restart     # 重启 OBAgent

        # 配置变更
        agentctl reload      # 热加载配置文件(无需重启进程)

        # 状态查看
        agentctl status      # 查看 OBAgent 各组件的运行状态
🔄 配置热更新

OBAgent 支持配置热更新能力,修改配置文件后可通过 mgragent 运维接口应用新的采集配置,无需重启整个采集进程,确保监控数据采集不中断[reference:9]。

📝 日志管理
  • monagent 日志大小默认 200 MB,保留 30 天,最多保留 15 个备份文件
  • mgragent 日志大小默认 30 MB,保留 30 天,最多保留 15 个备份文件
  • 可通过 OBD 配置文件或 OCP 参数管理界面调整日志级别(info/warn/error/debug)和日志轮转策略。

五、告警体系集成

OBAgent 采集的监控数据通过 Prometheus 协议暴露后,可无缝集成 Prometheus Server 和 Alertmanager,构建完整的监控告警链路。

🔔 告警集成架构
+--------------+     +-------------------+     +-----------------+     +--------------+
        |  OBAgent     | --> |  Prometheus       | --> |  Alertmanager   | --> |  通知接收端   |
        |  (monagent)  |     |  Server           |     |  (告警分组/抑制) |     |  企业微信等   |
        +--------------+     +-------------------+     +-----------------+     +--------------+
                |                        |
                | (可选的 Push 模式)      | (Pull 模式)
                v                        v
        +--------------+     +-------------------+
        | Pushgateway  |     | Grafana 可视化    |
        +--------------+     +-------------------+
🎯 告警配置说明

OBAgent 支持推送数据到 pushgateway、vmagent、es、sls、alertmanager[reference:10]。告警配置时需在 Prometheus 的配置文件中增加报警相关配置,报警规则文件需放在 rules 目录,且命名满足 *rule.yaml 格式[reference:11]。

📢 核心告警项建议
  • 租户 MemStore 使用率 > 90%:接近限流阈值,需扩容或优化写入。
  • 合并任务超过 24 小时未完成:合并调度异常,影响查询性能。
  • 日志流无主(Leader 缺失)超过 1 分钟:日志流健康异常,影响集群容灾能力。
  • SQL 执行响应时间(rt)突增:可能存在慢 SQL 或系统负载过高。
  • 节点磁盘使用率 > 85%:存储空间不足,需扩容或清理数据。
  • OBServer 节点 offline 超过 5 分钟:节点故障,需立即介入处理。
💡 告警配置提示
Prometheus 可通过 monagent.alertmanager.address 配置项的地址推送告警到 Alertmanager 进行处理。手动部署 OBAgent 时,Alertmanager 为可选组件[reference:12]。

六、生产环境最佳实践

⚡ 1. 部署与配置建议
  • 新集群部署推荐使用 OBD 同时部署 方式,可自动生成 Prometheus 配置文件。
  • 为已部署集群配置 OBAgent 必须使用 手动部署 方式[reference:13]。
  • OBAgent 仅需部署在 OceanBase 集群节点上,每台 OB Server 节点均需部署。
  • 如节点上同时部署了 OBProxy,OBAgent 会自动采集 OBProxy 的相关指标。
  • monagent 与 mgragent 日志建议使用独立存储卷,避免日志写满影响主数据盘。
  • 在 Kubernetes 环境中部署 OceanBase 时,推荐使用 ob-operator 部署管理,OBAgent 作为监控采集组件由 ob-operator 自动部署[reference:14]。
📈 2. 监控指标采集优化
  • 根据业务规模和监控需求,合理调整采集频率(建议默认 15s 即可)和采集插件开关,避免因采集频率过高占用系统资源。
  • 通过配置监控数据标签处理插件,为不同租户、不同环境的数据添加自定义标签,便于在大规模多租户场景下进行分类筛选和聚合分析。
  • 利用 OBAgent 提供的数据推送能力,将采集数据推送到持久化存储进行长期保存和历史趋势分析。
🔐 3. 安全与访问控制
  • 配置 http_basic_auth_userhttp_basic_auth_password,为 Prometheus HTTP 接口启用基础认证。
  • 为 OBAgent 在 OceanBase 中创建的监控账号(monitor_user)仅授予必要的只读权限,遵循最小权限原则。
  • 如需将监控数据推送至外部存储,建议配置 HTTPS/TLS 传输加密,保障数据传输安全。
📊 4. 可视化与告警整合
  • 启动 Prometheus 后,通过浏览器访问 http://prometheus_ip:9090,在 Status → Targets 中查看 OBAgent 各采集端点的指标状态。
  • 使用 Grafana 作为可视化面板,通过 Prometheus 数据源展示 OceanBase 集群的监控图表。
  • 将告警规则配置在 Prometheus 告警规则文件中,由 Alertmanager 进行告警分组、抑制和静默,最终通过 Webhook、企业微信、钉钉等方式发送通知。
✅ 生产环境部署 Checklist
  • ✅ 确认 8088、8089 端口未被占用且已开放防火墙。
  • ✅ OBAgent 配置文件中的 cluster_name、cluster_id、sql_port 等字段与 OceanBase 集群保持一致。
  • ✅ 配置 http_basic_auth 认证保护监控端点。
  • ✅ Prometheus Server 添加 OBAgent 采集端点(/metrics/ob/basic 和 /metrics/node/host)。
  • ✅ 在 Prometheus 的 rules 目录下配置报警规则文件(*rule.yaml)。
  • ✅ 通过 Grafana 导入 OceanBase 官方 Dashboard,验证可视化展示正常。

七、生态协同与社区支持

OBAgent 与 OceanBase 运维生态中的其他组件协同工作,构成完整的可观测性矩阵:

  • OCP:OBAgent 作为 ocp_exporter 部署在每台 OB Server 节点,是 OCP 监控能力的基础采集组件。
  • ob-operator:在 Kubernetes 环境中,OBAgent 作为监控采集组件由 ob-operator 自动部署,ob-operator 会按 zone 依次更新 observer 对应 pod 的 spec,加入 OBAgent 的 container[reference:15]。
  • Prometheus + Grafana:OBAgent 提供 Prometheus 兼容的监控指标端点,是构建开源监控栈的核心数据源。
  • obdiag:OBAgent 负责日常监控数据采集和告警检测,obdiag 负责集群健康巡检和故障根因分析,二者在可观测性层面形成互补。

八、常见问题速查

问题现象可能原因排查建议 OBAgent 启动失败端口被占用或配置错误执行 ps -ef|grep agent 检查进程状态,确认 8088/8089 端口是否被占用,检查配置文件路径和权限[reference:16]。 /metrics/ob/basic 返回 404HTTP 鉴权失败或配置不一致检查 http_basic_auth 配置的用户名和密码是否正确,查看 OBAgent 启动日志中的路由初始化信息[reference:17]。 OCP-Agent 频繁重启集群分区数过多导致内存超限集群分区数过多时,OCP-Agent 采集分区数据会占用较多内存,可适当增加内存限制或减少分区数量[reference:18]。 监控数据采集不全采集插件未正确配置检查 monagent.yaml 中对应插件的 enabled 状态,确认采集指标项配置正确。
🏆 小结
OBAgent 是 OceanBase 可观测性体系的基石组件。它通过推拉双模数据采集、插件驱动架构和一体化运维能力,将 OceanBase 4.x 的监控经验代码化、工具化。建议在生产环境中为每台 OB Server 部署 OBAgent,并搭配 Prometheus + Grafana 构建完整的监控告警体系。无论是日常性能巡检,还是故障时的指标回溯,OBAgent 都能为 DBA 提供全面、及时、可靠的数据支撑,让 OceanBase 运维从“被动响应”走向“主动观测和自动化预警”的新阶段。

第16章:应用开发最佳实践

16.1 连接池配置与负载均衡策略

在 OceanBase 分布式数据库环境中,连接池配置负载均衡策略是保障应用高并发下稳定性和性能的核心环节。OceanBase 提供了一套完整的“客户端池化 + 服务端代理”两级连接架构:客户端侧通过应用层连接池(HikariCP/Druid/Proxool/C3P0)控制连接建立频率与并发数量;服务端侧通过 OBProxy 提供会话级连接池与智能路由能力,将客户端请求负载均衡至后端 OBServer 节点。本节将围绕连接池参数调优、OBProxy 负载均衡与路由策略、运维监控等方面,提供从原理到实践的全方位指南。

🎯 本章内容概览
  • 应用层连接池配置:HikariCP/Druid 的核心参数调优与推荐配置。
  • 客户端负载均衡:OceanBase 驱动多 IP 负载均衡模式与 JDBC 核心参数。
  • 服务端负载均衡与路由策略:OBProxy 智能路由机制与关键配置项。
  • OBProxy 会话级连接池:底层连接复用机制与适用场景。
  • 租户连接限制与层级检查:租户级与 OBServer 级的连接数限制。
  • 运维监控与问题排查:常用 SQL 监控命令与告警配置建议。

一、应用层连接池配置

应用层连接池负责集中维护与 OBProxy(或直连 OBServer)之间的物理连接。合理的连接池参数设置是避免高并发下连接耗尽、连接泄漏和性能劣化的关键。

通用连接池参数建议

无论使用哪种连接池实现,以下通用参数值得重点关注,官方建议配置值如下表所示[reference:0]。

配置参数说明推荐值 最大连接数(maxActive/maximumPoolSize)连接池允许的最大连接数,超出时抛出连接池已满异常根据业务并发量评估,建议最小 8,较大并发时可设为 20~50,具体参考下方计算公式 最小空闲连接数(minIdle/minimumIdle)连接池保持的最小空闲连接数2~5,根据业务稳定性要求调整,管控台日常保留 2 个即可[reference:1] 连接空闲超时(minEvictableIdleTimeMillis/idleTimeout)空闲连接被回收前可保留的最大时长建议 30 分钟,避免连接被 MySQL Server 侧 8 小时断开导致脏连接[reference:2] 获取连接超时(maxWait/connectionTimeout)从连接池获取连接的最大等待时间建议 30 秒,防止连接池满时请求堆积 连接泄漏检测(removeAbandoned/leakDetectionThreshold)连接超时未归还时主动回收(Druid 专有)建议 300 秒,并记录堆栈用于排查泄漏问题[reference:3]
📌 连接池最大连接数的计算逻辑
推荐计算公式:总连接数 = 核心服务数 × 每个服务分配的最大连接数
参考值推导:按 8000 请求/秒,每个请求耗时 100 毫秒,每个连接可服务约 4 个并发计算,示例:maxActive = 向下取整(8000 / 100 / 4) = 20[reference:4][reference:5]。
HikariCP 配置示例(生产推荐)
final HikariDataSource ds = new HikariDataSource();
        ds.setDriverClassName("com.oceanbase.jdbc.Driver");
        ds.setJdbcUrl("jdbc:oceanbase://10.10.10.1:2883,10.10.10.2:2883,10.10.10.3:2883/demo");
        ds.setUsername("root");
        ds.setPassword("******");

        // 核心连接池参数
        ds.setMaximumPoolSize(20);        // 最大连接数
        ds.setMinimumIdle(5);             // 最小空闲连接
        ds.setIdleTimeout(1800000);       // 空闲超时 30 分钟(毫秒)
        ds.setConnectionTimeout(30000);   // 获取连接超时 30 秒(毫秒)
        ds.setValidationTimeout(5000);    // 连接校验超时 5 秒
        ds.setLeakDetectionThreshold(60000); // 连接泄漏检测 60 秒

        // JDBC 参数通过 dataSourceProperties 传递
        ds.addDataSourceProperty("cachePrepStmts", "true");
        ds.addDataSourceProperty("prepStmtCacheSize", "250");
        ds.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        ds.addDataSourceProperty("useServerPrepStmts", "true");
        ds.addDataSourceProperty("rewriteBatchedStatements", "true");
        ds.addDataSourceProperty("allowMultiQueries", "true");
        ds.addDataSourceProperty("useLocalSessionState", "true");
Druid 连接池配置示例
DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.oceanbase.jdbc.Driver");
        ds.setUrl("jdbc:oceanbase://10.10.10.1:2883,10.10.10.2:2883,10.10.10.3:2883/demo");
        ds.setUsername("root");
        ds.setPassword("******");

        // 核心连接池参数
        ds.setInitialSize(5);                // 初始连接数
        ds.setMinIdle(5);                    // 最小空闲连接
        ds.setMaxActive(20);                 // 最大连接数
        ds.setMaxWait(30000);                // 获取连接超时 30 秒
        ds.setTimeBetweenEvictionRunsMillis(60000);  // 空闲连接检测周期 60 秒
        ds.setMinEvictableIdleTimeMillis(1800000);   // 空闲连接回收阈值 30 分钟

        // 连接泄漏检测
        ds.setRemoveAbandoned(true);
        ds.setRemoveAbandonedTimeout(300);   // 连接泄漏回收超时 300 秒
        ds.setLogAbandoned(true);            // 记录泄漏连接堆栈

        // JDBC 参数配置
        ds.setConnectionInitSqls(Arrays.asList("set ob_query_timeout = 30000000"));

        Properties props = new Properties();
        props.setProperty("cachePrepStmts", "true");
        props.setProperty("prepStmtCacheSize", "250");
        props.setProperty("prepStmtCacheSqlLimit", "2048");
        props.setProperty("useServerPrepStmts", "true");
        props.setProperty("rewriteBatchedStatements", "true");
        props.setProperty("allowMultiQueries", "true");
        ds.setConnectProperties(props);

二、客户端驱动层负载均衡

OceanBase Connector/J 自 V2.2.10 版本起新增 LoadBalance 功能,应用可通过 URL 或配置文件指定多 IP 连接策略,实现客户端侧负载均衡[reference:6]。

JDBC 多 IP 负载均衡模式

场景 A:随机负载均衡模式(OBLB_STRATEGY=RANDOM),业务请求随机分发至多台 OBProxy[reference:7]。

jdbc:oceanbase:loadbalance://@
        (NET_SERVICE_NAME=(DESCRIPTION=
            (ADDRESS_LIST=
                (OBLB_STRATEGY=RANDOM)
                (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.100.1)(PORT=2883))
                (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.100.2)(PORT=2883))
                (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.100.3)(PORT=2883))
        ))/testdb_demo?rewriteBatchedStatements=true&allowMultiQueries=true

场景 B:轮询/容灾模式(OBLB_STRATEGY=ROTATION),按顺序优先使用 IP1,IP1 不可用时切换至 IP2,依次类推[reference:8]。

jdbc:oceanbase:loadbalance://@
        (NET_SERVICE_NAME=(DESCRIPTION=
            (ADDRESS_LIST=
                (OBLB_STRATEGY=ROTATION)
                (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.100.1)(PORT=2883))
                (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.100.2)(PORT=2883))
                (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.100.3)(PORT=2883))
        ))/testdb_demo?rewriteBatchedStatements=true&allowMultiQueries=true

场景 C:先容灾再负载均衡模式,将业务请求优先发给 IP1,只有当 IP1 无法访问时才随机均衡分发给 IP2 和 IP3[reference:9]。

jdbc:oceanbase:loadbalance://@
        (NET_SERVICE_NAME=(DESCRIPTION=
            (OBLB=ON)
            (OBLB_GROUP_STRATEGY=ROTATION)
            (ADDRESS_LIST=
                (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.100.1)(PORT=2883))
                (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.100.2)(PORT=2883))
                (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.100.3)(PORT=2883))
        ))/testdb_demo?rewriteBatchedStatements=true&allowMultiQueries=true
JDBC 核心参数详解

以下 JDBC 参数必须在连接池配置中强制设置,可通过连接池的 ConnectionProperties 或 JDBC URL 添加[reference:10]。

参数名推荐值说明 socketTimeout10000~30000 msSocket 读超时,SQL 执行等待时长,0 表示无限制[reference:11] connectTimeout30000 msTCP 连接建立超时,0 表示无限制[reference:12] rewriteBatchedStatementsTRUE将 executeBatch 批量执行合并为单条 INSERT VALUES,大幅提升批量写入性能[reference:13] allowMultiQueriesTRUE允许 JDBC 以分号分隔多 SQL 一次性发送[reference:14] useLocalSessionStateTRUE避免频繁向数据库发送 autocommit 等 session 变量查询,减少网络往返[reference:15] cachePrepStmtsTRUE驱动端缓存 PreparedStatement,避免重复 prepare[reference:16] useServerPrepStmtsTRUE启用服务端 PS 协议,SQL 执行分为 Prepare 和 Execute 两步[reference:17] prepStmtCacheSize250驱动端 PreparedStatement 缓存大小 prepStmtCacheSqlLimit2048可缓存的最大 SQL 长度(字节)
⚠️ 注意事项:rewriteBatchedStatements 必须配合 PreparedStatement 和 addBatch 使用,且需在应用重启后生效[reference:18]。

三、OBProxy 服务端负载均衡与路由策略

OBProxy(ODP)是 OceanBase 的专用代理层,其最佳路由能力将客户端 SQL 请求智能转发至最优 OBServer 节点,同时提供丰富的路由策略配置项[reference:19]。

负载均衡策略配置(OCP 管控)

通过 OCP 可对 OBProxy 集群配置负载均衡策略,支持 RANDOM(随机)、SERVERAFFINITY(加权)、ROTATION(轮询)三种策略,加权模式下权重取值范围为 1~10[reference:20]。

# 通过 OCP 配置 OBProxy 负载均衡:
        # 1. 登录 OCP 控制台 → OBProxy
        # 2. 选择目标 OBProxy 集群 → 客户端配置
        # 3. 添加应用,选择负载均衡策略(RANDOM/SERVERAFFINITY/ROTATION)
        # 4. 在 OBProxy 列表中勾选节点并分配权重(仅 SERVERAFFINITY 模式)
        # 5. 可选配置黑名单策略(NORMAL/RETRYTIMES)和解黑超时参数
核心路由策略与配置项

OBProxy 提供以下关键路由策略,通过配置项精细控制[reference:21][reference:22]。

  • 强制路由:配置项 target_db_serverproxy_primary_zone_name 将请求强制路由到指定 OBServer 或 Zone,优先级最高。
  • 分区表路由:将 SQL 中的分区键值解析为分区 ID,精准路由到 Leader 副本。
  • 强一致性读路由:集群 1-1-1 场景 + enable_primary_zone=true + enable_cached_server=true 可能导致全部请求路由到单一节点[reference:23]。
  • 弱一致性读路由ob_proxy_readonly_transaction_routing_policy 自 V4.1.0 起废弃,建议 SELECT 独立成单语句且 autocommit=1[reference:24]。
📌 分布式查询负载不均原因与解决
现象:分布式 SQL 全部路由到某个节点导致 CPU 飙升。
原因:enable_primary_zone=true 且 enable_cached_server=true 时,OBProxy 将请求发送至第一次执行节点(Primary Zone)
解决:设置 ALTER PROXYCONFIG SET enable_primary_zone=false, enable_cached_server=false;,对无法精准定位 Leader 的 SQL 改用随机路由[reference:25]。
OBProxy 关键配置项速查
配置项说明示例命令 target_db_server强制路由到指定 OBServer IP:Port,优先级最高[reference:26]ALTER PROXYCONFIG SET target_db_server = '127.0.0.1:2882'; proxy_primary_zone_name强制路由到指定 Zone[reference:27]ALTER PROXYCONFIG SET proxy_primary_zone_name='z2'; enable_primary_zone是否优先将请求发送至 Primary Zone,建议关闭避免负载不均[reference:28]ALTER PROXYCONFIG SET enable_primary_zone=false; enable_cached_server是否复用之前连接的 OBServer,建议关闭[reference:29]ALTER PROXYCONFIG SET enable_cached_server=false; observer_query_timeout_deltaOBProxy 超时在 ob_query_timeout 基础上增加的缓冲时间(默认 20s)[reference:30]ALTER PROXYCONFIG SET observer_query_timeout_delta=20s;

四、OBProxy 会话级连接池

针对业务短连接场景下 ODP 频繁新建后端连接的开销,ODP 自 V4.3.5 起支持会话级连接池功能,ODP 可缓存与 OBServer 之间的会话,大幅提升并发性能[reference:31]。

# 启用会话级连接池
        ALTER PROXYCONFIG SET connection_pool_mode='session';

        # 查看当前配置
        SHOW PROXYCONFIG LIKE 'connection_pool_mode';

        # 查看会话连接池中的会话信息(root@sys 登录)
        SHOW GLOBALSESSION ATTRIBUTE 'obcluster:test'\G
📌 版本与使用限制:ODP 需 V4.3.5 及以上,OceanBase 数据库需 V4.4.0 及以上。以下场景连接池不可用:使用 Service Name、server_protocol=compressed mysql、Oracle 租户、开启分布式事务、使用 SSL 或 BINLOG 请求[reference:32]。

五、租户级与 OBServer 级连接限制

OceanBase 在租户和 OBServer 两个层面存在最大连接数限制,应用设计时需重点关注,避免因超限而拒绝连接。

租户级最大连接数

租户可配置的 max_connections 上限受租户内存规格控制,建议单租户 max_connections 不超过 2000(根据内存容量灵活调整)[reference:33]。

-- 查看租户当前最大连接数配置
        SHOW VARIABLES LIKE 'max_connections';

        -- 动态调整租户最大连接数
        SET GLOBAL max_connections = 2000;

        -- 查看租户当前活跃连接数
        SELECT COUNT(*) FROM information_schema.PROCESSLIST WHERE db = 'your_database';
OBServer 节点 max_connections 默认值与调整方法

每个 OBServer 节点的默认最大连接数为 4000,所有租户的连接共享该上限,超出时新连接将被拒绝。可通过修改集群级或租户级配置项进行调整:

-- 查看 OBServer 当前最大连接数
        SHOW PARAMETERS LIKE 'max_connections';

        -- 修改集群级最大连接数(sys 租户下执行)
        ALTER SYSTEM SET max_connections = 8000;

        -- 验证修改结果
        SHOW PARAMETERS LIKE 'max_connections';
⚠️ 连接数配比优化建议
若使用 OBProxy,连接数配比遵循“应用连接 → OBProxy → OBServer”两级映射。建议应用连接数 ≈ OBServer 连接数 × 0.8,且每个 OBServer 节点连接数小于节点内存/500KB(粗略估算),避免 OBServer 内存因连接占用过大而性能下降。

六、连接超时与重连机制

OceanBase 涉及多重超时参数,需要系统配置与应用配合,确保连接稳定可靠[reference:34]。

OBServer 侧超时配置(会话级变量)
参数名默认值推荐值说明 ob_query_timeout10000000 微秒(10 秒)30000000~86400000 微秒(30~864 秒)语句执行超时,超时返回错误码 4012[reference:35] ob_trx_timeout100000000 微秒(10 秒)864000000 微秒(864 秒)事务执行总超时,超时回滚未提交事务[reference:36]
-- 全局生效
        SET GLOBAL ob_query_timeout = 864000000;
        SET GLOBAL ob_trx_timeout = 864000000;

        -- 单一会话生效
        SET ob_query_timeout = 30000000;
        SET ob_trx_timeout = 30000000;
OBProxy 侧超时配置
-- OBProxy 级别 ob_query_timeout(转发等待超时)
        ALTER PROXYCONFIG SET ob_query_timeout = '30s';

        -- 额外缓冲时长(默认 20s),规避 OBProxy 与 OBServer 之间的网络 RT
        ALTER PROXYCONFIG SET observer_query_timeout_delta = '20s';
应用层重试策略

推荐设置:connectTimeout = 1s,配合重试机制;应用日志记录标准 OceanBase 错误码及连接信息(IP、Port、用户名),便于 DBA 排查[reference:37]。

  • 4012(Timeout):SQL 执行超时或事务超时 → 业务可安全重试,建议增加 ob_query_timeout/ob_trx_timeout。
  • 1226(User 'xxx' has exceeded the 'max_connections_per_hour'):租户连接超限 → 调大租户 max_connections 或扩容。
  • 网络层面“Connection reset/Connection refused”:短时间连续重试可能加剧雪崩,建议增加重试间隔(指数退避)。
🎯 事务操作规范
  • 单个事务必须在一个连接中完成:必须执行 getConnection → 执行事务 → close connection,不能跨连接执行一个事务[reference:38]。
  • 切换数据库:使用 Connection.setCatalog(dbname) 而非直接执行 use dbname[reference:39]。
  • 设置只读属性:使用 Connection.setReadOnly(xx) 而非直接执行 set session transaction readonly[reference:40]。
  • 禁止事务内执行无关查询:OceanBase 分布式事务成本高,严禁事务内查询序列号或配置表,将事务粒度降到最低[reference:41]。

七、运维监控与问题排查

以下常用 SQL 视图和系统表可用于实时查看连接状态、负载均衡效果和异常根因。

1. 查看租户当前活跃连接
SELECT tenant, user, host, db, command, time, state
        FROM information_schema.PROCESSLIST
        WHERE user NOT LIKE 'r__%' AND db = 'your_database';
2. 查看 OBProxy 路由分发统计
-- 查看连接分布(路由节点)
        SELECT proxy_ip, proxy_port, route_ip, route_port, COUNT(*) as conn_cnt
        FROM information_schema.PROXYSESSION
        WHERE proxy_cluster_name = 'your_odp_cluster'
        GROUP BY route_ip, route_port
        ORDER BY conn_cnt DESC;
3. 查询 SQL 慢查询与路由来源(GV$OB_SQL_AUDIT)
SELECT 
            SQL_ID,
            tenant_name,
            user_name,
            client_ip,
            server_ip,
            elapsed_time/1000 AS elapsed_ms,
            execute_time/1000 AS execute_ms,
            query_sql
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE request_time > TIME_TO_USEC(NOW() - INTERVAL 15 MINUTE)
        AND elapsed_time > 5000000   -- 5 秒
        ORDER BY elapsed_time DESC
        LIMIT 20;
4. 诊断“分布式查询负载不均”或路由错误问题
-- 查看 Primary Zone 配置
        SELECT tenant_name, primary_zone FROM oceanbase.DBA_OB_TENANTS;

        -- 查看当前租户的 zone 分布
        SELECT * FROM oceanbase.DBA_OB_UNIT_JOBS WHERE job_type = 'CREATE_TENANT';

        -- 检查 OBProxy 缓存配置
        SHOW PROXYCONFIG LIKE '%cache%';
        SHOW PROXYCONFIG LIKE '%primary_zone%';
5. 检查租户与 OBServer 连接数使用率
-- 租户当前连接数 vs max_connections 上限
        SELECT 
            @@max_connections AS max_conn_allowed,
            (SELECT COUNT(*) FROM information_schema.PROCESSLIST) AS current_conn,
            ROUND( (SELECT COUNT(*) FROM information_schema.PROCESSLIST) / @@max_connections * 100, 2) AS usage_pct;

        -- 各租户连接消耗详情
        SELECT 
            USER AS tenant_user,
            SUBSTRING_INDEX(USER, '@', 1) AS tenant_name,
            COUNT(*) AS connection_count
        FROM information_schema.PROCESSLIST
        GROUP BY USER
        ORDER BY connection_count DESC;

八、生产环境最佳实践 Checklist

✅ 上线前检查清单
  • ✅ 评估业务峰值 QPS 和请求耗时,用公式计算连接池 maxActive 初始值并压测验证。
  • ✅ 应用侧配置 JDBC 核心参数(rewriteBatchedStatements、useLocalSessionState、socketTimeout、connectTimeout)。
  • ✅ 启用连接池空闲超时(30 分钟)和泄漏检测(Druid removeAbandoned/HikariCP leakDetectionThreshold)。
  • ✅ OBProxy 侧关闭 enable_primary_zone 和 enable_cached_server(1-1-1 或多 zone 同 Region 场景),避免分布式查询负载不均。
  • ✅ 配置客户端负载均衡,JDBC URL 中至少配置 2 个 OBProxy IP 以实现高可用。
  • ✅ 调整租户 ob_query_timeout、ob_trx_timeout 符合业务长事务需要,并监控 4012 错误。
  • ✅ 确认租户 max_connections 和 OBServer 节点 max_connections 上限足够,监控连接使用率。
  • ✅ ODP V4.3.5+ 且场景匹配时,启用会话级连接池(connection_pool_mode='session'),降低后端建连开销。
  • ✅ 应用日志记录连接建立时的错误码和连接信息,建立重试策略并避免瞬时过多重试。
🏆 小结
OceanBase 4.x 的连接架构设计充分兼顾了高并发接入与分布式集群内部路由的效率。应用侧通过科学配置 HikariCP/Druid 等连接池参数,并结合 JDBC 多 IP 负载均衡,能够有效控制客户端连接资源;服务侧利用 OBProxy 的智能路由与会话级连接池,最大化后端 OBServer 的资源利用率。掌握从客户端超时、连接池阈值到服务端路由配置的全链路调优方法,是保障 OceanBase 生产环境稳定运行的必备能力。
16.2 SQL 设计与事务大小限制

OceanBase 作为原生分布式数据库,其 SQL 开发与事务管理模式与单机数据库存在显著差异。低效的 SQL 设计和不受控的大事务会引发集群性能急剧下降、内存溢出甚至服务不可用等严重问题。本章围绕 OceanBase 4.x 的特性,从SQL 设计原则、事务大小限制与拆解、核心超时参数和错误排查四大维度出发,提供可落地的最佳实践指南,帮助开发者和 DBA 高效构建稳定可靠的分布式应用。

🎯 本章内容概览
  • SQL 设计原则:SELECT/DML/多表关联/分区表/索引设计规范。
  • 事务大小限制:4.x 版本的事务限制配置与触发条件。
  • 大事务拆解与优化:分批提交、改写技巧与参数调整策略。
  • 核心参数配置:ob_query_timeout、ob_trx_timeout、ob_trx_idle_timeout。
  • 错误码与处理:常见事务相关错误码的排查与应对。
  • 运维与诊断工具:obdiag 事务诊断场景一览与常用 SQL。

一、SQL 设计最佳实践

好的 SQL 设计是良好数据库性能的基础,尤其在分布式数据库环境中,一个看似简单的查询可能引发全表扫描或大量跨节点通信。下表汇总了 OceanBase 官方推荐的核心设计原则。

类别规范与限制说明 主键与唯一索引 分区表主键/唯一索引必须包含分区键 OceanBase 中,分区表的主键和唯一约束必须包含分区键,否则无法保证全局唯一性 索引设计 优先使用本地索引,谨慎使用全局索引 全局索引拥有独立的分区规则,跨分区访问会带来额外网络和协调开销 分区策略 控制单分区数据量,单表超 10 亿行建议分区 单分区数据量过大将影响查询和 DDL 效率,合理分区可提升运维效率并大幅减少 IO SELECT 语句 指定字段名;禁止使用 SELECT * 明确字段可避免回表和多余的 I/O,也有利于结果集字段变更时的稳定性 INSERT 语句 必须指定列名(INSERT INTO tab(col_name) VALUES(...)) 避免使用 INSERT INTO tab VALUES(...) 形式,以便于表结构变更时兼容 UPDATE/DELETE 必须带 WHERE 条件,并控制修改行数 不带条件修改几十万行会产生大事务,可能执行失败 多表 JOIN 显式使用别名;避免产生笛卡尔积 SELECT 列表中用别名引用字段,确保关联字段的 collation 一致,便于优化器正确使用索引
💡 为什么分区表主键/唯一键必须包含分区键?
在分布式场景下,OceanBase 需要通过分区键确定数据的位置。如果主键不包含分区键,插入或更新操作需要扫描所有分区来校验唯一性,性能开销极大。将分区键纳入主键后,每个主键值对应的分区位置确定,校验和操作可以在单个分区内完成,是分布式数据库设计的核心原则。

二、事务大小限制

OceanBase 4.x 已移除早期版本对事务大小的硬性限制,但大事务仍存在不可忽视的风险。

事务大小上限的演进
  • V2.x:单个事务大小默认为 100 MB,与 _max_trx_size/_tenant_max_trx_size 配置相关。
  • V3.x 起:大事务大小限制被移除,但并发度需严格控制,防止内存激增。
  • V4.x 及以后无显式事务大小上限,但因分布式两阶段提交(2PC)机制,大事务在提交阶段的资源消耗和失败风险显著高于单机数据库。
⚠️ 大事务的三大核心风险
1. 内存爆炸:事务未提交时数据驻留 MemStore,大事务可能导致 memstore 使用率飙升,触发冻结和合并,影响整体写入性能。
2. 事务超时(-6210 / -6224):事务执行超过 ob_trx_timeout(默认 24 小时)报错 -6210;事务空闲超过 ob_trx_idle_timeout(默认 24 小时)报错 -6224。
3. 分布式死锁:大事务长时间锁定多个分区,多个此类事务相互等待的概率增加,可能导致死锁或参与者过多导致事务无法推进。
大事务的识别与拆解策略
策略说明适用场景拆解示例 按批次拆分(Batch Commit) 将大批量 DML 拆分为多个小批次,每批次执行 COMMIT 数据迁移、批量导入、周期性数据清理,对实时性要求不高的场景 将 10 万行拆成 10 批,每批 1 万行,逐批提交 按业务拆分(Business Split) 根据业务边界拆解事务,重新设计事务边界 复杂业务流程(如订单 + 库存 + 积分),可拆为各自独立事务处理 订单创建(事务1)→ 库存扣减(事务2)→ 积分赠送(事务3) 按时间拆分(Time-based Split) 将长事务拆分为多个执行窗口,配合 OFFSET/LIMIT 分批推进 历史数据归档、无时间截止要求的大表全量数据更新 每晚定时处理 5 万行,多日完成一次大表全量更新
-- 大事务风险示例(不推荐):更新 100 万行数据在一个事务中
        UPDATE orders SET status = 'ARCHIVED' WHERE create_time < '2024-01-01';

        -- 推荐做法:按主键范围拆分,每次更新 10000 行后 COMMIT
        -- 假设主键为 id,范围从 1 到 1000000
        WHILE @start <= 1000000 DO
            UPDATE orders SET status = 'ARCHIVED' 
            WHERE id BETWEEN @start AND @start + 9999 AND create_time < '2024-01-01';
            COMMIT;
            SET @start = @start + 10000;
        END WHILE;

三、事务相关核心参数

参数名默认值作用范围说明推荐值 ob_query_timeout 10,000,000 微秒(10 秒) 会话级/全局级 单条 SQL 语句执行超时。超时错误码通常为 -4012-6212 OLTP: 10-30 秒;OLAP: 60-300 秒 ob_trx_timeout 86,400,000,000 微秒(24 小时) 会话级/全局级 整个事务的执行时长上限(从 BEGIN 到 COMMIT)。超时错误码为 -6210 8-24 小时,视业务而定 ob_trx_idle_timeout 86,400,000,000 微秒(24 小时) 会话级/全局级 事务进入空闲状态(等待下一个 DML)的最大时长。超时后事务自动回滚,错误码为 -6224 60-300 秒,避免长空闲事务占用锁资源 ob_trx_lock_timeout -1(无限等待) 会话级/全局级 事务获取行锁时的最长等待时间。超时返回错误码 -6002 10-100 秒,避免死锁堆积
-- 示例:为当前会话设置合理的超时参数
        SET SESSION ob_query_timeout = 30000000;      -- 30 秒
        SET SESSION ob_trx_timeout = 36000000000;    -- 10 小时
        SET SESSION ob_trx_idle_timeout = 300000000; -- 5 分钟
        SET SESSION ob_trx_lock_timeout = 30000000;  -- 30 秒

        -- 全局生效(需 sys 租户权限)
        SET GLOBAL ob_trx_timeout = 43200000000;     -- 12 小时
        SET GLOBAL ob_trx_idle_timeout = 600000000;  -- 10 分钟
📌 超时参数优先级说明
Session 级别的 SET 语句会覆盖全局设置,优先级最高。对于核心 OLTP 业务,建议在应用程序初始化时通过 SET SESSION 显式指定超时配置,确保不受全局配置变更影响。

四、事务隔离级别

OceanBase 4.x 在不同兼容模式下支持的事务隔离级别存在差异,合理的选择可以在数据一致性与系统吞吐量之间取得平衡。

  • MySQL 模式:支持 READ COMMITTED(默认)和 REPEATABLE READ。
  • Oracle 模式:支持 READ COMMITTED 和 Serializable,不支持 REPEATABLE READ。

选型建议:绝大多数 OLTP 场景下,选用默认的 READ COMMITTED 隔离级别即可。仅在涉及金额计算等对数据一致性有严格要求的场景下,才考虑使用 Serializable 或 REPEATABLE READ,但需注意这两种隔离级别性能较低,应尽量减少长事务。

五、常见错误码与处理策略(修正版)

错误码错误原因处理策略是否可重试 -4012 SQL 执行超过 ob_query_timeout 增加 ob_query_timeout;优化 SQL;若为批量操作可拆分为多个小事务 是 -6210 事务总时长超过 ob_trx_timeout(默认 24 小时) 事务已被回滚,应用收到后必须执行 ROLLBACK,然后拆分事务或调整超时设置后重试 否(需回滚后重试) -6224 事务空闲超时:超过 ob_trx_idle_timeout 未收到新 DML 事务已被系统自动回滚,应用必须执行 ROLLBACK 清理会话状态,重新开启事务 否(需回滚后重试) -6002 事务获取锁等待超过 ob_trx_lock_timeout 检测是否存在长事务或锁冲突;适当调大 lock_timeout;优化事务顺序 是(需等待锁释放后重试) -4038 事务过大导致内存超限 立即拆解事务;若必须保留,需评估后调大 _max_trx_size(不推荐) 否(需调整设计) -4179 内部错误,参与表过多 简化 SQL 或拆解 JOIN;联系技术支持排查 否 -5698 分布式事务参与者过多 减少事务涉及的跨节点操作,或拆分为多个小事务 否(需调整设计)
🔧 ob_error 使用示例
ob_error 是 OceanBase 官方提供的错误码解析工具,使用前需从官方仓库进行编译安装。安装后可通过输入错误码快速获取对应的错误原因和解决方案,避免查阅冗长文档。
$ ob_error -6210
        OceanBase Error Code: OB_ERR_TRX_TIMEOUT(-6210)
        Message: Transaction timeout.
        Cause: Transaction execution time exceeds the ob_trx_timeout limit.
        Solution: Increase ob_trx_timeout or split the transaction.

        $ ob_error -6224
        OceanBase Error Code: OB_ERR_TRX_IDLE_TIMEOUT(-6224)
        Message: Transaction idle timeout.
        Cause: Transaction idle time exceeds the ob_trx_idle_timeout limit.
        Solution: Increase ob_trx_idle_timeout or commit/rollback the transaction promptly.

六、事务诊断与 obdiag 工具应用

obdiag 提供了专门的事务诊断场景,可以一键收集长事务信息或进行根因分析。

# 1. 收集长事务诊断信息(适用于排查事务长时间不结束)
        obdiag gather scene run --scene=observer.long_transaction

        # 2. 事务不结束报错根因分析
        obdiag rca run --scene=transaction_not_ending

        # 3. 悬挂事务根因分析
        obdiag rca run --scene=suspend_transaction

        # 4. 事务断连场景根因分析
        obdiag rca run --scene=transaction_disconnection

        # 5. 事务回滚报错根因分析
        obdiag rca run --scene=transaction_rollback

通过上述命令可快速定位长事务和异常事务的根因,分析事务超时或回滚的原因。

七、事务监控与运维

DBA 和开发者可以通过以下 SQL 实时监控事务运行状态,及时发现并处理异常事务。

-- 1. 查看当前长事务(执行时间超过 1 小时)
        SELECT 
            tenant_name,
            session_id,
            trans_id,
            start_time,
            TIMEDIFF(NOW(), start_time) AS duration,
            last_activate_time,
            timeout_us
        FROM oceanbase.CDB_OB_TRX
        WHERE TIMESTAMPDIFF(SECOND, start_time, NOW()) > 3600;

        -- 2. 查看锁等待
        SELECT 
            blocked_session_id,
            blocking_session_id,
            blocked_trans_id,
            blocking_trans_id,
            table_name,
            row_key
        FROM oceanbase.CDB_OB_DEADLOCK_HISTORY;

        -- 3. 查看当前活跃事务数量(按租户)
        SELECT 
            tenant_id,
            COUNT(*) AS active_trx_cnt
        FROM oceanbase.GV$OB_TRANSACTION
        GROUP BY tenant_id;

        -- 4. 查看 MemStore 使用率(判断大事务是否导致内存压力)
        SELECT 
            tenant_id,
            ROUND(active_memstore_used/1024/1024, 2) AS active_mb,
            ROUND(total_memstore_used/1024/1024, 2) AS total_mb,
            ROUND(active_memstore_used/total_memstore_used*100, 2) AS usage_pct
        FROM oceanbase.GV$OB_MEMSTORE
        WHERE total_memstore_used > 0
        ORDER BY usage_pct DESC;

八、生产环境最佳实践 Checklist

✅ 上线前检查清单
  • SQL 规范遵循:确保所有开发 SQL 均符合官方规范,尤其注意分区表主键包含分区键、索引优先本地等核心原则。
  • 事务边界设计:最小化事务粒度,一个事务只包含必要的 DML,避免在事务中执行无关查询。
  • 大事务拆分:批量 DML 使用分批提交(batch commit),单次事务建议不超过 10,000 行 的修改量(可按业务场景调整)。
  • 超时配置:在应用启动时显式设置 ob_query_timeoutob_trx_timeoutob_trx_idle_timeout
  • 错误处理:应用层实现重试逻辑,区分可重试错误(锁超时等)与设计错误(事务过大、参与者过多),注意 -6210/-6224 需先 ROLLBACK 再重试。
  • 监控告警:对 CDB_OB_TRX 持续监控,对执行时间超过 30 分钟的事务进行告警。
  • 压测验证:核心高频 SQL 必须经过分区裁剪验证和 EXPLAIN 计划分析。
🏆 小结
OceanBase 4.x 已从“事务大小受限”演进到“大事务不推荐”的阶段。良好的 SQL 设计 + 克制的事务大小 + 合理的超时参数 + 主动的监控告警,是保障 OceanBase 集群稳定运行的核心策略。建议开发团队建立常态化的 SQL 审核机制,DBA 持续跟踪事务运行状态,结合 obdiag 等诊断工具提前发现和消除隐患,真正实现分布式数据库的高效管理与稳健运行。
16.3 错误码处理与重试策略

在 OceanBase 分布式数据库环境中,完善的错误处理机制合理的重试策略是保障应用高可用性和用户体验的关键环节。OceanBase 提供了从客户端连接服务端代理再到后端 OBServer 的全链路错误处理能力。本章从应用层重试框架JDBC 驱动层重试ODP 服务端重试与黑名单机制错误码分类速查表以及重试最佳实践五个维度,提供可落地的最佳实践指南,帮助开发者和 DBA 构建高可用的分布式应用。

🎯 本章内容概览
  • 全链路重试架构:应用层 + JDBC 驱动层 + ODP 服务端三层协同机制。
  • 应用层重试框架:可重试/不可重试错误分类、重试参数配置与代码模板。
  • JDBC 驱动层重试:Oracle 兼容模式下驱动的自动重试能力说明。
  • ODP 服务端重试与黑名单机制:故障探测、黑名单路由与 SQL 自动重试。
  • 错误码分类速查表:SQL/事务层、分布式层、连接层、系统层错误码完整对照。
  • 运维监控与最佳实践:监控 SQL 与全链路巡检建议。

一、全链路重试架构

OceanBase 的错误重试能力由应用层ODP 服务端两层协同构成,共同保障 SQL 执行的成功率和系统的高可用性。

层级组件重试能力适用场景 应用层 应用程序代码 区分可重试/不可重试错误,实现自定义重试逻辑 幂等操作、临时性故障(网络闪断、Leader 切换) 服务端 ODP(OBProxy) 基于黑名单机制的 SQL 自动重试 OBServer 节点故障、内存不足等已知错误
📌 设计原则
两层重试机制相互配合:ODP 保障服务端节点故障自动恢复,应用层负责整体业务流程的容错编排。应用层不应完全依赖底层自动重试,而应主动识别和处理可恢复错误。

二、应用层重试框架

应用层是重试策略的核心执行者。一个健壮的重试框架需要区分可重试错误与不可重试错误,并配置合理的重试参数。

可重试 vs 不可重试错误分类
分类错误类型示例错误码重试策略 可重试 查询超时 -4012 增加 ob_query_timeout 或优化 SQL 可重试 事务空闲超时 -6224 执行 ROLLBACK 后重试 可重试 锁等待超时 -6002 检查长事务,调大 ob_trx_lock_timeout 可重试 Leader 切换 -4038 等待后重试 可重试 网络/连接异常 Connection refused、Socket closed、Read timed out 指数退避重试 可重试 内部重试码 -6001、-6005 自动向上层重试,应用层通常无需额外处理 不可重试 语法错误 语法错误类 直接报错,无需重试 不可重试 约束违反 主键冲突、唯一键冲突 业务逻辑处理 不可重试 事务过大 -4038(内存超限)、-5698(参与者过多) 需调整事务设计,重试无效 不可重试 权限错误 权限不足类 直接报错,无需重试
重试参数配置建议
参数推荐值说明 最大重试次数 3 ~ 5 次 避免无限重试 初始重试间隔 100 ~ 500 ms 避免频繁重试加剧故障 最大重试间隔 5 ~ 30 s 指数退避上限 退避倍数 2 指数退避因子
重试框架代码模板(Java + Spring)
import org.springframework.retry.annotation.Backoff;
        import org.springframework.retry.annotation.Retryable;
        import org.springframework.stereotype.Service;
        import org.springframework.dao.DataAccessException;

        import java.sql.SQLException;
        import java.sql.SQLTransientException;

        @Service
        public class OceanBaseRetryService {

            /**
            * 区分 OceanBase 可重试错误与不可重试错误
            * 参考 ODP 故障探测与黑名单机制:服务端已处理的故障应用层无需重复重试
            */
            @Retryable(
                value = {SQLTransientException.class},
                include = {
                    "com.oceanbase.jdbc.SQLTimeoutException",         // -4012 查询超时
                    "java.sql.SQLRecoverableException"                // 连接可恢复异常
                },
                exclude = {
                    "java.sql.SQLSyntaxErrorException",    // 语法错误
                    "java.sql.SQLIntegrityConstraintViolationException",  // 约束违反
                },
                maxAttempts = 3,
                backoff = @Backoff(delay = 500, multiplier = 2, maxDelay = 10000)
            )
            public Object executeWithRetry(String sql, Object... params) throws DataAccessException {
                // 业务逻辑实现
                // 注意:确保操作幂等,避免重复执行产生副作用
                return null;
            }

            /**
            * 手动判断是否可重试(适用于需动态决策的场景)
            */
            public boolean isRetryable(Exception e) {
                if (e == null) return false;
                String msg = e.getMessage();
                if (msg == null) return false;

                // ODP SQL 重试机制:OBServer 节点返回特定错误码时,ODP 会触发自动重试
                // 应用层应避免在 ODP 已重试的场景下发起额外重试
                return msg.contains("Timeout")            // 超时类错误
                    || msg.contains("Connection refused") // 连接拒绝
                    || msg.contains("Socket closed")      // Socket 关闭
                    || msg.contains("Read timed out")     // 读超时
                    || (e instanceof SQLException && ((SQLException)e).getErrorCode() == 6002); // 锁等待
            }
        }
⚠️ 重试注意事项
  • 幂等性:重试的操作必须是幂等的,避免重复执行产生副作用。非幂等操作(如余额扣减)需在业务层做防重处理。
  • 重试上下文清理:重试前必须回滚失败的事务,清理连接状态,避免残留上下文影响后续请求。
  • 日志记录:重试时记录详细日志(包括错误码、重试次数、原始 SQL),便于问题排查。应用日志应记录标准 OceanBase 错误码及连接信息(IP、端口、用户名),以便 DBA 排查。
  • 重试风暴:大量客户端同时重试可能引发“重试风暴”,建议在重试策略中加入随机抖动(jitter)。

三、ODP 服务端重试与黑名单机制

ODP 通过故障探测黑名单机制SQL 重试三种手段实现服务端的高可用保障[reference:0]。在应用层进行重试之前,ODP 服务端已通过下述机制完成了第一轮故障屏蔽。

1. 故障探测

ODP 定义了多种 OBServer 节点状态来精细化描述节点健康状况,并基于状态进行故障探测和路由决策。ODP 会周期性(20s/次)从 DBA_OB_SERVERS 和 DBA_OB_ZONES 视图中获取集群的机器状态信息[reference:1]。对于 ACTIVE 状态,ODP 会从黑名单中移除该 OBServer 节点[reference:2];对于 INACTIVE/REPLAY 状态,ODP 会将该节点加入状态黑名单[reference:3]。ODP 支持三种类型的黑名单:状态黑名单、探测黑名单和活不可用黑名单[reference:4]。ODP 的策略是在处理 OBServer 节点机器和进程故障的基础上,根据一些已知现象去处理一些业务逻辑问题,如 OBServer 节点返回的特定错误码(例如机器内存不足)[reference:5]。

2. 黑名单机制

根据故障探测结果,ODP 将疑似故障节点加入黑名单(Blocklist),影响 ODP 的路由选择,避免将请求发送到已知故障节点。ODP 通过使用黑名单机制实现对 OBServer 节点运维操作(如低峰期合并、升级、切主、宕机、启动、关闭等)的自适应访问控制[reference:6]。黑名单会持续一段时间,待节点恢复后自动移出。

3. SQL 重试

当请求被发往黑名单节点或因其他原因失败时,ODP 会将 SQL 重新发往其他健康节点重试,保证 SQL 尽可能执行成功。重试过程中,ODP 会剔除已知故障节点,提高成功率。

💡 ODP 重试的典型场景
  • 节点故障:OBServer 进程崩溃、网络中断,ODP 将其加入黑名单,请求自动切换到其他节点。
  • 内存不足:OBServer 返回内存不足错误,ODP 识别并触发重试,将请求路由到内存充足的节点。[reference:7]
  • 无响应超时:OBServer 长时间无响应,ODP 判定为疑似故障,触发重试机制。
4. ODP 重试相关配置
# 查看 ODP 重试相关配置
        SHOW PROXYCONFIG LIKE '%retry%';

        # 详细配置说明可参考官方文档

四、错误码分类速查表

SQL/事务层错误码(-4000 ~ -4999 / 6000 ~ 6999)
错误码分类典型场景处理方式 -4012 查询超时 SQL 执行超过 ob_query_timeout 未返回结果 调大 ob_query_timeout 或优化 SQL[reference:8] -6224 事务空闲超时 事务内两条语句的执行间隔超过 ob_trx_idle_timeout 执行 ROLLBACK,调大 ob_trx_idle_timeout 或拆分事务 -6002 锁等待超时 事务获取锁等待超过 ob_trx_lock_timeout 或事务空闲超时 检查长事务,调大 ob_trx_lock_timeout,优化事务顺序 -6001 事务集在执行中改变 更新前数据发生变化,向上层返回并重试 内部错误码,通常可重试,频繁请联系技术支持 -6005 锁行冲突 更新操作加锁失败,向上层返回并重试 内部错误码,通常可重试,频繁请联系技术支持 -4038 OB_NOT_MASTER,Leader 不在本节点 发生切主后 Location Cache 未刷新,或日志流无主 少量重试符合预期;频繁重试需排查切主和 Location Cache 问题 -4179 Operation not allowed now 有 Offline DDL 正在执行;主备切换日志不同步;primary_zone 配置不满足多数派等 等待 DDL 结束;同步主备日志;调整 primary_zone 配置 -5698 分布式事务参与者过多 事务涉及过多跨节点操作或表过多 减少事务涉及的跨节点操作,拆分为多个小事务;或调大相关参数 -4023 OB_EAGAIN ID Service 没有可分配区间,日志回调延迟 检查日志回调状态,联系技术支持
连接层错误码与异常
错误类型典型场景处理方式 Connection refused OBProxy 或 OBServer 端口未开放/服务未启动/防火墙拦截 检查服务状态、端口监听、防火墙规则 Socket closed by server ODP 异常断链;OBServer 执行报错主动断链;网络链路问题 分析 ODP 日志,使用 tcpdump 抓包排查断链原因 Read timed out SQL 执行较慢触发了 Socket timeout 调大 socketTimeout;优化慢 SQL;排查 ODP comm-error 日志 Unexpected end of stream ODP 收到 OBServer 的 -4016 错误触发主动断链;网络闪断 排查 ODP 和 OBServer 日志,检查网络链路稳定性
🔧 ob_error 使用示例
ob_error 是 OceanBase 官方提供的错误码解析工具,使用前需从官方仓库进行编译安装。安装后可通过输入错误码快速获取对应的错误原因和解决方案。
$ ob_error -4038
        OceanBase Error Code: OB_NOT_MASTER(-4038)
        Message: Not master
        Cause: The current node is not the leader.
        Solution: Check leader status and location cache.

        $ ob_error -4179
        OceanBase Error Code: OB_OP_NOT_ALLOW(-4179)
        Message: Operation not allowed
        Cause: Another offline DDL is being executed, or switchover logs are out of sync.
        Solution: Wait for DDL to complete, or synchronize logs.

五、重试策略最佳实践

1. 分层重试设计
  • ODP 服务端层:处理节点故障、OBServer 无响应、内存不足等基础设施故障。
  • 应用层:处理业务层面的错误,区分可重试错误,实现重试逻辑和断路保护。
2. 重试参数配置建议
场景maxAttemptsinitialIntervalmaxIntervalmultiplier OLTP 在线业务 3 ~ 5 100 ms 5 s 2 批量处理/离线任务 5 ~ 10 1 s 30 s 2 高并发服务 2 ~ 3 50 ms 2 s 1.5
3. 瞬态故障与硬故障区分
  • 瞬态故障:Leader 切换、网络抖动、锁冲突,可通过有限次重试解决。
  • 硬故障:节点宕机、磁盘满、配置错误,重试无效,需人工介入。
  • ODP 服务端黑名单机制:ODP 已将节点加入黑名单,应用层无需对同一故障节点反复重试。
4. 断路保护与限流
  • 当错误率达到阈值时,应触发断路保护,暂停重试以保护后端。
  • 使用限流器控制重试请求的速率,避免重试风暴。
5. 错误码精细化处理
// 错误码精细化处理示例
        void handleException(Exception e, String sql) {
            if (e instanceof SQLException) {
                int errorCode = ((SQLException) e).getErrorCode();

                // -4012: 查询超时
                if (errorCode == 4012) {
                    LOG.warn("Query timeout, retrying. sql={}", sql);
                    retryWithBackoff();
                    return;
                }

                // 6224: 事务空闲超时,需回滚后重试
                if (errorCode == 6224) {
                    LOG.warn("Transaction idle timeout, rolling back. sql={}", sql);
                    connection.rollback();
                    retryWithBackoff();
                    return;
                }

                // 6002: 锁冲突
                if (errorCode == 6002) {
                    LOG.warn("Lock conflict, retrying. sql={}", sql);
                    retryWithBackoff();
                    return;
                }

                // -4038: Leader 切换,等待后重试
                if (errorCode == 4038) {
                    LOG.warn("Leader not master, retry after delay. sql={}", sql);
                    retryWithBackoff();
                    return;
                }
            }

            // 不可重试错误,直接抛出
            throw new BusinessException("Unretryable error", e);
        }
✅ 上线前检查清单
  • ✅ 明确区分可重试错误与不可重试错误,避免无限重试。
  • ✅ 配置合理的重试参数(次数、间隔、退避策略),避免重试风暴。
  • ✅ 确保重试操作幂等,非幂等操作需做防重处理。
  • ✅ 重试前回滚失败的事务,清理连接上下文。
  • ✅ 记录重试日志(错误码、重试次数、耗时),用于故障复盘。
  • ✅ 理解 ODP 服务端重试和黑名单机制,避免应用层对 ODP 已屏蔽的节点重复重试。
  • ✅ 监控错误率,配置阈值告警和断路保护。

六、监控与诊断工具

obdiag 根因分析场景

obdiag 提供了针对事务错误的根因分析场景:

# 事务执行超时报错根因分析(支持 statement is timeout 或 transaction is timeout)
        obdiag rca run --scene=transaction_execute_timeout --env tenant_id=1002 --env trace_id=YXXXXXXXXXX --env err_type="statement is timeout"

        # 分布式系统根因分析
        obdiag rca run --scene=distributed_system_scene
常用监控 SQL
-- 查看 SQL 执行错误统计(按错误码分组)
        SELECT
            ret_code,
            COUNT(*) AS error_count,
            AVG(elapsed_time)/1000 AS avg_elapsed_ms
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE ret_code != 0
        AND request_time > TIME_TO_USEC(NOW() - INTERVAL 1 HOUR)
        GROUP BY ret_code
        ORDER BY error_count DESC;

        -- 查询最近 15 分钟的异常 SQL
        SELECT
            SQL_ID,
            tenant_name,
            user_name,
            client_ip,
            server_ip,
            ret_code,
            elapsed_time/1000 AS elapsed_ms
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE ret_code != 0
        AND request_time > TIME_TO_USEC(NOW() - INTERVAL 15 MINUTE)
        ORDER BY request_time DESC
        LIMIT 50;

        -- 查看当前锁等待情况
        SELECT
            blocked_session_id,
            blocking_session_id,
            blocked_trans_id,
            blocking_trans_id,
            table_name,
            row_key
        FROM oceanbase.CDB_OB_DEADLOCK_HISTORY;

        -- 查看租户级错误统计
        SELECT
            tenant_name,
            ret_code,
            COUNT(*) AS cnt
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE ret_code != 0
        AND request_time > TIME_TO_USEC(NOW() - INTERVAL 1 HOUR)
        GROUP BY tenant_name, ret_code
        ORDER BY cnt DESC;
🏆 小结
OceanBase 的错误码处理与重试策略覆盖了从 ODP 服务端到应用业务端的全链路。ODP 通过故障探测、黑名单和 SQL 重试实现服务端高可用,应用层则负责整体业务流程的容错编排。建议团队建立常态化的错误码监控机制,根据业务特点配置合理的重试参数,并结合 obdiag 等工具进行定期巡检,真正实现分布式应用的高可用架构。
16.4 数据分片与分区键选择

在 OceanBase 分布式数据库中,数据分片是通过分区(Partition)实现的。数据表中每一行用于计算所属分区的列集合称为分区键,由分区键构成的表达式称为分区表达式[reference:0]。合理的数据分片设计是发挥分布式数据库性能优势的关键,其核心原则是选择高区分度的列作为分区键,并优先采用“RANGE(时间)+ HASH(业务)”的二级分区策略,确保数据均匀分布、避免热点,并最大化查询性能与运维效率[reference:1]。本章围绕分区类型选择、分区键设计原则、分区裁剪原理、组合分区策略、运维监控与最佳实践等维度,提供可落地的最佳实践指南,帮助开发者和 DBA 构建高可用的分布式应用。

🎯 本章内容概览
  • 数据分片核心概念:分区表、分区键、分区表达式的定义与作用。
  • 分区类型详解:Range/Range Columns、List/List Columns、Hash/Key 分区的场景与用法。
  • 分区键选择策略:高基数、避免数据倾斜、配合分区裁剪等核心原则。
  • 组合分区(二级分区):Range + Hash 等组合方式的适用场景与设计建议。
  • 分区裁剪原理:如何通过条件中分区键精准定位分区,避免全分区扫描。
  • 自动分区分裂:4.x 版本自动扩展能力与使用限制。
  • 运维监控与最佳实践:常用 SQL 与 Checklist。

一、数据分片的核心价值

在 OceanBase 中,分区表的核心价值主要体现在以下四个方面[reference:2]:

  • 性能优化:按照规则将大表拆分为多个物理子分区,查询时通过分区裁剪仅扫描目标分区数据,大幅减少 IO 开销,提升 SQL 执行速度。
  • 运维轻量化:支持分区级别的删除、截断、迁移、归档操作。针对历史冷数据,可直接删除对应分区,无需逐行清理数据,运维效率提升显著。
  • 资源均衡:不同分区可分散存储在不同 OB 节点,均衡集群负载,避免单节点数据压力过高。
  • 冷热数据分离:按时间、业务维度分区后,可对冷热分区配置不同存储策略,实现精细化的数据管理。
📌 分区表的核心限制:主键必须包含分区键
在 OceanBase 中,分区表的主键和唯一约束必须包含分区键,即每个主键字段(或组合)中至少有一个字段是分区键字段[reference:3]。这一设计保证了在分布式环境中数据的全局唯一性。因此,在分区表设计之初,主键与分区键的选择必须协同考虑,选择一个合适的列作为分区键,同时该列必须是主键的子集或主键本身

二、分区类型详解与选型建议

OceanBase 支持 Range、List、Hash 三种基础分区类型,以及由它们组合而成的二级分区[reference:4]。下面逐一介绍每种分区类型的适用场景、使用要点和最佳实践。

2.1 Range 分区(范围分区)

适用场景:按时间归档的流水表、日志表、交易明细表,例如用户操作日志、支付流水、系统运行日志等,这类数据天然按时间递增,历史数据可定期清理[reference:5]。

使用要点

  • 分区键常选择日期字段(create_timetrade_time)、自增 ID 等有序字段[reference:6]。
  • 分区区间建议根据数据增长量合理规划,日数据量大可按天/小时分区,中长期数据可按月/季度分区[reference:7]。
  • 支持自动分区,无需手动频繁新增分区,适配持续新增的时序数据。
  • Range 分区的分区键只能使用单列,且分区键必须是 INT 类型或 YEAR 类型;如果要使用多列或其他数据类型作为分区键,可选择 Range Columns 分区,Range Columns 分区支持多种数据类型(数值、日期时间、字符、二进制类型)和多列作为分区键[reference:8]。

实践建议:对于每日产生大量流水的业务表,优先使用按天范围分区。当业务需要归档近一年历史数据时,直接执行分区删除操作,几秒内即可完成;同时查询某一天数据时,数据库会自动触发分区裁剪,只访问对应日期分区,查询性能提升明显[reference:9]。

-- Range 分区示例:按日分区
        CREATE TABLE orders (
            order_id INT,
            order_date DATE,
            user_id INT,
            amount DECIMAL(10,2),
            PRIMARY KEY (order_id, order_date)
        ) PARTITION BY RANGE COLUMNS(order_date) (
            PARTITION p20250101 VALUES LESS THAN ('2025-01-02'),
            PARTITION p20250102 VALUES LESS THAN ('2025-01-03'),
            PARTITION p20250103 VALUES LESS THAN ('2025-01-04')
        );

        -- 启用自动分区分裂的 Range 分区表(V4.3.5+)
        CREATE TABLE orders_auto (
            order_id INT,
            order_date DATE,
            user_id INT,
            amount DECIMAL(10,2),
            PRIMARY KEY (order_id, order_date)
        ) PARTITION BY RANGE COLUMNS(order_date) SIZE('50GB') (
            PARTITION p_start VALUES LESS THAN ('2025-01-01')
        );
        -- 当分区数据量超过 50GB 时,系统将自动进行分裂操作
2.2 List 分区(列表分区)

适用场景:数据按固定维度分类的业务表,例如按地域、业务线、渠道、用户类型划分的数据[reference:10]。

使用要点

  • 分区键一般为字符串、短整型等枚举类字段,取值固定且可提前枚举[reference:11]。
  • 分区数量建议和枚举值数量匹配,避免单个分区数据量过大。
  • 可设置默认分区,收纳未匹配到枚举值的异常数据,防止数据写入失败。
  • List 分区的分区键必须为列名,不能使用表达式,支持数值类型、字符串类型、时间类型等[reference:12]。

实践建议:以多地域业务为例,将不同省份的数据划分至独立分区。当需要单独统计某个省份数据时,直接命中对应分区;若某条业务下线,可直接清理该业务对应的分区,管理十分灵活[reference:13]。

-- List 分区示例:按区域分区
        CREATE TABLE user_profile (
            user_id INT,
            user_name VARCHAR(100),
            region VARCHAR(20),
            create_time DATE,
            PRIMARY KEY (user_id, region)
        ) PARTITION BY LIST COLUMNS(region) (
            PARTITION p_north VALUES IN ('Beijing', 'Tianjin', 'Hebei'),
            PARTITION p_south VALUES IN ('Guangdong', 'Guangxi', 'Hainan'),
            PARTITION p_east VALUES IN ('Shanghai', 'Jiangsu', 'Zhejiang'),
            PARTITION p_default VALUES IN (DEFAULT)
        );
2.3 Hash 分区(哈希分区)

适用场景:Hash 分区通过对分区键应用 Hash 算法,将数据均匀分散到各个分区中,适合于以下场景[reference:14]:

  • 不能指定数据的分区键的列表特征。
  • 不同范围内的数据大小相差非常大,并且很难手动调整均衡。
  • 使用 Range 分区后数据聚集严重。
  • 并行 DML、分区裁剪和分区连接等性能非常重要。

使用要点

  • Hash 分区的分区键必须为列名,不能使用表达式,支持数值类型、字符串类型、时间类型等[reference:15]。
  • 分区键返回值必须是 INT 类型[reference:16]。
  • 建议选择区分度高的列作为 Hash 分区键,如订单号、用户 ID 等[reference:17]。
  • 当分区数为 2 的幂次方时,Hash 算法能保证每个分区包含几乎相同数量的行[reference:18]。
  • Key 分区与 Hash 分区类似,区别在于 Key 分区键可以是多列组合,且不限制分区键类型,支持非整数类型列作为分区键,无需额外的转换操作[reference:19][reference:20]。
-- Hash 分区示例:按用户 ID 打散
        CREATE TABLE user_events (
            user_id INT,
            event_time DATETIME,
            event_type VARCHAR(50),
            event_data TEXT,
            PRIMARY KEY (user_id, event_time)
        ) PARTITION BY HASH(user_id) PARTITIONS 16;

        -- Key 分区示例:按字符串类型列分区
        CREATE TABLE orders_key (
            order_id VARCHAR(32),
            order_date DATE,
            user_id INT,
            PRIMARY KEY (order_id, order_date)
        ) PARTITION BY KEY(order_id) PARTITIONS 8;
⚠️ 分区类型选择速查表
  • Range/Range Columns:按时间归档的流水表、日志表(最常用)→ 推荐:Range Columns
  • List/List Columns:按地域、渠道、状态等固定枚举值分类 → 推荐:List Columns
  • Hash/Key:无明确分区规则,需要均匀打散数据的场景 → 推荐:Hash(数值型)/ Key(字符串型)

三、分区键选择策略

分区键是决定数据分布和查询性能的关键,需要遵循以下核心原则[reference:21][reference:22][reference:23]。

3.1 原则一:确保分区裁剪有效

根据业务需求谨慎选择分区键,以保证大多数查询能够使用分区键进行分区裁剪,从而减少数据访问量。分区裁剪是优化器通过 WHERE 条件中的分区键精准定位并减少扫描分区数量的重要技术[reference:24]。对于关联性较强的表(如事实表与维表),建议使用关联键作为分区键,并采用相同的分区方式,将这些表放入同一个 Table Group 中,以减少跨节点的数据交互[reference:25]。

3.2 原则二:避免数据倾斜

确保分区键的选择具有高基数(NDV,Number of Distinct Values),以避免某些分区存储过多数据,导致查询性能下降。例如,优先选择整型列或时间列作为分区键,而不是低基数的 VARCHAR 列[reference:26]。如果分区键是自增列,在 Range 分区场景下会导致所有写入落到最后一个分区,造成单点瓶颈[reference:27]。

3.3 原则三:配合主键约束

分区表的主键必须包含分区键,即每个主键字段(或组合)中至少有一个字段是分区键字段[reference:28]。因此,在分区表设计之初,主键与分区键的选择必须协同考虑,主键与分区键应统一规划,不可割裂设计

3.4 分区键选择决策矩阵
业务场景推荐分区类型分区键选择建议 订单流水表(按时间查询为主) Range Columns(日期) order_datecreate_time,配合主键设计为 (order_id, order_date) 用户行为表(按用户查询为主) Hash/Key(用户标识) 高基数用户列(如 user_id),确保分布均匀 全国多地域业务表(按省份查询为主) List Columns(地域字段) 省份/城市代码列,配合默认分区处理未知值 海量流水表 + 按时间分区 + 按用户打散 复合分区(Range + Hash) 一级分区键:时间列;二级分区键:用户列 订单表(需全局唯一订单号 + 按时间归档) Range Columns 主键必须包含时间列和订单号,分区键为时间列

四、分区裁剪详解

分区裁剪(Partition Pruning)是优化器通过 WHERE 条件中的分区键来精准定位并减少扫描分区数量的重要技术[reference:29]。合理利用分区裁剪,可以大幅减少查询扫描的数据量,提升 SQL 执行效率。

一级分区裁剪原理
  • Hash/List 分区:根据 WHERE 子句中分区列的值,计算目标分区,只访问符合条件的分区。如果分区条件为表达式且该表达式作为一个整体出现在等值条件中,也可以做分区裁剪[reference:30]。
  • Range 分区:通过 WHERE 子句中分区键的范围与表定义的分区范围求交集,确定需要访问的分区。如果分区表达式是一个函数且查询条件是非等值条件(范围查询),则不支持分区裁剪[reference:31]。
  • Range Columns、List Columns 分区支持 IN 列表进行分区裁剪,当查询条件中包含分区键的多个枚举值时,优化器可以计算出这些值所在的分区并进行裁剪[reference:32]。
二级分区裁剪原理

对于二级分区,先按照一级分区键确定一级需要访问的分区,再通过二级分区键确定二级分区需要访问的分区,然后做乘积确定最终需要访问的所有物理分区[reference:33]。

-- Hash 分区裁剪示例
        CREATE TABLE t1 (c1 INT, c2 INT) PARTITION BY HASH(c1) PARTITIONS 5;
        -- 查询条件中使用了分区键 c1,优化器只访问对应的分区
        EXPLAIN SELECT * FROM t1 WHERE c1 = 1;
        -- 执行计划中 partitions 部分仅显示 p1,而非全部 5 个分区

        -- Range 分区裁剪示例(分区键上使用函数,范围查询无法裁剪)
        CREATE TABLE t2 (c1 INT, c2 INT) PARTITION BY RANGE(c1 + 1) (
            PARTITION p0 VALUES LESS THAN (100),
            PARTITION p1 VALUES LESS THAN (200)
        );
        -- 以下查询因为分区键是表达式且查询条件是非等值条件,无法进行分区裁剪
        EXPLAIN SELECT * FROM t2 WHERE c1 < 150 AND c1 > 110;
        -- 执行计划中 partitions 部分显示 p[0-1],需要扫描两个分区

        -- Range Columns 分区裁剪示例(推荐)
        CREATE TABLE t3 (c1 DATE, c2 INT) PARTITION BY RANGE COLUMNS(c1) (
            PARTITION p202501 VALUES LESS THAN ('2025-02-01'),
            PARTITION p202502 VALUES LESS THAN ('2025-03-01')
        );
        -- 以下查询可以精准裁剪
        EXPLAIN SELECT * FROM t3 WHERE c1 BETWEEN '2025-01-15' AND '2025-01-20';
        -- 执行计划中 partitions 部分仅显示 p202501
📌 分区裁剪失效的常见场景
  • 查询条件中未使用分区键
  • 分区键上使用了函数或类型转换,而查询条件中未使用相同的表达式
  • Range 分区中分区表达式为函数,且查询条件是非等值条件(范围查询)
  • 二级分区表中仅提供了部分分区键的条件

五、组合分区(二级分区)

组合分区通常是先使用一种分区策略,然后在子分区再使用另外一种分区策略,适合于业务表的数据量非常大时。使用组合分区能发挥多种分区策略的优点[reference:34]。

5.1 常用组合方式

OceanBase 支持 Range + Hash、Range + Range、Range + List、Hash + Range、Hash + Hash 等多种组合分区策略,其中Range + Hash 是最为推荐的组合方式,尤其适用于流水大表。

  • Range + Hash(最推荐):一级按时间范围分区便于归档管理,二级按业务标识(如用户 ID)哈希打散避免热点,兼顾运维便利性与负载均衡[reference:35]。
  • Hash + Range:一级按用户 ID 等字段均匀打散数据,二级按时间范围分区,适合需要数据均匀分布且按时间查询的场景。
  • Range + List:一级按时间分区,二级按地域或状态分类,适合多维度数据分析场景。
5.2 模板化二级分区 vs 非模板化二级分区
  • 模板化二级分区(推荐):使用 SUBPARTITION TEMPLATE 子句定义统一的子分区结构,每个一级分区都有相同的子分区模板,适用于分区模式统一的业务场景。
  • 非模板化二级分区:每个一级分区可以单独定义不同的子分区结构,适用于不同时间范围需要不同子分区策略的复杂场景。
-- Range + Hash 模板化二级分区示例(推荐)
        CREATE TABLE orders_range_hash (
            order_id BIGINT,
            order_date DATE NOT NULL,
            user_id INT NOT NULL,
            amount DECIMAL(10,2),
            PRIMARY KEY (order_id, order_date, user_id)
        ) PARTITION BY RANGE COLUMNS(order_date)
        SUBPARTITION BY HASH(user_id) SUBPARTITIONS 8
        SUBPARTITION TEMPLATE (
            SUBPARTITION sp0,
            SUBPARTITION sp1,
            SUBPARTITION sp2,
            SUBPARTITION sp3,
            SUBPARTITION sp4,
            SUBPARTITION sp5,
            SUBPARTITION sp6,
            SUBPARTITION sp7
        ) (
            PARTITION p202501 VALUES LESS THAN ('2025-02-01'),
            PARTITION p202502 VALUES LESS THAN ('2025-03-01'),
            PARTITION p202503 VALUES LESS THAN ('2025-04-01')
        );

        -- 查询会自动进行分区裁剪:一级分区按日期裁剪,二级分区按用户 ID 裁剪
        -- 以下查询只访问 p202502 分区的对应子分区
        SELECT * FROM orders_range_hash 
        WHERE order_date BETWEEN '2025-02-01' AND '2025-02-28' AND user_id = 12345;
⚠️ 组合分区使用注意事项
  • 主键必须包含一级分区键和二级分区键的组合
  • 一级分区数×二级分区数 = 总分片数,总分片数不宜过多(建议单表总分区数不超过 10,000)
  • 自动分区分裂(V4.3.5+)不支持二级分区表[reference:36]
  • 创建组合分区时,建议使用模板化语法,便于管理和维护

六、自动分区分裂(Auto Partition Splitting)

OceanBase 从 V4.3.5 版本开始支持自动分区分裂功能,允许用户预先设定自动分区规则,在数据增长到一定程度时自动进行分区拆分,避免单分区数据量过大带来的负载不均衡和性能下降问题[reference:37]。

使用限制
  • 当前版本仅支持 Range 分区的一级分区有主键表支持自动分区能力[reference:38]。
  • 不支持自动分裂 List、Hash 分区表。
  • 不支持自动分裂二级分区表。
  • 不支持无主键表的自动分区分裂。
  • 不支持列存表、物化视图的自动分区分裂[reference:39]。
配置方式
-- 创建表时启用自动分区分裂
        CREATE TABLE orders_auto (
            order_id INT,
            order_date DATE,
            user_id INT,
            amount DECIMAL(10,2),
            PRIMARY KEY (order_id, order_date)
        ) PARTITION BY RANGE COLUMNS(order_date) SIZE('50GB') (
            PARTITION p_start VALUES LESS THAN ('2025-01-01')
        );
        -- 当分区数据量超过 50GB 时,系统将自动进行分裂操作

        -- 修改现有表的自动分区属性
        ALTER TABLE orders_auto PARTITION BY RANGE COLUMNS(order_date) SIZE('100GB');

        -- 查看表的自动分区属性
        SELECT table_name, partition_type, subpartition_type, auto_partition_size 
        FROM information_schema.TABLES 
        WHERE table_name = 'orders_auto';

七、分区表运维监控

以下常用 SQL 可用于查看分区表信息、分区数据分布和分区使用情况。

-- 1. 查看表的分区信息(使用 information_schema.PARTITIONS)
        SELECT 
            TABLE_NAME,
            PARTITION_NAME,
            SUBPARTITION_NAME,
            PARTITION_ORDINAL_POSITION,
            TABLE_ROWS
        FROM information_schema.PARTITIONS 
        WHERE TABLE_NAME = 'your_table_name';

        -- 2. 查询分区表的总分区数(Oracle 模式单个表最多支持 65536 个分区[reference:40])
        SELECT 
            TABLE_NAME,
            PARTITION_TYPE,
            COUNT(*) AS partition_count
        FROM information_schema.PARTITIONS 
        WHERE TABLE_NAME = 'your_table_name'
        GROUP BY TABLE_NAME, PARTITION_TYPE;

        -- 3. 查看各分区数据分布情况(估算表大小)
        SELECT 
            PARTITION_NAME,
            SUBPARTITION_NAME,
            DATA_LENGTH / 1024 / 1024 AS data_mb,
            INDEX_LENGTH / 1024 / 1024 AS index_mb
        FROM information_schema.PARTITIONS 
        WHERE TABLE_NAME = 'your_table_name'
        ORDER BY DATA_LENGTH DESC;

        -- 4. 查看分区表的主键与分区键关系(检查主键是否包含分区键)
        SELECT 
            CONSTRAINT_NAME,
            CONSTRAINT_TYPE,
            COLUMN_NAME
        FROM information_schema.KEY_COLUMN_USAGE 
        WHERE TABLE_NAME = 'your_table_name'
        ORDER BY CONSTRAINT_TYPE, ORDINAL_POSITION;

        -- 5. 查看表是否在 Table Group 中
        SELECT 
            TABLE_NAME,
            TABLEGROUP_NAME
        FROM information_schema.TABLES 
        WHERE TABLE_NAME = 'your_table_name';

        -- 6. 使用 __all_virtual_partition_item 系统表查询分区详细信息(仅限 sys 租户)
        SELECT 
            table_id,
            partition_id,
            zone,
            svr_ip,
            svr_port
        FROM oceanbase.__all_virtual_partition_item 
        WHERE table_id = (SELECT table_id FROM oceanbase.__all_table WHERE table_name = 'your_table_name');

八、生产环境最佳实践 Checklist

✅ 上线前检查清单
  • ✅ 确认分区类型与业务查询模式匹配:按时间查询为主选 Range 分区,按实体查询为主选 Hash 分区,按枚举值分类选 List 分区。
  • ✅ 确认分区表主键包含分区键,这是分区表的硬性约束。
  • ✅ 分区键选择高基数列,确保数据分布均匀,避免单分区热点。
  • ✅ 预计数据量:单表低于 5000 万行时可不分区,超过 5000 万行建议分区,单表超 10 亿行必须分区[reference:41]。
  • ✅ 控制单分区数据量,建议单分区数据量不超过 50GB,超过此阈值可能影响查询和 DDL 效率。V4.3.5+ 版本可启用自动分区分裂特性,简化运维。
  • ✅ 总分区数不宜过多:普通分区表建议不超过 1000 个分区,组合分区总分区数(一级×二级)建议不超过 10,000 个,避免分区元数据膨胀导致的内存消耗[reference:42]。
  • ✅ 对于关联性强的多个表,使用相同的分区键和分区数,放入同一个 Table Group 中,减少跨节点交互[reference:43]。
  • ✅ 对于历史数据清理需求,使用 Range 分区并按时间设计分区区间,便于直接删除旧分区。
  • ✅ 核心查询确保使用 EXPLAIN 验证分区裁剪生效,避免全分区扫描。
  • ✅ 监控分区数据分布,定期检查是否存在数据倾斜。
🏆 小结
OceanBase 的数据分片设计核心在于合理选择分区键与分区类型。分区键选择需遵循高基数、配合主键约束、确保大多数查询能触发分区裁剪三大原则。分区类型根据业务场景选择:Range/Range Columns 适用于时间序列表,List/List Columns 适用于枚举分类表,Hash/Key 适用于均匀打散场景。对于海量数据,建议采用Range + Hash 组合分区策略,以兼顾归档效率与负载均衡。通过合理的数据分片设计,结合分区裁剪优化和自动分区分裂等特性,可以充分发挥 OceanBase 分布式架构的优势,实现高性能、高可用、易运维的数据管理。
16.5 预编译语句与计划缓存利用

在 OceanBase 数据库中,计划缓存(Plan Cache) 是一个关键的优化机制,旨在为相似的 SQL 查询共享执行计划,从而显著提升查询处理能力。对于开发者而言,理解其工作原理并合理配置预编译语句,是将应用性能推向极致的关键环节。本章从快速参数化原理、计划缓存核心参数、预编译语句配置、监控视图与 Outline 绑定、自适应计划缓存及最佳实践六大维度出发,提供可落地的开发指南,帮助开发者和 DBA 充分发挥 OceanBase 计划缓存的优势。

🎯 本章内容概览
  • 计划缓存原理:快速参数化、软解析与硬解析的工作机制。
  • 计划缓存核心参数:内存控制、淘汰策略与自适应开关配置。
  • 预编译语句(PreparedStatement):JDBC 参数配置与计划缓存协同优化。
  • 监控视图与诊断工具:关键系统视图字段解析与 SQL 审计分析。
  • 计划绑定与 Outline:使用 Outline 固定执行计划,解决数据倾斜场景下的计划跳变问题。
  • 生产最佳实践 Checklist:参数调优、应用开发规范与运维建议。

一、计划缓存工作原理

OceanBase 的执行计划缓存以键值对形式存储,键是参数化后的 SQL 字符串,值是其对应的执行计划。当数据库收到 SQL 请求时,并非直接进行复杂的解析,而是先经过一个 “快速参数化” 模块。

1. 快速参数化(Fast Parameterization)

快速参数化模块会将 SQL 文本中的常量替换为通配符(?),生成标准化键值。该过程在词法分析阶段直接进行,而不是基于 AST(抽象语法树),因此效率更高[reference:0][reference:1]。例如:

原始 SQL:   SELECT * FROM t1 WHERE c1 = 1 AND c2 = 'abc'
        快速参数化: SELECT * FROM t1 WHERE c1 = ? AND c2 = ?

重要限制:并非所有常量都会被参数化[reference:2]。以下场景中的常量不会被参数化:

  • ORDER BY / GROUP BY 后的常量(如 ORDER BY 1,2 中的位置常量)。
  • 格式字符串中的字符串常量(如 SELECT DATE_FORMAT('2006-06-00', '%d') 中的 '%d')。
  • 函数输入参数中影响执行计划的常量(如 CAST(999.88 AS NUMBER(2,1)) 中的 2,1)。
  • INSERT 语句的 VALUES 列表中的常量通常会被参数化,但如果值列表长度不一致可能导致无法命中。
📌 为什么需要快速参数化?
传统的基于 AST(抽象语法树)的参数化方式开销较大,快速参数化在词法分析阶段直接替换常量,效率更高,能够支持更高的 QPS[reference:3]。但这也意味着某些语义相同的 SQL 可能会因为常量位置特殊而无法共享计划缓存,开发者应避免在 SQL 中动态拼接上述特殊位置的常量。
2. 缓存命中(软解析)

OceanBase 使用参数化后的 SQL 字符串作为键,在计划缓存中查找执行计划[reference:4]。如果计划存在,数据库便会直接使用该计划,这个过程称为软解析,其效率远高于重新生成计划。软解析阶段的耗时通常小于 0.1 ms[reference:5]。

3. 缓存未命中(硬解析)

如果未找到可用计划,系统会执行硬解析,进行完整的 SQL 编译(包括语法分析、语义分析、查询重写、优化器代价估算等),生成新的执行计划,并将其存入缓存中以备用[reference:6]。硬解析阶段耗时超过 100 ms 的情况可被视为异常[reference:7]。

⚡ 性能对比
从计划缓存中获取执行计划至少比重新生成计划快一个数量级[reference:8]。软解析的平均耗时约为 0.1 ms,而硬解析的平均耗时约为 5~20 ms(取决于 SQL 复杂度)。对于每秒数千乃至数万 QPS 的 OLTP 系统,计划缓存命中率直接影响整体性能。建议核心业务查询的计划缓存命中率保持在 95% 以上

二、计划缓存核心参数

OceanBase 提供了多项参数对计划缓存进行精细化管理。下表汇总了最关键的配置项和系统变量。

参数/变量名类型默认值说明 ob_enable_plan_cache 租户级系统变量 ON 计划缓存全局开关,关闭后所有 SQL 均执行硬解析,仅用于调试。可在会话级或全局级设置。 ob_plan_cache_percentage 租户级系统变量 5 计划缓存可使用内存占租户总内存的百分比。计划缓存最多可使用内存 = 租户内存上限 × 该值 / 100。[reference:9][reference:10] plan_cache_evict_interval 集群级配置项 5s 后台淘汰线程检查计划缓存是否需要淘汰的时间间隔。[reference:11] ob_plan_cache_evict_high_percentage 租户级系统变量 90 触发淘汰计划的缓存内存占用百分比。触发淘汰的内存大小 = 内存上限 × 该值 / 100。[reference:12][reference:13] ob_plan_cache_evict_low_percentage 租户级系统变量 50 停止淘汰计划的缓存内存占用百分比。停止淘汰的内存大小 = 内存上限 × 该值 / 100。[reference:14][reference:15] ob_enable_adaptive_plan_cache 租户级系统变量 false 自适应计划缓存开关,V4.3.5 版本引入,用于为 TP 类 SQL 开启计划缓存,为 AP 类 SQL 自动关闭计划缓存。默认阈值为 1 秒。
参数微调示例

假设租户内存上限为 10 GB,设置 ob_plan_cache_percentage=10,则计划缓存上限为 1 GB。当缓存使用超过 0.9 GB 时触发淘汰,降至 0.5 GB 时停止淘汰[reference:16][reference:17]。如果监控到硬解析问题(V$OB_SQL_AUDIT 中的 GET_PLAN_TIME 远超 0.1 ms),常见原因之一是缓存过小导致计划被频繁淘汰,可尝试适当调大 ob_plan_cache_percentage(如 10~15)。

-- 查看当前计划缓存内存使用情况
        SHOW PARAMETERS LIKE 'ob_plan_cache_percentage';
        SET GLOBAL ob_plan_cache_percentage = 10;

        -- 查看计划缓存的淘汰阈值
        SHOW VARIABLES LIKE 'ob_plan_cache_evict_high_percentage';
        SHOW VARIABLES LIKE 'ob_plan_cache_evict_low_percentage';

        -- 查看租户当前计划缓存的实际占用(需登录对应租户,不支持 sys 租户)
        SELECT * FROM GV$OB_PLAN_CACHE_STAT\G

三、预编译语句(PreparedStatement)与计划缓存协同

预编译(PreparedStatement)是应用开发中利用计划缓存的最佳实践。通过将 SQL 语句的结构与参数分离,可以实现:

  • 计划复用:相同的 SQL 结构即使参数不同,也能在计划缓存中命中,避免硬解析。
  • 提升性能:减少 SQL 编译开销,尤其在高并发场景下效果显著。
  • 防止 SQL 注入:参数化查询能有效防范 SQL 注入攻击。
JDBC 连接参数优化

为充分利用 OceanBase 的服务端计划缓存,需要在 JDBC 连接串或连接池中配置以下核心参数:

参数名推荐值说明 useServerPrepStmts true 是否使用 Server 端 PreparedStatement。当设置为 true 时,会在数据库端进行预编译,提高性能。 cachePrepStmts true 客户端是否缓存 PreparedStatement,避免重复预编译。 prepStmtCacheSize 250 客户端缓存的 PreparedStatement 最大数量,默认 25。 prepStmtCacheSqlLimit 2048 被缓存的 PreparedStatement 的最大 SQL 长度,默认 2048。 rewriteBatchedStatements true 是否重写 INSERT 批处理语句。设置为 true 时,会将多条 INSERT 语句重写为一条多 VALUES 的 INSERT 语句,需配合 useServerPrepStmts=true 使用。
// JDBC URL 配置示例
        jdbc:oceanbase://10.10.10.1:2883,10.10.10.2:2883/test_db?
        useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=250&prepStmtCacheSqlLimit=2048

        // 使用 PreparedStatement 的代码示例
        PreparedStatement pstmt = connection.prepareStatement("SELECT * FROM orders WHERE user_id = ? AND order_date BETWEEN ? AND ?");
        pstmt.setInt(1, 12345);
        pstmt.setDate(2, startDate);
        pstmt.setDate(3, endDate);
        ResultSet rs = pstmt.executeQuery();
📌 MySQL 模式 vs Oracle 模式差异
Oracle 模式下,OceanBase 支持 COM_STMT_PREPARE_EXECUTE 协议,可以一次性下发 COM_STMT_PREPARE 和 COM_STMT_EXECUTE 所包含的所有信息,执行 Prepared Statement 操作。[reference:18]

四、计划缓存监控视图

OceanBase 提供了丰富的系统视图,用于监控计划缓存的使用状态、查看具体执行计划及分析性能瓶颈。

1. GV$OB_PLAN_CACHE_STAT:计划缓存整体统计
SELECT 
            tenant_id,
            svr_ip,
            svr_port,
            mem_used / 1024 / 1024 AS mem_used_mb,
            mem_limit / 1024 / 1024 AS mem_limit_mb,
            access_count,                  -- 计划缓存被访问的次数
            hit_count,                     -- 计划缓存命中的次数
            ROUND(hit_count * 100 / access_count, 2) AS hit_rate,
            plan_num
        FROM oceanbase.GV$OB_PLAN_CACHE_STAT
        WHERE tenant_id = 1002;   -- 替换为实际租户 ID

关键字段:mem_used(当前内存使用)、mem_limit(内存上限)、access_count(访问次数)、hit_count(命中次数)、hit_rate(命中率,核心指标)、plan_num(缓存中的计划数量)[reference:19]。

2. GV$OB_PLAN_CACHE_PLAN_STAT:单个计划统计
SELECT 
            plan_id,
            sql_id,
            statement,                     -- 参数化后的 SQL 语句
            query_sql,                     -- 原始 SQL 语句
            hit_count,
            executions,
            avg_exe_usec,
            outline_id,
            first_load_time,
            last_active_time
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
        WHERE tenant_id = 1002
        ORDER BY executions DESC
        LIMIT 20;

关键字段:outline_id(绑定的 Outline ID)、avg_exe_usec(平均执行时间,微秒)、executions(执行次数)、query_sql(原始 SQL 语句)[reference:20]。

3. V$OB_SQL_AUDIT:SQL 审计中的计划获取时间
SELECT 
            sql_id,
            query_sql,
            get_plan_time,   -- 获取计划耗时(微秒),若远大于 0.1ms 则可能为硬解析
            elapsed_time,
            execute_time
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE get_plan_time > 1000   -- 超过 1 毫秒的计划获取,可能为硬解析
        ORDER BY request_time DESC
        LIMIT 50;

正常情况下,GET_PLAN_TIME 字段的耗时小于 0.1 ms,异常请求下该字段的耗时可超过 100 ms[reference:21]。

4. V$OB_PS_ITEM_INFO(V4.0+):预编译语句信息
SELECT 
            svr_ip,
            tenant_id,
            statement_id,
            param_sql,
            ref_count,                   -- 预编译语句的引用计数
            exec_count,                  -- 预编译语句的执行次数
            delete_count                 -- 预编译语句被删除的次数
        FROM oceanbase.V$OB_PS_ITEM_INFO
        WHERE tenant_id = 1002;

该视图记录了在 OBServer 中缓存的预编译语句信息,可用于监控 PreparedStatement 在服务端的缓存情况。

五、计划绑定与 Outline(解决计划跳变)

在数据倾斜场景下,由于计划缓存基于首次参数生成执行计划,可能导致次优计划被用于所有参数,造成性能问题。OceanBase 通过 Outline(大纲) 技术允许用户为指定 SQL 绑定固定的执行计划,避免计划跳变[reference:22]。Outline 可以在不修改应用代码的情况下,通过 DDL 操作将一组 Hint 加入到 SQL 中,控制优化器按指定的行为生成执行计划[reference:23]。

创建 Outline 的两种方式
-- 方式一:通过 SQL_TEXT 绑定(推荐)
        -- 在目标数据库下执行(如 use test_db;)
        CREATE OUTLINE outline_name ON SELECT /*+ NO_REWRITE */ * FROM tbl1 WHERE col1 = 4 AND col2 = 6 ORDER BY 2;

        -- 方式二:通过 SQL_ID 绑定
        -- 首先通过 GV$OB_PLAN_CACHE_PLAN_STAT 查询目标 SQL 的 SQL_ID
        SELECT sql_id, query_sql FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT WHERE query_sql LIKE '%your_sql%';
        -- 再基于 SQL_ID 创建 Outline
        CREATE OUTLINE outline_name ON SQL_ID('A1B2C3D4E5F6') USING HINT /*+ INDEX(tbl1 idx_c2) */;

        -- 查看 Outline 信息
        SELECT * FROM oceanbase.DBA_OB_OUTLINES WHERE NAME = 'outline_name';

        -- 删除 Outline
        DROP OUTLINE outline_name;
⚠️ Outline 使用限制
  • 创建 Outline 需要进入对应的数据库下执行[reference:24]。
  • 使用 SQL_TEXT 方式绑定时,必须在原始 SQL 中显式指定 Hint(如 /*+ INDEX(...) */),不能仅在 TO 子句中指定[reference:25]。
  • 绑定 Outline 的 SQL 必须能够通过快速参数化生成标准化字符串,使用 SQL_TEXT 方式绑定时,要求提供的 SQL 与目标 SQL 在去掉 Hint 后完全匹配[reference:26]。
  • Outline 中的 Hint 必须合法,否则创建成功但不会生效(可查询 DBA_OB_OUTLINESerror_message 字段确认)。
  • Outline 仅对之后新建立连接中的 SQL 生效,对已有连接不生效。
  • V4.x 之前的版本存在部分基于代价的改写场景可能导致绑定的 Outline 失效,V4.x 已修复该问题。

六、自适应计划缓存(V4.3.5+)

从 OceanBase V4.3.5 版本开始,引入 ob_enable_adaptive_plan_cache 系统变量,允许系统自动为 TP 类短查询开启计划缓存,为 AP 类长查询关闭计划缓存,以平衡 OLTP 与 OLAP 混合负载的性能。该功能通过识别 SQL 的历史执行耗时和资源消耗,动态判断是否为该 SQL 启用计划缓存。

-- 启用自适应计划缓存
        SET GLOBAL ob_enable_adaptive_plan_cache = true;

        -- 查看自适应计划缓存状态
        SHOW VARIABLES LIKE 'ob_enable_adaptive_plan_cache';
💡 自适应机制适用场景
在 HTAP 混合负载场景中,复杂的 AP 查询如果被计划缓存,可能会因为内存限制导致大量 TP 计划被淘汰。开启自适应后,系统可以根据 SQL 的历史执行情况自动判断是否需要缓存该计划,保障 TP 查询的缓存命中率。建议仅在确需混合负载时开启,默认保持关闭。

七、生产环境最佳实践 Checklist

✅ 上线前检查清单
  • ✅ 应用代码中所有 SQL 均使用 PreparedStatement,禁止字符串拼接 SQL。
  • ✅ JDBC 连接池配置中启用 useServerPrepStmts=truecachePrepStmts=true
  • ✅ 根据实际并发量评估计划缓存内存,设置合理的 ob_plan_cache_percentage(建议 5~15)。
  • ✅ 定期监控 GV$OB_PLAN_CACHE_STAT.hit_rate,确保核心租户命中率 > 95%。
  • ✅ 对于已知数据倾斜的表,为核心查询创建 Outline 固定最优执行计划。
  • ✅ 避免在 SQL 中动态拼接 ORDER BYLIMIT 后的常量,以免破坏快速参数化。
  • ✅ 批量操作使用 addBatch + executeBatch,并开启 rewriteBatchedStatements 且配合 useServerPrepStmts=true
  • ✅ 定期清理不活跃的 Outline(通过 DBA_OB_OUTLINES 查询使用次数)。
  • ✅ 升级到 V4.3.5+ 后,可根据 HTAP 负载情况评估开启自适应计划缓存。

八、常见问题与诊断

问题现象可能原因排查建议 计划缓存命中率低于 80% 缓存内存不足频繁淘汰;SQL 未使用预编译;快速参数化失败 调大 ob_plan_cache_percentage;检查应用是否使用 PreparedStatement;分析 GV$OB_PLAN_CACHE_PLAN_STAT 中计划数量是否接近上限 相同 SQL 偶发性慢查询 计划跳变:首次参数生成次优计划 对比 GV$OB_PLAN_CACHE_PLAN_STAT 中同一 SQL 的多个 plan_id,若存在多个计划则说明跳变,使用 Outline 绑定 硬解析频繁导致 CPU 飙升 计划缓存被关闭;缓存内存不足导致频繁淘汰 确认 ob_enable_plan_cache = ON;检查 mem_used 是否接近 mem_limit;检查是否有大量不同的参数化 SQL(SQL 种类过多)
🏆 小结
OceanBase 计划缓存是提升 SQL 执行效率的核心能力,其高效运作依赖于开发侧正确的预编译语句使用和 DBA 侧合理的参数配置。通过快速参数化与软解析机制,OLTP 场景下的 SQL 执行开销可降至微秒级别;配合 Outline 计划绑定和自适应计划缓存等高级特性,能够从容应对数据倾斜和混合负载等复杂场景。建议开发团队将 PreparedStatement 作为强制编码规范,DBA 定期监控命中率与计划分布,结合 SQL 审计视图持续优化,真正实现数据库资源利用的最大化。

第17章:Online DDL 与 Schema 变更

17.1 Online DDL 实现原理

在关系型数据库的发展历程中,DDL(Data Definition Language,数据定义语言)操作一直是运维人员最为头痛的任务之一。传统数据库中,执行 DDL 操作往往意味着长时间的表锁定,业务需要停机或只能在低峰期执行变更。OceanBase 从诞生之初就将 Online DDL 作为核心能力进行设计,目标是让 DDL 操作对业务方和运维人员都做到透明[reference:13]。在 OceanBase 4.0 及后续版本中,Online DDL 能力得到了进一步强化,支持了主键变更、分区规则修改、修改列类型和修改字符集等更复杂的 DDL 操作[reference:14][reference:15]。本章将深入剖析 OceanBase 4.x 中 Online DDL 的实现原理、架构设计、执行模型以及相关的性能优化与运维实践。

🎯 本章内容概览
  • Online DDL 概述:Online DDL 与 Offline DDL 的定义与区别。
  • DDL 架构与核心组件:ObDDLService、ObRootService、ObDDLScheduler 的职责与协作。
  • DDL 执行模型与分类:同步 DDL 与异步 DDL 的区分,以及各类型 DDL 的执行特征。
  • Online DDL 核心实现机制:隐藏列 + 数据回填、两表双写等核心方案。
  • DDL 任务调度与并发控制:串行 DDL 到并行 DDL 的演进与并发参数配置。
  • 4.x 版本 Online DDL 增强特性:旁路写入、分布式可扩展性、节点异常高可用等。
  • Online DDL 操作支持矩阵:各类 DDL 操作的 Online 支持情况与耗时特征。
  • 性能优化与参数调优:并行度控制、线程池配置与 DDL 加速方法。
  • 监控与运维管理:常用 SQL 与 DDL 任务状态监控。

一、Online DDL 概述

在 OceanBase 中,DDL 操作根据执行期间是否允许用户并发访问数据库,分为 Online DDLOffline DDL 两种类型。

1.1 Online DDL(在线 DDL)
  • 定义:在进行 DDL 操作时,用户仍然可以访问数据库,包括读取和写入操作。
  • 优点:对业务的影响小,能够在系统运行时执行 DDL 操作,适用于高可用性要求的场景。
  • 特点:支持并发访问和更少的锁定,能够显著减少停机时间。
1.2 Offline DDL(离线 DDL)
  • 定义:进行 DDL 操作时,会锁定相关表,导致用户无法访问数据库的相关数据。
  • 优点:实现简单,适用于对业务影响不大的场景。
  • 特点:执行 DDL 操作时会造成一定的停机时间,影响正常的查询和事务处理。
📌 Online DDL vs Offline DDL 的事务行为差异
Online DDL 通常不需要等待当前事务结束,可以并发执行;而 Offline DDL 在执行期间会锁定相关表,通常需要等待当前事务结束。在 OceanBase 中,Online DDL 操作通过精心设计的并发控制机制,实现了对业务流量的最小化影响。

二、DDL 架构与核心组件

OceanBase 的 DDL 服务架构由三个核心组件构成:ObDDLService(DDL 编排服务)、ObRootService(中央协调器)和 ObDDLScheduler(异步任务调度器)[reference:16]。

组件名称主要职责关键特性 ObDDLService DDL 操作的主入口,负责创建和验证表/索引 Schema、编排简单同步 DDL、将复杂 DDL 提交给调度器、与 Schema 服务协调版本管理 初始化时依赖 RPC 代理、SQL 代理、Schema 服务和快照管理器 ObRootService(RS) 顶层协调器,管理 ObDDLService 和 ObDDLScheduler 实例的生命周期,处理 DDL RPC 请求并将其路由到合适的服务 DDL 任务由 RootServer 统一调度执行,保证全局范围内的 Schema 一致性[reference:17] ObDDLScheduler 异步任务管理,负责 DDL 任务的生命周期管理,包括任务的持久化、加载和调度执行[reference:18] 每个租户独立的 MTL(Multi-Tenant Library)组件
⚙️ DDL 执行的基本流程
1. Client 向 OBServer 发送 DDL 请求[reference:19]。
2. OBServer 经过 Parser 和 Resolver 解析生成 Stmt,并通过 RPC 调用 RS[reference:20]。
3. RS 立即通过广播 RPC 通知所有其他 OBServer 刷新 Schema,同时自身开启事务向内部表写入 Schema 变更信息[reference:21]。
4. RS 向 OBServer 返回执行结果,OBServer 再向 Client 返回结果[reference:22]。
5. DDL 事务提交后,RS 本地进行 Schema 刷新,并通知各 OBServer 进行 Schema 刷新[reference:23]。
注意:RS 会不断刷新直到成功,但其他 OBServer 仅尽力而为[reference:24]。因此只能保证在同一个 Session 下 DDL 后查询可见,不同 Session 或直连到 Schema 刷新慢的机器可能出现不可见[reference:25]。

三、DDL 执行模型与分类

OceanBase 根据 DDL 操作的执行特征和资源需求,将其分为 同步 DDL异步 DDL 两类。

3.1 同步 DDL(Synchronous DDL)

同步 DDL 在 DDL 事务内完整执行,操作完成后再返回客户端。主要包括:

  • CREATE DATABASECREATE TABLEGROUP
  • 简单的 ALTER TABLE 操作(如末尾加列、重命名列等)
3.2 异步 DDL(Asynchronous DDL)

异步 DDL 会创建一个持久化任务并立即返回任务 ID 给客户端,任务在后台异步执行。主要包括:

  • 索引构建(DDL_CREATE_INDEX
  • 表重定义(DDL_TABLE_REDEFINITION
  • 需要数据拷贝的列修改操作

四、Online DDL 核心实现机制

OceanBase 的 Online DDL 实现主要包含以下几种核心机制,根据 DDL 操作类型的不同采用不同的实现策略。

4.1 仅修改元数据(Instant DDL)

对于某些 DDL 操作,如末尾加列、重命名列、修改列默认值等,OceanBase 只需要修改表的 Schema 元数据信息,无需重整数据[reference:26]。这类操作执行速度极快,通常在秒级内完成。

典型操作:末尾加列、重命名列、修改列默认值、删除列默认值、修改自增列值、增加列类型长度或精度等[reference:27]。

4.2 隐藏列 + 数据回填(Online Data Reorganization)

对于复杂 DDL(如添加带非常量默认值的列、修改列类型等),OceanBase 采用 隐藏列 + 数据回填 方案[reference:28]:

  • 新建隐藏列:在表中创建一个对用户不可见的隐藏列,应用新的 Schema 定义。
  • 后台数据回填:在后台逐步将原列的数据按照新 Schema 格式回填到隐藏列中,回填期间允许并发读写
  • Schema 切换:回填完成后,通过 Schema 切换将隐藏列变为可见列,整个过程对业务几乎无影响。
📌 典型应用场景
添加带非常量默认值的列(如 CURRENT_TIMESTAMP)、修改列类型等操作,均采用此方案。回填期间数据一致性和并发性通过行级锁、多版本两阶段锁以及 Schema 刷新机制来保证。
4.3 两表双写(Offline DDL 方案)

对于需要离线执行的 DDL(如部分列类型变更、表重定义等),OceanBase 采用 两表双写 方案:

  1. 用户发起对源表 T1 的 DDL 操作。
  2. 停读写并新建一张隐藏表 T2,T2 基于 T1 做 DDL 得到新的 Schema。
  3. 等待 T1 的所有分区上使用过比当前 Schema Version 小的事务都结束,获取一个快照点。
  4. 主表补全:基于快照点扫描 T1 的数据,按 T2 的 Schema 形式排序并写入 T2。
  5. 重建依赖对象(索引表、约束等)。
  6. 表名切换:将 T1 重命名为 T3,T2 重命名为 T1。
  7. 写 Barrier 日志(每个分区独立处理)。
  8. 恢复读写,允许新表上的读写操作。
  9. 删除原表 T3。
⚠️ Offline DDL 与 Online DDL 的取舍
Offline DDL 的实现与 MySQL 的 COPY 方式类似,但 OceanBase 作为分布式数据库加入了更多分布式元素和保障,底层做了大量性能优化。对于需要重整数据的 DDL 操作,OceanBase 会尽可能采用 Online 方式执行;仅在特定场景(如某些列类型变更)下使用 Offline 方式。

五、DDL 任务调度与并发控制

5.1 从串行 DDL 到并行 DDL 的演进

在 OceanBase V4.1 版本之前,整个集群仅有一个线程串行处理所有的 DDL 请求(RS 上的 DDLQueueTh)[reference:29]。随着用户对 DDL 吞吐量需求的增长,OceanBase 从 V4.1 版本开始逐步开放并行 DDL 能力[reference:30]:

版本新增的并行 DDL 功能 V4.1.0.0TRUNCATE TABLE[reference:31] V4.2.1.0CREATE TABLE[reference:32] V4.2.2.0CREATE INDEXCREATE COMMENT[reference:33] V4.2.3.0CREATE VIEW[reference:34] V4.2.5.1DROP TABLE[reference:35]

并行 DDL 通过增加一个并行 DDL 任务的线程池(DDLPQueueTh),让多个线程同时处理 DDL 任务,有效缓解了串行 DDL 队列积压的问题[reference:36]。

📌 注意
表中该版本新增的并行 DDL 功能表示该版本首次支持的并行执行 DDL 操作;后续版本默认会继承之前版本已经支持的操作[reference:37]。
5.2 DDL 并行度控制

OceanBase 数据库 V4.x 版本 DDL 执行时默认不开启并行,可以通过 HINT、SESSION 和 TABLE DOP 三个级别来控制并行度,优先级为:HINT > SESSION > TABLE DOP[reference:38]。

-- 方式一:通过 SESSION 变量开启并行(MySQL 模式)
        SET _ENABLE_PARALLEL_DDL = 1;                      -- 启用并行 DDL[reference:39]
        SET SESSION _FORCE_PARALLEL_DDL_DOP = 8;          -- 设置并行度[reference:40][reference:41]

        -- 方式二:通过 SESSION 变量开启并行(Oracle 模式)
        ALTER SESSION ENABLE PARALLEL DDL;                -- 启用并行 DDL[reference:42]
        ALTER SESSION FORCE PARALLEL DDL PARALLEL 8;      -- 设置并行度[reference:43]

        -- 方式三:通过 PARALLEL Hint 开启并行(目前仅支持 CREATE INDEX)[reference:44]
        CREATE /*+PARALLEL(32)*/ INDEX idx ON tt(i);

        -- 方式四:通过 Table DOP 开启并行[reference:45]
        CREATE TABLE tbl8(c1 INT PRIMARY KEY, c2 INT) PARALLEL 3;

        -- 查看当前并行度设置
        SHOW VARIABLES LIKE '_force_parallel_ddl_dop';
📌 并行度设置注意事项
  • 所有 DDL 的并行度之和不能超过租户的 max_cpu 上限。
  • 对于 V4.1.0 BP3 之前的版本,由于临时文件实现的限制,建议所有 DDL 的并行度之和不超过 64
  • 调整并行度后,建议同步调整 parallel_servers_target 参数,该值应比所有并行度之和大。
5.3 ddl_thread_score 参数

ddl_thread_score 参数用于设置 DDL 任务使用的线程数,从 V4.2.1 版本开始引入[reference:46]。该参数可用于控制 DDL 删列执行的并行度 以及 表级恢复跨租户导表阶段的并行度[reference:47]。

-- 查看 ddl_thread_score 默认值
        SHOW PARAMETERS LIKE '%ddl_thread_score%';

        -- 设置租户级别的 ddl_thread_score
        ALTER SYSTEM SET ddl_thread_score = 4;  -- 设置为 4[reference:48]

        -- 参数说明
        -- 类型:Int[reference:49]
        -- 范围:[0, 100],0 表示默认线程数为 2[reference:50]
        -- 是否重启生效:否,设置后立即生效[reference:51]
        -- 建议:如果发现低优先级的任务运行速度过慢,可以适当提高该参数值,建议每次将数值翻倍[reference:52]
⚠️ ddl_thread_score 使用建议
参数 ddl_thread_score 描述的是 DDL 任务的并行度。当机器可支持核数一定时,过高的并行度可能会挤压到其他模块(如合并速度等),一般建议设置为 8。表级恢复执行结束之后,建议将 ddl_thread_score 恢复成默认值[reference:53]。

六、4.x 版本 Online DDL 增强特性

OceanBase 4.0 及后续版本在 Online DDL 方面进行了多项重要创新[reference:54]:

6.1 旁路写入数据同步方法

OceanBase 自研了一套专门用于 旁路写入 的数据同步方法,用于提升 DDL 操作的可用性[reference:55]。与传统模拟用户插入的方式不同,OceanBase 通过集成单机数据库的设计思路,实现了数据补全阶段的性能加速[reference:56]。

6.2 分布式执行可扩展性优化

OceanBase 4.0 对 DDL 的单机性能和分布式执行的可扩展性进行了全面优化,大幅提升了用户 DDL 操作的响应速度[reference:57]。

6.3 节点异常高可用

在 OceanBase 4.0 中,Online DDL 除了具备业务请求优先、对业务透明的特点之外,还在数据库节点出现异常场景时增强了 DDL 操作的高可用特性[reference:58]。

6.4 更完善的 Online DDL 框架能力

OceanBase 4.0 进一步完善了原生 Online DDL 的框架能力,新增支持了以下复杂 DDL 操作[reference:59][reference:60]:

  • 主键变更
  • 分区规则修改
  • 修改列类型
  • 修改字符集

七、Online DDL 操作支持矩阵

OceanBase V4.x 在 Oracle 模式下支持的 Online DDL 操作如下表所示[reference:61]。

分类操作耗时特征备注 索引操作增加索引与数据量有关,需重整数据[reference:62]支持全局/局部索引、空间索引(V4.1.0+)[reference:63] 删除索引与是否有活跃事务有关[reference:64]— 重命名索引仅修改元数据[reference:65]— 混合索引操作与耗时最长的操作有关[reference:66]Oracle 模式不支持[reference:67] 列操作末尾加列仅修改元数据[reference:68]包括 LOB(TEXT)列[reference:69] 添加 VIRTUAL 列仅修改元数据[reference:70]— 删除列仅修改元数据[reference:71]— 修改列为 NOT NULL与数据量有关,需查询数据[reference:72]— 修改列为 NULL仅修改元数据[reference:73]— 重命名列仅修改元数据[reference:74]— 增加列类型长度或精度仅修改元数据[reference:75]例如 INT 长度增长、VARCHAR 变长[reference:76] 约束操作增加/删除外键、CHECK/NOT NULL 约束与数据量有关,需查询数据[reference:77]— 表操作重命名表仅修改元数据[reference:78]— 修改行格式仅修改元数据[reference:79]— 修改块大小仅修改元数据[reference:80]— 修改压缩算法仅修改元数据[reference:81]— 分区操作添加分区仅修改元数据[reference:82]—
📌 Offline DDL 操作(需谨慎使用)
以下操作在 V4.x 中属于 Offline DDL,执行期间会阻塞写入[reference:83]:
  • 添加自增列[reference:84]
  • 修改列为主键[reference:85]
  • 添加/删除 STORED 生成列[reference:86]
  • 删除 VIRTUAL 列[reference:87]
  • 清除废弃列[reference:88]
  • 添加/删除主键[reference:89]
  • 修改分区规则[reference:90]
  • 删除分区(对该分区加分区级的表锁)[reference:91]
  • TRUNCATE 表[reference:92]

八、性能优化与参数调优

8.1 DDL 执行速度优化方法

对于需要重整数据的 DDL(如索引构建、主键操作、修改列类型、修改分区规则等),OceanBase 提供了以下优化手段[reference:93]:

  • 设置并行度:通过 _force_parallel_ddl_dop(MySQL 模式)或 FORCE PARALLEL DDL(Oracle 模式)控制 DDL 并行度[reference:94]。
  • 调整 ddl_thread_score:控制删列等 DDL 操作的线程数[reference:95]。
  • 调整 parallel_servers_target:确保并行查询排队条件合理。
⚠️ 优化操作的适用场景
本文所涉及的参数调整会导致 DDL 占用大量资源,适用于最大化 DDL 执行速度的场景,不适用于有业务流量的场景。在生产环境中调整这些参数时,务必在业务低峰期操作,并充分评估对业务的影响。
8.2 参数调优示例
-- 1. 启用并行 DDL 并设置并行度(MySQL 模式)
        SET _ENABLE_PARALLEL_DDL = 1;
        SET SESSION _force_parallel_ddl_dop = 8;

        -- 2. 启用并行 DDL 并设置并行度(Oracle 模式)
        ALTER SESSION ENABLE PARALLEL DDL;
        ALTER SESSION FORCE PARALLEL DDL PARALLEL 8;

        -- 3. 设置 parallel_servers_target(需大于所有并行度之和)
        SET GLOBAL parallel_servers_target = 100;

        -- 4. 设置 ddl_thread_score(租户级别)
        ALTER SYSTEM SET ddl_thread_score = 8 TENANT = 'your_tenant';

        -- 5. 查看当前 DDL 任务的并行度设置
        SHOW VARIABLES LIKE '_force_parallel_ddl_dop';
        SHOW PARAMETERS LIKE '%ddl_thread_score%';

九、监控与运维管理

9.1 DDL 任务状态监控

OceanBase 提供了系统表 __all_virtual_ddl_task_status 来记录 DDL 任务的持久化状态[reference:96]。以下 SQL 可用于监控 DDL 任务的执行情况[reference:97]:

-- 查看当前租户的 DDL 任务状态(需 sys 租户权限)
        SELECT 
            task_id,
            object_id,
            target_object_id,
            ddl_type,
            trace_id,
            status,
            message
        FROM oceanbase.__all_virtual_ddl_task_status
        ORDER BY task_id DESC
        LIMIT 20;

        -- ObDDLTaskStatus 状态枚举[reference:98]
        -- PREPARE = 0, LOCK_TABLE = 1, WAIT_TRANS_END = 2, REDEFINITION = 3,
        -- VALIDATE_CHECKSUM = 4, COPY_TABLE_DEPENDENT_OBJECTS = 5, TAKE_EFFECT = 6,
        -- WRITE_BARRIER_LOG = 7, CHECK_CONSTRAINT_VALID = 8, SET_CONSTRAINT_VALIDATE = 9,
        -- MODIFY_AUTOINC = 10, SET_WRITE_ONLY = 11, WAIT_TRANS_END_FOR_WRITE_ONLY = 12,
        -- SET_UNUSABLE = 13, WAIT_TRANS_END_FOR_UNUSABLE = 14, DROP_SCHEMA = 15,
        -- FAIL = 99, SUCCESS = 100

        -- 分析 DDL 任务状态:根据 object_id 和 target_object_id 确定 DDL 操作的表和目标对象[reference:99]
        -- 根据 status 枚举值判断 DDL 执行的当前阶段

        -- 获取 DDL 执行的 trace id[reference:100]
        SELECT last_trace_id() FROM DUAL;

        -- 使用 trace id 追踪 RS 日志[reference:101]
        -- grep [trace_id] 在执行该 SQL 的 OBServer 日志中查找相关信息
9.2 常见问题与诊断
问题现象可能原因排查建议 DDL 执行时间过长 数据量大、并行度不足、资源竞争 检查 _force_parallel_ddl_dop 设置;通过 __all_virtual_ddl_task_status 查看进度;考虑在低峰期执行 DDL 任务积压 串行 DDL 队列阻塞[reference:102] 升级到支持并行 DDL 的版本;检查是否有长时间运行的 DDL 阻塞队列 DDL 执行期间业务受影响 操作属于 Offline DDL 或 Online DDL 实现中的短暂锁 确认操作的 Online/Offline 属性;对于 Online DDL,检查是否有大事务阻塞 Schema 刷新不一致 部分 OBServer 节点 Schema 刷新慢[reference:103] RS 会持续刷新直到成功;不同 Session 可能出现短暂不可见,属正常现象[reference:104]

十、生产环境最佳实践 Checklist

✅ 上线前检查清单
  • ✅ 确认目标 DDL 操作在 V4.x 中属于 Online DDL 还是 Offline DDL。
  • ✅ 对于需要重整数据的大表 DDL,评估数据量并规划执行窗口。
  • ✅ 根据业务负载情况,合理设置 DDL 并行度(_force_parallel_ddl_dop),避免资源争抢[reference:105]。
  • ✅ 启用并行 DDL 前,先执行 SET _ENABLE_PARALLEL_DDL = 1(MySQL 模式)[reference:106]。
  • ✅ 对于删除列等操作,评估是否调整 ddl_thread_score 以加速执行[reference:107]。
  • ✅ 监控 DDL 任务状态,通过 __all_virtual_ddl_task_status 跟踪执行进度[reference:108]。
  • ✅ 对于 Offline DDL 操作,提前通知业务方并规划停机窗口。
  • ✅ 并行度调整后,同步调整 parallel_servers_target 参数。
  • ✅ V4.1.0 BP3 之前的版本,注意 DDL 并行度之和不超过 64。
  • ✅ 生产环境调整 DDL 相关参数时,务必在业务低峰期操作。
🏆 小结
OceanBase 4.x 的 Online DDL 实现是分布式数据库领域的一项重要创新。通过 ObDDLService + ObRootService + ObDDLScheduler 的三层架构,配合 隐藏列 + 数据回填两表双写等核心机制,OceanBase 实现了对业务几乎无感的在线 Schema 变更[reference:109]。从 V4.1 版本开始的并行 DDL 演进,进一步提升了 DDL 的吞吐量和执行效率[reference:110]。建议 DBA 和开发人员深入理解 Online DDL 的实现原理,合理利用并行度控制和性能优化参数,在保障业务连续性的前提下高效完成数据库 Schema 的演进与迭代。
17.2 快速加列与修改列类型

在数据库运维中,加列(ADD COLUMN)修改列类型(MODIFY COLUMN)是最为频繁的 Schema 变更操作。传统数据库中,这些操作往往伴随着长时间的表锁定或全表数据重建,对业务影响巨大。OceanBase 4.x 通过 Instant DDL(即时 DDL)Online Data Reorganization(在线数据重组) 两大核心机制,实现了对业务几乎无感的列变更能力。本章将深入剖析 OceanBase 4.x 中快速加列与修改列类型的实现原理、操作指南、限制条件及性能优化方法。

🎯 本章内容概览
  • 快速加列原理:Instant DDL 机制与“立刻加列”的实现方式。
  • 快速加列操作指南:尾部加列与非尾部加列的语法与差异。
  • 修改列类型原理:隐藏列 + 数据回填方案与 Offline DDL 两表双写方案。
  • 列类型变更规则:表级禁用规则、变更规则与列级转换规则。
  • Online/Offline DDL 操作矩阵:各类列操作的耗时特征与 Online 支持情况。
  • 性能优化与参数调优:ddl_thread_score 等关键参数配置。
  • 监控与运维管理:DDL 任务状态监控与常见问题诊断。

一、快速加列:Instant DDL 机制

1.1 什么是“立刻加列”

OceanBase 的“立刻加列”(Instant Add Column)是一种 仅修改元数据(Metadata Only) 的 DDL 操作[reference:0]。与传统数据库需要重建全表数据不同,OceanBase 在执行尾部加列时,不变更数据行的物理结构,只修改表的 Schema 元数据信息[reference:1]。当查询需要读取表中数据时,OceanBase 会将新增列的默认值“伪造”到读取的数据后面并返回[reference:2]。

📌 核心优势
  • 执行速度极快:仅修改元数据,通常在毫秒到秒级内完成[reference:3]。
  • 对业务完全透明:执行期间不阻塞读写操作[reference:4]。
  • 无需数据重组:不涉及数据拷贝或回填,不消耗额外存储空间。
1.2 快速加列的适用场景与限制

适用场景:在表尾部添加列(末尾加列)[reference:5][reference:6]。

⚠️ 关键限制
  • 加列位置限制:“立刻加列”的加列位置只能在表的最后,不能加在其他列之间[reference:7]。
  • 非尾部加列:使用 BEFOREAFTERFIRST 关键字在表中间或表首添加列时,属于 Offline DDL,需要重整数据[reference:8][reference:9]。
  • Oracle 模式限制:Oracle 模式下不支持 BEFORE/AFTER/FIRST 语法[reference:10]。
1.3 快速加列操作示例
-- 尾部加列(Instant DDL,仅修改元数据)
        ALTER TABLE tbl1 ADD COLUMN c1 INT;[reference:11]

        -- 添加 LOB(TEXT)列(同样支持 Instant DDL)[reference:12]
        ALTER TABLE tbl1 ADD COLUMN c3 LOB;

        -- 非尾部加列(Offline DDL,需要重整数据)
        -- MySQL 模式:在表首添加列
        ALTER TABLE tbl1 ADD COLUMN c1 INT FIRST;[reference:13]

        -- MySQL 模式:在指定列之前/之后添加列
        ALTER TABLE tbl1 ADD COLUMN c2 INT BEFORE name;[reference:14]
        ALTER TABLE tbl1 ADD COLUMN c3 INT AFTER name;

        -- 查看表结构验证
        DESCRIBE tbl1;[reference:15]

二、修改列类型:Online Data Reorganization

2.1 实现原理:隐藏列 + 数据回填

对于修改列类型等复杂 DDL 操作,OceanBase 采用 “隐藏列 + 数据回填”(Online Data Reorganization) 方案[reference:16][reference:17]:

  • 新建隐藏列:在表中创建一个对用户不可见的隐藏列,应用新的数据类型 Schema 定义。
  • 后台数据回填:在后台逐步将原列的数据按照新数据类型格式转换并回填到隐藏列中,回填期间允许并发读写[reference:18]。
  • Schema 切换:回填完成后,通过 Schema 切换将隐藏列变为可见列,整个过程对业务几乎无影响[reference:19]。
📌 典型应用场景
添加带非常量默认值的列(如 CURRENT_TIMESTAMP)、修改列类型等操作,均采用此方案[reference:20]。回填期间数据一致性和并发性通过行级锁、多版本两阶段锁以及 Schema 刷新机制来保证。
2.2 Offline DDL 方案:两表双写

对于需要离线执行的列类型变更(如列上有依赖对象时),OceanBase 采用 两表双写 方案[reference:21]:

  1. 用户发起对源表 T1 的 DDL 操作。
  2. 停读写并新建一张隐藏表 T2,T2 基于 T1 应用 DDL 得到新的 Schema[reference:22]。
  3. 等待 T1 的所有分区上使用过比当前 Schema Version 小的事务都结束,获取一个快照点[reference:23]。
  4. 主表补全:基于快照点扫描 T1 的数据,按 T2 的 Schema 形式排序并写入 T2[reference:24]。
  5. 重建依赖对象(索引表、约束等)[reference:25]。
  6. 表名切换:将 T1 重命名为 T3,T2 重命名为 T1[reference:26]。
  7. 写 Barrier 日志(每个分区独立处理)[reference:27]。
  8. 恢复读写,允许新表上的读写操作[reference:28]。
  9. 删除原表 T3[reference:29]。
⚠️ Offline DDL 的影响
Offline DDL 执行期间会阻塞写操作(但读操作不受影响)[reference:30]。因此,对于 Offline DDL 操作,建议在业务低峰期执行,并提前通知相关业务方。

三、列类型变更规则

OceanBase 对列类型变更有一系列严格的规则约束,分为 表级规则列级规则 两个层次[reference:31]。

3.1 表级禁用规则
约束类型Online DDLOffline DDL说明 外键约束(FOREIGN KEY) ❌ 禁止 ❌ 禁止 只支持 VARCHAR、VARCHAR2、NVARCHAR2 数据类型的改大或改小精度[reference:32] 分区键 ❌ 禁止 ❌ 禁止 当列作为分区键一部分时,禁掉修改类型和修改长度的功能[reference:33][reference:34] 生成列引用 ❌ 禁止 ❌ 禁止 如果修改的列被生成列引用,则禁掉列类型变更功能[reference:35][reference:36] Trigger 约束 — ⚠️ 条件限制 当列上有非 DISABLE 的 Trigger 约束时,Offline DDL 禁止修改列类型;全部为 DISABLE 则不影响[reference:37] CHECK 约束(MySQL 模式) ❌ 禁止 ❌ 禁止 不能修改有 CHECK 约束的列的数据类型,INTEGER 类型列除外[reference:38]
3.2 表级变更规则
场景DDL 类型说明 同类型长度变长(如 CHAR → CHAR(11)) Online 如果列上只有索引表,可以通过 Online 方式修改[reference:39] 同类型长度变长 + 主键/分区键/CHECK 约束 Offline 有主键、分区键或 CHECK 约束依赖时需要 Offline[reference:40][reference:41] VARCHAR 类型长度变长 Online 即使变更了主键或分区键,也可以是 Online 方式[reference:42] 大类型内部变更 + 无依赖对象 Online 没有被索引表、外键、CHECK 约束、主键等引用时,可通过 Online 方式变更[reference:43][reference:44] 大类型内部变更 + 有依赖对象 Offline 被索引表、外键、CHECK 约束、主键等引用时,需要通过 Offline 方式变更[reference:45][reference:46] SIGNED ↔ UNSIGNED 转换 Offline 数值数据类型的有符号与无符号转换,需要执行 Offline 操作[reference:47][reference:48]
3.3 列级转换规则

OceanBase 将所有列数据类型分为五大类:数值数据类型、字符数据类型、时间数据类型、间隔数据类型和 ROWID 类型[reference:49]。大类型之间不允许互相转换,只关注大类型内部的转换[reference:50]。

Oracle 模式数值数据类型大类内转换[reference:51]:

数据类型→ NUMBER→ FLOAT→ BINARY_FLOAT→ BINARY_DOUBLE NUMBER✅ 支持✅ 支持❌ 禁止❌ 禁止 FLOAT✅ 支持✅ 支持❌ 禁止❌ 禁止 BINARY_FLOAT❌ 禁止❌ 禁止✅ 支持❌ 禁止 BINARY_DOUBLE❌ 禁止❌ 禁止❌ 禁止✅ 支持

MySQL 模式数据类型分类:MySQL 模式下,所有列数据类型分为三类:数值数据类型、字符数据类型、日期时间数据类型[reference:52]。不同类别之间的转换规则有所不同,具体可参考官方文档的转换规则表[reference:53]。

四、Online/Offline DDL 操作矩阵

下表汇总了 OceanBase V4.x 中各类列操作的 Online/Offline 属性及耗时特征[reference:54]。

分类操作耗时特征DDL 类型 列操作末尾加列仅修改元数据✅ Online 添加 VIRTUAL 列仅修改元数据✅ Online 删除列仅修改元数据✅ Online 修改列为 NOT NULL与数据量有关,需查询数据✅ Online 修改列为 NULL仅修改元数据✅ Online 列操作重命名列仅修改元数据✅ Online 增加列类型长度或精度仅修改元数据✅ Online 设列默认值仅修改元数据✅ Online 删除列默认值仅修改元数据✅ Online Offline 列操作添加自增列与数据量有关,需重整数据❌ Offline 修改列为主键与数据量有关,需重整数据❌ Offline 添加/删除 STORED 生成列与数据量有关,需重整数据❌ Offline 删除 VIRTUAL 列与数据量有关,需重整数据❌ Offline 清除废弃列与数据量有关,需重整数据❌ Offline 非尾部加列(BEFORE/AFTER/FIRST)与数据量有关,需重整数据❌ Offline 混合列操作(含 Offline 操作)与耗时最长的操作有关❌ Offline
📌 Online DDL 与 Offline DDL 的区分
  • Online DDL:执行期间不影响读写操作,对业务透明[reference:55]。
  • Offline DDL:执行期间会阻塞写操作(但读操作不受影响)[reference:56]。
  • 混合列操作中,如果包含任何 Offline 操作,整个 DDL 会升级为 Offline DDL[reference:57]。

五、性能优化与参数调优

5.1 ddl_thread_score 参数

ddl_thread_score 参数可用于控制 DDL 删列执行的并行度,通过提高并行度来加速需要重整数据的 DDL 操作[reference:58][reference:59]。

-- 查看 ddl_thread_score 当前值
        SHOW PARAMETERS LIKE '%ddl_thread_score%';

        -- 设置租户级别的 ddl_thread_score
        ALTER SYSTEM SET ddl_thread_score = 8 TENANT = 'your_tenant';

        -- 参数说明
        -- 类型:Int
        -- 范围:[0, 100],0 表示默认线程数为 2
        -- 是否重启生效:否,设置后立即生效
        -- 建议:如果发现低优先级的任务运行速度过慢,可以适当提高该参数值,建议每次将数值翻倍[reference:60]
⚠️ ddl_thread_score 使用注意事项
  • ddl_thread_score租户级别的参数,修改后租户下的删除列操作都将以此作为执行的并行度[reference:61]。
  • 表级恢复执行结束之后,建议将 ddl_thread_score 恢复成默认值[reference:62]。
  • 过高的并行度可能会挤压到其他模块(如合并速度等),一般建议设置为 8。
5.2 列类型变更的 Online 优化建议
  • 优先设计为 Online 兼容:在设计表结构时,尽量避免在列上创建不必要的依赖对象(如外键、CHECK 约束),以便后续列类型变更可以采用 Online 方式执行[reference:63]。
  • 利用“仅修改元数据”的特性:对于增加列类型长度或精度的操作(如 INT 长度增长、VARCHAR 变长、NUMBER 类转换),OceanBase 仅修改元数据,执行速度极快[reference:64]。
  • 大类型内部转换优先:在相同大数据类型内部进行转换(如 INT → BIGINT),比跨大类型转换(如 INT → VARCHAR)更容易采用 Online 方式[reference:65]。
  • 合理安排执行窗口:对于必须使用 Offline DDL 的操作(如非尾部加列、添加自增列等),安排在业务低峰期执行。

六、监控与运维管理

6.1 DDL 任务状态监控
-- 查看当前租户的 DDL 任务状态
        SELECT 
            task_id,
            object_id,
            target_object_id,
            ddl_type,
            trace_id,
            status,
            message
        FROM oceanbase.__all_virtual_ddl_task_status
        ORDER BY task_id DESC
        LIMIT 20;

        -- 使用 trace_id 追踪 DDL 执行日志
        SELECT last_trace_id() FROM DUAL;
        -- 然后在 OBServer 日志中 grep 该 trace_id
6.2 常见问题与诊断
问题现象可能原因排查建议 加列操作执行时间过长 使用了非尾部加列(BEFORE/AFTER/FIRST),属于 Offline DDL 确认加列位置;如需快速加列,应使用尾部加列[reference:66] 修改列类型报错 列上有外键、CHECK 约束、分区键等依赖对象 检查 DBA_CONSTRAINTS 确认列上约束;考虑先删除约束再修改类型[reference:67] 列类型变更期间业务受影响 操作属于 Offline DDL,阻塞了写操作 确认操作的 Online/Offline 属性;安排在业务低峰期执行[reference:68] 删列操作速度慢 ddl_thread_score 设置过低 适当调高 ddl_thread_score 值[reference:69]

七、生产环境最佳实践 Checklist

✅ 上线前检查清单
  • 确认加列位置:尾部加列为 Instant DDL(仅修改元数据),非尾部加列为 Offline DDL(需重整数据)[reference:70]。
  • 检查列上的依赖对象:确认待修改列是否有外键、CHECK 约束、分区键、生成列等依赖[reference:71]。
  • 评估 Online/Offline 属性:根据列类型变更规则,确认操作属于 Online DDL 还是 Offline DDL[reference:72]。
  • 规划执行窗口:对于 Offline DDL 操作,安排在业务低峰期执行,并提前通知业务方。
  • 考虑并行度优化:对于删列等需要重整数据的操作,评估是否调整 ddl_thread_score 以加速执行[reference:73]。
  • 监控 DDL 进度:通过 __all_virtual_ddl_task_status 跟踪 DDL 任务执行状态。
  • 准备回滚方案:对于 Offline DDL 操作,提前准备回滚方案(如备份表结构或数据)。
🏆 小结
OceanBase 4.x 的快速加列与修改列类型能力,通过 Instant DDL(仅修改元数据)Online Data Reorganization(隐藏列 + 数据回填) 两大核心机制,实现了对业务几乎无感的在线 Schema 变更。尾部加列仅需修改元数据,毫秒级完成;修改列类型则根据列上依赖对象的复杂程度,自动选择 Online 或 Offline 方式执行。建议 DBA 和开发人员深入理解列类型变更规则,合理利用 ddl_thread_score 等性能优化参数,在保障业务连续性的前提下高效完成数据库 Schema 的演进与迭代。
17.3 并发 DDL 控制策略

在 OceanBase 数据库的演进历程中,DDL(数据定义语言)操作经历了从串行执行并行执行的重大变革。早期版本中,OceanBase 认为 DDL 是低频操作,整个集群仅有一个线程串行处理所有的 DDL 请求[reference:0][reference:1]。随着用户规模的增长和业务复杂度的提升,DDL 的并发需求日益凸显,OceanBase 从 V4.1.0 版本开始逐步引入并行 DDL 能力[reference:2]。本章将深入剖析 OceanBase 4.x 中并发 DDL 的控制策略,包括并行 DDL 的演进与架构开关控制并行度控制锁机制与并发安全超时控制以及最佳实践等核心内容。

🎯 本章内容概览
  • 并发 DDL 的演进与架构:从串行 DDL 到并行 DDL 的改造历程与核心架构。
  • 并行 DDL 开关控制_enable_parallel_table_creation_parallel_ddl_control 配置项详解。
  • 并行度控制:HINT、SESSION、TABLE DOP 三级控制与优先级。
  • 锁机制与并发安全:元数据锁(MDL)、行级锁与 Schema 刷新机制。
  • DDL 超时控制_ob_ddl_timeout 参数与超时处理。
  • 并发 DDL 最佳实践:版本支持、并行度设置、资源隔离与常见问题。

一、并发 DDL 的演进与架构

1.1 从串行 DDL 到并行 DDL

在 OceanBase V4.1 版本之前,整个集群仅有一个线程(RS 上的 DDLQueueTh)串行处理所有的 DDL 请求[reference:3][reference:4]。所有租户的 DDL 任务都会打到 RS 上,由 RS 来串行执行[reference:5]。这种设计在 DDL 操作低频的场景下是合理的,但随着业务迁移到 OceanBase 时需要通过 OMS 做结构导入,创建数十万乃至百万的表成为常态,DDL 串行执行的瓶颈日益突出[reference:6]。

⚠️ 串行 DDL 的瓶颈
在串行 DDL 模式下,如果有一个 DDL 任务执行时间较长,就会产生大量积压,造成队列抢占,DDL 任务在队列中等待时间过长,从而导致整体耗时大幅增加[reference:7]。不论集群与租户的规格有多大,始终只有一个线程执行 DDL[reference:8]。
1.2 并行 DDL 的架构设计

OceanBase 从 V4.1.0 版本开始引入并行 DDL 执行能力[reference:9]。其核心设计思路是:

  • 扩展 DDL 线程池:在 RS 上增加一个并行 DDL 任务的线程池(DDLPQueueTh),让多个线程并发处理 DDL 任务[reference:10]。
  • 分阶段并行化:将单一 DDL 的实现拆分成不同阶段,同时将其中的部分阶段进行并行化改造[reference:11]。
  • 资源弹性扩展:RootService 所在节点扩展出来的 DDL 线程数目跟集群的规格相关,当集群与租户的规格扩展时,DDL 的执行能力也能随之扩展[reference:12]。

这种设计使得 RootService 可以将所在节点中更多的空闲资源利用起来,有效提高整体 DDL 的吞吐量[reference:13]。

1.3 并行 DDL 的版本演进

OceanBase 在不同版本中逐步开放了不同 DDL 操作的并行执行能力[reference:14][reference:15]:

版本新增的并行 DDL 功能 V4.1.0.0TRUNCATE TABLE V4.2.1.0CREATE TABLE(无冲突建表语句间并发执行)[reference:16] V4.2.2.0CREATE INDEXCREATE COMMENT V4.2.3.0CREATE VIEW V4.2.5.1DROP TABLE
📌 注意
表中该版本新增的并行 DDL 功能表示该版本首次支持的并行执行 DDL 操作;后续版本默认会继承之前版本已经支持的操作[reference:17]。例如,V4.2.1 版本已支持 TRUNCATE TABLE 和 CREATE TABLE 的并行执行[reference:18]。

二、并行 DDL 开关控制

OceanBase 提供了两个核心配置项来灵活控制并行 DDL 功能[reference:19][reference:20]。

2.1 _enable_parallel_table_creation

该配置项用于控制是否在租户级别开启并发创建表(CREATE TABLE)的功能[reference:21]。

属性描述 参数类型布尔类型(Boolean) 默认值True,表示默认开启并发创建表的功能[reference:22] 生效模式立即生效[reference:23]
-- 在 sys 租户中关闭用户租户 mysql001 的并发建表功能
        ALTER SYSTEM SET _enable_parallel_table_creation = false TENANT = mysql001;

        -- 在用户租户中关闭并发建表功能(MySQL 模式)
        ALTER SYSTEM SET _enable_parallel_table_creation = false;

        -- 在用户租户中关闭并发建表功能(Oracle 模式,隐藏配置项需加双引号)
        ALTER SYSTEM SET "_enable_parallel_table_creation" = false;[reference:24]
💡 自动开启说明
当集群以 OceanBase 4.2.1 之后的版本部署或升级后,并发建表的功能会自动开启,无需用户额外进行配置[reference:25]。
2.2 _parallel_ddl_control

该配置项用于控制是否在租户级别开启各类并行 DDL 的功能[reference:26]。

属性描述 参数类型字符串类型(STRING) 默认值空字符串。语义如下:TRUNCATE TABLE 默认为 ON(默认开启并行能力);其他类型 DDL 默认为 OFF(默认关闭并行能力)[reference:27] 生效模式立即生效[reference:28]

支持的 DDL 类型[reference:29]:

  • TRUNCATE_TABLE:{ON | OFF}
  • SET_COMMENT:{ON | OFF}
  • CREATE_INDEX:{ON | OFF}
  • DROP_TABLE:{ON | OFF}(V4.3.5 BP3 版本开始支持[reference:30])
-- 在 sys 租户中设置用户租户 mysql001:关闭并行 TRUNCATE TABLE,开启并行 CREATE INDEX
        ALTER SYSTEM SET _parallel_ddl_control = 'CREATE_INDEX:ON, TRUNCATE_TABLE:OFF' TENANT = mysql001;

        -- 在用户租户中设置(MySQL 模式)
        ALTER SYSTEM SET _parallel_ddl_control = 'CREATE_INDEX:ON, TRUNCATE_TABLE:OFF';

        -- 在用户租户中设置(Oracle 模式,隐藏配置项需加双引号)
        ALTER SYSTEM SET "_parallel_ddl_control" = 'CREATE_INDEX:ON, TRUNCATE_TABLE:OFF';[reference:31]
⚠️ 当前限制
OceanBase 当前版本暂不支持全文索引、多值索引和向量索引的并发执行[reference:32]。

三、并行度控制

OceanBase 数据库 V4.x 版本 DDL 执行时默认不开启并行,可以通过 HINT、SESSION 和 TABLE DOP 三个级别来控制并行度[reference:33]。三种并行设置的优先级为:HINT > SESSION > TABLE DOP[reference:34]。

3.1 通过 PARALLEL Hint 开启并行

目前仅支持 CREATE INDEX 操作[reference:35]。

CREATE /*+PARALLEL(32)*/ INDEX idx ON tt(i);[reference:36]
3.2 通过 Table DOP 开启并行

在建表时指定该表默认的查询与 DDL 并行度[reference:37]。

CREATE TABLE tbl8(c1 INT PRIMARY KEY, c2 INT) PARALLEL 3;[reference:38]
3.3 通过 SESSION 变量开启并行

MySQL 模式[reference:39]:

SET _ENABLE_PARALLEL_DDL = 1;                    -- 启用并行 DDL
        SET SESSION _FORCE_PARALLEL_DDL_DOP = 8;          -- 设置并行度

Oracle 模式[reference:40]:

ALTER SESSION ENABLE PARALLEL DDL;                -- 启用并行 DDL
        ALTER SESSION FORCE PARALLEL DDL PARALLEL 8;      -- 设置并行度
        ALTER SESSION DISABLE PARALLEL DDL;               -- 禁用并行 DDL
3.4 并行度设置的重要约束
⚠️ 并行度约束
  • 对于 OceanBase 数据库 V4.x 及之后的版本,DDL 默认是串行执行的,可以通过 SESSION 级别的变量来控制并行度[reference:41]。
  • 所有 DDL 的并行度之和不能超过租户的 max_cpu 上限[reference:42]。
  • 对于 V4.1.0 BP3 之前的版本,由于临时文件实现的限制,建议所有 DDL 的并行度之和不超过 64
  • 调整并行度后,建议同步调整 parallel_servers_target 参数,该值应比所有并行度之和大。
3.5 删列并行度控制:ddl_thread_score

ddl_thread_score 参数用于控制 DDL 删列执行的并行度以及表级恢复跨租户导表阶段的并行度[reference:43][reference:44]。

属性描述 参数类型Int 默认值0(表示默认线程数为 2) 取值范围[0, 100] 生效模式设置后立即生效
-- 查看 ddl_thread_score 当前值
        SHOW PARAMETERS LIKE '%ddl_thread_score%';

        -- 设置租户级别的 ddl_thread_score
        ALTER SYSTEM SET ddl_thread_score = 8 TENANT = 'your_tenant';

        -- 建议:如果发现删列等 DDL 任务运行速度过慢,可以适当提高该参数值,建议每次将数值翻倍
📌 使用建议
ddl_thread_score租户级别的参数,修改后租户下的删除列操作都将以此作为执行的并行度。过高的并行度可能会挤压到其他模块(如合并速度等),一般建议设置为 8。表级恢复执行结束之后,建议将 ddl_thread_score 恢复成默认值。

四、锁机制与并发安全

在并发 DDL 场景下,锁机制是保障数据一致性和并发安全的关键。OceanBase 通过 元数据锁(MDL)行级锁Schema 刷新机制 共同保障 DDL 操作期间的数据一致性和并发性[reference:45]。

4.1 元数据锁(MDL)

元数据锁(Metadata Lock,简称 MDL)主要用于保证 DDL 与 DML 之间,以及 DDL 与 DDL 之间 的并发安全性[reference:46]。当操作需要修改或读取表结构时,就会涉及元数据锁[reference:47]。

  • MDL 读锁:允许并发访问,但阻止独占访问[reference:48]。
  • MDL 写锁:阻止其他用户对表进行任何访问,确保 DDL 操作的原子性。
4.2 表锁与行锁

OceanBase 在 Online DDL 场景下主要依赖行级锁多版本两阶段锁来保证并发性[reference:49]。表锁主要用于实现一些较为复杂的 DDL 操作,用于在操作期间阻止对整个数据库表的并发访问,以确保事务的原子性和一致性[reference:50]。

锁模式[reference:51][reference:52]:

  • ROW SHARE:允许并发访问锁定表,但禁止其他用户以独占方式锁定表[reference:53]。
  • ROW EXCLUSIVE:与 ROW SHARE 类似,但还阻止以 SHARE 模式锁定表[reference:54]。
4.3 Schema 刷新机制

DDL 事务提交后,RS 本地进行 Schema 刷新,并通知各 OBServer 进行 Schema 刷新[reference:55]。需要特别注意的是:

  • RS 会不断刷新直到成功,但其他 OBServer 仅尽力而为,不保证 Schema 刷新成功[reference:56]。
  • 只能保证在同一个 Session 下做完 DDL 后再次查询是可见的[reference:57]。
  • 不同 Session 或者直连到 Schema 刷新慢的机器上,可能出现不可见的情况[reference:58]。
4.4 并发 DDL 的锁冲突风险
⚠️ 常见锁冲突场景
  • DDL 与 DML 冲突:Online DDL 期间,DML 操作可能因 MDL 锁等待而阻塞。
  • DDL 与 DDL 冲突:对同一张表并发执行 DDL 操作可能引发锁等待或冲突。
  • Schema 槽位满:在并发执行 DDL 脚本时,可能出现错误代码 5627,提示 schema 槽位满导致 DDL 执行失败[reference:59]。即使将并发度降低至 2,仍然可能出现报错,需进一步降低并发度[reference:60]。

五、DDL 超时控制

DDL 语句是在 RS 中执行的,其超时时间由参数 _ob_ddl_timeout 控制[reference:61]。

属性描述 影响范围集群级别[reference:62] 默认值1000 秒[reference:63] 生效模式动态生效(修改后立即生效)[reference:64]
-- 查看当前 DDL 超时设置
        SHOW PARAMETERS LIKE '_ob_ddl_timeout';

        -- 修改 DDL 超时时间(集群级别)
        ALTER SYSTEM SET _ob_ddl_timeout = 3600;  -- 设置为 3600 秒
📌 超时参数说明
  • 默认值 1000 秒已经足够长,一般不需要进行调整[reference:65]。
  • 如果执行的 DDL 超时了,可以考虑将 _ob_ddl_timeout 参数设置得更大[reference:66]。
  • 需要注意,DDL 超时与 SQL 语句超时(ob_query_timeout)和事务超时(ob_trx_timeout)是不同维度的超时控制[reference:67]。

六、并发 DDL 最佳实践

6.1 版本支持检查

在使用并行 DDL 功能前,首先确认集群版本是否支持目标 DDL 操作的并行执行[reference:68]:

-- 查看当前租户的版本
        SHOW PARAMETERS LIKE "compatible";

        -- 当版本 >= 4.2.1 时,并发建表功能自动开启[reference:69]
6.2 并行 DDL 的适用场景
  • 结构迁移:通过 OMS 进行结构导入时,无冲突的建表语句可以并发执行[reference:70]。
  • 批量建表/删表:创建或删除大量表时,可显著提升吞吐量。
  • 批量创建索引:多个索引创建操作可并发执行。
6.3 并行 DDL 的限制与注意事项
⚠️ 核心限制
  • 串行 DDL 会阻塞并行 DDL:如果插入了串行 DDL,则整体 DDL 执行将转为串行[reference:71]。因此,在执行并行 DDL 时,最好仅包含支持并行的 DDL[reference:72]。
  • 索引类型限制:当前版本暂不支持全文索引、多值索引和向量索引的并发执行[reference:73]。
  • 资源限制:所有 DDL 的并行度之和不能超过租户的 max_cpu 上限[reference:74]。
  • Schema 槽位限制:并发 DDL 过多可能导致 schema 槽位满(错误码 5627),需要降低并发度[reference:75]。
6.4 并行度设置建议
  • 生产环境:建议根据业务低峰期的空闲资源合理设置并行度,避免 DDL 占用过多资源影响业务。
  • 批量操作场景:可适当提高并行度(如 8~16),但需关注租户 max_cpu 上限。
  • 删列操作:通过 ddl_thread_score 控制并行度,建议设置为 8[reference:76]。
  • V4.1.0 BP3 之前版本:建议所有 DDL 的并行度之和不超过 64。
6.5 并发 DDL 的监控与诊断
-- 查看 DDL 任务状态(需 sys 租户权限)
        SELECT 
            task_id,
            object_id,
            target_object_id,
            ddl_type,
            trace_id,
            status,
            message
        FROM oceanbase.__all_virtual_ddl_task_status
        ORDER BY task_id DESC
        LIMIT 20;

        -- 查看 DDL 相关的配置项
        SHOW PARAMETERS LIKE '%ddl%';

        -- 查看当前租户的并行 DDL 控制配置(MySQL 模式)
        SHOW VARIABLES LIKE '_enable_parallel_table_creation';
        SHOW VARIABLES LIKE '_parallel_ddl_control';
        SHOW VARIABLES LIKE '_force_parallel_ddl_dop';

        -- 使用 trace_id 追踪 DDL 执行日志
        SELECT last_trace_id() FROM DUAL;

七、生产环境最佳实践 Checklist

✅ 上线前检查清单
  • 确认版本支持:确认集群版本是否支持目标 DDL 操作的并行执行(参考版本演进表)。
  • 检查默认配置:确认 _enable_parallel_table_creation_parallel_ddl_control 的默认值是否符合预期。
  • 评估资源容量:确认租户 max_cpu 足够支撑预期的 DDL 并行度。
  • 设置合理的并行度:根据业务场景和资源情况,通过 HINT/SESSION/TABLE DOP 设置合适的并行度。
  • 避免串并行混合:在执行并行 DDL 时,避免插入串行 DDL 导致整体降级为串行执行[reference:77]。
  • 监控 DDL 进度:通过 __all_virtual_ddl_task_status 跟踪 DDL 任务执行状态。
  • 关注 Schema 槽位:并发 DDL 过多时,关注是否出现错误码 5627(schema 槽位满)。
  • 低峰期执行:对于资源消耗较大的 DDL 操作,建议在业务低峰期执行。
🏆 小结
OceanBase 4.x 的并发 DDL 控制策略经历了从串行到并行的重大演进。通过 _enable_parallel_table_creation_parallel_ddl_control 两个核心配置项,DBA 可以灵活控制各类 DDL 操作的并行开关;通过 HINT、SESSION 和 TABLE DOP 三级控制,可以精细调节 DDL 的并行度。在享受并发 DDL 带来的吞吐量提升的同时,也需要关注资源隔离、锁冲突和 Schema 槽位等潜在风险。建议 DBA 和开发人员深入理解并发 DDL 的控制机制,合理利用并行能力,在保障业务稳定的前提下高效完成数据库 Schema 的演进与迭代。
17.4 DDL 任务监控与取消

在生产环境中,DDL(数据定义语言)操作,尤其是针对大表的复杂变更(如创建索引、添加主键、删除列等),执行时间可能长达数小时甚至更久[reference:0][reference:1]。在此期间,DBA 和开发人员需要清晰了解 DDL 任务的执行进度、当前状态以及潜在的阻塞原因;在必要时,还需要能够安全地取消正在进行的 DDL 任务,以释放资源或纠正错误的变更操作。OceanBase 4.x 提供了完善的 DDL 任务监控取消能力,本章将深入剖析其实现原理、核心视图、操作命令及最佳实践。

🎯 本章内容概览
  • DDL 任务状态监控__all_virtual_ddl_task_status 视图详解与 ObDDLTaskStatus 状态枚举。
  • DDL 实时进度监控GV$SESSION_LONGOPS 视图的使用方法与进度解读。
  • DDL 任务取消ALTER SYSTEM CANCEL TASK 命令的语法与使用限制。
  • DDL 超时控制_ob_ddl_timeout 参数的作用与调整方法。
  • DDL 锁冲突排查:长事务与 DDL 互相阻塞的诊断与处理方法。
  • 生产环境最佳实践:监控、取消与故障排查的 Checklist。

一、DDL 任务状态监控

1.1 核心视图:__all_virtual_ddl_task_status

OceanBase 通过系统表 __all_virtual_ddl_task_status 持久化记录 DDL 任务的执行状态[reference:2][reference:3]。该视图是排查 DDL 卡住、失败或长时间未完成问题的首要工具[reference:4]。

-- 查询当前所有 DDL 任务状态(需在 sys 租户下执行)
        SELECT
            task_id,
            object_id,
            target_object_id,
            ddl_type,
            trace_id,
            status,
            message
        FROM oceanbase.__all_virtual_ddl_task_status
        ORDER BY task_id DESC
        LIMIT 20;

关键字段说明

  • task_id:DDL 任务的唯一标识 ID。
  • object_id:DDL 操作的目标表 ID[reference:5]。
  • target_object_id:DDL 的目标对象 ID,可能是原表、两表 DDL 中的隐藏表,或约束/外键 ID[reference:6]。
  • ddl_type:DDL 操作类型(如 DDL_CREATE_INDEXDDL_TABLE_REDEFINITION 等)。
  • trace_id:DDL 任务的追踪 ID,可用于关联 OBServer 和 RS 日志进行深度排查[reference:7]。
  • status:DDL 任务的当前执行阶段,对应 ObDDLTaskStatus 枚举值[reference:8]。
  • message:附加信息,通常包含错误详情或当前阶段的补充说明。
1.2 DDL 任务状态枚举(ObDDLTaskStatus

status 字段的取值对应 ObDDLTaskStatus 枚举,完整列表如下[reference:9]:

状态值状态名称说明 0PREPARE准备阶段 1LOCK_TABLE锁定表阶段 2WAIT_TRANS_END等待事务结束 3REDEFINITION表重定义(数据回填)阶段 4VALIDATE_CHECKSUM校验 Checksum 阶段 5COPY_TABLE_DEPENDENT_OBJECTS拷贝表依赖对象阶段 6TAKE_EFFECT生效阶段 7WRITE_BARRIER_LOG写 Barrier 日志阶段 8CHECK_CONSTRAINT_VALID检查约束有效性阶段 9SET_CONSTRAINT_VALIDATE设置约束校验阶段 10MODIFY_AUTOINC修改自增列阶段 11SET_WRITE_ONLY设置只写阶段 12WAIT_TRANS_END_FOR_WRITE_ONLY等待只写阶段的事务结束 13SET_UNUSABLE设置不可用阶段 14WAIT_TRANS_END_FOR_UNUSABLE等待不可用阶段的事务结束 15DROP_SCHEMA删除 Schema 阶段 99FAIL任务失败 100SUCCESS任务成功完成
📌 状态分析建议
若 DDL 长时间停留在 WAIT_TRANS_END(状态值 2)或 WAIT_TRANS_END_FOR_WRITE_ONLY(状态值 12)等阶段,通常意味着有长事务正在阻塞 DDL 的执行[reference:10]。此时应排查当前活跃的长事务,并评估是否终止阻塞事务以推进 DDL。
1.3 获取 Trace ID 与日志追踪

当 DDL 执行失败或出现异常时,可通过 last_trace_id() 获取当前会话的 Trace ID,进而在 OBServer 和 RS 日志中定位问题[reference:11]。

-- 获取当前会话的 Trace ID
        SELECT last_trace_id() FROM DUAL;

        -- 在 OBServer 日志中搜索 Trace ID(需登录到对应 OBServer 节点)
        grep "YB42AC1405B2-000627D8F649686A-0-0" /home/admin/oceanbase/log/observer.log

排查路径建议[reference:12]:

  • 直连模式:检查客户端连接的 OBServer 机器的日志。
  • 通过 OBProxy 连接:需要在所有可能的 OBServer 机器上进行查找;建议先检查 RS(RootService)日志[reference:13]。
  • 执行阶段错误:使用 Trace ID 在 RS 日志中搜索具体错误码[reference:14]。
  • RPC 调用错误:检查 RPC 目的端的 OBServer 日志[reference:15]。
  • 单副本构建阶段的 inner SQL 错误:使用 __all_virtual_ls_meta_table 确定日志流主机,并在对应 OBServer 上搜索 Trace ID[reference:16]。

二、DDL 实时进度监控

2.1 视图:GV$SESSION_LONGOPS

OceanBase 数据库 V4.1 版本引入了 DDL 实时执行进度监控 功能[reference:17],通过 GV$SESSION_LONGOPS 视图展示 DDL 操作的执行状态和进度[reference:18][reference:19]。该视图特别适用于监控大表上的耗时 DDL 操作,如创建索引、添加主键、删除列等[reference:20]。

-- 查询 DDL 实时进度(在普通租户下执行)
        SELECT
            TRACE_ID,
            OPNAME,
            TARGET,
            SVR_IP,
            SVR_PORT,
            START_TIME,
            ELAPSED_SECONDS,
            TIME_REMAINING,
            MESSAGE
        FROM oceanbase.GV$SESSION_LONGOPS\G

关键字段说明[reference:21]:

  • TRACE_ID:DDL 操作的追踪 ID。
  • OPNAME:操作名称(如 create indextable redefinitionalter column group 等)[reference:22][reference:23][reference:24]。
  • TARGET:操作目标对象[reference:25]。
  • ELAPSED_SECONDS:已消耗的时间(秒)[reference:26]。
  • TIME_REMAINING:预估剩余时间(秒)[reference:27]。
  • MESSAGE:进度详情,包含 TENANT_IDTASK_IDSTATUSROW_SCANNEDROW_SORTEDROW_INSERTED 等关键进度指标[reference:28][reference:29]。
⚠️ 使用注意事项
监控 DDL 执行进度需要创建两个会话:一个用于执行 DDL 操作,另一个用于查询 GV$SESSION_LONGOPS 视图[reference:30][reference:31]。两个会话均以普通租户身份登录即可[reference:32]。
2.2 进度信息解读

MESSAGE 字段在不同阶段展示不同的关键信息[reference:33]:

  • 等待事务结束阶段STATUS: WAIT TRANS END,并显示第一个未完成事务的 ID(PENDING_TX_ID)[reference:34]。可通过该事务 ID 查询 __all_virtual_trans_stat 视图获取更多信息[reference:35]。
  • 数据回填阶段STATUS: REPLICA BUILD,并显示已扫描行数(ROW_SCANNED)、已排序行数(ROW_SORTED)、已插入行数(ROW_INSERTED)等进度指标[reference:36]。
  • 并行 DDL 信息MESSAGE 中还会显示 PARALLELISM 字段,展示当前 DDL 操作的并行度[reference:37][reference:38]。

三、DDL 任务取消

3.1 取消命令:ALTER SYSTEM CANCEL TASK

当 DDL 任务执行时间过长、卡住或确认需要撤销时,可以通过 ALTER SYSTEM CANCEL TASK 命令取消正在执行的 DDL 任务[reference:39][reference:40]。

-- 在 sys 租户下取消 DDL 任务(指定 trace_id)
        ALTER SYSTEM CANCEL TASK 'trace_id';

        -- 在 sys 租户下取消指定租户的 DDL 任务(部分版本支持)
        ALTER SYSTEM CANCEL TASK 'trace_id' TENANT = 'tenant_name';
⚠️ 取消操作的重要说明
  • trace_id 可以从 __all_virtual_ddl_task_status 视图的 trace_id 列获取[reference:41][reference:42]。
  • 取消之后,当前 DDL 任务已发生的变更将会回滚,数据表将恢复到 DDL 任务执行之前的状态[reference:43][reference:44]。
  • 取消操作需要 sys 租户 权限[reference:45]。
  • 部分版本可能不支持在 ALTER SYSTEM CANCEL TASK 后直接指定 TENANT 子句,具体语法请以对应版本的官方文档为准[reference:46]。
  • 对于已处于 SUCCESS(100)或 FAIL(99)状态的 DDL 任务,取消操作无效。
3.2 取消操作的适用场景
  • DDL 卡住:DDL 长时间处于 WAIT_TRANS_END 等状态,无法推进[reference:47]。
  • 错误 DDL:发起了错误的 DDL 变更(如错误的分区规则、错误的索引类型等),需要紧急撤销。
  • 资源抢占:DDL 占用过多资源,影响业务正常运行,需要释放资源。
  • 升级/运维窗口关闭:DDL 未能在预定维护窗口内完成,需要取消并重新规划执行时间。
3.3 取消操作的替代方案(紧急场景)

在极少数无法通过 ALTER SYSTEM CANCEL TASK 取消 DDL 的紧急场景下,可考虑以下替代方案(需谨慎评估风险):

  • 手工更新任务状态:在 sys 租户下执行 UPDATE __all_ddl_task_status SET status = 10, ret_code = 0 WHERE task_id = xxx;,然后重启 OBServer 集群[reference:48][reference:49]。
  • 重启 OBServer 集群:通过 obd cluster restart 重启集群(生产环境需谨慎操作)[reference:50]。
⚠️ 风险提示
手工更新系统表或重启集群属于非常规操作,可能引发数据不一致或其他不可预知的风险。仅在确认无法通过正常方式取消 DDL,且经过充分评估后,方可考虑使用。建议在执行前备份相关系统表数据,并联系 OceanBase 技术支持确认操作方案。

四、DDL 超时控制

4.1 参数:_ob_ddl_timeout

DDL 语句在 RS(RootService)中执行,其超时时间由隐藏参数 _ob_ddl_timeout 控制[reference:51][reference:52]。该参数与 DML 语句的超时(ob_query_timeout)和事务超时(ob_trx_timeout)是独立的[reference:53][reference:54]。

属性描述 影响范围集群级别 默认值1000 秒(约 16.7 分钟)[reference:55] 生效模式动态生效,修改后立即生效[reference:56] 参数类型隐藏参数(以 _ 开头),一般不建议修改[reference:57][reference:58]
-- 查看当前 DDL 超时设置
        SHOW PARAMETERS LIKE '_ob_ddl_timeout';

        -- 修改 DDL 超时时间(集群级别)
        ALTER SYSTEM SET _ob_ddl_timeout = 3600;  -- 设置为 3600 秒(1 小时)
📌 超时参数使用建议
  • 默认值 1000 秒已经足够长,一般不需要进行调整[reference:59]。
  • 如果执行的 DDL 超时了,可以考虑将 _ob_ddl_timeout 参数设置得更大[reference:60][reference:61]。
  • 由于该参数是隐藏参数,修改前建议充分评估对集群的影响。

五、DDL 锁冲突排查

长事务是阻塞 DDL 操作最常见的原因之一[reference:62]。当 DDL 长时间处于 WAIT_TRANS_END 状态时,通常意味着有未结束的事务正在持有表上的锁,阻止 DDL 推进[reference:63]。

5.1 诊断步骤
  1. 确认 DDL 状态:查询 __all_virtual_ddl_task_status,确认 DDL 是否处于 WAIT_TRANS_END 状态。
  2. 定位阻塞事务:通过 GV$SESSION_LONGOPSMESSAGE 字段获取 PENDING_TX_ID[reference:64]。
  3. 查询事务详情:使用事务 ID 查询 __all_virtual_trans_stat 视图[reference:65]。
  4. 评估是否终止事务:根据事务的持续时间、所属会话、执行的 SQL 等信息,判断是否终止该事务以释放锁资源。
-- 查询 DDL 任务状态,确认是否处于 WAIT_TRANS_END
        SELECT task_id, status, trace_id
        FROM oceanbase.__all_virtual_ddl_task_status
        WHERE status = 2;  -- WAIT_TRANS_END

        -- 查询 DDL 实时进度,获取阻塞事务 ID
        SELECT MESSAGE FROM oceanbase.GV$SESSION_LONGOPS WHERE OPNAME = 'create index'\G

        -- 查询阻塞事务的详细信息(需在对应租户下执行)
        SELECT * FROM oceanbase.__all_virtual_trans_stat WHERE trans_id = 'PENDING_TX_ID';
⚠️ 终止事务的风险
终止事务(通过 KILL SESSION 等方式)可能导致该事务回滚,业务侧需处理相应的异常。在终止事务前,建议与业务方确认该事务是否可以安全终止。

六、生产环境最佳实践 Checklist

✅ 上线前检查清单
  • 建立监控流程:定期查询 __all_virtual_ddl_task_status,关注长时间未完成(如超过 1 小时)的 DDL 任务。
  • 启用进度监控:对于预计耗时较长的 DDL 操作,通过 GV$SESSION_LONGOPS 实时跟踪执行进度。
  • 准备取消方案:提前熟悉 ALTER SYSTEM CANCEL TASK 命令的用法,确保在紧急情况下能够快速响应。
  • 评估超时设置:根据业务 DDL 操作的数据量,评估是否需要调整 _ob_ddl_timeout 参数。
  • 排查长事务:在执行大表 DDL 前,检查是否存在可能阻塞 DDL 的长事务,提前处理。
  • 记录操作日志:所有 DDL 操作(包括执行、监控、取消)均记录详细日志,便于事后审计和问题复盘。
  • 制定回滚预案:对于关键的 DDL 变更,提前准备回滚方案(如备份表结构、记录原始 DDL 语句等)。
🏆 小结
OceanBase 4.x 提供了完善的 DDL 任务监控与取消能力。通过 __all_virtual_ddl_task_status 可以查看 DDL 任务的详细状态和执行阶段[reference:66];通过 GV$SESSION_LONGOPS 可以实时追踪 DDL 的执行进度[reference:67];通过 ALTER SYSTEM CANCEL TASK 可以在必要时安全地取消正在执行的 DDL 任务[reference:68]。建议 DBA 和开发人员建立常态化的 DDL 监控机制,在 DDL 执行前评估数据量和潜在风险,执行中持续跟踪进度,遇到异常时快速定位阻塞原因并果断采取措施,从而保障数据库 Schema 变更的安全、高效与可控。
17.5 大表 DDL 优化与临时空间管理

在 OceanBase 数据库中,针对大表执行 DDL 操作(如创建索引、修改列类型、修改分区规则等)会消耗大量的系统资源,对磁盘空间和 CPU 使用产生显著影响[reference:0]。如果缺乏合理的优化策略和空间管理,DDL 操作可能因磁盘空间不足而失败,或因资源争抢而影响业务正常运行[reference:1]。本章将深入剖析 OceanBase 4.x 中大表 DDL 的性能优化方法临时空间管理策略,包括并行度控制、临时空间估算与监控、临时文件压缩、Offline DDL 空间放大应对以及生产环境最佳实践。

🎯 本章内容概览
  • 大表 DDL 的性能挑战:磁盘空间放大、资源消耗与执行时间问题。
  • 并行度优化:通过 SESSION 变量、HINT 和 TABLE DOP 控制 DDL 并行度。
  • 临时空间管理:临时空间限制、空间估算、监控与释放机制。
  • 临时文件压缩_ob_ddl_temp_file_compress_func 配置项详解。
  • Offline DDL 空间放大应对:两表双写机制与空间放大系数。
  • 生产环境最佳实践 Checklist:优化与空间管理的完整指南。

一、大表 DDL 的性能挑战

1.1 DDL 操作的资源消耗特征

大表 DDL 操作(如创建索引、修改列类型、修改分区规则等)在 OceanBase 中属于重整数据 DDL,需要扫描全表数据并重新组织[reference:2]。这类操作的主要资源消耗包括:

  • 磁盘空间:DDL 执行期间会产生大量临时文件,在 Offline DDL 场景下还存在空间放大问题[reference:3][reference:4]。
  • CPU 资源:数据排序、哈希计算、数据压缩等操作消耗大量 CPU。
  • 内存资源:排序缓冲区、哈希表、DTL 缓冲区等内存消耗[reference:5]。
  • IO 资源:全表扫描、临时文件读写产生大量磁盘 IO。
1.2 磁盘空间放大问题

在 OceanBase 4.x 的部分版本中,一些 DDL 操作存在磁盘空间放大问题[reference:6][reference:7]。主要表现包括:

  • Offline DDL 两表双写:执行 Offline DDL 时,会走两表双写逻辑,将老表的数据用新的 DDL Schema 重写到新表,导致表空间放大 2 倍[reference:8]。
  • 临时文件膨胀:创建索引等操作需要将数据排序后写入临时文件,在并行度较高时临时文件可能占用大量磁盘空间[reference:9]。
  • 版本差异:不同版本的空间放大系数不同,V4.2.3 之前的版本放大更为严重[reference:10]。
⚠️ 空间放大风险评估
在执行大表 DDL 前,必须评估目标表的当前数据量,并确保磁盘剩余空间满足 DDL 操作的空间需求。否则 DDL 可能因磁盘空间不足而失败[reference:11]。

二、并行度优化

2.1 V4.x 并行 DDL 的默认行为

OceanBase 数据库 V4.x 版本 DDL 执行时默认不开启并行,这与 V3.x 版本默认开启并行的行为不同[reference:12]。用户可以通过 HINT、SESSION 和 TABLE DOP 三个级别来控制并行度,优先级为:HINT > SESSION > TABLE DOP[reference:13]。

2.2 并行度控制方法

方式一:通过 PARALLEL Hint 开启并行(目前仅支持 CREATE INDEX)[reference:14]

CREATE /*+PARALLEL(32)*/ INDEX idx ON tt(i);

方式二:通过 Table DOP 开启并行(在建表时指定该表默认的查询与 DDL 并行度)[reference:15]

CREATE TABLE tbl8(c1 INT PRIMARY KEY, c2 INT) PARALLEL 3;

方式三:通过 SESSION 变量开启并行[reference:16]

-- MySQL 模式
        SET _ENABLE_PARALLEL_DDL = 1;
        SET SESSION _FORCE_PARALLEL_DDL_DOP = 8;

        -- Oracle 模式
        ALTER SESSION ENABLE PARALLEL DDL;
        ALTER SESSION FORCE PARALLEL DDL PARALLEL 8;
2.3 并行度设置的重要约束
⚠️ 并行度约束
  • 所有 DDL 的并行度之和不能超过租户的 max_cpu 上限[reference:17]。
  • 针对 OceanBase 数据库 V4.1.0 BP3 之前的版本,由于临时文件实现上的限制,建议所有 DDL 的并行度之和不超过 64[reference:18]。
  • 一般在调整并行度之后,也需要调整相应的 parallel_servers_target 值,该值比所有并行度之和大即可[reference:19]。
  • 本文所涉及的参数调整会导致 DDL 占用大量资源,适用于最大化 DDL 执行速度的场景,不适用于有业务流量的场景[reference:20]。
2.4 删列操作的并行度控制:ddl_thread_score

删除列操作是借助 DAG 来调度完成的,其并行度由 ddl_thread_score 参数控制[reference:21]。该参数还可用于控制表级恢复跨租户导表阶段的并行度[reference:22]。

属性描述 参数类型Int 默认值0(表示使用内部默认值 2 作为并行度)[reference:23] 取值范围[0, 100][reference:24] 生效模式动态生效[reference:25] 适用版本V4.1.0 BP3 和 V4.2.1 及之后版本[reference:26]
-- 查看 ddl_thread_score 当前值
        SHOW PARAMETERS LIKE '%ddl_thread_score%';

        -- 设置租户级别的 ddl_thread_score
        ALTER SYSTEM SET ddl_thread_score = 8 TENANT = 'your_tenant';

        -- 使用建议:当机器可支持核数一定时,过高的并行度可能会挤压到其他模块(如合并速度等),一般设置为 8[reference:27]

三、临时空间管理

3.1 临时空间的版本差异

OceanBase 在不同版本中对临时空间的限制存在显著差异[reference:28]:

版本临时空间限制 V3.x单个租户最大可用临时文件总空间为 4TB,不可调整 V4.1.x、V4.2.x单个租户与租户内存大小相关:tenant_memory × 70% × 500,最大可达 100TB V4.3.x、V4.4.x默认不设限,可通过 temporary_file_max_disk_size 配置项设置[reference:29]
3.2 临时文件空间估算

在执行大表 DDL 前,准确估算所需的临时空间至关重要[reference:30]。

方法一:基于统计信息估算(适用于创建索引)[reference:31]

-- 获取表级统计信息
        SELECT NUM_ROWS, AVG_ROW_LEN FROM ALL_TAB_STATISTICS 
        WHERE OWNER = 'xxx' AND TABLE_NAME = 'xxx' AND OBJECT_TYPE = 'TABLE';

        -- 获取列级统计信息
        SELECT TABLE_NAME, COLUMN_NAME, AVG_COL_LEN 
        FROM ALL_TAB_COL_STATISTICS WHERE TABLE_NAME = 'xxx' AND OWNER = 'xxx';

        -- 索引空间估算公式:index_size = data_size × (avg_column_length / avg_row_length)[reference:32]

方法二:基于内部表估算(更精确)[reference:33][reference:34]

-- 1. 获取租户 ID
        SELECT tenant_id FROM __all_tenant WHERE tenant_name = '租户名';

        -- 2. 获取表 ID
        SELECT table_id FROM __all_virtual_table 
        WHERE table_name = '表名' AND tenant_id = '租户id';

        -- 3. 估算源表数据空间
        SELECT svr_ip, svr_port, SUM(original_size) AS estimated_data_size
        FROM __all_virtual_tablet_sstable_macro_info
        WHERE tablet_id IN (
            SELECT tablet_id FROM __all_virtual_tablet_to_table_history WHERE table_id = xxx
        ) AND (svr_ip, svr_port) IN (
            SELECT svr_ip, svr_port FROM __all_virtual_ls_meta_table WHERE role = 1
        )
        GROUP BY svr_ip, svr_port;

方法三:索引空间精确估算(针对创建索引)[reference:35]

-- 1. 查询索引表 ID
        SELECT table_id FROM __all_virtual_table_history 
        WHERE tenant_id = 'xxx' AND data_table_id = 'xxx' AND table_name LIKE '%索引名%';

        -- 2. 查询主表所有列的长度之和
        SELECT table_id, SUM(data_length) FROM __all_virtual_column_history 
        WHERE tenant_id = 'xxx' AND table_id = '主表table_id';

        -- 3. 查询索引所有列的长度之和
        SELECT table_id, SUM(data_length) FROM __all_virtual_column_history 
        WHERE tenant_id = 'xxx' AND table_id = '索引表table_id';

        -- 4. 通过索引表 data_length / 主表 data_length × estimated_data_size 估算最终空间

        -- 5. 最终磁盘空间大小(版本差异):
        -- V4.2.3 及之后的版本:1.5 × estimated_index_size[reference:36]
        -- V4.2.3 之前的版本:5.5 × estimated_index_size[reference:37]
3.3 临时文件空间监控

通过以下视图可以监控临时文件的空间使用情况[reference:38][reference:39]:

-- 通过 __all_space_usage 查询临时文件空间占用
        SELECT svr_ip, svr_port, file_type, SUM(data_size) 
        FROM oceanbase.__all_space_usage 
        WHERE file_type = 'tenant tmp data'
        GROUP BY svr_ip, svr_port, file_type;[reference:40]

        -- 通过 CDB_OB_SERVER_SPACE_USAGE 监控各租户临时文件使用(V4.3.4+)[reference:41]
        SELECT * FROM oceanbase.CDB_OB_SERVER_SPACE_USAGE;

        -- 通过 DBA_OB_TEMP_FILES 查看临时文件详细信息(V4.3.3+)[reference:42]
        SELECT * FROM oceanbase.DBA_OB_TEMP_FILES;
3.4 临时文件释放机制

正常情况下,SQL 执行结束时会清理中间结果,对应的临时文件会被立即回收。但空间释放需要等一轮宏块 mark sweep[reference:43]:

  • V3.2.x 版本:5 分钟[reference:44]
  • V4.x 版本:30 秒[reference:45]
📌 临时文件释放建议
如果临时文件不能及时释放,可能导致磁盘空间使用异常[reference:46]。在执行大规模 DDL 操作后,建议监控临时文件空间是否已及时释放。
3.5 临时文件大小限制(V4.3.5+)

从 V4.3.5 BP1 版本开始,OceanBase 引入 temporary_file_max_disk_size 配置项,用于设置租户的单个节点内临时文件最大可占用的磁盘空间大小[reference:47]。

属性描述 参数类型Capacity 默认值0M(表示不限制)[reference:48] 取值范围[0M, +∞)[reference:49] 生效模式设置后立即生效[reference:50]
-- 设置租户的单个节点内临时文件最大可占用的磁盘空间大小为 100 GB
        ALTER SYSTEM SET temporary_file_max_disk_size = '100GB';[reference:51]

四、临时文件压缩

4.1 自动压缩机制(V4.2.3+)

在 V4.2.3 版本及之后,如果执行创建索引的并行度大于等于 8,系统默认会对创建索引过程中产生的临时文件进行压缩[reference:52][reference:53][reference:54]。

4.2 手动压缩配置

如果并行度小于 8,且磁盘空间不足时,可以通过 _ob_ddl_temp_file_compress_func 配置项手动开启压缩[reference:55]。

取值说明 'ZSTD'使用 ZSTD 压缩算法[reference:56] 'LZ4'使用 LZ4 压缩算法[reference:57] 'NONE'关闭压缩[reference:58] 'AUTO'根据并行度(大于等于 8 开启压缩)自动判断[reference:59]
-- 开启压缩并使用 ZSTD 算法
        ALTER SYSTEM SET _ob_ddl_temp_file_compress_func = 'ZSTD';

        -- 开启压缩并使用 LZ4 算法
        ALTER SYSTEM SET _ob_ddl_temp_file_compress_func = 'LZ4';

        -- 关闭压缩
        ALTER SYSTEM SET _ob_ddl_temp_file_compress_func = 'NONE';

        -- 自动模式(并行度 >= 8 时开启压缩)
        ALTER SYSTEM SET _ob_ddl_temp_file_compress_func = 'AUTO';[reference:60]
⚠️ 压缩的权衡
开启临时文件压缩可以减少磁盘空间占用,但会消耗额外的 CPU 资源用于压缩和解压缩。在 CPU 资源充裕、磁盘空间紧张的场景下,建议开启压缩;在 CPU 资源紧张的场景下,建议关闭压缩以换取更快的执行速度。

五、Offline DDL 空间放大应对

5.1 空间放大原理

Offline DDL 在执行时走两表双写逻辑,即将老表的数据用新的 DDL Schema 重写到新表,导致表空间放大 2 倍[reference:61]。此外,创建索引等操作产生的临时文件也会占用额外空间[reference:62]:

  • V4.2.3 及之后的版本:放大系数约为 1.5 倍(含临时文件)[reference:63]
  • V4.2.3 之前的版本:放大系数约为 5.5 倍[reference:64]
5.2 空间充足性判断

在 DDL 执行前,应判断磁盘剩余空间是否满足需求[reference:65]:

-- 1. 获取 data_disk_usage_limit_percentage 配置
        SHOW PARAMETERS LIKE 'data_disk_usage_limit_percentage';[reference:66]

        -- 2. 获取各节点的磁盘总空间和使用空间
        SELECT total_size, used_size FROM __all_virtual_disk_stat 
        WHERE svr_ip = 'xxx' AND svr_port = xxx;[reference:67]

        -- 3. 计算剩余可用磁盘空间
        -- 剩余可用空间 = total_size × data_disk_usage_limit_percentage - used_size[reference:68]

        -- 4. 对比预期需要使用的空间与剩余空间,判断是否磁盘空间不足[reference:69]
5.3 Offline DDL 执行建议
  • 提前评估:使用上述方法估算目标表的当前数据量,并确保磁盘剩余空间满足 DDL 操作的空间需求[reference:70]。
  • 版本升级:如果可能,建议升级到 V4.2.3 及之后的版本,以利用更优的空间放大系数(1.5 倍 vs 5.5 倍)[reference:71]。
  • 业务低峰期执行:Offline DDL 会阻塞写操作[reference:72],应在业务低峰期执行。
  • 分阶段执行:对于超大表,可考虑将 DDL 拆分为多个小操作分阶段执行,减少单次操作的空间压力。
  • 准备回滚方案:在执行前准备回滚方案,如备份表结构或数据[reference:73]。

六、内存与资源规划

6.1 内存需求估算

大表 DDL 操作的内存消耗主要来自以下几个方面[reference:74]:

  • 临时文件元数据内存:Memory = Temporary Space (GB) × 3.75MB[reference:75]
  • Channel 的 Range 信息:Memory = Parallelism × Partition_Count × 1.5KB[reference:76]
  • DTL 缓冲区:Memory = Parallelism × Parallelism × 64KB × 3[reference:77]
6.2 资源规划建议
  • 根据预估的空间和内存需求,合理调整 OBServer 的内存设置[reference:78]。
  • 确保 CPU 和 IO 资源可以满足并行度的需求[reference:79]。
  • 调整并行度后,同步调整 parallel_servers_target 参数[reference:80]。

七、生产环境最佳实践 Checklist

✅ 上线前检查清单
  • 评估数据量:使用内部表或统计信息估算目标表的数据量[reference:81]。
  • 估算临时空间:根据 DDL 类型和版本,估算所需的临时空间[reference:82]。
  • 检查磁盘剩余空间:确保剩余磁盘空间满足 DDL 操作的空间需求[reference:83]。
  • 设置合理的并行度:根据租户 max_cpu 上限和业务负载,设置适当的并行度[reference:84]。
  • 评估是否需要临时文件压缩:V4.2.3+ 版本,如果并行度 ≥ 8,系统默认开启压缩[reference:85];如果并行度 < 8 且磁盘空间紧张,手动开启压缩[reference:86]。
  • 确认 DDL 类型:确认目标操作是 Online DDL 还是 Offline DDL,Offline DDL 需规划停机窗口[reference:87]。
  • 版本确认:确认集群版本,不同版本的空间放大系数不同[reference:88]。
  • 调整 parallel_servers_target:确保该值大于所有并行度之和[reference:89]。
  • 监控临时文件空间:DDL 执行期间,通过 __all_space_usage 等视图监控临时文件空间使用[reference:90]。
  • 准备回滚方案:对于关键 DDL 操作,提前准备回滚方案[reference:91]。
🏆 小结
OceanBase 4.x 中大表 DDL 的优化与临时空间管理是一项系统性工程。通过合理设置 DDL 并行度、准确估算临时空间需求、利用临时文件压缩特性、以及针对 Offline DDL 的空间放大做好充分准备,DBA 和开发人员可以在保障业务连续性的前提下,高效完成大表的 Schema 变更。建议在实际操作中遵循本章提供的最佳实践 Checklist,在 DDL 执行前充分评估资源需求,执行中持续监控空间使用,执行后及时清理临时文件,确保数据库的稳定运行。

第18章:索引优化进阶

18.1 全局索引设计原则

在 OceanBase 分布式数据库中,全局索引(Global Index) 是一种与主表分区规则相互独立的索引结构[reference:0][reference:1]。与局部索引(Local Index)不同,全局索引将所有主表分区的数据合成一个整体来看,索引中的一个键可能映射到多个主表分区中的数据[reference:2]。全局索引本质上是一张独立的索引表,拥有自己的分区规则和分区数量[reference:3][reference:4]。合理设计和使用全局索引,能够在无法利用分区键进行查询的场景下大幅提升检索效率,但同时也需要权衡其带来的维护成本和写入性能开销。本章将深入剖析 OceanBase 4.x 中全局索引的设计原则、适用场景、性能影响及最佳实践

🎯 本章内容概览
  • 全局索引核心概念:全局索引的定义、分类与架构原理。
  • 全局索引 vs 局部索引:核心差异对比与选型决策矩阵。
  • 全局索引设计原则:分区规则设计、索引键选择、唯一性约束等核心原则。
  • 全局索引的适用场景:全局唯一性需求、非分区键查询、数据重新分区。
  • 全局索引的性能代价:写入性能、查询性能与维护成本分析。
  • 生产环境最佳实践 Checklist:全局索引设计与使用的完整指南。

一、全局索引核心概念

1.1 什么是全局索引

全局索引是在创建索引时通过指定 GLOBAL 关键字创建的索引[reference:5]。与局部索引相比,全局索引最大的特点是:全局索引的分区规则跟表分区是相互独立的,全局索引允许指定自己的分区规则和分区个数,不一定需要跟表分区规则保持一致[reference:6][reference:7]。

全局索引本质上是一张独立的索引表——它拥有自己的数据分布模式,既可以选择非分区模式,也可以选择分区模式;在分区模式中,分区方式既可以与主表相同,也可以与主表不同[reference:8]。

1.2 全局索引的分类

根据索引数据是否分区,全局索引分为以下两种形式[reference:9]:

  • 全局非分区索引(Global Non-Partitioned Index):索引数据不做分区,保持单一的数据结构,和非分区表的索引类似。但由于主表已经做了分区,会出现索引中的某一个键映射到不同主表分区的情况,即一对多的对应关系[reference:10]。
  • 全局分区索引(Global Partitioned Index):索引数据按照指定的方式做分区处理(如 Hash 分区或 Range 分区),将索引数据分散到不同的分区中。索引分区和主表分区之间是多对多的对应关系[reference:11]。
📌 全局索引的架构本质
由于全局索引的分区模式和主表的分区模式完全没有关系,全局索引更像是另一张独立的表,因此也可以将全局索引叫做索引表(和主表相对应),理解起来会更容易一些[reference:12]。非分区表也可以创建全局分区索引,但如果主表没有分区的必要,通常来说索引也就没有必要分区了[reference:13]。

二、全局索引 vs 局部索引

2.1 核心差异对比

理解全局索引与局部索引的核心差异,是做出正确索引选型决策的基础。

对比维度局部索引(Local Index)全局索引(Global Index) 分区规则与主表分区规则相同,一一对应[reference:14]独立于主表,可自定义[reference:15][reference:16] 数据映射关系一对一(每个索引分区对应一个主表分区)[reference:17]一对多或多对多[reference:18] 物理存储位置与主表分区存储在同一位置[reference:19]可能与主表不同[reference:20] 全局唯一性不支持(仅保证分区内唯一)[reference:21]支持[reference:22] 维护代价低[reference:23]高(可能涉及分布式事务)[reference:24][reference:25] 分区裁剪支持(需查询条件包含分区键)[reference:26]支持(基于索引分区键)[reference:27] 默认创建行为MySQL 模式默认[reference:28]Oracle 模式默认[reference:29]
2.2 选型决策矩阵

在决定使用全局索引还是局部索引时,应综合考虑以下因素[reference:30]:

场景特征推荐索引类型理由 查询条件包含分区键✅ 局部索引可充分利用分区裁剪技术,提高查询性能[reference:31] 查询条件不包含分区键,且需要全局唯一性✅ 全局索引局部索引无法保证全局唯一性,且不带分区键的查询会导致全分区扫描[reference:32] 查询条件不包含分区键,但写入性能敏感⚠️ 谨慎使用全局索引全局索引在高并发写入场景下会带来显著的性能代价[reference:33] 需要索引覆盖(索引包含全部查询字段)✅ 全局索引覆盖索引情况下,全局索引代价可能比局部索引更小[reference:34] 对性能非常敏感⚠️ 尽量避免全局索引除非业务需求强制要求全局唯一性[reference:35]
⚠️ 全局索引的核心权衡
全局索引虽然为全局唯一数据重新分区带来了可能,解决了一些业务需要根据不同维度进行查询的强需求,但是为此付出的代价是每一笔数据的写入都有可能变成跨机的分布式事务,在高并发的写入场景下它将严重影响系统的写入性能[reference:36]。当业务的查询可以拥有分区键的条件谓词时,OceanBase 数据库依旧推荐构建局部索引,通过数据库优化器的分区裁剪功能排除掉不符合条件的分区,这样的做法可以同时兼顾查询和写入的性能[reference:37]。

三、全局索引设计原则

3.1 原则一:优先考虑局部索引

尽可能确保主表的分区键覆盖更多的查询条件,从而减少对全局索引的需求[reference:38][reference:39]。如果全局索引的分区规则和主表的分区规则相同并且分区数相同,推荐创建一个局部索引,因为全局索引的维护代价更大,且无法保证和主表分区的物理位置相同[reference:40][reference:41]。

3.2 原则二:全局索引的分区键即索引键

全局索引的分区键一定是索引键本身,因此在使用全局索引的过程中就会指定索引分区键的查询条件[reference:42][reference:43]。设计全局索引时,应确保索引键能够有效支撑业务查询,并能够利用索引的分区规则进行分区裁剪。

3.3 原则三:全局唯一索引的特殊约束

在 OceanBase 中,分区表的主键约束和唯一键约束必须包含分区键[reference:44][reference:45]。对于局部分区唯一索引,索引键需要包含主表的分区键[reference:46];但对于全局分区唯一索引,没有这个限制[reference:47][reference:48]。

-- 局部唯一索引必须包含分区键
        CREATE TABLE test(pk INT, c2 INT, c3 INT, PRIMARY KEY(pk))
            PARTITION BY HASH(pk) PARTITIONS 5;

        -- 以下语句会报错:UNIQUE INDEX must include all columns in the table's partitioning function
        CREATE UNIQUE INDEX idx ON test(c2) LOCAL;  -- ERROR 1503

        -- 正确的局部唯一索引:必须包含分区键 pk
        CREATE UNIQUE INDEX idx ON test(c2, pk) LOCAL;  -- ✅ 成功

        -- 全局唯一索引不需要包含分区键
        CREATE UNIQUE INDEX idx ON test(c2) GLOBAL;  -- ✅ 成功[reference:49]
3.4 原则四:遵循最左前缀原则

创建复合索引时,应遵循最左前缀原则——将查询中最常使用的、区分度最高的列放在索引的最左侧[reference:50][reference:51]。如果查询条件中未包含索引的最左列,该索引将无法被使用[reference:52]。

3.5 原则五:评估写入性能代价

全局索引的每一次数据更新都可能涉及跨分区的分布式事务[reference:53][reference:54]。在设计全局索引时,必须评估业务写入负载的大小。如果业务有高并发的写入,使用全局索引将显著影响系统的写入性能[reference:55]。

3.6 原则六:考虑索引覆盖

如果索引键能够覆盖用户检索的全部字段,索引检索不需要再去访问主表,这种情况下全局索引的代价会比局部索引更小[reference:56]。在设计全局索引时,可考虑将查询中频繁使用的字段包含在索引中,以实现索引覆盖。

四、全局索引的适用场景

根据 OceanBase 官方文档,以下场景推荐使用全局索引[reference:57][reference:58]:

4.1 场景一:全局唯一性需求

业务上除了主键外,还有其他列的组合需要满足全局唯一性的强需求,这个业务需求仅能通过全局性的唯一索引来实现[reference:59]。局部索引仅能保证分区内的唯一性,无法满足跨分区的全局唯一约束[reference:60]。

4.2 场景二:查询条件不包含分区键

业务的查询无法得到分区键的条件谓词,且业务表没有高并发的写入。为避免进行全分区的扫描,可以根据查询条件构建全局索引,必要时可以将全局索引按照新的分区键来分区[reference:61]。当查询条件中包含索引分区键时,全局索引可以进行分区裁剪,在查询到索引键值后利用索引表中存储的主键信息计算出主表的分区位置,进而对主表也能进行快速的分区定位,避免扫描主表的所有分区[reference:62][reference:63]。

4.3 场景三:数据重新分区的灵活性

全局索引提供了数据重新分区的灵活性——可以独立定义索引的分区规则和分区数量,不受主表分区规则的约束[reference:64]。这在某些需要按不同维度组织索引数据的场景下非常有用。

📌 全局索引的分区裁剪示例
对于查询 SELECT * FROM t1 WHERE b=1,如果 t1 上创建了以 b 为索引键的全局索引,优化器会首先通过 WHERE 条件 b=1 裁剪出全局索引的分区,然后对全局索引表进行 table scan,最后利用索引表中存储的主键信息定位主表分区[reference:65][reference:66]。执行计划中会显示 is_global_index=true[reference:67]。

五、全局索引的性能代价

5.1 写入性能代价

全局索引在数据写入时可能需要进行跨分区的分布式事务,导致写入性能显著下降[reference:68]。每次插入、更新或删除操作,如果涉及全局索引的维护,都可能触发跨节点的 RPC 调用和分布式事务[reference:69]。

5.2 查询性能代价

全局索引在查询时可能需要进行跨分区的 RPC 调用以获取主表数据,增加查询延迟[reference:70]。当全局索引的分区与主表分区不在同一物理位置时,执行 TABLE SCAN 操作需要从远程服务器获取主表数据[reference:71]。

5.3 维护成本

全局索引的维护成本较高,尤其是在主表分区频繁变化的情况下[reference:72]。与局部索引相比,全局索引需要更多的维护工作[reference:73]。

⚠️ 性能代价的关键结论
如果对性能非常敏感,应尽量避免使用全局索引,除非业务需求强制要求全局唯一性[reference:74]。OceanBase 的调优第一原则是:LOCAL 优先、REMOTE 为次、DISTRIBUTE 是最坏的[reference:75]。在设计索引时,应优先考虑能否通过优化分区键设计来避免全局索引的使用。

六、全局索引的限制

6.1 不支持全局非前缀索引

OceanBase 数据库不支持全局非前缀索引,因此全局非前缀索引对于查询优化没有意义[reference:76]。

6.2 GTS 依赖

全局索引依赖全局时间戳服务(GTS)来维护全局一致的快照[reference:77]。因此,只有在 GTS 启用时才能使用全局索引,如果 GTS 被禁用,则无法创建全局索引[reference:78]。

6.3 表组 SHARDING 约束

当为 SHARDING = 'NONE' 的表组中的表创建全局索引时,全局索引也会绑定到该表组[reference:79]。

七、生产环境最佳实践 Checklist

✅ 全局索引设计检查清单
  • 优先评估局部索引:查询条件是否包含分区键?如果是,优先使用局部索引[reference:80]。
  • 明确全局唯一性需求:是否真的需要跨分区的全局唯一约束?只有强需求才使用全局唯一索引[reference:81]。
  • 评估写入负载:业务是否有高并发写入?如果是,谨慎使用全局索引[reference:82]。
  • 检查索引覆盖:全局索引是否能覆盖查询的全部字段?如果是,全局索引的代价可能更小[reference:83]。
  • 确认 GTS 启用:使用全局索引前,确认 GTS 已启用[reference:84]。
  • 验证分区裁剪:通过 EXPLAIN 验证全局索引是否能够进行有效的分区裁剪[reference:85]。
  • 监控性能影响:在生产环境中持续监控全局索引的写入延迟和查询响应时间。
  • 定期评估索引有效性:通过 GV$OB_PLAN_CACHE_PLAN_STAT 等视图监控全局索引的使用频率和效果。
🏆 小结
OceanBase 4.x 的全局索引是一把双刃剑。一方面,它为全局唯一性非分区键查询数据重新分区等场景提供了强大的能力;另一方面,全局索引的每一次维护都可能触发跨分区的分布式事务,在高并发写入场景下会带来显著的性能代价[reference:86]。设计全局索引的核心原则是:LOCAL 优先、GLOBAL 审慎[reference:87]——只有在局部索引无法满足业务需求时,才考虑使用全局索引,并充分评估其对写入性能的影响。建议 DBA 和开发人员在设计阶段就充分考虑分区键的选择,尽可能让查询条件包含分区键,从根本上减少对全局索引的依赖[reference:88]。
18.2 本地索引与分区对齐

在 OceanBase 分布式数据库中,本地索引(Local Index) 是与主表分区保持一一对应关系的索引结构,也被称为局部分区索引[reference:0][reference:1]。本地索引的分区键和分区数完全等同于主表,索引分区与主表分区共享相同的分区规则[reference:2][reference:3]。这种“分区对齐”的设计使得本地索引在写入性能维护成本上具有显著优势,是 OceanBase 官方推荐的索引首选[reference:4][reference:5]。本章将深入剖析 OceanBase 4.x 中本地索引的核心概念、分区对齐机制、唯一性约束、分区裁剪能力、前缀索引 vs 非前缀索引、以及设计原则与最佳实践

🎯 本章内容概览
  • 本地索引核心概念:本地索引的定义、架构原理与“分区对齐”的本质。
  • 分区对齐机制:索引分区与主表分区的一一对应关系及其优势。
  • 本地唯一索引约束:分区内唯一 vs 全局唯一的区别与设计要点。
  • 分区裁剪能力:本地索引的分区裁剪原理与使用前提。
  • 前缀索引 vs 非前缀索引:两种本地索引类型的适用场景与性能差异。
  • 本地索引 vs 全局索引:核心差异对比与选型决策矩阵。
  • 生产环境最佳实践 Checklist:本地索引设计与使用的完整指南。

一、本地索引核心概念

1.1 什么是本地索引

本地索引是在创建索引时通过指定 LOCAL 关键字创建的索引[reference:6]。与全局索引不同,本地索引不要求指定独立的分区规则,而是直接继承主表的分区属性——分区键相同、分区数相同、分区机制相同[reference:7][reference:8]。

本地索引是针对单个分区上的数据创建的索引[reference:9][reference:10]。主表的每一个分区都有自己独立的索引数据结构,索引中的键(Key)只映射到自己分区中的主表数据,不会映射到其他分区[reference:11]。因此,本地索引的索引键值跟主表中的数据是一一对应的关系,即局部索引上的一个分区一定对应到一个表分区[reference:12][reference:13]。

📌 本地索引的本质:分区对齐
本地索引的核心特征可以概括为“分区对齐”——索引分区与主表分区保持完全一致的对应关系。这种设计使得本地索引在写入时不需要跨分区协调,避免了分布式事务的开销,是大规模分布式数据库中最优的索引选择[reference:14]。
1.2 本地索引的架构图示

假设 employee 表基于 emp_id 列使用 Range 分区方式划分为 3 个分区,并在 emp_name 列上创建了本地索引。则每个表分区都有自己独立的索引数据结构,索引中的键只映射到同一个分区内的主表数据[reference:15]。

主表分区: 分区0 (emp_id 1-100)  ←→  本地索引分区0
                分区1 (emp_id 101-200) ←→  本地索引分区1
                分区2 (emp_id 201-300) ←→  本地索引分区2

        -- 每个索引分区的键只映射到对应主表分区的数据
        -- 索引分区与主表分区保持一一对应关系

二、分区对齐机制

2.1 分区对齐的含义

本地索引与主表的分区对齐体现在以下三个层面:

  • 分区键对齐:本地索引的分区键等同于主表的分区键[reference:16][reference:17]。
  • 分区数对齐:本地索引的分区数等同于主表的分区数[reference:18][reference:19]。
  • 分区操作联动:本地索引会跟随主表的分区操作(如添加分区、删除分区、合并分区等)自动变更[reference:20][reference:21]。
2.2 分区对齐的优势
优势维度说明 写入性能最优索引数据和主表数据存储在同一个物理位置,写入时无需跨分区 RPC 调用,避免了分布式事务开销[reference:22] 维护成本最低分区操作(如 TRUNCATE、DROP、ADD 分区)对本地索引的影响与主表同步,无需额外维护 分区裁剪友好当查询条件包含分区键时,可以精准裁剪到目标索引分区,大幅减少扫描数据量[reference:23] 高可用性保障本地索引与主表数据在同一分区内,分区级别的故障隔离不会影响其他分区
⚠️ 分区对齐的代价
分区对齐也意味着本地索引的灵活性受限——无法像全局索引那样独立定义分区规则。如果查询条件不包含分区键,本地索引将无法进行分区裁剪,需要扫描所有分区,查询效率会显著下降[reference:24][reference:25]。这是选择本地索引时需要考虑的核心权衡。

三、本地唯一索引的约束

3.1 分区内唯一 vs 全局唯一

本地唯一索引(UNIQUE INDEX ... LOCAL)只能保证分区内部的唯一性,而无法保证表数据的全局唯一性[reference:26][reference:27]。这是因为每个索引分区独立维护自己的唯一性约束,跨分区的相同索引键值不会被检测到。

-- 假设表 t1 按 user_id 进行 HASH 分区
        CREATE TABLE t1(user_id INT, order_id INT, PRIMARY KEY(user_id, order_id))
            PARTITION BY HASH(user_id) PARTITIONS 4;

        -- 在 order_id 上创建本地唯一索引
        CREATE UNIQUE INDEX idx_order_id ON t1(order_id) LOCAL;

        -- 问题:不同分区中可能同时存在 order_id = 100 的记录
        -- 本地唯一索引无法检测到跨分区的重复值
3.2 如何使用本地唯一索引做全局唯一约束

如果必须使用本地唯一索引来对数据唯一性做约束,则本地唯一索引中必须包含表分区键[reference:28][reference:29]。当索引键中包含分区键时,相同的索引键值必然落入同一个分区,此时分区内的唯一性等价于全局唯一性。

-- 正确:本地唯一索引包含分区键 user_id
        CREATE UNIQUE INDEX idx_order_id ON t1(order_id, user_id) LOCAL;

        -- 此时 order_id + user_id 的组合在全局是唯一的
        -- 因为相同的 order_id 和 user_id 必然在同一个分区中
📌 设计要点
如果需要全局唯一性且索引键不包含分区键,则必须使用全局唯一索引UNIQUE INDEX ... GLOBAL)。全局唯一索引没有“必须包含分区键”的限制,但会带来写入性能代价[reference:30]。

四、分区裁剪能力

4.1 分区裁剪的原理

在 OceanBase 数据库中,局部索引同样支持分区裁剪。使用分区裁剪的前提条件查询条件中必须包含分区键[reference:31][reference:32][reference:33]。当满足此条件时,优化器可以根据分区键的值精准定位到目标索引分区,大幅减少扫描的分区数量,提升查询效率[reference:34][reference:35]。

4.2 分区裁剪示例

场景一:查询条件包含分区键(可裁剪)

-- 假设表 t1 按 a 列进行分区,并在 b 列上创建了本地索引 idx
        -- 查询条件中同时包含索引键 b 和分区键 a
        EXPLAIN SELECT /*+index(t1 idx)*/ b FROM t1 WHERE b=1 AND a=1\G

        *************************** 1. row ***************************
        Query Plan:
        =====================================
        |ID|OPERATOR |NAME |EST. ROWS|COST|
        |0 |TABLE GET|t1(idx)|1 |52 |
        =====================================
        Outputs & filters:
        0 - output([t1.b]), filter(nil), access([t1.b]), partitions(p1)
        -- partitions(p1) 表示只扫描了分区 p1,分区裁剪生效[reference:36][reference:37]

场景二:查询条件不包含分区键(无法裁剪)

-- 查询条件中只有索引键 b,没有分区键 a
        EXPLAIN SELECT /*+index(t1 idx)*/ b FROM t1 WHERE b=1\G

        *************************** 1. row ***************************
        Query Plan:
        ====================================================
        |ID|OPERATOR |NAME |EST. ROWS|COST|
        |0 |EXCHANGE IN DISTR | |4950 |3551|
        |1 | EXCHANGE OUT DISTR |:EX10000|4950 |3083|
        |2 | PX PARTITION ITERATOR| |4950 |3083|
        |3 | TABLE SCAN |t1(idx)|4950 |3083|
        ====================================================
        -- 扫描了所有分区,分区裁剪未生效[reference:38][reference:39]
⚠️ 分区裁剪的关键结论
  • 查询条件包含分区键 → 本地索引可进行分区裁剪 → 查询效率高 ✅
  • 查询条件不包含分区键 → 本地索引无法进行分区裁剪 → 扫描所有分区 ❌
因此,在设计分区表时,应确保大多数查询条件能够包含分区键,以充分利用本地索引的分区裁剪能力。

五、前缀索引 vs 非前缀索引

本地索引根据索引列与分区键的关系,分为本地前缀索引本地非前缀索引两种类型[reference:40]。

5.1 本地前缀索引(Local Prefixed Index)

分区键是索引列的最左前缀时,该本地索引称为本地前缀索引[reference:41]。例如,表 A 按 c1 列分区,索引为 idx(c1, c2, c3),则 c1 是索引列的最左前缀,该索引为本地前缀索引[reference:42]。

  • 特点:查询时可以根据分区键值唯一确定目标索引分区,分区裁剪效率最高[reference:43]。
  • 适用场景:结果集较小的点查或范围查询,需要精准分区裁剪的场景[reference:44]。
  • 唯一性支持:本地前缀索引可以是唯一索引或非唯一索引[reference:45]。
5.2 本地非前缀索引(Local Non-Prefixed Index)

分区键不是索引列的最左前缀时,该本地索引称为本地非前缀索引[reference:46]。例如,表 A 按 c4 列分区,索引为 idx(c1, c2, c3),则分区键 c4 不在索引列中,该索引为本地非前缀索引[reference:47]。

  • 特点:查询时无法通过索引键唯一定位目标索引分区,需要扫描所有索引分区[reference:48]。
  • 适用场景:需要并行执行访问大量数据的场景[reference:49]。
  • 唯一性限制:如果分区键列不是索引列的子集,本地非前缀索引不能是唯一索引[reference:50]。
对比维度本地前缀索引本地非前缀索引 分区键位置索引列的最左前缀不在索引列的最左前缀 分区裁剪✅ 可精准定位索引分区❌ 需扫描所有索引分区 适用场景小结果集的点查/范围查询大数据量的并行扫描 唯一索引支持✅ 支持❌ 分区键不在索引列子集中时不支持
📌 设计建议
在设计本地索引时,应优先考虑创建本地前缀索引,以确保查询能够利用分区裁剪能力。本地非前缀索引仅在需要并行扫描大量数据时才考虑使用。

六、本地索引 vs 全局索引

6.1 核心差异对比

理解本地索引与全局索引的核心差异,是做出正确索引选型决策的基础。

对比维度本地索引(Local Index)全局索引(Global Index) 分区规则继承主表分区规则,无需指定[reference:51]独立于主表,可自定义[reference:52] 数据映射关系一对一(每个索引分区对应一个主表分区)[reference:53]一对多或多对多[reference:54] 物理存储位置与主表分区存储在同一位置可能与主表不同[reference:55] 全局唯一性不支持(仅保证分区内唯一)[reference:56]支持[reference:57] 写入性能优(无跨分区分布式事务)[reference:58]差(可能涉及跨分区分布式事务) 分区裁剪支持(需查询条件包含分区键)[reference:59]支持(基于索引分区键) 默认创建行为MySQL 模式默认[reference:60]Oracle 模式默认[reference:61]
6.2 选型决策矩阵
场景特征推荐索引类型理由 查询条件包含分区键✅ 本地索引可充分利用分区裁剪,写入性能最优[reference:62] 查询条件不包含分区键,且需要全局唯一性✅ 全局索引本地索引无法保证跨分区唯一性 查询条件不包含分区键,但写入性能敏感⚠️ 全局索引需谨慎全局索引的写入代价可能不可接受 对性能非常敏感✅ 优先本地索引OceanBase 官方推荐:尽可能使用本地索引[reference:63]
⚠️ 官方推荐
OceanBase 官方明确建议:尽可能使用本地索引,仅在必要时使用全局索引[reference:64]。当业务的查询可以拥有分区键的条件谓词时,依旧推荐构建局部索引,通过优化器的分区裁剪功能排除不符合条件的分区,这种做法可以同时兼顾查询和写入的性能[reference:65]。

七、生产环境最佳实践 Checklist

✅ 本地索引设计检查清单
  • 优先使用本地索引:除非有强制的全局唯一性需求或查询无法包含分区键,否则默认选择本地索引[reference:66]。
  • 确保查询条件包含分区键:设计分区键时,应确保大多数业务查询能够包含分区键,以充分利用本地索引的分区裁剪能力[reference:67]。
  • 本地唯一索引必须包含分区键:如果需要用本地唯一索引做全局唯一约束,索引键必须包含分区键[reference:68][reference:69]。
  • 优先创建本地前缀索引:确保分区键是索引列的最左前缀,以获得最优的分区裁剪效果[reference:70]。
  • 避免本地非前缀索引:除非需要并行扫描大量数据,否则避免使用无法精准定位分区的本地非前缀索引[reference:71]。
  • 通过 EXPLAIN 验证分区裁剪:创建索引后,使用 EXPLAIN 验证查询是否能够进行分区裁剪[reference:72]。
  • 监控索引使用效果:通过 GV$OB_PLAN_CACHE_PLAN_STAT 等视图监控本地索引的使用频率和效果。
  • 评估全局索引的替代方案:在考虑全局索引前,先评估能否通过调整分区键设计,让更多查询包含分区键,从而使用本地索引替代[reference:73]。
🏆 小结
OceanBase 4.x 的本地索引通过分区对齐机制,实现了索引分区与主表分区的一一对应关系,在写入性能维护成本上具有显著优势。本地索引支持分区裁剪,但前提是查询条件必须包含分区键;本地唯一索引仅保证分区内唯一,如需全局唯一则索引键必须包含分区键。本地索引进一步分为本地前缀索引本地非前缀索引,前者可精准定位索引分区,后者需扫描所有分区。OceanBase 官方推荐的核心原则是:尽可能使用本地索引,仅在必要时使用全局索引[reference:74][reference:75]。建议 DBA 和开发人员在设计阶段就充分考虑分区键的选择,确保大多数查询能够包含分区键,从而充分发挥本地索引的性能优势。
18.3 索引合并与覆盖索引

在 OceanBase 分布式数据库中,索引合并(Index Merge)覆盖索引(Covering Index) 是两种重要的索引访问优化技术。索引合并允许优化器在单表查询中结合使用多个索引,通过合并多个索引扫描的结果来减少数据访问量;覆盖索引则通过将查询所需的所有列包含在索引中,避免回表访问主表,从而大幅提升查询性能[reference:0][reference:1]。本章将深入剖析 OceanBase 4.x 中索引合并与覆盖索引的实现原理、使用场景、设计原则及最佳实践

🎯 本章内容概览
  • 索引合并原理:索引合并的定义、工作原理与使用场景。
  • 索引合并的限制:不支持全局非前缀索引等使用限制。
  • 覆盖索引原理:覆盖索引的定义、核心优势与工作原理。
  • 覆盖索引适用场景:高 QPS 查询、高 I/O 压力、索引覆盖查询等。
  • 索引设计原则:覆盖索引的列选择、列数控制与维护成本权衡。
  • 生产环境最佳实践 Checklist:索引合并与覆盖索引的完整使用指南。

一、索引合并(Index Merge)

1.1 什么是索引合并

索引合并是指 OceanBase 优化器在单表查询中,利用多个索引来访问数据,并将这些索引的扫描结果合并(取交集、并集或进行其他集合运算),以减少最终需要访问的数据量[reference:2]。索引合并是优化器基于代价自动选择的访问路径之一,旨在处理单一索引无法高效过滤数据的复杂查询场景[reference:3]。

📌 索引合并的核心价值
当查询的 WHERE 条件涉及多个列,但没有任何一个单独的索引能够对所有条件进行有效过滤时,索引合并可以通过组合多个索引的过滤能力,大幅减少扫描的数据量和回表次数,从而提升查询性能[reference:4]。
1.2 索引合并的工作方式

OceanBase 索引合并通过以下步骤完成查询:

  1. 多索引扫描:优化器选择多个可用的索引,分别对每个索引执行扫描,获取各自满足条件的主键(RowID)集合。
  2. 结果合并:将多个索引扫描得到的主键集合进行合并运算(如交集、并集等),得到最终需要访问的主键列表。
  3. 回表访问:根据合并后的主键列表,回表访问主表获取完整的数据行。
1.3 索引合并的使用场景
  • 多条件 OR 查询:当查询条件包含多个 OR 子句,且每个子句都能利用不同的索引时[reference:5]。
  • 复合条件查询:WHERE 条件包含多个列的组合过滤,但单一索引无法覆盖所有条件。
  • JSON 多值索引场景:V4.5.0+ 版本中,通过将 JSON 多值索引接入 Index Merge 框架,可显著提升 JSON 列上 member ofjson_overlapsjson_contains 三种表达式过滤的性能[reference:6]。
  • 全文索引与向量索引混合查询:V4.5.0+ 版本支持全文索引与向量索引的 Index Merge 组合查询[reference:7]。
1.4 索引合并的控制

OceanBase 优化器会基于代价模型自动判断是否使用索引合并[reference:8]。用户也可以通过 INDEX Hint 影响优化器的选择[reference:9]:

-- 指定可用索引列表,优化器从中选择代价最低的访问路径
        -- 如果扫描多个索引并合并的访问路径成本最低,优化器将选用此方案
        SELECT /*+ INDEX(t1 idx1 idx2) */ * FROM t1 WHERE c1 = 1 OR c2 = 2;
1.5 索引合并的限制
  • 全局非前缀索引不支持:OceanBase 数据库不支持全局非前缀索引,因此全局非前缀索引对于索引合并没有意义[reference:10]。
  • 索引合并的代价评估:优化器需要权衡多个索引扫描、结果合并和回表的总体代价,并非所有场景下索引合并都是最优选择。

二、覆盖索引(Covering Index)

2.1 什么是覆盖索引

覆盖索引(Covering Index) 是指索引中包含了查询所需的所有列,使得数据库引擎仅通过扫描索引即可获取全部查询结果,而无需回表访问主表数据[reference:11][reference:12]。覆盖索引是提升查询性能最有效的手段之一。

⚡ 覆盖索引的核心优势
对于二级索引,如果所需的投影列没有包括在索引列之中,则需要使用回表机制访问主表,查询的代价会大幅增加[reference:13]。覆盖索引通过将投影列加入到索引列中,完全避免了回表操作,可显著降低查询的 I/O 开销和响应时间[reference:14]。
2.2 覆盖索引的工作原理

OceanBase 表为聚簇索引表(Clustered Index Table)[reference:15],主键索引即数据本身。在非覆盖索引的查询中,执行流程为:

  1. 扫描二级索引,获取满足条件的主键值。
  2. 根据主键值回表访问主表(聚簇索引),获取所需的投影列数据。

而使用覆盖索引时,执行流程简化为:

  1. 扫描二级索引,索引中已包含查询所需的所有列。
  2. 直接从索引中返回结果,无需回表访问主表[reference:16]。
2.3 覆盖索引的适用场景

根据 OceanBase 官方文档和最佳实践,以下场景强烈推荐使用覆盖索引[reference:17][reference:18]:

  • 高 QPS 查询:高频执行的查询,覆盖索引可显著降低每次查询的 I/O 开销[reference:19]。
  • 高 I/O 压力场景:数据库 I/O 压力较大时,覆盖索引可减少回表带来的额外 I/O[reference:20]。
  • 索引覆盖查询:查询所需字段不多,且均可纳入索引中[reference:21]。
  • 点查询与范围查询:指定主键的单行读取或指定索引范围的小范围扫描[reference:22]。
  • 列存表点查询:对列存表某一列或几列建索引构造覆盖索引,可提升点查询性能[reference:23]。
2.4 覆盖索引的创建示例
-- 原始查询:需要 user_id、username、email 三个字段
        SELECT user_id, username, email FROM users WHERE user_id = 100;

        -- 方案一:普通索引(需要回表)
        CREATE INDEX idx_user_id ON users(user_id);
        -- 执行流程:扫描 idx_user_id 获取主键 → 回表获取 username、email

        -- 方案二:覆盖索引(无需回表)
        CREATE INDEX idx_user_covering ON users(user_id, username, email);
        -- 执行流程:扫描 idx_user_covering,索引中已包含所有所需字段,直接返回结果
2.5 覆盖索引在 OR 条件中的特殊应用

当查询条件包含 OR 且涉及多个索引列时,结合覆盖索引可以取得更好的优化效果:

-- 查询:通过 user_id 或 email 查找用户,返回 user_id、username、email
        SELECT user_id, username, email FROM users WHERE user_id = 100 OR email = 'test@example.com';

        -- 覆盖索引方案:创建包含所有查询列的组合索引
        CREATE INDEX idx_user_covering ON users(user_id, email, username);
📌 设计要点
在上述 OR 条件场景中,覆盖索引需要包含 OR 条件中的所有列以及 SELECT 列表中的所有列,才能保证无论优化器选择哪种访问路径,都能实现索引覆盖。

三、索引设计原则与权衡

3.1 覆盖索引的设计原则
  • 索引列顺序:将过滤性最强的列(WHERE 条件中的列)放在索引的最左侧,遵循最左前缀原则[reference:24]。
  • 包含投影列:将 SELECT 列表中所有列包含在索引中,实现索引覆盖[reference:25]。
  • 控制索引列数:联合索引的列数量不宜过多,一般不超过 5 个[reference:26]。
  • 区分度优先:优先选择区分度高的列作为索引的前缀列[reference:27]。
3.2 覆盖索引的代价与权衡
维度优势代价 查询性能避免回表,减少 I/O,查询响应时间显著降低[reference:28]— 存储空间—索引占用更多存储空间[reference:29] 写入性能—每次 DML 操作需要维护更多索引列,写入性能下降[reference:30][reference:31] 索引维护—索引列越多,维护成本越高
⚠️ 核心权衡
覆盖索引通过“空间换时间”的策略提升查询性能,但会增加存储空间占用和写入维护成本。建议在高频查询、对响应时间敏感的场景下使用覆盖索引;对于频繁更新的表,应避免创建过多索引[reference:32]。

四、索引使用监控

OceanBase 提供了多个系统视图,用于监控索引的使用情况和执行计划的统计信息。

4.1 执行计划统计视图
-- 查看执行计划的统计信息,包括索引使用情况
        SELECT
            plan_id,
            sql_id,
            statement,
            executions,
            avg_exe_usec,
            outline_id
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
        ORDER BY executions DESC
        LIMIT 20;

GV$OB_PLAN_CACHE_PLAN_STAT 视图记录了每个缓存执行计划的具体信息和执行统计,每个缓存的计划在该视图中有一条记录[reference:33]。

4.2 SQL 审计视图
-- 查看 SQL 的执行详情,判断是否存在大量回表操作
        SELECT
            sql_id,
            query_sql,
            elapsed_time,
            execute_time,
            get_plan_time
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE elapsed_time > 1000000  -- 超过 1 秒的查询
        ORDER BY elapsed_time DESC
        LIMIT 20;

通过 GV$OB_SQL_AUDIT 可以查看 SQL 的排队时间、执行时间等详细信息[reference:34]。

4.3 执行计划详情视图
-- 查询指定执行计划的树形结构,确认是否使用了索引合并或覆盖索引
        SELECT
            plan_id,
            id,
            parent_id,
            operator,
            name,
            rows,
            cost
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_EXPLAIN
        WHERE plan_id = 12345
        ORDER BY id;

GV$OB_PLAN_CACHE_PLAN_EXPLAIN 视图用于查询计划缓存中执行计划的树形结构[reference:35]。

4.4 索引使用效果评估建议
  • 定期分析索引使用率:通过 GV$OB_PLAN_CACHE_PLAN_STAT 识别高频使用的索引和未被使用的冗余索引[reference:36]。
  • 监控回表操作:通过执行计划中的 TABLE ACCESS BY INDEX PRIMARY KEY 操作识别是否存在大量回表[reference:37]。
  • 动态调整索引策略:根据业务需求增减索引[reference:38]。

五、生产环境最佳实践 Checklist

✅ 索引合并与覆盖索引设计检查清单
  • 优先考虑覆盖索引:对于高频查询,将 SELECT 列表中的列纳入索引,避免回表操作。
  • 控制索引列数量:联合索引列数一般不超过 5 个,避免过度消耗存储和影响写入性能。
  • 评估索引合并的必要性:确认是否存在单一索引无法有效过滤的复杂查询条件。
  • 验证执行计划:通过 EXPLAIN 验证查询是否使用了覆盖索引或索引合并。
  • 监控索引使用效果:通过 GV$OB_PLAN_CACHE_PLAN_STAT 等视图监控索引的实际使用情况。
  • 避免在频繁更新的表上创建过多索引:索引维护成本会显著影响写入性能[reference:39]。
  • 数据量小的表慎用索引:数据量较小时,全表扫描可能比索引扫描更高效[reference:40]。
  • 定期清理冗余索引:通过监控视图识别未被使用的索引,及时删除以释放存储空间。
🏆 小结
OceanBase 4.x 的索引合并与覆盖索引是两种重要的索引访问优化技术。索引合并通过组合多个索引的过滤能力,处理单一索引无法高效过滤的复杂查询;覆盖索引通过将查询所需的所有列包含在索引中,彻底避免回表操作,是提升查询性能最有效的手段之一[reference:41]。在设计索引时,应优先考虑高频查询的覆盖索引需求,同时权衡存储空间和写入性能的代价[reference:42]。建议 DBA 和开发人员通过 EXPLAIN 验证执行计划,利用 GV$OB_PLAN_CACHE_PLAN_STAT 等视图持续监控索引使用效果,定期清理冗余索引,实现查询性能与资源消耗的最佳平衡。
18.4 索引监控与维护

在 OceanBase 分布式数据库中,索引监控与维护是保障查询性能和系统稳定性的关键环节。随着业务数据的持续增长和查询模式的变化,索引的使用效果和维护状态会动态变化。OceanBase 4.x 提供了从 索引使用监控统计信息管理索引有效性检查 的完整工具链,帮助 DBA 和开发人员及时发现无效索引、优化索引设计、确保索引统计信息的准确性。本章将深入剖析 OceanBase 4.x 中索引监控与维护的核心视图、配置参数、维护操作及最佳实践

🎯 本章内容概览
  • 索引使用监控DBA_INDEX_USAGE 视图详解与配置参数。
  • 执行计划与索引统计GV$OB_PLAN_CACHE_PLAN_STAT 等视图的索引分析用法。
  • 索引统计信息管理:统计信息的收集、查看与更新策略。
  • 索引碎片与合并:LSM-Tree 架构下的索引碎片管理机制。
  • 索引有效性检查:识别无效索引与异常索引状态。
  • 生产环境最佳实践 Checklist:索引监控与维护的完整指南。

一、索引使用监控:DBA_INDEX_USAGE

1.1 视图概述

OceanBase 数据库从 V4.3.0 版本开始提供索引监控功能[reference:0][reference:1]。通过视图 DBA_INDEX_USAGE 可以查询索引的使用情况,从而更清晰地了解哪些索引被频繁使用、哪些索引长期未被访问[reference:2][reference:3]。

📌 核心特性
  • 索引监控功能默认处于启动状态,无需用户进行任何额外配置或操作即可使用[reference:4][reference:5]。
  • 视图 DBA_INDEX_USAGE 仅展示用户租户的索引统计数据,非用户租户的索引使用不在统计范围[reference:6][reference:7]。
  • 当索引表被删除后,OceanBase 数据库会在后台定期清理相应的监控数据记录,无需手动删除[reference:8][reference:9]。
  • 当前版本重点关注识别和排除用户租户中的无效索引,监控数据的统计仅限于用户租户[reference:10]。
  • OceanBase 数据库可能最多需要 15 分钟通过后台定时任务将收集的索引监控统计信息刷新到内部表[reference:11]。
1.2 视图字段说明

DBA_INDEX_USAGE 视图的字段如下[reference:12]:

字段名类型说明 OBJECT_IDbigint(20)索引表的 ID NAMEvarchar(128)索引名称 OWNERvarchar(128)数据库(Schema)名称 TOTAL_ACCESS_COUNTbigint(20)索引总访问次数 TOTAL_EXEC_COUNTbigint(20)索引总执行次数 TOTAL_ROWS_RETURNEDbigint(20)基于索引表返回的总行数 BUCKET_0_ACCESS_COUNTbigint(20)索引表未被访问的次数 BUCKET_1_ACCESS_COUNTbigint(20)索引表被访问 1 次的次数 BUCKET_2_10_ACCESS_COUNTbigint(20)索引表被访问 2~10 次的次数 BUCKET_11_100_ACCESS_COUNTbigint(20)索引表被访问 11~100 次的次数 BUCKET_101_1000_ACCESS_COUNTbigint(20)索引表被访问 101~1000 次的次数 BUCKET_1000_PLUS_ACCESS_COUNTbigint(20)索引表被访问超过 1000 次的次数 LAST_USEDvarchar(128)索引表最后一次被访问的时间
1.3 查询示例
-- 查询所有索引的使用情况
        SELECT * FROM oceanbase.DBA_INDEX_USAGE;

        -- 查询从未被访问的索引(可能存在无效索引)
        SELECT OWNER, NAME, OBJECT_ID, TOTAL_ACCESS_COUNT, LAST_USED
        FROM oceanbase.DBA_INDEX_USAGE
        WHERE TOTAL_ACCESS_COUNT = 0
        ORDER BY OWNER, NAME;

        -- 查询访问频率最高的索引
        SELECT OWNER, NAME, TOTAL_ACCESS_COUNT, TOTAL_ROWS_RETURNED, LAST_USED
        FROM oceanbase.DBA_INDEX_USAGE
        ORDER BY TOTAL_ACCESS_COUNT DESC
        LIMIT 20;
1.4 索引监控配置参数

OceanBase 提供了以下租户级配置项用于控制索引监控行为,不同租户之间的配置和数据汇总是相互独立的[reference:13][reference:14]。

配置项默认值说明 _iut_enable true 控制索引监控是否开启。默认值为 true,表示打开监控;false 表示关闭监控[reference:15][reference:16]。 _iut_max_entries 30000 控制监控的索引表数量上限,取值范围 [0, +∞)[reference:17][reference:18]。
注意:该配置项设置过大会造成租户内存消耗较多,不建议设置超过 30000[reference:19]。
当租户下的索引表数量超过该值时,后建的索引表可能无法被监控到,从而导致误判[reference:20]。 _iut_stat_collection_type SAMPLED 控制索引监控信息的统计模式[reference:21][reference:22]:
SAMPLED(默认):采样模式,采样率为 10%(基于索引表被引用的次数,而非索引表的数量)[reference:23][reference:24]。
ALL:全量监控模式,结果准确但资源开销较大,谨慎开启[reference:25][reference:26]。
1.5 采样模式与全量模式的权衡
⚠️ 采样模式 vs 全量模式
  • 默认采样模式(SAMPLED):对性能影响非常小,可以认为是可以忽略的[reference:27]。但数据的准确性并非绝对,可能会出现个别查询操作没有被记录的情况[reference:28]。视图 DBA_INDEX_USAGE 可能出现查询结果为空的情况[reference:29]。
  • 全量模式(ALL):虽然可以得到准确的结果,但是会有资源开销,可能导致索引查询性能的一定程度下降[reference:30][reference:31]。
  • 设置建议:日常监控使用默认的 SAMPLED 模式;仅在需要精确分析索引使用情况时,临时切换到 ALL 模式,分析完成后切回 SAMPLED 模式。
-- 查看当前索引监控配置
        SELECT name, data_type, value 
        FROM oceanbase.GV$OB_PARAMETERS 
        WHERE name LIKE '%iut%';

        -- 临时切换到全量模式(谨慎使用)
        ALTER SYSTEM SET _iut_stat_collection_type = "ALL";

        -- 切回采样模式
        ALTER SYSTEM SET _iut_stat_collection_type = "SAMPLED";

        -- 关闭索引监控(不推荐)
        ALTER SYSTEM SET _iut_enable = false;

二、执行计划与索引统计

2.1 计划缓存视图中的索引信息

OceanBase 提供了多个计划缓存相关视图,可用于分析索引的实际使用效果[reference:32]。

GV$OB_PLAN_CACHE_PLAN_STAT:展示当前租户在所有 OBServer 节点上的计划缓存中缓存的每一个缓存对象的状态[reference:33][reference:34]。该视图不仅缓存了 SQL 计划对象,也缓存了 PL 对象[reference:35][reference:36]。

-- 查询执行计划统计信息,分析索引使用情况
        SELECT 
            SQL_ID,
            PLAN_ID,
            STATEMENT,
            EXECUTIONS,
            AVG_EXE_USEC,
            HIT_COUNT,
            FIRST_LOAD_TIME,
            LAST_ACTIVE_TIME
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
        WHERE EXECUTIONS > 100
        ORDER BY AVG_EXE_USEC DESC
        LIMIT 20;

关键字段说明[reference:37]:

  • PLAN_ID:缓存对象的 ID。
  • SQL_ID:缓存对象对应的 SQL ID。
  • STATEMENT:参数化后的 SQL 语句。
  • EXECUTIONS:执行次数。
  • AVG_EXE_USEC:平均执行时间(微秒)。
  • HIT_COUNT:被命中次数。
  • FIRST_LOAD_TIME:第一次被加载时间。
  • LAST_ACTIVE_TIME:上一次被执行时间。

GV$OB_PLAN_CACHE_PLAN_EXPLAIN:展示计划缓存中执行计划的树形结构[reference:38][reference:39]。通过该视图可以查看执行计划中是否使用了索引、使用了哪个索引。

-- 查询指定执行计划的树形结构
        SELECT 
            plan_id,
            id,
            parent_id,
            operator,
            name,
            rows,
            cost
        FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_EXPLAIN
        WHERE plan_id = 12345
        ORDER BY id;
2.2 SQL 审计视图

GV$OB_SQL_AUDIT 视图记录了 SQL 的详细执行信息,可用于分析慢查询是否与索引选择不当有关[reference:40]。

-- 查询慢查询,分析是否因索引问题导致
        SELECT 
            sql_id,
            query_sql,
            elapsed_time,
            execute_time,
            get_plan_time,
            ret_code
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE elapsed_time > 1000000  -- 超过 1 秒
        ORDER BY elapsed_time DESC
        LIMIT 20;

三、索引统计信息管理

3.1 统计信息的作用

在 OceanBase 数据库优化器中,统计信息以普通数据的形式存储在内部表中,并在本地维护一个统计信息缓存,以提高优化器对统计信息的访问速度[reference:41]。统计信息主要分为四大类:表统计信息、列统计信息、索引统计信息和物化视图刷新统计信息[reference:42]。

索引统计信息是优化器选择执行计划的重要依据。当索引统计信息过期或不准确时,优化器可能选择次优的执行计划,导致查询性能下降。

3.2 查看索引统计信息

OceanBase 提供了多个视图用于查看索引的优化器统计信息。

-- Oracle 模式:查看当前用户拥有的表的索引统计信息
        SELECT * FROM USER_IND_STATISTICS WHERE INDEX_NAME = 'your_index_name';

        -- 查看当前租户下所有索引的优化器统计信息(V4.0.0+)
        SELECT * FROM oceanbase.DBA_IND_STATISTICS WHERE INDEX_NAME = 'your_index_name';

        -- 查看索引统计信息是否过期
        SELECT table_name, last_analyzed 
        FROM DBA_TAB_STATISTICS 
        WHERE stale_stats = 'YES' AND owner = 'your_tenant';
3.3 收集索引统计信息

OceanBase 提供了 DBMS_STATS 系统包用于管理统计信息[reference:43]。

-- 收集指定索引的统计信息
        CALL dbms_stats.gather_index_stats('schema_name', 'index_name');

        -- 收集表的所有统计信息(包括索引)
        CALL dbms_stats.gather_table_stats('schema_name', 'table_name', method_opt=>'for all columns size 1');

        -- 大表并行收集统计信息
        CALL dbms_stats.gather_table_stats('schema_name', 'table_name', degree=>4);
3.4 统计信息过期识别标准

根据 OceanBase 官方最佳实践,统计信息过期的识别标准如下[reference:44]:

  • 自动收集阈值:分区数据增删改量超过 10% 时标记为过期[reference:45]。
  • 手动验证命令:通过 DBA_TAB_STATISTICS 视图的 stale_stats 字段判断[reference:46]。
  • 执行计划异常:通过 EXPLAIN EXTENDED 对比历史计划,发现索引选择退化(如全表扫描替代索引扫描)[reference:47]。

四、索引碎片与合并机制

4.1 OceanBase 的索引碎片特点

与传统数据库不同,OceanBase 基于 LSM-Tree 存储架构,通过定期合并(Major Compaction)机制重新组织数据[reference:48]。

📌 核心结论
OceanBase 基本不存在索引碎片化问题,因为每晚都会做合并,重新组织数据[reference:49]。对于已建立索引的表,即使每天进行数据插入、更新、删除操作,也不会产生索引碎片化[reference:50]。
4.2 合并对索引的影响
  • 数据重组:合并期间 OceanBase 会对数据进行两层压缩,重新组织数据和索引的物理存储[reference:51]。
  • 空间回收:合并可以整理存储空间,合并空闲空间,释放无效空间[reference:52]。
  • 索引空间查询:OceanBase 无法直接通过内部视图查询某个索引的容量,只能通过手动执行合并后查看索引占用空间的方式查询索引大小[reference:53]。
-- 手动触发合并(Major Freeze)
        ALTER SYSTEM MAJOR FREEZE;

        -- 查看合并进度
        SELECT * FROM oceanbase.GV$OB_MERGE_INFO ORDER BY START_TIME DESC LIMIT 1;

        -- 查看合并后的存储空间使用
        SELECT * FROM oceanbase.GV$OB_SERVER_STORAGE_INFO;

五、索引有效性检查

5.1 识别无效索引

通过 DBA_INDEX_USAGE 视图可以识别长期未被使用的索引:

-- 查询从未被访问的索引(可能为无效索引)
        SELECT OWNER, NAME, OBJECT_ID, TOTAL_ACCESS_COUNT, LAST_USED
        FROM oceanbase.DBA_INDEX_USAGE
        WHERE TOTAL_ACCESS_COUNT = 0
        ORDER BY OWNER, NAME;

        -- 结合索引创建时间判断:创建后长期未被使用的索引
        -- 注意:DBA_INDEX_USAGE 的 LAST_USED 字段记录最后访问时间
5.2 索引状态异常排查

对于 OceanBase 4.x 版本,可以通过系统表查询索引状态[reference:54]:

-- 查询索引状态(V4.x 版本)
        SELECT 
            t.table_name,
            i.index_name,
            i.status,
            i.last_analyzed
        FROM information_schema.TABLES t
        JOIN information_schema.STATISTICS i ON t.table_name = i.table_name
        WHERE i.index_name IS NOT NULL
        AND i.index_name != 'PRIMARY';
⚠️ 异常索引处理建议
  • 如果索引出错,可通过 DROP INDEX 语句删除索引后,执行 CREATE INDEX 语句重新创建索引[reference:55]。
  • 如果索引状态仍为 UNAVAILABLE,建议联系 OceanBase 技术支持进行分析和排查[reference:56]。
  • V4.2.0 版本开始,OceanBase 支持了统计信息收集监控诊断功能,可以监控统计信息收集的状态并反馈收集情况[reference:57]。
5.3 常见索引失效场景

根据 OceanBase 社区实践经验,以下场景可能导致索引失效[reference:58]:

  • 数据类型不匹配:查询条件中的数据类型与索引列类型不一致。
  • 字符集相关属性不匹配:查询条件与索引列的字符集或排序规则不一致。
  • 过滤/连接条件上包含系统函数:在索引列上使用函数导致索引无法使用。

六、生产环境最佳实践 Checklist

✅ 索引监控与维护检查清单
  • 开启索引监控:确认 _iut_enable = true(默认已开启)。
  • 定期查询索引使用情况:通过 DBA_INDEX_USAGE 识别长期未被使用的索引,评估是否需要删除。
  • 监控索引统计信息时效性:定期检查 DBA_TAB_STATISTICSstale_stats = 'YES' 的记录。
  • 及时收集过期统计信息:对于数据变化超过 10% 的表,通过 DBMS_STATS 重新收集统计信息。
  • 利用执行计划视图分析:通过 GV$OB_PLAN_CACHE_PLAN_STATGV$OB_PLAN_CACHE_PLAN_EXPLAIN 分析索引是否被正确使用。
  • 定期执行合并:通过 ALTER SYSTEM MAJOR FREEZE 触发合并,重组数据和索引,释放存储空间。
  • 删除无效索引:对于确认长期未被使用的索引,使用 DROP INDEX 删除以释放存储空间。
  • 监控索引数量:确保索引总数不超过 _iut_max_entries(默认 30000)的上限。
  • 谨慎使用全量监控模式:仅在需要精确分析时临时切换到 ALL 模式,分析完成后切回 SAMPLED
🏆 小结
OceanBase 4.x 提供了从 索引使用监控(DBA_INDEX_USAGE执行计划分析(GV$OB_PLAN_CACHE_PLAN_STAT 再到 统计信息管理(DBMS_STATS 的完整索引监控与维护工具链。索引监控功能默认开启,通过采样模式(SAMPLED)对性能影响极小;LSM-Tree 架构通过定期合并机制从根本上避免了索引碎片化问题。建议 DBA 和开发人员建立常态化的索引监控机制,定期检查索引使用情况、更新过期统计信息、删除无效索引,确保索引始终处于最佳工作状态,为数据库查询性能提供坚实保障。
18.5 全文索引与空间索引

在 OceanBase 4.x 版本中,除了常规的 B-Tree 索引外,还提供了针对文本检索地理空间数据两类特殊场景的索引能力——全文索引(Fulltext Index)空间索引(Spatial Index)。全文索引通过分词器将大段文本拆分为词元(Token)并建立倒排索引,实现对 CHAR、VARCHAR、TEXT 类型列的高效文本搜索[reference:0];空间索引则基于几何对象的 MBR(最小边界矩形)和 SRID(空间参考标识符),为 GIS 地理数据提供快速的空间查询能力[reference:1]。本章将深入剖析 OceanBase 4.x 中全文索引与空间索引的核心概念、使用限制、创建语法、查询方式及最佳实践

🎯 本章内容概览
  • 全文索引概述:全文索引的定义、工作原理与核心价值。
  • 全文索引使用限制:列类型、唯一性、分区类型等关键限制。
  • 分词器(Parser):SPACE、NGRAM、BENG、IK 等分词器的特性与选型。
  • 全文索引创建与查询:CREATE TABLE / ALTER TABLE / CREATE INDEX 语法与 MATCH AGAINST 查询。
  • 空间索引概述:空间索引的定义、核心概念(SRID、MBR)与工作原理。
  • 空间索引使用限制与创建:NOT NULL 约束、SRID 要求、分区表支持与创建语法。
  • 生产环境最佳实践 Checklist:全文索引与空间索引的完整使用指南。

一、全文索引(Fulltext Index)

1.1 什么是全文索引

全文索引是一种专门用于对文本内容(如 CHAR、VARCHAR、TEXT 类型的列)进行高效、灵活搜索的数据库索引[reference:2]。全文索引通过分词器(Parser)将大段文本拆分成一个个有意义的“词元”(Token),然后基于这些词元建立倒排索引,使得用户可以进行关键词搜索、模糊匹配等复杂的文本查询[reference:3]。

全文索引的核心价值在于:

  • 替代 LIKE 模糊查询:避免 LIKE '%keyword%' 带来的全表扫描和性能暴跌问题[reference:4]。
  • 支持相关性排序:通过 BM25 等算法对搜索结果按相关性排序[reference:5]。
  • 多语言支持:通过不同分词器适配中英文等多语言场景[reference:6]。
📌 全文索引的典型应用场景
系统日志分析、用户行为分析、画像分析、文章/新闻/商品描述检索等场景中,全文索引能够对数据做高效率过滤筛选或高质量相关性评估[reference:7]。在 AI 方面,将稀疏稠密向量和全文结合在一起的多路召回架构,能在具有特殊知识领域的 RAG 系统中取得更高更好的召回效果[reference:8]。
1.2 全文索引的使用限制

OceanBase 4.x 中全文索引的使用有以下关键限制[reference:9][reference:10]:

  • 列类型限制:全文索引仅适用于 CHAR、VARCHAR 和 TEXT 类型的列[reference:11]。
  • 唯一性限制:创建全文索引时不能指定 UNIQUE 关键字[reference:12]。
  • 分区类型限制:当前版本仅支持创建局部(LOCAL)全文索引,不支持全局全文索引[reference:13]。
  • 多列索引字符集限制:如果对多列创建全文索引,这些列必须使用相同的字符集[reference:14]。
  • 分区修改限制:OceanBase 数据库不支持对带有全文索引的分区表进行分区修改操作(如 ADD PARTITION)[reference:15]。
⚠️ 分区修改限制的处理方式
如果表已创建全文索引且需要执行分区修改操作,建议采用以下变通方案[reference:16]:
  1. 首先执行 DROP INDEX 删除现有的全文索引。
  2. 然后执行 ALTER TABLE ... ADD PARTITION 添加所需分区。
  3. 最后重新创建全文索引。
1.3 分词器(Parser)

分词器是全文索引的核心组件,负责将文本内容拆分为有意义的词元。OceanBase 4.x 支持以下分词器[reference:17][reference:18][reference:19]:

分词器说明适用场景 SPACE按空格分词(默认)英文等以空格分隔的语言 NGRAM按 N-gram 算法分词中文等无空格分隔的语言(V4.3.5 BP1 前) NGRAM2NGRAM 的增强版本中文等无空格分隔的语言 BENG基础英文分词器英文文本 IKIK 中文分词器,支持 ik_mode = 'smart' | 'max_word'中文文本(V4.3.5 BP1 开始支持)[reference:20]
📌 分词器选型建议
  • 纯英文场景:使用默认的 SPACE 分词器即可。
  • 中文场景(V4.3.5 BP1+):推荐使用 IK 分词器,支持 smart(智能切分)和 max_word(最细粒度切分)两种模式[reference:21]。
  • 中文场景(V4.3.5 BP1 之前):使用 NGRAM 分词器,可通过 ngram_token_size 属性控制分词粒度。
  • 自定义分词:OceanBase 支持分词器插件,允许自定义分词逻辑以满足特定业务需求。当前分词器插件为实验特性,暂不建议在生产环境使用[reference:22]。
1.4 全文索引的创建语法

OceanBase 支持在 CREATE TABLEALTER TABLECREATE INDEX 语句中创建全文索引[reference:23]。

-- 方式一:建表时创建全文索引(默认 SPACE 分词器)
        CREATE TABLE articles (
            id INT PRIMARY KEY,
            title VARCHAR(200),
            content TEXT,
            FULLTEXT INDEX ft_idx (title, content)
        );

        -- 方式二:建表时指定 IK 中文分词器
        CREATE TABLE articles_cn (
            id INT PRIMARY KEY,
            title VARCHAR(200),
            content TEXT,
            FULLTEXT INDEX ft_idx_cn (title, content) WITH PARSER IK
                PARSER_PROPERTIES = ('ik_mode' = 'smart')
        );

        -- 方式三:通过 ALTER TABLE 添加全文索引
        ALTER TABLE articles ADD FULLTEXT INDEX ft_idx (title, content) LOCAL;

        -- 方式四:通过 CREATE INDEX 创建全文索引
        CREATE FULLTEXT INDEX ft_idx ON articles (title, content) WITH PARSER NGRAM
            PARSER_PROPERTIES = ('ngram_token_size' = 2) LOCAL;
1.5 全文索引的查询语法

全文索引通过 MATCH ... AGAINST 语法进行查询,支持 自然语言模式布尔模式两种搜索方式[reference:24]。

-- 自然语言模式(默认):按相关性返回结果
        SELECT id, title, MATCH(title, content) AGAINST('数据库') AS relevance
        FROM articles
        WHERE MATCH(title, content) AGAINST('数据库' IN NATURAL LANGUAGE MODE);

        -- 布尔模式:精确控制关键词的必须存在/禁止出现
        SELECT id, title
        FROM articles
        WHERE MATCH(title, content) AGAINST('+computer -oceanbase' IN BOOLEAN MODE);

        -- 查询扩展模式:自动扩展相关同义词
        SELECT id, title
        FROM articles
        WHERE MATCH(title, content) AGAINST('数据库' WITH QUERY EXPANSION);
📌 查询模式说明
  • 自然语言模式(NATURAL LANGUAGE MODE):最常用的全文检索方式,根据文本的相关性返回结果[reference:25]。
  • 布尔模式(BOOLEAN MODE):允许更精确地控制哪些词必须存在、哪些不应该出现等[reference:26]。
  • 查询扩展(WITH QUERY EXPANSION):在自然语言模式基础上,自动进行扩展查询相关同义词(如 db、database 等),进行二次搜索[reference:27]。
  • MATCH ... AGAINST 过滤语义:当 MATCH ... AGAINST 表达式用于 WHERE 子句时,OceanBase 会扫描全文索引计算结果,然后回表执行查询[reference:28]。
1.6 全文索引的性能与增强

OceanBase 4.3.5 版本对全文索引进行了多项性能增强[reference:29]:

  • TAAT/DAAT 流程优化:针对全文索引查询流程做了针对性优化[reference:30]。
  • Functional Lookup:对标 Oracle 的功能,提升查询效率[reference:31]。
  • Index Merge 支持:全文索引可与多索引间进行 Index Merge,结合更多复杂的查询特性完成数据检索[reference:32]。
  • 存储效率:OceanBase 的全文索引存储效率高,在相同数据量下占用空间更小[reference:33]。

二、空间索引(Spatial Index)

2.1 什么是空间索引

空间索引是一种专门用于地理空间数据(GIS)查询的索引类型。OceanBase 数据库支持创建常规索引的语法创建空间索引,但使用 SPATIAL 关键字[reference:34][reference:35]。

空间索引的核心原理是:

  • MBR(Minimum Bounding Rectangle,最小边界矩形):空间索引存储了几何对象的 MBR[reference:36],查询时通过比较 MBR 快速过滤不相关的几何对象[reference:37]。
  • SRID(Spatial Reference Identifier,空间参考标识符):MBR 的比较依赖于 SRID[reference:38]。创建了空间索引的列必须定义 SRID,否则空间索引在查询时无法生效[reference:39]。
  • 实现方式:OceanBase 的空间索引底层使用四叉树(Quad-Tree)实现,最底层与其他索引一样基于 B-Tree[reference:40]。
📌 空间索引的典型应用场景
地理位置查询(如“查找附近 10 公里内的门店”)、地图服务、路径规划、地理围栏等 GIS 相关业务场景。
2.2 空间索引的使用限制

OceanBase 4.x 中空间索引的使用有以下关键限制[reference:41][reference:42][reference:43]:

  • NOT NULL 约束:创建空间索引的列定义中必须包含 NOT NULL 约束[reference:44]。
  • SRID 要求:创建了空间索引的列必须定义 SRID,否则空间索引在查询时无法生效[reference:45]。
  • 生成列限制:空间索引可以创建在STORED 生成列上,但不能创建在 VIRTUAL 生成列上[reference:46]。
  • 分区表限制:分区表创建空间索引,目前仅支持创建局部分区索引(必须显式指定 LOCAL 关键字),不支持全局分区索引[reference:47]。
  • 兼容性说明:OceanBase 创建空间索引的语法与 Oracle 不同——OceanBase 使用 MySQL 风格兼容 Oracle 模式,不维护 USER_SDO_GEOM_METADATA 表[reference:48]。
2.3 空间索引的创建语法

OceanBase 支持在 CREATE TABLEALTER TABLECREATE INDEX 语句中创建空间索引[reference:49]。

-- 方式一:建表时创建空间索引
        CREATE TABLE geom (
            g GEOMETRY NOT NULL SRID 4326,
            SPATIAL INDEX(g)
        );

        -- 方式二:通过 ALTER TABLE 添加空间索引
        CREATE TABLE geom (g GEOMETRY NOT NULL SRID 4326);
        ALTER TABLE geom ADD SPATIAL INDEX(g);

        -- 方式三:通过 CREATE INDEX 创建空间索引
        CREATE TABLE geom (g GEOMETRY NOT NULL SRID 4326);
        CREATE SPATIAL INDEX g ON geom (g);

        -- 方式四:分区表上创建局部空间索引(必须指定 LOCAL)
        CREATE TABLE geom_par (
            id INT PRIMARY KEY,
            g GEOMETRY NOT NULL SRID 4326
        ) PARTITION BY HASH(id) PARTITIONS 4;
        CREATE SPATIAL INDEX g ON geom_par (g) LOCAL;
2.4 空间索引的查询示例

空间索引配合 GIS 函数进行空间查询:

-- 查找包含指定点的几何对象
        SELECT id, name
        FROM geom
        WHERE MBRContains(g, ST_GeomFromText('POINT(1 1)', 4326));

        -- 查找与指定区域相交的几何对象
        SELECT id, name
        FROM geom
        WHERE MBRIntersects(g, ST_GeomFromText('POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))', 4326));

        -- 查找距离指定点 10 单位内的几何对象
        SELECT id, name
        FROM geom
        WHERE ST_Distance(g, ST_GeomFromText('POINT(0 0)', 4326)) < 10;
📌 空间索引生效的必要条件
  1. 空间列必须声明为 NOT NULL
  2. 空间列必须指定 SRID(如 SRID 4326 表示 WGS 84 坐标系)[reference:50]。
  3. 查询条件中必须使用空间函数(如 MBRContainsMBRIntersects 等)[reference:51]。
2.5 空间索引的准备工作

使用 GIS 功能前,需在业务租户配置空间数据元数据[reference:52]。OceanBase 提供了两种导入方式:

-- 方式一:通过 import_srs_data.py 脚本导入(推荐)
        python /home/admin/oceanbase/bin/import_srs_data.py \
            -p'password' -h localhost -P 2881 -t tenant_name \
            -f /home/admin/oceanbase/etc/default_srs_data_mysql.sql[reference:53]

        -- 方式二:通过 ALTER SYSTEM 命令导入(仅限 SYS 租户)
        ALTER SYSTEM LOAD MODULE DATA module=gis tenant=mysql
            infile = 'etc/default_srs_data_mysql.sql';[reference:54]
📌 空间坐标系导入说明
通过 OCP 创建的租户会自动导入空间坐标系;黑屏化(命令行)和 OBD 创建的租户需要手动导入空间坐标系[reference:55]。导入成功后,__all_spatial_reference_systems 表中会有约 5152 条空间参考系统记录[reference:56]。

三、生产环境最佳实践 Checklist

✅ 全文索引与空间索引使用检查清单
  • 全文索引:确认列类型——仅 CHAR、VARCHAR、TEXT 类型列支持全文索引[reference:57]。
  • 全文索引:确认分区类型——当前版本仅支持 LOCAL 全文索引[reference:58]。
  • 全文索引:选择合适的分词器——英文用 SPACE,中文用 IK(V4.3.5 BP1+)或 NGRAM[reference:59]。
  • 全文索引:注意分区修改限制——带全文索引的表无法直接 ADD PARTITION,需先删除索引[reference:60]。
  • 全文索引:验证查询效果——通过 EXPLAIN 确认是否使用了全文索引。
  • 空间索引:列必须为 NOT NULL——空间索引列必须包含 NOT NULL 约束[reference:61]。
  • 空间索引:必须指定 SRID——空间列必须定义 SRID,否则索引无法生效[reference:62]。
  • 空间索引:导入空间坐标系——使用前需通过 import_srs_data.py 或 ALTER SYSTEM 导入 GIS 元数据[reference:63]。
  • 空间索引:分区表必须指定 LOCAL——分区表上创建空间索引必须显式指定 LOCAL 关键字[reference:64]。
  • 空间索引:使用空间函数查询——查询时需配合 MBRContains、MBRIntersects 等空间函数[reference:65]。
🏆 小结
OceanBase 4.x 的全文索引与空间索引为文本检索地理空间数据两类特殊场景提供了强大的索引能力。全文索引通过分词器将文本拆分为词元并建立倒排索引,支持自然语言、布尔和查询扩展三种搜索模式,是替代 LIKE 模糊查询的最优方案[reference:66];空间索引基于 MBR 和 SRID,为 GIS 地理数据提供快速的空间查询能力[reference:67]。在使用这两类索引时,需特别注意各自的使用限制——全文索引仅支持 LOCAL 且无法对带全文索引的表进行分区修改[reference:68];空间索引要求列必须为 NOT NULL 且必须定义 SRID[reference:69][reference:70]。建议 DBA 和开发人员根据业务场景合理选择索引类型,遵循本章提供的最佳实践,充分发挥 OceanBase 4.x 在文本检索和地理空间查询方面的能力。

第19章:热点行与事务性能优化

19.1 热点行更新原理与解决方案

热点行更新是数据库系统在高并发场景下面临的核心挑战之一。随着在线交易和电商行业的发展,热点账户的余额在短时间内被大量更新,或者热门商品在限时抢购活动中库存被频繁扣减,都是典型的热点行更新场景。本章将深入剖析热点行更新的技术原理,并详细介绍 OceanBase 4.x 的核心解决方案——提前解行锁(Early Lock Release,ELR)

🎯 本章内容概览
  • 热点行更新的技术挑战:持锁时间的构成与性能瓶颈公式。
  • 核心解决方案:ELR:提前解行锁的工作原理与实现细节。
  • ELR 的开启与验证:默认状态、配置参数与验证方法。
  • 其他优化手段:表级热点缓存策略与热点行排查方法。
  • 生产环境最佳实践 Checklist:热点行优化的完整使用指南。

一、热点行更新的技术挑战

1.1 热点行更新的本质

热点行更新是指短时间内对数据库中的同一行数据的某些字段(如余额、库存等)进行高并发的修改。其性能瓶颈在于:关系型数据库为了保持事务的一致性,对数据行的更新都必须经过 “加锁 → 更新 → 写日志提交 → 释放锁” 的过程,而这个过程本质上是串行的。无论并发量有多高,同一时刻只能有一个事务持有该行的行锁并进行更新。

1.2 持锁时间的构成

在传统方案中,事务的持锁时间包含以下几个部分:

  • 数据写入:将内存中的数据序列化并提交给本地 Buffer Manager。
  • 日志序列化:将事务日志序列化。
  • 同步备机的网络通信:将日志数据发送到所有备机。
  • 日志刷盘:等待多数备机同步日志成功。

对于一个三地五中心部署或磁盘性能较差的系统,持锁时间较长,容易对热点行产生显著的性能影响。

1.3 性能瓶颈公式

热点行场景下的吞吐量可以表示为:

📌 吞吐量 = 1 / 单事务热点行持锁耗时
其中,持锁耗时指的是从加锁开始到事务提交完成的时间间隔。在三地五中心的场景下,SQL 的整体执行耗时约为 30ms,这意味着理论上每秒最多只能完成约 33 次热点行更新。

二、核心解决方案:提前解行锁(ELR)

2.1 什么是 ELR

学术界很早就提出了“提前解行锁(Early Lock Release,ELR)”的方案,但因为 ELR 的异常处理场景非常复杂,业界很少有成熟的工业实现。OceanBase 数据库通过持续的探索,提出了一种基于分布式架构的 ELR 实现方式,作为 OceanBase 数据库“可扩展的 OLTP”中的关键能力之一。

2.2 ELR 的核心原理

优化后的流程中,整体提交流程基本保持不变,但对解锁时机进行了关键调整:

  • 传统方式:日志持久化完成(包括多数备机同步成功)后才释放锁。
  • ELR 方式:日志序列化完成并提交给 Buffer Manager 后,立即触发解锁操作,不再等待多数备机的日志刷盘完成。

具体而言,在两阶段提交(2PC)过程中,参与者收到协调者发送的 pre-commit 请求后,推高本地版本号,就可以解开行锁了,不用等到 commit 阶段

2.3 ELR 对事务语义的影响

提前解行锁后,如果事务最终成功提交,那么后续依赖它的事务都可以正常执行;如果事务最终失败,那么依赖它的其他事务也会跟着回滚。

对于读操作而言,读事务能够更快地读到前一个写操作更新后的数据,但需要等前一个事务确认提交之后再将查询结果返回,最终满足“读已提交”隔离级别的要求。ELR 不影响隔离级别,对用户而言是透明的。

📌 ELR 的效果评估
基于 ELR 优化方案,事务解锁后允许后续事务对同一行进行操作,实现了多个事务并发更新同一行的效果,从而显著提升系统吞吐量。在三地五中心的场景下,通过 ELR 优化,性能基本能够与同城部署保持一致

三、ELR 的开启与验证

3.1 默认开启状态

OceanBase 4.1 和 4.2 版本中,ELR 默认是打开的

3.2 核心配置参数
配置项描述推荐设置 enable_early_lock_release开启提前解行锁热点行场景建议开启(4.x 默认开启) enable_monotonic_weak_read开启单调弱一致性读性能场景建议关闭 weak_read_version_refresh_interval弱一致性读版本号的刷新周期性能场景建议关闭(设为 0)
-- 查看 ELR 配置状态
        SHOW PARAMETERS LIKE '%enable_early_lock_release%';

        -- 手动开启 ELR(如需确认已开启)
        ALTER SYSTEM SET enable_early_lock_release = true;

        -- 关闭单调弱一致性读(性能场景推荐)
        ALTER SYSTEM SET enable_monotonic_weak_read = false;

        -- 关闭弱一致性读版本号刷新(性能场景推荐)
        ALTER SYSTEM SET weak_read_version_refresh_interval = 0;
3.3 验证方法

通过构造多并发单行更新的场景,可以验证 ELR 特性的效果:

# 测试脚本 ob_elr.py 核心逻辑
        parallel = 50        # 并发线程数
        batch_num = 2000     # 每个线程的更新次数

        def update_elr():
            update_hot_row = "UPDATE sbtest1 SET k=k+1 WHERE id=1"
            # 每个线程执行 batch_num 次更新
📌 硬件配置建议
建议使用至少 16C-128GB 配置的节点进行体验和验证,以达到更好的效果。
3.4 监控指标

热点行场景下,建议通过以下方式监控:

  • 通过 GV$OB_SQL_AUDIT 审计视图进行 SQL 执行行为的聚合分析,识别资源密集型 SQL 语句,帮助定位热表。
  • 监控行锁冲突情况:当遇到对特定行进行并发更新的场景时,容易出现行锁冲突问题,可能导致各请求等待时间变长,进一步传导到应用连接池满问题。
-- 查看热点行更新相关的 SQL 执行情况
        SELECT 
            sql_id,
            query_sql,
            executions,
            elapsed_time,
            row_lock_wait_time
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE query_sql LIKE '%UPDATE%'
        ORDER BY executions DESC
        LIMIT 20;

四、其他优化手段

4.1 表级热点缓存策略(V4.3.5+)

OceanBase V4.3.5 版本支持通过 STORAGE_CACHE_POLICY 修改表级热点缓存策略:

  • HOT:指定表中所有数据作为热点数据缓存到本地磁盘。
  • AUTO(默认):自动冷热识别,由共享存储自行决定数据的存储方式。

通过 enable_manual_storage_cache_policy 配置项可启用用户手动指定的热点缓存策略(从 V4.3.5 BP2 版本开始引入)。

-- 创建表时指定热点缓存策略
        CREATE TABLE hot_table (
            id INT PRIMARY KEY,
            value INT
        ) STORAGE_CACHE_POLICY = HOT;

        -- 修改现有表的缓存策略
        ALTER TABLE hot_table SET STORAGE_CACHE_POLICY = HOT;

        -- 查看表的缓存策略
        SELECT table_name, storage_cache_policy 
        FROM information_schema.TABLES 
        WHERE table_name = 'hot_table';
4.2 热点行排查方法

OceanBase 不直接提供热表的视图,但可以通过组合多个性能视图的数据来识别热表:

  • 使用 GV$OB_SQL_AUDIT 进行 SQL 执行行为的聚合分析,识别高频更新的表和行。
  • 关注 Buffer Cache 命中率等全局调优点。
  • 针对吞吐量的性能调优,主要关注 CPU、IO、网络等资源的使用情况。
-- 识别高频更新的表
        SELECT 
            SUBSTRING_INDEX(query_sql, ' ', 2) AS sql_prefix,
            COUNT(*) AS exec_count,
            AVG(row_lock_wait_time) AS avg_lock_wait
        FROM oceanbase.GV$OB_SQL_AUDIT
        WHERE query_sql LIKE '%UPDATE%'
        GROUP BY sql_prefix
        ORDER BY exec_count DESC
        LIMIT 10;

五、生产环境最佳实践 Checklist

✅ 热点行优化检查清单
  • 确认 ELR 已开启:检查 enable_early_lock_release 是否为 true(4.x 版本默认开启)。
  • 评估热点行场景:通过 GV$OB_SQL_AUDIT 识别是否存在频繁更新同一行的业务模式。
  • 考虑业务拆分:对于极端的单行热点(如全局计数器),考虑业务层拆分(如分桶计数器)。
  • 合理配置资源:热点行场景建议开启 ELR,关闭单调读(enable_monotonic_weak_read = false)。
  • 监控锁冲突:关注行锁等待时间,避免锁冲突传导至应用连接池满。
  • 评估表级缓存策略:V4.3.5+ 版本可评估是否使用 STORAGE_CACHE_POLICY=HOT 加速热点数据访问。
  • 优化 SQL 与事务设计:减少每个事务的持锁时间,将事务粒度降到最低。
🏆 小结
OceanBase 4.x 通过 ELR(提前解行锁) 机制,在分布式架构下实现了对热点行更新的有效优化。其核心思路是在日志序列化完成后立即释放行锁,不再等待多数备机的日志刷盘完成,从而将事务的持锁时间从“全链路耗时”缩短为“本地处理耗时”。这一优化在三地五中心等长距离部署场景下效果尤为显著,性能基本能够与同城部署保持一致。ELR 在 4.1 及 4.2 版本中默认开启,对用户完全透明,不影响事务的隔离级别语义。建议 DBA 和开发人员充分理解 ELR 的工作原理,在热点行高并发场景下合理利用这一能力,配合表级缓存策略等辅助手段,实现系统吞吐量的最大化。
19.2 Group Commit 与批量提交优化

在 OceanBase 分布式数据库中,Group Commit(组提交) 是一项关键的写入性能优化技术。它将多个事务的日志写入操作聚合成一批,一次性刷入磁盘,从而有效减少磁盘 I/O 次数,提升系统吞吐量。配合批量 DML(Batch DML) 等批量提交优化手段,可以进一步降低网络往返开销和服务端解析开销,显著提升数据写入效率。本章将深入剖析 Group Commit 的实现原理、核心配置参数,以及批量提交优化的实践方法。

🎯 本章内容概览
  • Group Commit 技术原理:组提交的核心思想与 OceanBase 实现。
  • Group Commit 相关配置参数enable_kv_group_commitkv_group_commit_batch_sizekv_group_commit_rw_mode
  • 批量提交优化实践:JDBC Batch DML、DataX、OMS 与 PDML。
  • 生产环境最佳实践 Checklist:组提交与批量优化的完整使用指南。

一、Group Commit 技术原理

1.1 Group Commit 的核心思想

在传统数据库系统中,每个事务提交时都需要将 Redo Log 刷入磁盘,这涉及频繁的磁盘 I/O 操作,成为事务处理的主要瓶颈之一。Group Commit 的核心思想是:将多个事务的日志写入操作合并为一批,一次性写入日志文件

其工作流程可以简化为:

  1. 多个写任务将日志拷贝到同一个日志缓冲区。
  2. 批处理线程将缓冲区中的日志内容一次性写入日志文件。

通过合并 I/O 操作,Group Commit 能够显著减少磁盘 I/O 次数,从而提升系统的写入吞吐量[reference:4]。

1.2 OceanBase 的 Group Commit 实现

OceanBase 不仅实现了基础的 Group Commit,还对其进行了深度优化。Laser 是 OceanBase 集成的高性能事务日志引擎,它采用无锁事务日志框架消除锁竞争,并提出了负载自适应的分组策略来为不同工作负载确定最优的分组时间。实验结果表明,Laser 相比传统方法可提升 1.4 倍至 2.4 倍的吞吐量,并降低 60% 以上的延迟[reference:5]。

1.3 OBKV-Table 组提交

OBKV-Table 模型下的组提交是针对单操作请求提供的性能优化特性。在数据库服务端对客户端的单操作请求做聚合,聚合后的单操作请求可以通过服务端的 batch 能力进行汇聚计算、写入或读取,从而降低事务和存储多次的调用开销[reference:6]。

当前组提交在 Table 模型下支持以下单操作:getputinsertdeletereplaceupdateinsert_or_updateincrementappend[reference:7]。

二、Group Commit 相关配置参数

2.1 enable_kv_group_commit(V4.2.3 引入,V4.2.5 起弃用)

该配置项用于控制是否开启组提交功能[reference:8]。

属性描述 参数类型Bool 默认值False 取值范围True:服务端将操作按执行计划分组执行;False:逐个执行操作 修改生效否,设置后立即生效[reference:9]
📌 版本说明
该参数在 V4.2.3 版本中引入[reference:10]。由于 V4.2.5 版本对组提交功能进行了优化,该参数已不再适用并被弃用[reference:11]。
2.2 kv_group_commit_batch_size(V4.2.5 起引入)

该配置项用于指定 OBKV 组提交中执行 batch 操作的大小[reference:12]。

属性描述 参数类型Int 默认值1 取值范围(0, +∞) 取值含义值为 1 时关闭组提交;大于 1 时开启组提交,该值作为每个提交 batch 的大小[reference:13] 修改生效否,设置后立即生效[reference:14]
-- 设置组提交 batch_size 大小为 10
        ALTER SYSTEM SET kv_group_commit_batch_size = 10;
2.3 kv_group_commit_rw_mode(V4.3.4 起引入)

该配置项用于控制走组提交的场景,仅在 OBKV-Table 模型下操作生效[reference:15]。

属性描述 参数类型ALL 默认值ALL 取值范围ALL:读写操作都走组提交;WRITE:仅写操作走组提交;READ:仅读操作走组提交[reference:16] 修改生效否,设置后立即生效[reference:17]
-- 设置仅写操作走组提交
        ALTER SYSTEM SET kv_group_commit_rw_mode = 'WRITE' TENANT = tenant_name;
📌 组提交监控视图
组提交提供了相应的监控视图 (G)V$OB_KV_GROUP_COMMIT_STATUS。在开启期间,通过查询组提交相关的视图可以观察相应请求走组提交的状态信息[reference:18]。

三、批量提交优化实践

3.1 JDBC Batch DML 优化

OceanBase 的 JDBC 驱动支持批量 DML 优化。当客户端通过 JDBC 发送批量 SQL 语句时,OBServer 会对其进行自动改写,生成高效的批量执行计划,从而显著减少参数化、查找计划缓存等过程引起的开销[reference:19]。

关键 JDBC 参数配置

参数推荐值说明 rewriteBatchedStatementstrue将多条 INSERT 重写为一条多 VALUES 的 INSERT 语句。注意:当该参数设置为 true 时,useServerPrepStmts 会被自动设置为 false[reference:20][reference:21] allowMultiQueriestrue允许多值查询[reference:22] useServerPrepStmtstrue(Oracle 模式配合 useArrayBinding)启用服务端预编译。在 MySQL 模式下,如果 rewriteBatchedStatements=true,该参数自动失效[reference:23] cachePrepStmtstrue客户端缓存 PreparedStatement prepStmtCacheSize250客户端缓存大小
⚠️ 重要说明:参数互斥关系
rewriteBatchedStatements=true 时,OceanBase Connector/J 仅使用文本协议useServerPrepStmts 会被自动设置为 false,PreparedStatement 由驱动在客户端处理[reference:24]。因此,不要同时依赖这两个参数——MySQL 模式下使用 rewriteBatchedStatements 批量改写,Oracle 模式下使用 useArrayBinding 配合 useServerPrepStmts[reference:25]。
// MySQL 模式 JDBC URL 配置示例(使用 rewriteBatchedStatements)
        jdbc:oceanbase://host:port/database?
        rewriteBatchedStatements=true&
        allowMultiQueries=true&
        cachePrepStmts=true

        // Oracle 模式 JDBC URL 配置示例(使用 useArrayBinding)
        jdbc:oceanbase://host:port/database?
        useArrayBinding=true&
        useServerPrepStmts=true&
        cachePrepStmts=true

        // 批量插入示例
        PreparedStatement pstmt = conn.prepareStatement(
            "INSERT INTO users(name, age) VALUES(?, ?)"
        );
        for (int i = 0; i < 10000; i++) {
            pstmt.setString(1, "user" + i);
            pstmt.setInt(2, i % 100);
            pstmt.addBatch();
            if (i % 1000 == 0) {
                pstmt.executeBatch();  // 每 1000 条提交一批
            }
        }
        pstmt.executeBatch();
3.2 批量导入工具优化

使用 DataX 等数据传输工具时,可以通过调整 batchSize 参数来控制每次写入的数据行数,增加每次写入的数据量可有效提升写入性能。

3.3 OMS 批量操作优化

在使用 OMS 进行数据迁移时,如果源端有批量操作导致增量延迟,可以调整事务分片参数 source.splitThreshold

  • 对于批量 INSERT 操作,建议将该值调大,最大为 512
  • 对于批量 UPDATE 和 DELETE 操作,建议将该值调小,最小为 1
3.4 并行 DML(PDML)

对于大批量数据导入场景,OceanBase 支持并行 DML(PDML)。启用 PDML 后,导入 600 万行数据仅需约 22 秒,性能提升约 5 倍[reference:26]。

推荐方式:通过 Hint 启用 PDML

-- 通过 Hint 启用并行 DML(推荐方式)
        INSERT /*+ ENABLE_PARALLEL_DML PARALLEL(4) */ INTO target_table SELECT * FROM source_table;

一般情况下,ENABLE_PARALLEL_DML Hint 和 PARALLEL Hint 必须配合使用才能开启并行 DML。不过,当目标表的 Schema 上指定了表级别的并行度时,仅需指定 ENABLE_PARALLEL_DML Hint[reference:27][reference:28]。

备选方式:通过 Session 变量启用(隐藏参数)

-- 通过 Session 变量启用(隐藏参数,不推荐生产环境直接使用)
        SET _ENABLE_PARALLEL_DML = 1;

        -- 或使用 Hint + 指定并行度
        INSERT /*+ ENABLE_PARALLEL_DML PARALLEL(4) */ INTO target_table SELECT * FROM source_table;
📌 PDML 使用说明
_ENABLE_PARALLEL_DML 为隐藏参数,官方推荐使用 Hint 方式启用 PDML[reference:29]。在 OceanBase V4.2.0 中,Direct Load 必须与 PDML 配合使用;如果未通过 Hint 或 Session 启用 PDML,Direct Load Hint 会被自动忽略[reference:30]。

四、生产环境最佳实践 Checklist

✅ 组提交与批量优化检查清单
  • 确认 Group Commit 配置:V4.2.5 及以上版本使用 kv_group_commit_batch_size 控制组提交;V4.2.3-V4.2.4 版本使用 enable_kv_group_commit
  • 开启 JDBC 批量改写:在 JDBC URL 中添加 rewriteBatchedStatements=true,并合理设置 batchSize。注意该参数与 useServerPrepStmts 互斥。
  • 区分 MySQL 与 Oracle 模式:MySQL 模式使用 rewriteBatchedStatements;Oracle 模式使用 useArrayBinding 配合 useServerPrepStmts[reference:31]。
  • 合理控制单批数据量:建议每批 1000~5000 条,避免单批过大导致内存压力或事务超时。
  • 大表导入场景使用 PDML:通过 /*+ ENABLE_PARALLEL_DML PARALLEL(n) */ 启用并行 DML,充分利用多核资源。
  • 监控 Group Commit 效果:通过 GV$OB_SQL_AUDIT 观察写入延迟变化,通过 GV$OB_KV_GROUP_COMMIT_STATUS 观察组提交状态[reference:32]。
  • 按需配置 kv_group_commit_rw_mode:读写流量不均匀时,可仅对读或写操作开启组提交[reference:33]。
🏆 小结
OceanBase 4.x 通过 Group Commit(组提交)批量提交优化 两方面的协同设计,显著提升了数据写入性能。服务端的 Group Commit 机制将多个事务的日志写入合并为一批,有效减少磁盘 I/O 次数;应用层的 Batch DML 优化则通过批量发送和自动改写降低网络往返和服务端解析开销。两者结合,能够在高并发写入场景下实现吞吐量的大幅提升。建议 DBA 和开发人员根据业务写入负载特点,合理配置 Group Commit 参数(注意不同版本的参数差异),并在应用层充分利用 Batch DML 和 PDML 等优化手段,充分发挥 OceanBase 的写入性能潜力。
19.3 大事务拆分与限流策略

在 OceanBase 分布式数据库中,大事务(Large Transaction) 是指涉及大量数据修改、跨多个分区或执行时间较长的事务。大事务会长时间持有行锁、阻止 MemStore 内存释放、阻碍日志回收和合并任务,是数据库性能抖动和稳定性风险的常见根源[reference:0]。与单机数据库不同,OceanBase 作为分布式数据库,大事务的影响会被进一步放大——跨分区的分布式事务协调开销、多节点日志同步延迟等因素,使得大事务的风险更为突出。本章将深入剖析大事务的定义与风险、拆分策略、限流机制、核心参数配置以及监控诊断方法

🎯 本章内容概览
  • 大事务的定义与风险:大事务的识别标准与四大核心风险。
  • 大事务拆分策略:按批次拆分、按业务拆分、按时间拆分三种实战方法。
  • 服务端限流策略:MemStore 内存限流机制与配置方法。
  • 事务拆分路由:OBProxy 事务拆分的工作原理与配置。
  • 核心配置参数:事务超时、限流阈值等关键参数速查。
  • 监控与诊断:常用 SQL 与 obdiag 诊断场景。
  • 生产环境最佳实践 Checklist:大事务治理的完整指南。

一、大事务的定义与风险

1.1 什么是大事务

大事务没有一个严格的数值定义,通常从以下维度识别:

  • 数据量维度:单事务修改行数超过 10,000 行
  • 时间维度:事务执行时间超过 30 分钟 仍未提交。
  • 跨分区维度:事务涉及 多个分区或跨节点 的数据修改。
  • 内存维度:事务占用的 MemStore 内存超过租户 MemStore 总量的 10%
📌 OceanBase 4.x 的大事务能力演进
OceanBase 在 V3.x 版本前并不支持大事务,用户可通过配置项 _max_trx_size 控制事务大小[reference:1]。从 V3.x 版本开始,OceanBase 具备了支持大事务的能力[reference:2]。V4.x 版本对事务大小不再设置硬性上限,但大事务的风险仍然存在,需要通过合理的拆分和限流策略来规避。
1.2 大事务的四大核心风险
风险类别具体影响后果 锁等待与锁冲突 大事务长时间持有行锁,阻塞其他事务 业务响应变慢,连接池耗尽[reference:3] MemStore 内存压力 大事务阻止 MemStore 转储(Dump),内存无法释放 触发写入限流,写入性能急剧下降[reference:4] 日志盘空间无法回收 大事务阻止日志回收,Clog 日志盘空间无法释放 日志盘满,集群进入只读状态[reference:5] 阻塞 DDL 与合并 大事务阻塞 DDL 操作和合并(Compaction)任务 Schema 变更无法执行,合并延迟[reference:6]

二、大事务拆分策略

2.1 策略一:按批次拆分(Batch Commit)

将大批量 DML 拆分为多个小批次,每批次处理一定量的数据后立即 COMMIT[reference:7]。这是最常用的大事务拆分方式,适用于数据迁移、批量导入、周期性数据清理等场景。

-- 大事务风险示例(不推荐):一次更新 100 万行
        UPDATE orders SET status = 'ARCHIVED' WHERE create_time < '2024-01-01';

        -- 推荐做法:按主键范围拆分,每次更新 10000 行后 COMMIT
        -- 假设主键为 id,范围从 1 到 1000000
        DECLARE @batch_size INT DEFAULT 10000;
        DECLARE @start_id INT DEFAULT 1;
        DECLARE @end_id INT;

        WHILE @start_id <= 1000000 DO
            SET @end_id = @start_id + @batch_size - 1;
            UPDATE orders SET status = 'ARCHIVED' 
            WHERE id BETWEEN @start_id AND @end_id AND create_time < '2024-01-01';
            COMMIT;
            SET @start_id = @start_id + @batch_size;
        END WHILE;
2.2 策略二:按业务拆分(Business Split)

根据业务边界将复杂事务拆解为多个独立小事务[reference:8]。例如,在电商下单场景中,将“订单创建 + 库存扣减 + 积分赠送”拆分为三个独立事务处理[reference:9]。

-- 原始大事务(不推荐)
        BEGIN;
        INSERT INTO orders VALUES (...);      -- 订单创建
        UPDATE inventory SET stock = stock - 1 WHERE product_id = ?;  -- 库存扣减
        UPDATE points SET points = points + 10 WHERE user_id = ?;     -- 积分赠送
        COMMIT;

        -- 拆分为三个独立事务(推荐)
        -- 事务1:订单创建
        BEGIN;
        INSERT INTO orders VALUES (...);
        COMMIT;

        -- 事务2:库存扣减
        BEGIN;
        UPDATE inventory SET stock = stock - 1 WHERE product_id = ?;
        COMMIT;

        -- 事务3:积分赠送
        BEGIN;
        UPDATE points SET points = points + 10 WHERE user_id = ?;
        COMMIT;
2.3 策略三:按时间拆分(Time-based Split)

将长事务按时间范围拆分为多个执行窗口[reference:10],适用于历史数据归档、无截止时间要求的大表全量更新等场景。例如,每晚定时处理 5 万行,多日完成一次大表全量更新[reference:11]。

2.4 拆分策略选型决策矩阵
场景推荐策略单批建议行数说明 数据迁移 / 批量导入 按批次拆分 5,000 ~ 10,000 行 配合 JDBC Batch 或 DataX batchSize 使用 复杂业务流程(下单、支付等) 按业务拆分 — 每个原子操作独立事务,通过补偿机制保证最终一致性 历史数据归档 / 全量更新 按时间拆分 5,000 ~ 10,000 行/天 配合定时任务分多日完成 在线业务批量操作 按批次拆分 1,000 ~ 2,000 行 避免长时间阻塞在线业务

三、服务端限流策略

3.1 MemStore 内存限流机制

OceanBase 使用 MemStore 存储最近写入的数据。为了避免内存溢出,系统提供了两个重要的保护机制[reference:12]:

  • 冻结机制:将内存中的数据持久化到磁盘,以释放内存空间[reference:13]。
  • 限流机制:当内存压力过大时,降低写入速度,为释放内存争取时间[reference:14]。

这两个机制通过两个租户级配置项来控制触发时机[reference:15]:

配置项默认值说明 freeze_trigger_percentage 20(V4.0+)[reference:16] 触发冻结的 MemStore 内存使用阈值百分比。当 Active MemStore 使用量达到 freeze_trigger_percentage × memstore_limit 时,自动触发冻结[reference:17] writing_throttling_trigger_percentage 80 触发写入限流的 MemStore 内存使用阈值百分比[reference:18]
⚠️ 重要约束
为了避免因过早限流而导致的性能下降,要求 writing_throttling_trigger_percentage 的值必须大于 freeze_trigger_percentage 的值[reference:19]。
3.2 限流配置示例
-- 查看当前限流阈值配置
        SHOW PARAMETERS LIKE 'freeze_trigger_percentage';
        SHOW PARAMETERS LIKE 'writing_throttling_trigger_percentage';

        -- 调整限流阈值(租户级别)
        ALTER SYSTEM SET freeze_trigger_percentage = 30 TENANT = 'your_tenant';
        ALTER SYSTEM SET writing_throttling_trigger_percentage = 85 TENANT = 'your_tenant';

        -- 当内存不足时,可以调高 memstore_limit_percentage 并调低 freeze_trigger_percentage
        -- 达到临时扩容和尽快转储释放的效果[reference:20]
        ALTER SYSTEM SET memstore_limit_percentage = 50 TENANT = 'your_tenant';
        ALTER SYSTEM SET freeze_trigger_percentage = 20 TENANT = 'your_tenant';
3.3 大查询限流

如果系统中同时运行着大查询和小查询,OceanBase 会将一部分 CPU 资源分配给大查询,并通过配置参数 large_query_worker_percentage(默认值为 30%)来限制执行大查询最多可以使用的租户活跃工作线程数[reference:21]。通过限制大查询能使用的租户活跃工作线程数来约束大查询最多能够使用的 CPU 资源,以此来保证系统还会有足够的 CPU 资源来执行 OLTP 负载[reference:22]。

-- 查看大查询相关配置
        SHOW PARAMETERS LIKE 'large_query%';

        -- 调整大查询线程占比(限制大查询最多使用 20% 的活跃线程)
        ALTER SYSTEM SET large_query_worker_percentage = 20 TENANT = 'your_tenant';

        -- 设置大查询阈值(执行时间超过 5 秒视为大查询)
        ALTER SYSTEM SET large_query_threshold = '5s';

四、事务拆分路由

4.1 OBProxy 事务拆分原理

OBProxy 针对事务有两种路由方式[reference:23]:

  • 统一路由:将同一个事务的语句统一路由到一个数据节点上执行。
  • 拆分路由:将事务中的语句拆分,路由到不同的数据节点执行,通过 OBProxy 同步不同数据节点的事务状态,完成事务的拆分执行[reference:24]。

事务路由的执行节点分为协调者参与者两种节点[reference:25]:

  • 协调者节点:事务的开启节点,负责执行 BEGINSTART TRANSACTIONCOMMITROLLBACK 等影响事务状态的非 DML 语句[reference:26]。
  • 参与者节点:负责执行事务中不会影响事务状态的 DML 语句[reference:27]。
4.2 事务拆分配置

默认情况下事务路由的功能是开启的[reference:28]。通过以下语句进行配置[reference:29]:

-- 检查并启用 2.0 协议(事务路由依赖 2.0 协议)[reference:30]
        SHOW PROXYCONFIG LIKE 'enable_ob_protocol_v2';
        ALTER PROXYCONFIG SET enable_ob_protocol_v2 = True;

        -- 配置事务路由
        SHOW PROXYCONFIG LIKE 'enable_transaction_internal_routing';
        ALTER PROXYCONFIG SET enable_transaction_internal_routing = True;

        -- 配置事务分割(OBProxy 级别)[reference:31]
        SHOW PROXYCONFIG LIKE 'enable_transaction_split';
        ALTER PROXYCONFIG SET enable_transaction_split = True;

五、核心配置参数速查

5.1 事务超时参数

以下参数用于控制事务的超时行为,合理配置可避免事务长时间不结束[reference:32]。

参数名默认值单位说明 ob_query_timeout 10,000,000 微秒(10 秒)[reference:33] 单条 SQL 语句执行超时[reference:34] ob_trx_timeout 86,400,000,000 微秒(24 小时)[reference:35] 事务总执行超时,超时错误码为 -4012[reference:36] ob_trx_idle_timeout 86,400,000,000 微秒(24 小时)[reference:37] 事务空闲超时(事务内两条语句的执行间隔)[reference:38] ob_trx_lock_timeout -1 微秒(无限等待) 事务获取行锁的最大等待时间
📌 超时参数设置建议
建议 ob_trx_timeout 小于 ob_query_timeout,确保事务整体执行时间不会超过单个查询的限制[reference:39]。对于批量操作场景,建议在会话级别设置合理的超时值,避免使用全局默认值。
5.2 限流与内存保护参数
参数名默认值说明 freeze_trigger_percentage 20(V4.0+)[reference:40] 触发冻结的 MemStore 内存阈值百分比[reference:41] writing_throttling_trigger_percentage 80 触发写入限流的 MemStore 内存阈值百分比[reference:42] memstore_limit_percentage 0(自适应) 租户内存中可用于 MemStore 写入的比例[reference:43] large_query_worker_percentage 30%[reference:44] 大查询最多可使用的租户活跃工作线程数比例[reference:45]

六、监控与诊断

6.1 常用监控 SQL
-- 1. 查询活跃事务参与者信息(OceanBase 4.x)[reference:46]
        SELECT 
            TX_ID,
            SESSION_ID,
            STATE,
            LAST_REQUEST_TIME,
            CTX_CREATE_TIME
        FROM oceanbase.GV$OB_TRANSACTION_PARTICIPANTS
        WHERE STATE = 'ACTIVE'
        AND LAST_REQUEST_TIME < DATE_SUB(NOW(), INTERVAL 600 SECOND);

        -- 2. 查询长事务(执行时间超过 10 分钟)
        SELECT 
            TX_ID,
            SESSION_ID,
            STATE,
            TIMESTAMPDIFF(SECOND, CTX_CREATE_TIME, NOW()) AS duration_sec,
            LAST_REQUEST_TIME
        FROM oceanbase.GV$OB_TRANSACTION_PARTICIPANTS
        WHERE STATE = 'ACTIVE'
        AND TIMESTAMPDIFF(SECOND, CTX_CREATE_TIME, NOW()) > 600;

        -- 3. 检查 MemStore 使用率[reference:47]
        SELECT 
            t.tenant_name,
            m.svr_ip,
            m.svr_port,
            ROUND(m.memstore_used / m.memstore_limit, 4) AS memstore_ratio,
            m.freeze_cnt
        FROM oceanbase.__all_virtual_tenant_memstore_info m
        JOIN oceanbase.__all_tenant t ON t.tenant_id = m.tenant_id
        WHERE m.memstore_used / m.memstore_limit > 0.8;

        -- 4. 检查日志盘使用率[reference:48]
        SELECT 
            t.tenant_name,
            a.svr_ip,
            ROUND(a.LOG_DISK_IN_USE / a.LOG_DISK_SIZE, 2) AS clog_ratio
        FROM oceanbase.gv$ob_units a
        JOIN oceanbase.__all_tenant t ON a.tenant_id = t.tenant_id;

        -- 5. 查询事务持有的锁(OceanBase 4.2+)[reference:49]
        SELECT * FROM oceanbase.GV$OB_LOCKS WHERE TX_ID = '{tx_id}';

        -- 6. 查询锁等待[reference:50]
        SELECT * FROM oceanbase.__all_virtual_lock_wait_stat;
6.2 obdiag 诊断场景

obdiag 提供了多个与事务相关的诊断场景,可用于快速定位大事务和异常事务[reference:51][reference:52]。

# 1. 收集长事务诊断信息(推荐用于排查大事务)[reference:53]
        obdiag gather scene run --scene=observer.long_transaction

        # 2. 事务不结束根因分析[reference:54]
        obdiag rca run --scene=transaction_not_ending --env phase=UNSUBMITTED --env tx_id=0x1

        # 3. 悬挂事务根因分析[reference:55]
        obdiag rca run --scene=suspend_transaction

        # 4. 事务等待超时根因分析[reference:56]
        obdiag rca run --scene=transaction_wait_timeout --env error_msg="Lock wait timeout exceeded"

        # 5. 事务执行超时根因分析[reference:57]
        obdiag rca run --scene=transaction_execute_timeout

        # 6. 事务回滚报错根因分析[reference:58]
        obdiag rca run --scene=transaction_rollback

        # 7. 事务断连场景根因分析[reference:59]
        obdiag rca run --scene=transaction_disconnection

七、生产环境最佳实践 Checklist

✅ 大事务治理检查清单
  • 识别大事务:通过 GV$OB_TRANSACTION_PARTICIPANTS 定期检查执行时间超过 10 分钟的活跃事务。
  • 拆分大事务:批量 DML 使用分批提交(建议每批 5,000~10,000 行),复杂业务流程按业务边界拆分为独立事务。
  • 配置合理的超时参数:根据业务特点设置 ob_query_timeoutob_trx_timeoutob_trx_idle_timeout
  • 配置限流阈值:合理设置 freeze_trigger_percentagewriting_throttling_trigger_percentage,避免 MemStore 内存溢出。
  • 监控 MemStore 使用率:当 MemStore 使用率超过 80% 时及时处理,必要时调整 memstore_limit_percentage 临时扩容。
  • 启用事务拆分路由:在 OBProxy 层面启用 enable_transaction_split,将大事务拆分为多个参与者节点执行。
  • 使用 obdiag 定期巡检:通过 obdiag gather scene run --scene=observer.long_transaction 定期收集长事务诊断信息。
  • 优化分区设计:合理设计分区键和 Table Group,尽量减少跨节点的数据交互,降低分布式事务开销[reference:60]。
  • 事务模型选择:优先设计单日志流事务,避免不必要的分布式事务开销[reference:61]。
🏆 小结
OceanBase 4.x 虽然不再对事务大小设置硬性上限,但大事务的四大核心风险——锁等待、MemStore 内存压力、日志盘空间无法回收、阻塞 DDL 与合并——依然存在。治理大事务需要拆分与限流双管齐下:在应用层通过按批次、按业务、按时间三种策略将大事务拆解为小事务;在服务端通过 freeze_trigger_percentagewriting_throttling_trigger_percentage 配置写入限流阈值,防止 MemStore 内存溢出。建议 DBA 和开发人员建立常态化的监控机制,利用 GV$OB_TRANSACTION_PARTICIPANTS 和 obdiag 等工具主动发现和治理大事务,保障数据库的稳定运行。
19.4 读写分离与弱一致性读应用

在 OceanBase 分布式数据库中,读写分离(Read-Write Splitting) 是指将读请求和写请求路由到不同的副本上执行,从而缓解 Leader 副本的读写压力,降低读写操作之间的相互影响[reference:0][reference:1]。OceanBase 的读写分离基于 弱一致性读(Weak-Consistency Read) 机制实现——写操作始终由 Leader 副本提供强一致性服务,而读操作可以配置为弱一致性,由 Follower 副本优先提供读服务[reference:2][reference:3]。

本章将深入剖析 OceanBase 4.x 中读写分离与弱一致性读的实现原理、配置方法、路由策略、有界旧与单调读特性、事务一致性级别以及生产最佳实践

🎯 本章内容概览
  • 读写分离与弱一致性读概述:Multi-Paxos 多副本架构下的两种一致性级别。
  • 弱一致性读的配置方式:Hint 方式、会话变量方式、OBProxy 全局参数方式。
  • OBProxy 路由策略proxy_route_policy 的多种路由策略详解。
  • 有界旧与单调读max_stale_time_for_weak_consistencyenable_monotonic_weak_read
  • 事务中的一致性级别:事务一致性级别的确定规则与使用限制。
  • 生产环境最佳实践 Checklist:读写分离与弱一致性读的完整使用指南。

一、读写分离与弱一致性读概述

1.1 OceanBase 的多副本架构

OceanBase 是基于 Multi-Paxos 协议 的分布式数据库,数据以多副本的形式保存在不同的 Zone 或节点上[reference:4]。在数据更新时:

  • Leader 副本 负责执行修改语句,并将提交日志(Clog)同步给 Follower 副本[reference:5]。
  • 只要包含 Leader 副本在内的多数派副本日志落盘,事务即算提交成功[reference:6]。
  • Follower 副本的日志并不保证在多数派日志落盘后立即完成回放,因此部分副本的数据相对于 Leader 可能存在延迟[reference:7][reference:8]。
1.2 两种一致性级别:STRONG 与 WEAK

基于多副本架构,OceanBase 提供了两种一致性级别[reference:9][reference:10]:

一致性级别语义路由目标适用场景 STRONG(强一致性) 读取最新数据 Leader 副本 对数据实时性要求高的业务(默认) WEAK(弱一致性) 不要求读取最新数据 优先路由到 Follower 副本 对数据时效性不敏感的业务(如报表查询、统计分析)
📌 核心设计原则
OceanBase 的写操作始终为强一致性,即始终由 Leader 副本提供服务[reference:11][reference:12]。读操作默认也是强一致性,由 Leader 提供服务;用户可以根据业务需求指定弱一致性读,由 Follower 副本优先提供读服务[reference:13][reference:14]。在一些数据访问时效不敏感的业务中,通过从 Leader 副本写入数据、从 Follower 副本读取数据,可以实现数据的读写分离,充分利用资源,提高数据库读取效率[reference:15]。

二、弱一致性读的配置方式

OceanBase 提供了三种方式来启用弱一致性读[reference:16]。

2.1 方式一:通过 Hint 指定(推荐)

在 SQL 语句中添加 /*+READ_CONSISTENCY(WEAK)*/ Hint,为单条 SELECT 语句启用弱一致性读[reference:17]。这是最精确、最推荐的方式。

-- 为单条查询指定弱一致性读
        SELECT /*+READ_CONSISTENCY(WEAK)*/ * FROM orders WHERE create_time > '2024-01-01';

        -- 强制指定强一致性读(覆盖会话级设置)
        SELECT /*+READ_CONSISTENCY(STRONG)*/ * FROM accounts WHERE account_id = 1001;
⚠️ Hint 优先级
Hint 方式的优先级最高,会覆盖会话变量和全局变量的设置[reference:18]。对于需要精确控制一致性级别的查询,推荐使用 Hint 方式。
2.2 方式二:通过会话变量设置

通过设置会话级或全局级变量 ob_read_consistency,为当前会话或所有后续会话启用弱一致性读[reference:19]。

-- 会话级设置:仅影响当前会话
        SET ob_read_consistency = WEAK;

        -- 会话级设置:切换回强一致性
        SET ob_read_consistency = STRONG;

        -- 全局级设置:影响所有后续创建的会话
        SET GLOBAL ob_read_consistency = WEAK;

        -- 查看当前一致性级别设置
        SHOW VARIABLES LIKE 'ob_read_consistency';
2.3 方式三:通过 OBProxy 全局参数设置

通过修改 OBProxy 的 obproxy_read_consistency 参数,为所有经过该 OBProxy 的查询统一设置一致性级别[reference:20][reference:21]。该方式适合将整个应用的读请求统一切换为弱一致性读。

-- 通过 root@proxysys 账号登录 OBProxy
        mysql -h obproxy_host -P 2883 -uroot@proxysys

        -- 设置为弱一致性读
        ALTER PROXYCONFIG SET obproxy_read_consistency = 1;

        -- 恢复为强一致性读(默认)
        ALTER PROXYCONFIG SET obproxy_read_consistency = 0;
取值说明 0(默认)强一致性读,请求路由到 Leader 副本 1弱一致性读,请求优先路由到 Follower 副本
2.4 三种方式的优先级与适用场景
方式优先级粒度适用场景 Hint最高单条 SQL需要精确控制一致性级别的查询 会话变量中等当前会话同一应用会话中所有查询统一设置 OBProxy 全局参数最低整个 OBProxy整个应用统一切换为弱一致性读

三、OBProxy 路由策略

启用弱一致性读后,还需要通过 proxy_route_policy 参数配置 OBProxy 的路由策略,以控制弱一致性读请求具体路由到哪些副本[reference:22]。

3.1 proxy_route_policy 参数详解
取值说明行为 ''(空字符串,默认) 使用 ODP 默认路由策略(MERGE_IDC_ORDER) 弱一致性读请求可路由到 Follower 和只读副本[reference:23] FOLLOWER_FIRST 优先读 Follower 副本 请求优先路由到 Follower;若所有 Follower 不可用,则路由到 Leader[reference:24] FOLLOWER_ONLY 只读 Follower 副本 请求仅路由到 Follower;若所有 Follower 不可用,客户端连接断开[reference:25] UNMERGE_FOLLOWER_FIRST 优先读未合并的 Follower 副本 优先路由到未处于 Major Compaction 中的 Follower[reference:26] TARGET_REPLICA_TYPE_FOLLOWER_FIRST 路由到指定副本类型,优先 Follower 路由到 route_target_replica_type 指定的副本类型,优先 Follower[reference:27] WEAKREAD_WEIGHT_LOAD_BALANCE 基于权重的负载均衡路由 通过 weakread_weight_zone 为每个 Zone 设置权重[reference:28]
3.2 路由策略配置示例
-- 通过 root@proxysys 账号登录 OBProxy
        mysql -h obproxy_host -P 2883 -uroot@proxysys

        -- 设置为优先读 Follower 副本(推荐)
        ALTER PROXYCONFIG SET proxy_route_policy = 'FOLLOWER_FIRST';

        -- 设置为只读 Follower 副本(高可用要求不高的场景)
        ALTER PROXYCONFIG SET proxy_route_policy = 'FOLLOWER_ONLY';

        -- 恢复为默认路由策略
        ALTER PROXYCONFIG SET proxy_route_policy = '';

        -- 查看当前路由策略
        SHOW PROXYCONFIG LIKE 'proxy_route_policy';
📌 路由策略选型建议
  • 生产环境推荐FOLLOWER_FIRST。在优先利用 Follower 副本的同时,保留 Leader 副本作为容灾备选。
  • 高可用要求不高的场景FOLLOWER_ONLY。确保读请求不会到达 Leader,最大化 Leader 的写入能力。
  • 多 IDC 部署场景:配合 LDC(逻辑数据中心)配置,将弱读请求路由到同 IDC 的 Follower 副本,降低跨 IDC 网络延迟[reference:29]。
  • 参数值大小写不敏感,但超出取值范围时设置可能成功但不生效[reference:30]。

四、有界旧(Bounded Staleness)与单调读(Monotonic Read)

4.1 有界旧一致性读

OceanBase 提供了租户级配置项 max_stale_time_for_weak_consistency 来定义弱一致性读允许的最大数据延迟时间,即有界旧(Bounded Staleness)一致性读[reference:31][reference:32]。

  • 作用:保证弱一致性读读取的数据不会落后于最新数据超过指定的时间阈值。
  • 默认值5 秒[reference:33]。
  • 行为:如果一个副本的数据落后时间超过该阈值,则该副本不可读,内部重试机制会尝试其他有效副本;如果所有副本都不可读,则持续重试直到语句超时[reference:34]。
-- 查看当前有界旧阈值
        SHOW PARAMETERS LIKE 'max_stale_time_for_weak_consistency';

        -- 设置弱一致性读最大延迟为 10 秒(租户级别)
        ALTER SYSTEM SET max_stale_time_for_weak_consistency = '10s';

        -- 恢复为默认值 5 秒
        ALTER SYSTEM SET max_stale_time_for_weak_consistency = '5s';
4.2 单调读(Monotonic Read)

不同的弱一致性读请求可能会路由到不同的 Follower 副本上,不同副本的数据延迟可能不同,因此即使保证了有界旧一致性,也无法保证多个请求之间数据版本的新旧顺序[reference:35]。

OceanBase 通过租户级配置项 enable_monotonic_weak_read 提供了单调读特性[reference:36][reference:37]:

  • 作用:如果进程已经看到过数据对象的某个值,则任何后续访问均不会再返回该值之前的值,保证弱一致性读的版本单调性[reference:38]。
  • 默认值false(关闭)[reference:39]。
  • 适用场景:对数据版本顺序有要求的业务场景。
-- 查看当前单调读配置
        SHOW PARAMETERS LIKE 'enable_monotonic_weak_read';

        -- 开启单调读特性
        ALTER SYSTEM SET enable_monotonic_weak_read = true;

        -- 关闭单调读特性
        ALTER SYSTEM SET enable_monotonic_weak_read = false;
4.3 弱一致性读版本号刷新周期

weak_read_version_refresh_interval 用于设置弱一致性读版本号的刷新周期,影响弱一致性读数据的延时[reference:40]。

属性描述 参数类型Time 默认值100ms(V4.0.0 起从 50ms 调整为 100ms)[reference:41] 取值范围[50ms, +∞)(V4.0.0 起从 [0ms, +∞) 调整为 [50ms, +∞))[reference:42] 修改生效设置后立即生效[reference:43]
-- 设置弱一致性读版本号刷新周期为 100ms
        ALTER SYSTEM SET weak_read_version_refresh_interval = '100ms';

五、事务中的一致性级别

弱一致性读的最佳实践是为不在事务中的 SELECT 语句指定 WEAK 一致性级别[reference:44]。在显式开启事务的场景下,一致性级别的规则如下[reference:45]:

5.1 事务一致性级别的确定规则
  • 一致性级别适用于整个事务:事务中的所有语句遵循相同的一致性级别[reference:46]。
  • 由第一条语句决定:事务的一致性级别由事务中的第一条语句决定[reference:47]。
  • 后续语句强制对齐:如果后续 SELECT 语句指定了不同的一致性级别,会被强制转换为事务的一致性级别[reference:48]。
  • 写语句仅支持强一致性INSERTDELETEUPDATESELECT FOR UPDATE 仅支持强一致性。如果事务的一致性级别为 WEAK,执行这些语句会报错 OB_NOT_SUPPORTED[reference:49]。
5.2 事务一致性级别示例
-- 示例1:事务以 INSERT 开始 → 事务级别为 STRONG
        BEGIN;
        INSERT INTO t1 VALUES (1);                        -- 强一致性(写操作)
        SELECT /*+READ_CONSISTENCY(WEAK)*/ * FROM t1;     -- Hint 被忽略,实际为 STRONG
        COMMIT;

        -- 示例2:事务以 SELECT FOR UPDATE 开始 → 事务级别为 STRONG
        BEGIN;
        SELECT * FROM t1 FOR UPDATE;                      -- SFU 强制强一致性
        SELECT /*+READ_CONSISTENCY(WEAK)*/ * FROM t1;     -- Hint 被忽略,实际为 STRONG
        COMMIT;

        -- 示例3:事务以弱一致性 SELECT 开始 → 事务级别为 WEAK
        BEGIN;
        SELECT /*+READ_CONSISTENCY(WEAK)*/ * FROM t1;     -- 事务级别为 WEAK
        -- 以下语句会报错:OB_NOT_SUPPORTED
        INSERT INTO t1 VALUES (1);                        -- 写操作不支持 WEAK
        COMMIT;
⚠️ 重要限制
如果在事务中需要同时进行读操作和写操作,必须以写操作或 SFU 语句作为事务的第一条语句,将事务一致性级别设置为 STRONG,否则后续的写操作会报错。建议不在事务中混用不同一致性级别的语句,保持事务语义的清晰和确定性。

六、生产环境最佳实践 Checklist

✅ 读写分离与弱一致性读检查清单
  • 评估业务对数据时效性的要求:确认业务是否允许读取延迟数据(如报表查询、统计分析等场景)。
  • 选择合适的配置方式:单条 SQL 使用 Hint;应用会话统一设置使用会话变量;整个应用切换使用 OBProxy 全局参数。
  • 配置合理的路由策略:生产环境推荐 FOLLOWER_FIRST,确保 Follower 不可用时自动 fallback 到 Leader。
  • 设置有界旧阈值:根据业务容忍的数据延迟,合理设置 max_stale_time_for_weak_consistency(默认 5 秒)。
  • 评估单调读需求:如果业务对数据版本的顺序有要求,开启 enable_monotonic_weak_read
  • 注意事务中的一致性级别:避免在事务中混用 STRONG 和 WEAK 一致性级别,事务的第一条语句决定了整个事务的一致性级别。
  • 通过 EXPLAIN 验证路由:使用 EXPLAIN 查看执行计划,确认弱一致性读请求是否路由到了 Follower 副本。
  • 监控 Follower 副本延迟:定期检查 Follower 副本的数据延迟,确保不超过 max_stale_time_for_weak_consistency 阈值。
🏆 小结
OceanBase 4.x 的读写分离基于弱一致性读机制实现,通过将读请求路由到 Follower 副本,有效缓解 Leader 副本的读写压力,提升系统的整体吞吐量。用户可以通过 Hint会话变量OBProxy 全局参数 三种方式灵活启用弱一致性读,并通过 proxy_route_policy 精细控制路由策略。配合 有界旧(Bounded Staleness)单调读(Monotonic Read) 特性,可以在弱一致性读的场景下提供可控的数据延迟保障和版本顺序保障。建议 DBA 和开发人员根据业务场景合理选择配置方式,充分利用 OceanBase 多副本架构的优势,在保障业务需求的前提下实现数据库读写性能的最大化。