ocean自增主键模式说明

OceanBase 自增主键模式说明

适用范围:OceanBase MySQL 兼容模式。AUTO_INCREMENT_MODE 的支持情况、默认值及具体行为应以目标版本为准。本文示例未连接实际实例执行,不应直接套用于原生 MySQL。

一、先区分四个概念

概念 作用 不保证什么
AUTO_INCREMENT 在插入时自动生成 ID 不保证编号连续
PRIMARY KEY (id) 强制 ID 非空且全表唯一 不表示 ID 就是创建时间或提交时间
AUTO_INCREMENT_MODE 控制自增值生成的有序性模式 不改变查询的排序规则
PARTITION BY HASH (id) 根据 ID 的 HASH 规则决定数据属于哪个分区 不按连续 ID 范围分区
text 复制代码
应用插入订单,不传 id
          |
          v
自增机制分配 id
          |
          v
HASH 分区规则确定目标分区
          |
          v
数据库写入数据并检查主键唯一性

这是概念上的职责划分,不代表数据库内部严格的执行步骤。

二、自增 ID 与并发插入

定义自增列后,应用可以不提供 ID:

sql 复制代码
INSERT INTO orders (user_id, amount)
VALUES (1001, 99.90);

并发插入时,数据库负责协调生成自增值,应用不需要使用 SELECT MAX(id) + 1 计算 ID。后者存在并发竞争,不应当作 ID 生成方案。

PRIMARY KEY (id) 是唯一性约束:即使应用手动提供 ID,数据库也不会允许相同 ID 的两条记录都成功提交。手动插入已有 ID 的普通 INSERT 会产生重复键错误。

为什么自增值不保证连续?

表中的 ID 可能是:

text 复制代码
101、102、105、108......

缺号不一定表示数据丢失。常见原因包括:

  • 事务分配 ID 后回滚,已分配的值通常不会回收。
  • 某些插入执行路径先分配 ID,随后因其他原因失败。
  • 已有记录被删除,空缺不会自动补回。
  • 预分配或缓存的自增值未用完,可能因重启等情况留下空缺,具体取决于版本和模式。
text 复制代码
事务 A:获得 101 -> 提交成功
事务 B:获得 102 -> 回滚
事务 C:获得 103 -> 提交成功

表中最终保留:101、103

因此不能使用 MAX(id) 计算订单总数,也不能仅凭缺号判断是否丢单。

三、AUTO_INCREMENT_MODE 的两种模式

3.1 ORDER:有序自增

ORDER 关注自动生成值的全局有序性。需要区分自增值的分配顺序、请求到达顺序和事务提交顺序,它们并不是同一件事。

text 复制代码
事务 A:先获得 ID 101,执行较慢
事务 B:后获得 ID 102,先提交
事务 A:随后提交

分配顺序:101 -> 102
提交顺序:B -> A

即使选择 ORDER

  • ID 也不保证连续。
  • ID 大小也不代表事务提交先后。
  • ID 顺序也不能严格代替业务创建时间顺序。
  • 全局有序生成需要协调,应结合实际负载评估性能成本。

3.2 NOORDER:无序自增

NOORDER 不要求跨节点生成的自增值按分配时间全局递增,适用于更关注分布式生成效率的场景。

可以通过号段缓存理解可能出现的现象:

text 复制代码
分配端 A 缓存较小的一段 ID
分配端 B 缓存较大的一段 ID

B 先生成一个 ID:10001
A 后生成一个 ID:101

这里的数字和缓存分配端只是示意,不代表固定号段大小,也不代表每个分区都固定拥有一个号段。具体缓存粒度和分配实现以版本为准。

无序表示生成顺序不要求全局递增,不表示允许主键重复,也不表示查询结果无法排序。

3.3 模式对比

比较项 ORDER NOORDER
自动生成值的全局有序性 关注全局有序生成 不要求全局有序生成
是否保证无跳号
是否保证按事务提交顺序编号
PRIMARY KEY (id) 是否约束 ID 唯一
是否影响 ORDER BY id 的正确性
选择侧重点 有序生成需求 分布式生成效率

不要把某种模式当作所有版本的默认值。未显式指定时,应根据实际版本和表定义确认生效模式。

四、建表 DDL

以下两种方案二选一,不要在同一数据库中重复创建同名表。

4.1 ORDER 模式

sql 复制代码
CREATE TABLE orders (
    id BIGINT NOT NULL AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    amount DECIMAL(18, 2) NOT NULL,
    status TINYINT NOT NULL DEFAULT 0,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,

    PRIMARY KEY (id),
    KEY idx_created_at (created_at)
)
AUTO_INCREMENT_MODE = 'ORDER'
PARTITION BY HASH (id)
PARTITIONS 8;

4.2 NOORDER 模式

sql 复制代码
CREATE TABLE orders (
    id BIGINT NOT NULL AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    amount DECIMAL(18, 2) NOT NULL,
    status TINYINT NOT NULL DEFAULT 0,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,

    PRIMARY KEY (id),
    KEY idx_created_at (created_at)
)
AUTO_INCREMENT_MODE = 'NOORDER'
PARTITION BY HASH (id)
PARTITIONS 8;

两个示例都是 id 单列自增主键,按 ID 做 HASH 分区。8 只是示例分区数,应结合数据量和租户资源确定。不需要分区时,可以移除末尾的 PARTITION BY HASH (id) PARTITIONS 8,保留语句结尾分号。

4.3 为什么不继续按 created_at 分区?

前面的按月分区设计采用:

sql 复制代码
PRIMARY KEY (id, created_at)

该主键只保证组合唯一,不单独保证 id 唯一。在这里讨论的 OceanBase MySQL 分区表规则下,主键需要包含分区键。因此,要求 PRIMARY KEY (id) 时,应将分区键改为 id,或使用非分区表。

如果业务必须按创建时间分区,又必须由数据库约束 ID 全表唯一,应结合目标版本评估"包含分区键的联合主键 + ID 全局唯一索引"。全局唯一索引不等于 ID 单列主键。

五、号段分配不等于按号段分区

这两个问题需要分开理解:

问题 对应机制
下一个 ID 如何产生? 自增模式及其分配、缓存机制
这个 ID 对应的记录存在哪个分区? 表的分区规则

当前 HASH 分区不表示:

text 复制代码
分区 1:ID 1~10000
分区 2:ID 10001~20000
分区 3:ID 20001~30000

按连续 ID 范围划分属于 RANGE 分区。内部即使存在自增号段缓存,也不会把 HASH 分区自动变成 RANGE 分区。

六、跨分区排序

6.1 按 ID 排序没有正确性问题

sql 复制代码
SELECT *
FROM orders
ORDER BY id DESC
LIMIT 20;

该查询返回整个查询范围中 ID 最大的 20 条记录,不是每个分区分别返回一组未合并的结果。

text 复制代码
多个分区的数据
      |
      v
数据库全局排序或有序归并
      |
      v
返回 ID 最大的 20 条

实际算法由执行计划决定。ID 跳号及 NOORDER 生成模式都不影响排序正确性。不写 ORDER BY,则不能依赖默认返回顺序。

6.2 按创建时间展示订单

"ID 最大的订单"不一定等于"最新创建的订单"。需要按创建时间展示时使用:

sql 复制代码
SELECT *
FROM orders
ORDER BY created_at DESC, id DESC
LIMIT 20;

id 用于在创建时间相同时提供确定的排序顺序。当前 DATETIME 没有小数秒精度,同一秒创建多条订单很正常;即使改成 DATETIME(6),时间戳也不能保证唯一。

6.3 跨分区查询的成本

HASH 分区不按 ID 连续范围排列。全表按 ID 取前 20 条通常需要访问多个分区,再合并结果,但不一定需要读取所有记录。

id = 12345 可以据分区键定位分区;id < 9801 或仅按时间过滤,通常不能据此裁剪 ID 的 HASH 分区。实际性能应通过执行计划和数据规模验证。

七、按 ID 游标分页

sql 复制代码
-- 第一页
SELECT *
FROM orders
ORDER BY id DESC
LIMIT 20;

-- 下一页,9801 为上一页最后一条记录的 ID
SELECT *
FROM orders
WHERE id < 9801
ORDER BY id DESC
LIMIT 20;

该方式无需 ID 连续,也能避免大偏移量 OFFSET 的开销。但它不自动提供跨多次查询的一致快照:

  • 并发写入、删除或晚提交可能改变后续页看到的数据。
  • NOORDER 下,后生成的 ID 可能小于已有 ID,不能把 ID 游标直接当作完整的增量变更水位。
  • 即使使用 ORDER,较小 ID 的事务晚提交,也可能让按 id > 上次最大值 轮询的程序漏读。

需要可靠地捕获所有变更时,应评估 CDC 或具有明确一致性保证的同步方案,而不是仅依赖自增 ID。

八、选型与验证

业务需求 建议
ID 只作为唯一标识,侧重分布式生成效率 评估 NOORDER
需要自动生成值全局有序 评估 ORDER,并测试性能
展示最新创建的订单 使用创建时间排序,ID 作为次级排序键
编号严格连续、不允许跳号 另行设计业务编号机制,两种模式都不能直接满足
防止同一订单请求重试后重复创建 使用稳定的业务唯一键或幂等机制,自增主键本身不能识别重复请求

先检查版本,再检查实际表定义:

sql 复制代码
SELECT VERSION();

SHOW CREATE TABLE orders;

如果表定义未显示模式,不要仅凭这一点推断默认值,应查阅对应版本文档。若建表提示不支持 AUTO_INCREMENT_MODE,需要核对版本和兼容模式,而不是假定设置已经生效。

九、总结

自增负责生成 ID,主键负责约束唯一,分区负责组织数据,ORDER BY 负责查询排序。

  • ORDERNOORDER 区分的是生成有序性,不是查询能否排序。
  • 两种模式都不保证 ID 连续,也不保证 ID 顺序等于事务提交顺序。
  • HASH 分区和自增号段是独立的概念。
  • 跨分区按 ID 排序可以得到正确的全局结果,但需要评估执行成本。
  • 默认模式及可用语法必须结合目标 OceanBase 版本确认。
相关推荐
leoZ2311 小时前
2026-09-08-mysql-57-init-walkthrough
前端·javascript·数据库·vue.js·opencv·mysql·adb
云飞云共享云桌面1 小时前
钣金制造研发优化:不采购传统工作站,实现 SolidWorks 团队共用服务器开展三维建模
运维·服务器·网络·数据库·3d·制造
4SAPI1 小时前
2026年大模型API接入选型指南:企业与个人用户的架构、稳定性与成本考量
大数据·开发语言·数据库·人工智能·架构·php
xixiaoyunya2 小时前
只备份数据远远不够:服务器系统备份的完整方案与实践
服务器·网络·数据库
大模型丫丫2 小时前
LangGraph + MCP(Model Context Protocol)完整讲解
java·开发语言·数据库
我不是程序员三三2 小时前
怎么监控电脑?企业办公终端监控建设实战与合规要点
服务器·数据库·电脑
这个DBA有点耶2 小时前
时间序列数据库选型2026:5款主流产品深度对比与场景适配
大数据·数据库·程序人生·架构·时序数据库·dba·数据库管理员
砚底藏山河2 小时前
拉数管道设计:从一次性脚本到可重启的数据流水线(魔码量化实战 #01)
java·数据库·python·金融·maven
2601_966377133 小时前
运营商密评密改落地实践:透明加密解决密码应用合规与安全痛点
数据库·安全·oracle