基于湖库一体架构,统一管理结构化、半结构化与非结构化等多模态数据,一个系统承载事务处理、实时分析与 AI 工作负载。
体验并行导入与高压缩
更新时间:2026-10-27 15:56:46
数据写入效率和存储成本是数据库在大数据量场景下的两大核心诉求。OceanBase 数据库基于 LSM-Tree 存储架构,提供了旁路导入(Direct Load)的并行数据加载能力,可以绕过 SQL 层直接将数据写入底层存储;同时在合并(Compaction)阶段对数据进行编码与压缩,在不影响写入性能的前提下显著降低存储占用。本文将通过一批 100 万行的样例数据,帮助您直观体验 OceanBase 数据库的写入效率与存储成本优化能力。
本文将引导您完成以下体验:
- 准备批量样例数据:生成 100 万行 CSV 格式的测试数据。
- 体验并行导入:使用旁路导入结合并行度完成大批量入库,并与常规导入对比耗时。
- 校验导入结果:核对导入行数,确认数据完整入库。
- 体验数据编码与压缩:创建不同压缩配置的表,对比压缩前后的存储占用。
- 验证查询可用性:确认压缩存储的数据可正常查询且结果一致。
说明
本文基于 OceanBase 数据库 V5.0.1(社区版)撰写,文中相关命令和功能说明均以 V5.0.1 版本为准。本文以 部署 OceanBase 中使用 All in One 安装包部署的单节点环境为例,全部操作均可在单节点环境中完成。
前提条件
在开始本文操作前,确保满足以下条件:
| 项目 | 描述 |
|---|---|
| OceanBase 数据库 | 已使用 All in One 安装包完成部署并成功启动(部署名为 demo),参见 部署 OceanBase。 |
| 机器资源 | 建议不低于 4 核 CPU、16 GB 内存,磁盘剩余空间不低于 5 GB。 |
| obclient | 已安装 obclient 并可正常连接 OceanBase 数据库。使用 All in One 安装包部署时已自动安装。 |
| sys 租户访问 | 可使用 root 用户登录集群的 sys 租户执行管理操作。sys 租户 root 用户的密码为部署时随机生成的密码,可在部署输出或 obd cluster display demo 命令的输出中查看。 |
| 多租户基础 | 本文会创建一个体验租户,租户与资源池的概念参见 体验多租户与资源隔离。 |
并行导入与高压缩简介
旁路导入
常规的数据导入需要经过 SQL 层的解析、优化和执行,将数据写入内存表(MemTable),再由后台任务转储为静态数据(SSTable)。旁路导入(Direct Load) 是一种导入执行模式,在 LOAD DATA、INSERT INTO SELECT 等语句中添加 DIRECT(...) Hint 即可启用。旁路导入绕过 SQL 层和 MemTable,将数据直接写入底层存储(SSTable),执行路径更短,适用于大表首次导入、10 GB~TB 级别的数据迁移,以及 CPU 和内存不充裕的场景。
在启用旁路导入的同时,通过 parallel(N) Hint 指定并行度,数据库会将导入任务拆分为多个并行子任务执行,进一步提升加载吞吐。
数据编码与压缩
OceanBase 数据库的 LSM-Tree 架构使数据的压缩只发生在合并(Compaction)阶段,不会影响数据的写入。数据落盘为 SSTable 时,以微块(Micro Block)为粒度进行两级压缩:
| 层级 | 机制 | 说明 |
|---|---|---|
| 数据编码(Encoding) | 按列探测数据相似性并编码 | 微块内行列混存,按列采用字典编码、游程编码(RLE)、差值编码等方式,合并时自适应选择最优编码 |
| 通用压缩 | 压缩算法对编码后的微块整体压缩 | 支持 lz4、snappy、zstd 等算法,其中 zstd 压缩率较高,lz4 压缩解压速度更快 |
在 MySQL 模式下,通过建表时的 ROW_FORMAT 和 COMPRESSION 子句控制编码与压缩行为,主要取值如下:
| 配置 | 取值 | 含义 |
|---|---|---|
ROW_FORMAT |
REDUNDANT / COMPACT |
数据不编码,以 flat 格式保存 |
DYNAMIC(默认) |
数据编码,以 encoding 格式保存 | |
CONDENSED |
数据编码,仅使用查询更友好的 selective encoding | |
COMPRESSION |
none |
数据不压缩 |
lz4_1.0 / snappy_1.0 / lz4_1.9.1 |
压缩速度较快的算法 | |
zstd_1.0 / zstd_1.3.8(默认) |
压缩率较高的算法 |
即:新建表默认开启数据编码,并默认使用 zstd_1.3.8 压缩算法。本文将对比"不编码、不压缩"与"默认编码压缩"两种配置下的存储占用差异。
说明
创建测试租户
使用 root 用户登录集群的 sys 租户(密码为部署时随机生成的密码,获取方式见上文前提条件):
obclient -h127.0.0.1 -uroot@sys -P2881 -p -A oceanbase创建一个 2C4G 规格的资源单元、资源池和租户
load_tenant:obclient [oceanbase]> CREATE RESOURCE UNIT load_unit MAX_CPU=2, MIN_CPU=2, MEMORY_SIZE='4G'; obclient [oceanbase]> CREATE RESOURCE POOL load_pool UNIT='load_unit', UNIT_NUM=1, ZONE_LIST=('zone1'); obclient [oceanbase]> CREATE TENANT IF NOT EXISTS load_tenant CHARSET='utf8mb4', PRIMARY_ZONE='zone1', RESOURCE_POOL_LIST=('load_pool') SET ob_tcp_invited_nodes='%';租户创建需要数十秒(本文环境实测约 6 秒),期间命令行没有输出,属正常现象,等待命令返回即可。
登录
load_tenant。新创建的租户 root 用户默认密码为空,提示输入密码时直接回车即可,后续操作均在此租户中进行:obclient -h127.0.0.1 -uroot@load_tenant -P2881 -p -A test
准备批量样例数据
在部署 OceanBase 的机器上,执行以下命令生成 100 万行测试数据:
seq 1 1000000 | awk '{printf "%d,user_%06d,city_%d,%d\n", $1, $1, $1%20, $1%1000}' > /tmp/load_sample.csv该命令生成包含以下字段的 CSV 文件:
id(递增编号)、user_name(用户名)、city(20 个城市值循环取值)、amount(金额)。其中city取值仅 20 种、user_name具有相同的user_前缀,数据中存在大量重复和相似内容,便于观察压缩效果。检查数据文件:
head -3 /tmp/load_sample.csv wc -l /tmp/load_sample.csv ls -lh /tmp/load_sample.csvhead命令返回的前 3 行数据如下:1,user_000001,city_1,1 2,user_000002,city_2,2 3,user_000003,city_3,3wc -l返回1000000表示数据行数正确;ls -lh显示文件大小约 29 MB。记录该文件大小,后续将与数据库中的实际存储占用进行对比。
体验并行导入
本节创建三张结构相同、配置不同的测试表,分别以不同方式导入同一份数据:
| 表名 | 导入方式 | 压缩配置 | 用途 |
|---|---|---|---|
load_normal |
常规导入(LOAD DATA) |
默认(编码 + zstd_1.3.8) | 常规导入的耗时基准 |
load_direct |
旁路导入(DIRECT Hint + 并行) |
默认(编码 + zstd_1.3.8) | 对比导入耗时;作为压缩后的存储基准 |
load_none |
旁路导入(DIRECT Hint + 并行) |
不编码、不压缩 | 压缩前的存储基准 |
操作步骤
登录
load_tenant,创建三张测试表:obclient [test]> CREATE TABLE load_normal ( id BIGINT PRIMARY KEY, user_name VARCHAR(32), city VARCHAR(32), amount INT ); obclient [test]> CREATE TABLE load_direct ( id BIGINT PRIMARY KEY, user_name VARCHAR(32), city VARCHAR(32), amount INT ); obclient [test]> CREATE TABLE load_none ( id BIGINT PRIMARY KEY, user_name VARCHAR(32), city VARCHAR(32), amount INT ) ROW_FORMAT=REDUNDANT COMPRESSION='none';load_none通过ROW_FORMAT=REDUNDANT COMPRESSION='none'同时关闭数据编码与压缩;其余两张表使用默认配置(ROW_FORMAT=DYNAMIC、COMPRESSION='zstd_1.3.8')。设置导入的文件路径。出于安全原因,
secure_file_priv系统变量只能通过本地 Unix Socket 连接执行修改:# Socket 路径取决于部署方式,obd demo 部署时可通过以下命令查找: find / -name sql.sock 2>/dev/null # 使用找到的 socket 路径连接(示例): obclient -S /path/to/sql.sock -uroot@load_tenant -p提示输入密码时直接回车(新创建的租户 root 用户默认密码为空)。
obclient [(none)]> SET GLOBAL secure_file_priv = "/tmp"; Query OK, 0 rows affectedsecure_file_priv用于限制LOAD DATA INFILE等语句的文件访问路径,该变量为全局变量,修改后新建立的连接生效。本文后续通过obclient -e执行的导入语句每次都会新建连接,无需额外操作。设置完成后,可新开一个连接执行SHOW VARIABLES LIKE 'secure_file_priv';进行确认,返回值为/tmp即表示设置成功。说明
secure_file_priv的详细说明请参见 secure_file_priv。在操作系统终端中执行以下命令,使用常规导入方式将数据导入
load_normal,并记录耗时:time obclient -h127.0.0.1 -uroot@load_tenant -P2881 -A test -e "SET ob_query_timeout=3600000000; LOAD DATA INFILE '/tmp/load_sample.csv' INTO TABLE load_normal FIELDS TERMINATED BY ',';"命令执行完成后,
time命令会输出real(实际耗时)、user(用户态耗时)、sys(内核态耗时)三行统计信息,记录real的值作为常规导入耗时。执行以下命令,使用旁路导入方式将数据导入
load_direct,并记录耗时:time obclient -h127.0.0.1 -uroot@load_tenant -P2881 -A test -e "SET ob_query_timeout=3600000000; LOAD DATA /*+ direct(true,1024,'full') parallel(4) */ INFILE '/tmp/load_sample.csv' INTO TABLE load_direct FIELDS TERMINATED BY ',';"命令中 Hint 的关键参数说明:
Hint 参数 说明 direct(true,1024,'full')启用全量旁路导入:数据排序后写入、最大容忍错误行数为 1024、全量模式(适用于空表首次装载) parallel(4)导入并行度为 4 使用旁路导入将数据导入
load_none:time obclient -h127.0.0.1 -uroot@load_tenant -P2881 -A test -e "SET ob_query_timeout=3600000000; LOAD DATA /*+ direct(true,1024,'full') parallel(4) */ INFILE '/tmp/load_sample.csv' INTO TABLE load_none FIELDS TERMINATED BY ',';"注意
- 新建租户的 root 用户默认密码为空,以上命令可直接执行;如已设置密码,请在命令中追加
-p密码(如-p'******'),将密码写入命令行可避免交互式输入密码影响计时结果。 - 命令中的
SET ob_query_timeout=3600000000用于将本会话的语句超时时间临时调整为 1 小时,避免大批量导入耗时超过语句默认超时时间(10 秒)导致执行失败,详细信息请参见 ob_query_timeout。
- 新建租户的 root 用户默认密码为空,以上命令可直接执行;如已设置密码,请在命令中追加
回到 obclient 会话,校验三张表的导入行数:
obclient [test]> SELECT COUNT(*) FROM load_normal; obclient [test]> SELECT COUNT(*) FROM load_direct; obclient [test]> SELECT COUNT(*) FROM load_none;三条语句的返回结果均为
1000000,与源数据文件行数一致,表示数据完整入库:+----------+ | COUNT(*) | +----------+ | 1000000 | +----------+ 1 row in set对比第 3 步与第 4 步记录的
real耗时。本文数据量为 100 万行(约 29 MB),两种导入方式的耗时通常相近(本文环境实测均在 3 秒左右完成),差异不明显属正常现象:数据量较小时,常规导入经过 SQL 层的开销占比很小;而旁路导入的优势体现在 GB 级及以上的大批量数据场景。如需进一步观察差异,可增大数据量后再次对比。
结果解读
| 观察项 | 预期现象 | 说明 |
|---|---|---|
| 导入耗时 | 本数据量下两者耗时相近 | 数据量较小(100 万行)时常规导入的 SQL 层开销占比可忽略;旁路导入的优势体现在 GB 级以上大批量数据 |
| 导入行数 | 三张表均为 1000000 | 行数与源文件一致,数据完整入库 |
| 旁路导入期间表状态 | 表只读 | 旁路导入期间会加表锁,导入过程中只能进行读操作 |
说明
- 并行导入将数据拆分为多个子任务并行执行,每个子任务视为独立的事务,因此对于没有主键的表,数据写入顺序可能与原文件不同。
- 全量旁路导入(
'full')适用于空表首次装载;目标表已有数据时,可使用增量旁路导入('inc')。通过INSERT INTO SELECT语句结合enable_parallel_dml与parallelHint 也可启用并行旁路导入,详细信息请参见 旁路导入概述。 - 如客户端与数据库不在同一台机器,可使用
LOAD DATA LOCAL INFILE从客户端本地文件导入,连接时需添加--local-infile参数,详细信息请参见 使用 LOAD DATA 语句导入数据。
体验数据编码与压缩
导入完成后,数据在合并(Compaction)生成 SSTable 的过程中才会应用表的编码与压缩配置。本节手动触发一轮合并,然后对比不同压缩配置下三张表的实际存储占用,并验证压缩后的数据可正常查询。
操作步骤
使用 root 用户登录集群的 sys 租户,对
load_tenant发起合并:obclient -h127.0.0.1 -uroot@sys -P2881 -p -A oceanbaseobclient [oceanbase]> ALTER SYSTEM MAJOR FREEZE TENANT = load_tenant; Query OK, 0 rows affected说明
发起合并前需确保全局合并开关已开启(集群级配置项
enable_major_freeze,默认开启)。合并的触发方式与详细说明请参见 手动触发合并。在
load_tenant的会话中查询合并状态:obclient [test]> SELECT STATUS FROM oceanbase.DBA_OB_MAJOR_COMPACTION;合并进行中时返回
COMPACTING(正在合并)或VERIFYING(正在校验 Checksum),合并完成后返回IDLE:+------------+ | STATUS | +------------+ | COMPACTING | +------------+ 1 row in set反复执行上述查询,直到
STATUS变为IDLE再进行后续操作。合并通常需要数分钟(本文环境实测约 6 分钟),期间每隔 10~30 秒查询一次即可;如查询时合并已完成,则直接显示IDLE。查询三张表的实际存储占用:
obclient [test]> SELECT t.TABLE_NAME, r.DATA_SIZE, r.REQUIRED_SIZE -> FROM oceanbase.DBA_OB_TABLE_LOCATIONS t -> JOIN oceanbase.DBA_OB_TABLET_REPLICAS r ON t.TABLET_ID = r.TABLET_ID -> WHERE t.TABLE_NAME IN ('load_normal', 'load_direct', 'load_none') -> ORDER BY t.TABLE_NAME;返回结果示例如下(实际大小与数据内容、数据库版本及集群状态相关,请以实际查询结果为准,重点关注三个
DATA_SIZE的相对关系):+-------------+-----------+---------------+ | TABLE_NAME | DATA_SIZE | REQUIRED_SIZE | +-------------+-----------+---------------+ | load_direct | 4057224 | 8114448 | | load_none | 63569253 | 127138506 | | load_normal | 4184897 | 6040515 | +-------------+-----------+---------------+ 3 rows in set查询结果说明:
DATA_SIZE:数据的实际大小,即压缩编码后落盘的数据量。load_none关闭了编码与压缩,其DATA_SIZE明显大于另外两张表;load_direct与load_normal开启了默认的编码压缩,存储占用明显更小(本文实测约为load_none的 1/15)。REQUIRED_SIZE:数据的分配空间大小,包含对齐等存储开销,因此REQUIRED_SIZE >= DATA_SIZE。
验证压缩存储的数据可正常查询。分别对
load_direct和load_none执行相同的查询:obclient [test]> SELECT city, COUNT(*), SUM(amount) FROM load_direct WHERE city IN ('city_0', 'city_1') GROUP BY city; obclient [test]> SELECT city, COUNT(*), SUM(amount) FROM load_none WHERE city IN ('city_0', 'city_1') GROUP BY city;两张表的返回结果完全一致,且与源数据的分布规律相符(100 万行数据按 20 个城市值均匀分布,每个城市值 50000 行;
GROUP BY的输出顺序不保证,可能与下例不同):+--------+----------+-------------+ | city | COUNT(*) | SUM(amount) | +--------+----------+-------------+ | city_1 | 50000 | 24550000 | | city_0 | 50000 | 24500000 | +--------+----------+-------------+ 2 rows in set执行点查,验证行级数据可正常解码读取:
obclient [test]> SELECT * FROM load_direct WHERE id = 500000;+--------+-------------+--------+--------+ | id | user_name | city | amount | +--------+-------------+--------+--------+ | 500000 | user_500000 | city_0 | 0 | +--------+-------------+--------+--------+ 1 row in set
结果解读
| 观察项 | 预期现象 | 说明 |
|---|---|---|
| 存储占用(DATA_SIZE) | load_none 明显大于 load_direct、load_normal |
数据编码按列消除冗余,通用压缩进一步压缩微块数据 |
| 相同数据、相同压缩配置 | load_direct 与 load_normal 大小接近 |
存储占用由数据内容与压缩配置决定,与导入方式无关 |
| 查询可用性 | 压缩表查询结果与不压缩表完全一致 | 编码与压缩对业务完全透明 |
| 写入性能 | 压缩不拖慢导入 | LSM-Tree 架构下压缩只发生在合并阶段,不影响数据写入 |
注意
- 压缩效果与数据特征相关:数据中重复值、相似前缀越多,字典编码、游程编码等机制的效果越明显。本文样例数据包含大量重复的
city取值和相同的user_前缀,压缩效果较为直观。 - 对已生成 SSTable 的表修改压缩选项时,需通过渐进合并(
progressive_merge_num)逐渐重写全部微块数据来完成变更,操作方法请参见 使用数据编码和压缩。
下一步
完成本文的并行导入与高压缩体验后,您可以继续以下探索之旅:
- 体验数据保护与恢复:通过闪回查询找回误操作前的数据,并了解备份恢复等完整的数据保护能力,参见 体验数据保护与恢复。
- 体验在线 DDL:在有持续读写的测试表上执行新增列、创建索引等常见 DDL,观察业务连续性,参见 体验在线 DDL。
- 深入数据加载与压测:体验 Sysbench 与 TPC-C 压测下的高并发性能,参见 体验高并发 OLTP 场景。
相关文档
- 旁路导入的原理与全量/增量模式说明请参见 旁路导入概述。
LOAD DATA语句的完整使用说明请参见 使用 LOAD DATA 语句导入数据。- 压缩与编码的架构原理请参见 压缩与编码。
- 数据编码与压缩的配置方法请参见 使用数据编码和压缩。
- 手动触发合并与查看合并进度的操作请参见 手动触发合并 和 查看合并信息。
- 视图的详细字段说明请参见 DBA_OB_TABLET_REPLICAS 和 DBA_OB_TABLE_LOCATIONS。