GBase 8c MOT 内存表落地前要先画清楚边界
我最近看 GBase 8c 多模存储和 MOT 内存引擎资料时,对内存表有一个很直接的感受:它很适合解决部分高并发、低延迟、热点读写问题,但不适合被当成"把表放进内存就会快"的通用按钮。真正落到现场时,MOT 的价值在边界清楚的场景里很明显;边界不清楚时,反而容易带来容量、恢复、SQL 兼容和运维解释成本。
GBase 8c 支持行存、列存、内存等多种存储形态。MOT 是面向事务负载优化的内存存储引擎,数据和索引主要驻留在内存中,同时通过 WAL 等机制保障事务持久性。这个定位决定了它更接近"关键热点表加速",而不是"全库内存化"。
我会先问三个问题
在评估一张表是否适合 MOT 前,我通常先问三个问题:
- 这张表的瓶颈是不是单行或小范围高频读写?
- 数据总量和增长速度是否能被内存长期承载?
- 业务是否能接受 MOT 相关限制和恢复窗口?
如果这三个问题答不清,我不会急着改表。很多慢的问题来自 SQL 访问路径、分布键、锁等待、应用连接池或者统计信息,换成内存表未必解决根因。
| 场景 | 是否适合优先评估 MOT | 原因 |
|---|---|---|
| 高频账户状态表 | 适合 | 单行读写多,延迟敏感 |
| 会话令牌、风控临时状态 | 适合 | 热点明显,数据模型简单 |
| 大批量历史明细 | 不适合优先 | 数据量大,分析查询多 |
| 宽表报表聚合 | 不适合优先 | 列存或分布式执行更匹配 |
| 复杂 join 中间表 | 谨慎 | 要看 SQL 支持和生命周期 |
我的经验是,MOT 更适合"窄、热、短路径"的 OLTP 表。表越宽、历史越长、查询越复杂,越要谨慎。
建表前先拆冷热
一个常见误区是把原来的业务表整体改成内存表。比如订单表既有最近 10 分钟频繁变更的状态,又有多年历史字段和扩展信息。如果整体放入 MOT,内存占用会迅速变大,热数据收益也被冷字段稀释。
我更倾向于拆出热点状态表,把长期明细留在普通表中。
sql
-- 普通历史明细表,保留完整订单信息
CREATE TABLE trade.t_order_detail (
order_id bigint PRIMARY KEY,
user_id bigint,
create_time timestamp,
pay_time timestamp,
amount numeric(18,2),
detail_json text,
status varchar(32)
);
-- 热点状态表,适合评估 MOT
CREATE FOREIGN TABLE trade.t_order_status_hot (
order_id bigint NOT NULL,
status varchar(32),
update_time timestamp,
version_no bigint,
PRIMARY KEY(order_id)
) SERVER mot_server;
不同版本的 MOT 建表语法、外部服务器名称和支持范围可能有差异,上面示例主要表达拆分思路。现场实施前要在当前版本测试库验证语法和限制。
冷热拆分后,应用访问路径也要调整:状态流转先访问热点表,结算归档后再同步到普通明细表。这样既能利用内存表低延迟,又不会把多年历史数据压进内存。
内存容量要按峰值和恢复算
MOT 数据和索引驻留在内存中,容量评估不能只看当前表大小。要把索引、版本、预留增长、恢复期间的资源、其他内存区域一起算进去。否则上线初期很快,几个月后内存压力上来,问题会比磁盘表更直接。
我会用下面的方式做粗算。
text
表名:trade.t_order_status_hot
当前行数:8000万
单行估算:order_id 8B + status 32B + update_time 8B + version 8B + 行头和对齐约 64B
数据估算:8000万 * 120B ≈ 9GB
主键索引估算:约 5GB
增长预留:6个月 40%
运维余量:30%
建议预留:至少 20GB 以上独立空间
这个估算不追求绝对准确,但能让评审时不只讨论"会不会快",也讨论"能不能长期放得下"。
| 成本项 | 为什么要算 |
|---|---|
| 数据行本身 | 决定基础内存占用 |
| 主键/二级索引 | 热点查询依赖索引,不能忽略 |
| 增长余量 | 防止短期上线后频繁扩容 |
| 恢复过程资源 | 崩溃恢复和重放期间也要资源 |
| 其他数据库内存 | 不能挤压共享内存、连接、排序等区域 |
如果容量评估已经很紧,我会优先考虑缩短热点表保留周期,而不是硬上 MOT。
SQL 设计要围绕主键和短事务
MOT 适合短事务和高并发点查。应用侧如果拿它做大范围扫描、复杂排序或长事务持有,就会偏离设计初衷。我的习惯是给 MOT 表配套访问规范。
sql
-- 推荐:按主键点查
SELECT status, update_time, version_no
FROM trade.t_order_status_hot
WHERE order_id = 900012345678;
-- 推荐:短事务内更新状态
BEGIN;
UPDATE trade.t_order_status_hot
SET status = 'PAID',
update_time = now(),
version_no = version_no + 1
WHERE order_id = 900012345678
AND version_no = 12;
COMMIT;
我会避免下面这类使用方式:
sql
-- 不建议:大范围扫描热点表
SELECT *
FROM trade.t_order_status_hot
WHERE update_time >= now() - interval '1 day';
-- 不建议:把 MOT 表当报表源
SELECT status, count(*)
FROM trade.t_order_status_hot
GROUP BY status;
不是说任何聚合都不能跑,而是这类需求更适合落到普通行存/列存表或专门的汇总表中。MOT 表应该服务核心短路径,不要承接所有查询愿望。
同步回普通表的链路要提前设计
如果热点状态只存在 MOT 表里,后续报表、审计、恢复核对都会不方便。我更倾向于让 MOT 承担在线状态,普通表承担历史事实。两者之间需要有同步或落盘规则。
| 方案 | 做法 | 优点 | 风险 |
|---|---|---|---|
| 应用双写 | 事务里同时写热点表和明细表 | 逻辑直观 | 应用复杂,失败补偿要设计 |
| 异步回写 | 先写热点表,再由任务同步 | 在线路径快 | 延迟和一致性要监控 |
| 状态日志表 | 每次变更写日志,异步合并 | 审计清晰 | 存储和清理要规划 |
| 定期快照 | 周期性把热点表落普通表 | 实现简单 | 秒级一致性不足 |
我比较喜欢"状态日志表 + 异步合并"的方式。核心交易路径只维护当前状态和变更日志,分析侧从日志或合并表取数。
sql
CREATE TABLE trade.t_order_status_log (
log_id bigserial,
order_id bigint,
old_status varchar(32),
new_status varchar(32),
change_time timestamp,
operator varchar(64)
);
-- 应用更新热点状态时同步写日志
INSERT INTO trade.t_order_status_log(
order_id, old_status, new_status, change_time, operator
)
VALUES (
900012345678, 'CREATED', 'PAID', now(), 'pay-service'
);
这样做多写了一份数据,但换来状态变化可追踪。涉及金融、交易、账务状态时,这个成本通常值得。
监控不要只看 QPS
MOT 上线后,很多团队只看接口 QPS 和平均响应时间。我的习惯是再加几类指标:内存占用、热点表行数、更新失败率、冲突重试次数、恢复演练耗时、同步延迟。
sql
-- 示例:热点表行数巡检
SELECT count(*) AS hot_rows
FROM trade.t_order_status_hot;
-- 示例:最近状态变更量
SELECT
date_trunc('minute', change_time) AS minute_bucket,
count(*) AS change_cnt
FROM trade.t_order_status_log
WHERE change_time >= now() - interval '30 minutes'
GROUP BY date_trunc('minute', change_time)
ORDER BY minute_bucket;
如果热点表行数持续增长,却没有归档和清理动作,就说明边界开始失控。内存表最怕"临时需求变永久需求",一开始只保存最近状态,后面慢慢把历史、扩展字段、报表字段都塞进去,最终变成另一个大表。
我会给 MOT 表设置生命周期。
| 对象 | 保留策略 |
|---|---|
| 当前状态表 | 只保留未完结或近 N 天活跃记录 |
| 状态日志表 | 按月分区或归档 |
| 异常补偿表 | 处理完成后定期清理 |
| 汇总表 | 按业务周期固化 |
清理也要走作业审计,不能随手 delete。对于高频热点表,批量清理要分批执行,避免影响在线更新。
一个比较稳的落地路径
我自己更倾向于小步上线:
- 选一张热点明显、结构简单、失败可回退的表;
- 用生产采样数据做容量和压测;
- 改造应用访问路径,只让核心点查和状态更新走 MOT;
- 保留普通表或日志表作为事实来源;
- 上线初期双轨对账,确认状态一致;
- 再逐步扩大范围。
不要一开始就把一组核心表全部改成 MOT。内存表收益很诱人,但每张表的访问模式和一致性要求都不同。先把一张表跑稳,比大范围改造更可靠。
小结
GBase 8c MOT 内存表适合解决高并发、低延迟、热点事务路径,但它需要清晰边界:哪些数据进内存、保留多久、如何回写普通表、如何恢复、如何监控。我的理解是,MOT 不是替代行存和列存,而是多模存储里的一把专用工具。
用它之前,先画边界;用它之后,持续看边界有没有被业务慢慢突破。这样才能把内存表用成加速能力,而不是新的运维风险点。
参考资料
text
GBase 8c 存储技术---内存引擎(三) https://blog.csdn.net/qq_37004539/article/details/127568732
GBase 8c 知识点汇总01 https://www.modb.pro/db/621686
GBase 8c 多模多态分布式数据库核心架构解析 https://www.cnblogs.com/gbase1/articles/19899632
GBase 8c 数据库使用 https://www.gbase.cn/docs/gbase-8c/03%20%E5%BC%80%E5%8F%91%E8%80%85%E6%8C%87%E5%8D%97/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BD%BF%E7%94%A8