简介:《OceanBase数据库应用开发基础》是一份面向数据库开发者的入门与进阶PDF文档,系统梳理蚂蚁集团自研分布式数据库OceanBase的核心开发知识,重点覆盖SQL标准操作、索引设计与优化、ACID事务模型、锁机制与并发控制,以及ODBC/JDBC等常用开发接口,适合正在学习国产分布式数据库或需要快速上手OceanBase应用开发的工程师。文档基于关系型与分布式数据库的共性原理展开,结合OceanBase特有的大规模数据存储、高并发访问、分布式事务和自适应性能优化机制,帮助读者理解从单机到分布式场景下的开发差异与设计思路。资源为单个PDF文件,大小14.27MB,内容结构完整、图文结合,便于在电脑端或移动端阅读,已有127人下载学习。学习后可以掌握OceanBase建表、查询、索引选择、事务处理及并发控制等关键能力,为实际业务场景下的应用开发打下扎实基础。
1. 与其猜 OceanBase 难不难,不如先搞懂“应用开发基础”里藏着哪些硬约束
第一次接手 OceanBase 的团队,最容易产生的幻觉是“这不就是个增强版 MySQL 嘛”。等真正开始写业务代码,才会发现 SQL 语法确实熟悉,但执行计划长不一样、慢查询的排查思路不一样、连数据库的用户名都要多一层租户概念。这份《OceanBase数据库应用开发基础》要解决的正是一线开发者的真实痛点:不是把运维手册读一遍,而是快速建立“在分布式约束下写正确代码”的思维框架。本文从架构约束出发,一步步带你完成本地环境搭建、连接配置、建表 CRUD、驱动与事务、数据导入,最后落在四个高频翻车场景和一批能直接用的优化习惯。无论你是刚接触 OceanBase,还是已经从 MySQL 迁移过来踩过几个坑,都能在这里找到可复现的操作和参数边界。
2. 先看懂 OceanBase 的架构约束,再决定怎么写业务代码
很多开发者习惯把数据库当成一个黑匣子:写好 SQL,建好索引,剩下的交给数据库。这个习惯在单机 MySQL 上勉强够用,到了 OceanBase 上就很容易翻车。原因很简单,OceanBase 的每条 SQL 都要被路由到正确的数据分片,如果表结构设计不合理,一次看似普通的查询也可能触发全分区扫描,性能直接掉一个数量级。所以我带项目的第一件事,不是先去装环境,而是先让团队理解三条架构约束:数据怎么存、请求怎么路由、事务怎么保证。理解这三件事,后续写代码才不会靠猜。
2.1 从单库到原生分布式:它解决了扩容问题,也改变了你的 SQL 习惯
在单机 MySQL 时代,业务增长到一定体量后,最常见的手段是分库分表。分库分表中间件能解决容量问题,但会引入新的麻烦:跨分片的 join 不能做、分布式事务要靠应用层补偿、扩容要重新分配数据、全局 ID 要额外维护一套发号器。这些痛点正是 OceanBase 这类原生分布式数据库想解决的。
OceanBase 的做法是把一张逻辑表按分区键拆成多个物理分区,每个分区存储在不同节点上,同时每个分区再保存多个副本,副本之间通过分布式一致性协议同步数据。应用看到的还是“一张表”,但底层数据已经被打散到多台机器。写入一条数据时,系统会根据分区键计算它属于哪个分区,再同步到该分区的多个副本;查询一条数据时,系统会先定位到对应分区,再返回结果。
这套机制给开发者的第一个约束是:SQL 里尽量带上分区键。举个例子,订单表orders如果以user_id作为分区键,那么WHERE user_id = 123这条查询就能直接路由到唯一分区,OceanBase 只需要扫描一个分区的数据;而如果查询条件是WHERE order_no = 'xxx',系统无法直接定位分区,只能扫所有分区的数据,再合并结果。数据量小的时候感觉不出来,数据量过了千万级,这种全分区扫描就是慢查询的常见根源。
第二个约束是:事务的代价与涉及的分区数量成正比。OceanBase 支持跨分事务变更,但跨节点协调一定比单分区事务慢。日常编码时,尽量把需要原子更新的数据放进同一个分区,比如同一用户的订单明细和支付流水都按user_id分区,就能在单分区内完成事务。
2.2 租户、分区、副本:开发前先建立这三个底层概念
第一次连接 OceanBase 时,很多开发者会被用户名吓到:为什么用户名是user@tenant而不是单纯的user?这就是租户概念。租户可以理解为一个隔离的资源容器,里面有独立的内存、CPU、连接数上限,逻辑上像一个独立的数据库实例。业务系统里不同的项目可以分配到不同的租户,互相之间资源隔离。
分区就是前面提到的数据分片。OceanBase 建表时可以显式指定分区键和分区方式,比如PARTITION BY HASH(user_id) PARTITIONS 8表示按用户 ID 散列成 8 个分区。分区数量直接影响并发能力,但也不是越多越好,分区太多会让执行计划的维护成本变高。
副本是数据冗余的单位,默认生产环境通常是 3 副本,副本越多容错能力越强,但写入同步的代价也越高。对应用开发而言,副本数一般由 DBA 配置,你只需要知道一件事:读请求默认会走主副本,如果想做读写分离,需要配置只读副本并在应用层通过 URL 指定,简单的连接串直连并不能自动分流。
我们实际建表时一般会这样写:
CREATE TABLE orders ( id BIGINT NOT NULL, user_id BIGINT NOT NULL, order_no VARCHAR(64) NOT NULL, amount DECIMAL(12,2), status TINYINT, create_time DATETIME, PRIMARY KEY (id, user_id) ) PARTITION BY HASH(user_id) PARTITIONS 8;这段 SQL 值得注意两个点。第一,主键是(id, user_id)而不是单独的id,因为 OceanBase 要求分区表的主键必须包含分区键,否则会报错或者在建全局索引时付出额外代价。第二,分区方式用的 HASH,适合像用户 ID 这种离散值,如果分区键是日期,改成PARTITION BY RANGE(create_time)会更合适。建表不是只会写CREATE TABLE就行,分区键选错,后续所有查询都在还债。
2.3 MySQL 兼容但不意味着能把 MySQL 直接搬过来
OceanBase 的 MySQL 兼容模式做得不错,常见的数据类型、SQL 语法、JDBC 驱动都能直接使用,这也是很多团队选择它的原因。但“兼容”和“完全一致”之间有一条缓冲带,需要开发者心里有数。
先说能用的部分:SELECT、INSERT、UPDATE、DELETE、JOIN、子查询、视图、存储过程、触发器在 MySQL 模式下都有支持,日常 CRUD 几乎无感。连驱动都可以用 MySQL 官方驱动连接,只要在连接串里把端口指向 OceanBase 的协议端口即可。
再说需要小心的部分。第一,分布式场景下,SELECT ... FOR UPDATE锁定的范围可能比 MySQL 更大,尤其是在跨分区事务里,锁竞争会更明显。第二,AUTO_INCREMENT自增列虽然在 OceanBase 中可用,但只能保证全局唯一,不能保证连续递增,如果业务上要求“订单号必须连续”,自增列会翻车,建议用雪花 ID 或应用层发号器。第三,部分 MySQL 系统表和运维指令在 OceanBase 里不存在,比如SHOW ENGINE INNODB STATUS这类命令就别指望了,要看执行计划请用EXPLAIN。
我一般建议团队在迁移前做一次 SQL 兼容性审计:把业务里所有手写的 SQL 收集起来,按类型分成查询、更新、事务、DDL 四类,先在测试环境跑一遍。重点检查三条:SQL 是否带分区键、事务是否跨分区、唯一索引是否包含分区键。这三条检查完,80% 的潜在问题都能在开发阶段暴露出来,而不是等上线后由用户来报告。
3. 本地搭一套 OceanBase 开发环境:部署、连接与第一张表
理解架构约束后,接下来要亲手把环境跑起来。OceanBase 的部署方式和企业级生产环境不完全一样,但对应用开发来说,本地起一个单机实例足够用了。这一章我会带着你走完三步:用 OBD 拉起一个 OceanBase 实例、用命令行和 JDBC 两种方式验证连接、建出第一张分区表并完成基础 CRUD。
3.1 用 OBD 在笔记本上拉起一个单机 OceanBase
OceanBase 的部署工具叫 OBD(OceanBase Deployer),它的作用类似一个集群管家,可以帮你在单台机器上模拟一套最小集群。本地开发时不需要搞三台服务器,单机部署也能启动 observer 进程。
先检查环境和 OBD 是否就绪:
# 检查系统资源,observer 建议至少 2 核 4G lscpu | grep -E '^CPU|^Model name' free -h # 查看 OBD 是否安装 obd --version如果obd命令不存在,需要先安装 OBD 工具。安装完成后,可以用obd demo快速拉起一套 mini 环境,这是最省事的方式:
obd demoobd demo会自动创建一套单机部署的 OceanBase 实例,默认监听 2881 端口(MySQL 协议端口)和 2882 端口(内部 RPC 端口)。如果你想手动控制配置,可以创建一个 YAML 配置文件,指定内存、磁盘和安装路径,示例配置大致如下:
oceanbase-ce: servers: - 127.0.0.1 global: devname: lo memory_limit: 4G system_memory: 1G datafile_size: 5G log_disk_size: 5G这个文件的意义在于能精准控制资源占用,避免笔记本因内存不足直接卡死。devname是网卡名称,Linux 里常用lo或eth0,macOS 上需要根据ifconfig的输出修改;memory_limit是 observer 进程的可用内存上限,不要超过物理内存的一半;datafile_size是数据文件占用磁盘的上限,本地调试不用给太大。
部署完成后的标准操作是查看集群状态:
obd cluster list obd cluster start <集群名>如果启动失败,先看日志,日志路径通常在~/obd/log/下。我遇到最多的问题是端口被占用和内存不足,前者用lsof -i:2881查,后者直接改小memory_limit。
3.2 客户端连接:从 mysql 命令行到 JDBC 连接串
OceanBase 提供 MySQL 协议端口,所以客户端工具可以直接用mysql命令行连接。但连接串的写法有几个约定俗成的坑,建议一开始就记住。
命令行连接方式如下:
mysql -h127.0.0.1 -P2881 -u'root@sys' -p这里root@sys中的root是用户名,sys是租户名。OceanBase 的身份体系是“用户名@租户名”,默认有一个名为sys的租户,是集群的管理员租户,里面没有业务数据。如果你想连业务租户,比如一个名为test的租户,用户名就写root@test。
用 JDBC 连接时,连接串长这样:
String url = "jdbc:mysql://127.0.0.1:2881/app_db?useSSL=false&useUnicode=true&characterEncoding=utf8mb4&rewriteBatchedStatements=true"; String username = "user@test"; String password = "your_password";注意username这里同样要带上租户名,这是新手最容易漏掉的地方。连接串参数里有两个值得关注:characterEncoding=utf8mb4保证中文不乱码;rewriteBatchedStatements=true能把多条 insert 语句重写为批量提交,对写入性能影响极大,后面会单独讲。
验证连接是否正常,可以执行一条最简单的 SQL:
SELECT 1 + 1 AS result;如果连接失败,优先看三件事:端口是否听对、租户名是否写对、密码里的特殊字符是否被 URL 转义。
3.3 建库建表与基础 CRUD:主键、分区和分区裁剪的第一次实战
连接成功后,我们建一个业务库和一张分区表。这里直接沿用上一章的订单表设计,再加一张订单明细表,两张表都按user_id分区,这样后续做 join 和事务都能保持在同一个分区内。
CREATE DATABASE app_db; CREATE TABLE orders ( id BIGINT NOT NULL, user_id BIGINT NOT NULL, order_no VARCHAR(64) NOT NULL, amount DECIMAL(12,2), status TINYINT, create_time DATETIME, PRIMARY KEY (id, user_id) ) PARTITION BY HASH(user_id) PARTITIONS 8; CREATE TABLE order_items ( id BIGINT NOT NULL, order_id BIGINT NOT NULL, user_id BIGINT NOT NULL, sku_id BIGINT, quantity INT, PRIMARY KEY (id, user_id) ) PARTITION BY HASH(user_id) PARTITIONS 8;建表逻辑说明:PRIMARY KEY (id, user_id)之所以把user_id放进去,是因为 OceanBase 要求分区表的主键必须包含分区键,否则建表会直接失败。PARTITION BY HASH(user_id) PARTITIONS 8将数据按用户 ID 散列到 8 个分区,这样同一个用户的数据只会落在同一个分区。
接下来做基础 CRUD:
INSERT INTO orders (id, user_id, order_no, amount, status, create_time) VALUES (1, 1001, 'NO20240001', 199.00, 0, NOW()); INSERT INTO order_items (id, order_id, user_id, sku_id, quantity) VALUES (1, 1, 1001, 10001, 2); SELECT o.order_no, o.amount, o.status, i.sku_id, i.quantity FROM orders o JOIN order_items i ON o.order_id = i.order_id AND o.user_id = i.user_id WHERE o.user_id = 1001; UPDATE orders SET status = 1 WHERE order_no = 'NO20240001' AND user_id = 1001; DELETE FROM order_items WHERE order_id = 1 AND user_id = 1001;这段 CRUD 最大的特点是:所有查询和更新都带上了分区键user_id。WHERE o.user_id = 1001让数据库能直接定位到对应分区;JOIN条件里也带上了user_id,避免跨分区做 join。如果查询不带分区键,OceanBase 也能跑,但会扫描所有分区,性能随着时间线明显下滑。这不是 OceanBase 的问题,而是分布式数据库的物理限制,写 SQL 时把分区键当成一等公民来对待,是成本最低的优化。
4. 应用接入 OceanBase:驱动、连接池、事务与数据导入
环境通了、表建好了,接下来是真正的应用开发阶段。这一章会讲四个绕不开的主题:驱动怎么选、连接池怎么配、事务怎么写才不踩雷、数据怎么批量导入。每个主题都有可以直接抄走的配置和代码。
4.1 JDBC 驱动与连接池:先配好这几个参数再上线
OceanBase 官方提供了 JDBC 驱动,直接依赖它就行。如果你用 Maven,在pom.xml里加:
<dependency> <groupId>com.oceanbase</groupId> <artifactId>oceanbase-client</artifactId> <version>2.4.x</version> </dependency>这里不写死具体版本,建议用当前稳定版。如果项目里已经有 MySQL Connector/J,也可以连 OceanBase,但我仍然推荐用官方驱动,毕竟内部实现更匹配协议细节。
连接池我一般用 HikariCP,配置简单而且监控信息全。一个可用的配置如下:
HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://127.0.0.1:2881/app_db?useSSL=false"); config.setUsername("user@test"); config.setPassword("your_password"); config.setDriverClassName("com.oceanbase.jdbc.Driver"); config.setMaximumPoolSize(20); config.setMinimumIdle(5); config.setConnectionTimeout(30000); config.setMaxLifetime(1800000); config.setValidationTimeout(5000);这里有三个参数值得专门说明。maximumPoolSize不是越大越好,OceanBase 对每租户的连接数有上限,默认可能只有几百个,如果每个应用实例都配 100 个连接,两三台机器就能把连接数打满。maxLifetime建议设置为 30 分钟以下,因为数据库端可能回收空闲连接,应用这边持有过久会导致连接突然失效。connectionTimeout是获取连接的超时时间,设 30 秒比较合理,太短会在数据库抖动时直接报错,太长会让请求线程堆积。
还有一个容易翻车的点:HikariCP 默认会执行一次 connection test,这段测试 SQL 在 OceanBase 上可能不兼容。如果启动时看到connection is not available或者 test 语句报错,可以把connectionTestQuery显式设置为SELECT 1,或者直接依赖validationTimeout做心跳。
4.2 事务与隔离级别:分布式事务没那么神秘,但代价要认清
OceanBase 支持标准事务语法,应用层写起来和 MySQL 差别不大:
Connection conn = dataSource.getConnection(); try { conn.setAutoCommit(false); // 扣减库存 PreparedStatement ps1 = conn.prepareStatement( "UPDATE inventory SET stock = stock - ? WHERE sku_id = ? AND user_id = ?"); ps1.setInt(1, 2); ps1.setLong(2, 10001L); ps1.setLong(3, 1001L); ps1.executeUpdate(); // 插入订单 PreparedStatement ps2 = conn.prepareStatement( "INSERT INTO orders (id, user_id, order_no, amount, status, create_time) VALUES (?, ?, ?, ?, ?, NOW())"); ps2.setLong(1, 1L); ps2.setLong(2, 1001L); ps2.setString(3, "NO20240002"); ps2.setBigDecimal(4, new BigDecimal("299.00")); ps2.setInt(5, 0); ps2.executeUpdate(); conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { conn.close(); }这段代码的核心思想是:两条 SQL 在同一个事务里,要么都成功,要么都失败。OceanBase 默认隔离级别是读已提交,这个级别下已经能避免脏读,大部分业务都够用。如果业务需要可重复读,可以手动设置:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;但要注意两点。第一,跨分区事务是支持的,但代价远高于单分区事务,所以写代码前先确认事务里的所有更新是否都在同一个分区上。第二,ob_trx_timeout是事务超时时间,默认 100 秒,如果事务里夹了远程调用或者大量数据更新,超过这个时间就会被强制回滚,应用层会看到异常的Transaction aborted错误。我一般建议把事务内的逻辑控制在 200 毫秒以内,超过这个阈值就考虑拆事务或者异步化。
4.3 批量写入与数据导入:初始化数据的三种姿势
应用上线时经常需要初始化数据,比如从历史库导入订单明细。这个场景下,逐条 INSERT 是最慢的,一般有三种选择:JDBC 批量提交、LOAD DATA 文件导入、旁路导入。
JDBC 批量提交是最简单的优化手段,关键在于连接串加rewriteBatchedStatements=true:
String sql = "INSERT INTO orders (id, user_id, order_no, amount, status, create_time) VALUES (?, ?, ?, ?, ?, NOW())"; try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { conn.setAutoCommit(false); for (Order order : orderList) { ps.setLong(1, order.getId()); ps.setLong(2, order.getUserId()); ps.setString(3, order.getOrderNo()); ps.setBigDecimal(4, order.getAmount()); ps.setInt(5, order.getStatus()); ps.addBatch(); if (orderList.size() % 500 == 0) { ps.executeBatch(); } } ps.executeBatch(); conn.commit(); }这段代码的要点是:addBatch先把 SQL 攒在内存里,每 500 条执行一次executeBatch,最后统一提交。rewriteBatchedStatements=true让连接串把这一批 SQL 重写成一条多行 INSERT,网络往返次数大幅减少。不加这个参数,executeBatch实际上还是逐条执行,性能提升有限。
如果是静态文件导入,LOAD DATA更直接:
LOAD DATA INFILE '/tmp/orders.csv' INTO TABLE orders FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES (id, user_id, order_no, amount, status, @dummy_create_time) SET create_time = NOW();LOAD DATA 能绕过应用层直接走数据库内部导入链路,百万行的文件通常几十秒就能导完。参数说明:FIELDS TERMINATED BY ','是字段分隔符,IGNORE 1 LINES是跳过文件头,@dummy_create_time表示不直接导入这一列,而是通过 SET 子句填充。需要注意的是,LOAD DATA客户端和服务端要在同一台机器或同一网络域,如果文件在应用服务器上,需要走标准客户端连接。
第三种是旁路导入,适合超大数据量初始化,但它通常是 DBA 操作范畴,应用开发阶段用前两种就足够了。把批量提交和 LOAD DATA 都跑通,日常数据初始化不会有压力。
5. OceanBase 应用开发避坑指南:4 个让线上翻车的真实场景
这一章是我最想写的内容。分布式数据库和单机数据库在故障表现上有很大差异,很多问题在测试环境根本复现不了,一上生产就爆发。下面四个场景来自实际项目里的血泪经验,每个都按“现象 → 原因 → 解决”的顺序讲清楚。
5.1 慢查询不是玄学:先查执行计划,再决定要不要加索引
现象:同一条 SQL 在测试环境跑 10 毫秒,到了生产环境变成 2 秒。测试环境数据量只有几十万,生产环境已经上千万。
原因:大多数时候不是数据库坏了,而是查询没走分区裁剪,也没走合适的索引。OceanBase 的优化器会根据 SQL 生成执行计划,如果 where 条件里没有分区键,它只能扫描多个分区,数据量大了自然慢。
解决:不要上来就加索引,先用EXPLAIN看执行计划。OceanBase 的 EXPLAIN 输出格式和 MySQL 略有不同,但关键信息很直接:
EXPLAIN SELECT * FROM orders WHERE user_id = 1001 AND status = 1;重点看输出里有几个Partitions,如果数值接近全部分区数,说明没裁剪成功。再看Access Path是走主表还是二级索引。定位到问题后,再决定加索引:
CREATE INDEX idx_status ON orders(status, user_id);这里有个细节:二级索引的字段顺序有讲究。status放前面适合按状态筛选,但必须把分区键user_id也放进去,否则索引无法直接定位分区,还是要回主表扫描。
5.2 字符集与连接串设置:中文不乱码的底线配置
现象:应用写入的中文变成???,或者数据能写进去但读出来乱码。
原因:租户的字符集、客户端连接串的字符集、应用服务器默认编码三层不一致。最常见的是数据库端字符集是latin1,连接串没写characterEncoding=utf8mb4。
解决:统一字符集链路。第一,建租户时把字符集设成utf8mb4;第二,连接串显式加:
jdbc:mysql://127.0.0.1:2881/app_db?useUnicode=true&characterEncoding=utf8mb4第三,应用代码里所有的字符串操作统一走 UTF-8。另外,OceanBase 的utf8mb4排序规则和 MySQL 略有差异,如果排序结果和你预期不符,检查排序规则是否设置为utf8mb4_general_ci或utf8mb4_bin,这两个行为不一样。
5.3 自增主键、唯一索引与分区键:分布式下最容易踩的索引坑
现象:自增主键在插入时突然跳号,比如 1、2、3、10001、10002;或者业务表建唯一索引时直接报错,说唯一约束必须包含分区键。
原因:OceanBase 的自增列是全局唯一的,但不保证连续,因为每个节点都有独立的缓存区间;唯一索引如果不能确定数据落在哪个分区,就必须建全局索引,而全局索引在分布式环境下的维护代价很高,某些配置下干脆禁止了这种建法。
解决:第一,主键列能用业务号就用业务号,比如订单号、流水号,别依赖自增列做业务展示。第二,如果坚持用自增列,应用层不要假设它是连续的。第三,唯一索引的字段列表里一定要带上分区键,比如订单表按user_id分区,那(order_no, user_id)做唯一索引就没问题,单独(order_no)就有隐患。这样调整后,唯一性检查能在本地分区完成,写入性能不会断崖下跌。
5.4 事务超时与连接池耗尽:一个事务里别干太多活
现象:线上偶尔报Transaction is aborted或Connection is not available,重启应用后暂时恢复正常,过几天又出现。
原因:这类问题通常是两个因素叠加。第一,事务执行时间太长,超过ob_trx_timeout,数据库主动回滚事务;第二,事务持有连接时间过长,连接池的连接都被这种长事务占满,新的请求拿不到连接。
解决:代码层面把事务拆小,事务内只做数据库操作,绝不放 HTTP 调用、文件读写、消息队列发送这些耗时操作。配置层面调大连接池的最大连接数只能缓解症状,治本是给事务设置明确的超时时间:
SET ob_trx_timeout = 30000000; SET ob_query_timeout = 10000000;这里的时间单位是微秒,ob_trx_timeout设为 30 秒,ob_query_timeout设为 10 秒,超过直接报错,让故障快速暴露,而不是把线程耗死。排查时用SHOW FULL PROCESSLIST看有没有长时间执行的会话,这个命令在 MySQL 兼容模式下可用。
6. 把性能优化养成开发习惯:分区裁剪、索引设计与批量提交的最后一公里
到了最后这一章,我不想再讲新特性,更想谈三个能直接提升开发质量的习惯。这三个习惯都不难,难的是在每天写代码时记住它们。
第一个习惯是写 SQL 前先问自己:这条语句能确定分区吗?OceanBase 的分区裁剪性能提升是数量级的,同样的条件WHERE order_no = ?,如果表按order_no分区那就是点查询,如果按user_id分区那就要全分区扫。所以建表阶段就要想清楚最常见的查询条件是什么,让分区键尽量匹配高频查询。我习惯每张表建完先跑一次EXPLAIN,确认典型查询的Partitions数量是 1,不是 8 或者 16。
第二个习惯是索引设计时把分区键带上。无论是普通二级索引还是唯一索引,字段顺序里都包含分区键,这能让索引裁剪和分区裁剪同时生效。不要迷信“多建索引就是优化”,分布式数据库的每个全局索引都会影响写入性能,索引数量控制得越精,写入路径越稳。
第三个习惯是批量操作必须开rewriteBatchedStatements=true。很多团队迁移 OceanBase 后写入变慢,排查到最后发现是批量 insert 没被真正重写,一条条执行当然慢。在连接串里加这个参数,配合executeBatch,就能享受到多行写入的加速。平时写代码时把连接串模板统一维护好,新项目直接复用,不会再踩同样的坑。
我曾经在一个订单迁移项目中,因为少带了分区键,一个数据清洗任务跑了整整一夜;后来把查询条件补上user_id,同样的数据量只用了二十分钟。那一刻我才意识到,分布式数据库的优化不是靠某条神奇的配置,而是靠对数据分布的理解和习惯的力量。希望这篇笔记能帮你少走那段夜路,让 OceanBase 的开发体验更顺一些。希望帮到你。
本文还有配套的精品资源,点击获取