报表总在扫大表,列存储应该放在哪一层

订单系统的明细表通常按交易写入:一笔订单一行,客户、渠道、金额、时间和状态放在同一条记录里。这样的行存布局适合新增订单、修改状态、按订单号查详情。等到月底做经营分析,查询只需要日期、区域和金额,却要把整张宽表一行行读出来,磁盘和内存都在为没有用到的列付出代价。

列存储解决的是另一类问题:同一列的数据连续组织,分析查询可以只读取参与过滤、分组和聚合的列。金仓数据库 KingbaseES 的产品简介把列存与并行计算放在分析型场景中介绍,也强调事务处理和分析处理可以按不同负载组织。实际落地时,我先把交易表、历史数据和分析宽表分开看,再决定哪些表适合列存。

先判断这张表到底在服务谁

在线订单表每天都有写入,客服会按订单号查详情,支付回调会更新状态。这类访问需要一行一行地拿到完整记录,行存更符合访问方式。历史订单汇总表则不同:它通常按月追加,查询读取的只是日期、区域、渠道和金额,写入很少,扫描和聚合占主要时间。

可以先用一条 SQL 看报表真正读取了哪些列:

sql 复制代码
SELECT order_date,
       region_code,
       channel_code,
       SUM(order_amount) AS amount_sum,
       COUNT(*) AS order_count
FROM fact_order_history
WHERE order_date >= DATE '2026-09-01'
  AND order_date <  DATE '2026-10-01'
GROUP BY order_date, region_code, channel_code
ORDER BY order_date, region_code, channel_code;

如果历史表还有几十个客户描述、收货地址、营销标签字段,这条查询并不需要它们。列存的收益来自"少读不相关列",不是来自把 SQL 写得更复杂。

先按时间切开历史数据

大表的存储方式和分区方式可以一起设计。KingbaseES 支持按范围、列表和哈希等方式划分大表,日期范围分区适合历史订单这类按月查询的场景。先按月份分区,报表查询只访问目标月份,后续归档和备份也能按分区处理:

sql 复制代码
CREATE TABLE fact_order_history (
    order_id      BIGINT NOT NULL,
    order_date    DATE NOT NULL,
    region_code   VARCHAR(16) NOT NULL,
    channel_code  VARCHAR(16) NOT NULL,
    customer_id   BIGINT NOT NULL,
    order_amount  DECIMAL(14, 2) NOT NULL,
    order_status  VARCHAR(16) NOT NULL
)
PARTITION BY RANGE (order_date) (
    PARTITION p202609 VALUES LESS THAN (DATE '2026-10-01'),
    PARTITION p202610 VALUES LESS THAN (DATE '2026-11-01'),
    PARTITION p202611 VALUES LESS THAN (DATE '2026-12-01')
);

分区解决的是访问范围和管理边界,列存解决的是分区内部读取哪些列。二者不是互相替代的关系。只建了分区而没有减少列读取,宽表扫描仍然可能很重;只把整张表改成列存而没有合理的时间边界,查询仍可能扫过大量历史数据。

列存表先用在低频更新的分析数据

对于已经封存的月份,可以将订单明细整理为只读分析数据。具体的列存实现和创建方式与 KingbaseES 版本、组件安装情况有关,目标环境应先确认列存扩展是否可用。以官方插件手册中的 cstore_fdw 示例为例,创建外部列式表时需要明确文件位置和压缩方式:

sql 复制代码
CREATE EXTENSION cstore_fdw;
CREATE SERVER cstore_server
FOREIGN DATA WRAPPER cstore_fdw;

CREATE FOREIGN TABLE fact_order_column (
    order_date    DATE,
    region_code   TEXT,
    channel_code  TEXT,
    order_amount  NUMERIC(14, 2),
    order_status  TEXT
)
SERVER cstore_server
OPTIONS (
    filename '/data/kingbase/column/fact_order_202609.cstore',
    compression 'zstd'
);

上面的 DDL 我只拿来说明两个参数:列式文件放在哪里、压缩方式写在哪里。真正落到生产环境前,我会先确认目标版本能否使用对应扩展,再检查数据库运行用户对目录的权限、备份任务能否覆盖这些文件,以及报表和归档任务是否会同时访问。若 CREATE EXTENSION 就无法执行,先处理组件和授权,不能直接把这段语句当成通用建表脚本。

报表 SQL 也按实际字段来写,先只取这次聚合需要的列:

sql 复制代码
SELECT region_code,
       channel_code,
       SUM(order_amount) AS amount_sum,
       COUNT(*) AS order_count
FROM fact_order_column
WHERE order_date >= DATE '2026-09-01'
  AND order_date <  DATE '2026-10-01'
  AND order_status = 'PAID'
GROUP BY region_code, channel_code
ORDER BY region_code, channel_code;

把查询改成 SELECT * 后,数据库还要读取那些报表根本用不到的列。报表需要哪些字段就写哪些字段;确实要看一笔订单的完整内容时,再拿订单号回到行存主表查询。

最近的订单和封存数据分开处理

最近七天的订单会不断更新,适合留在行存主表;超过结算周期的订单很少修改,可以转入历史分析层。查询入口可以用一个视图把两部分合在一起:

sql 复制代码
CREATE VIEW v_order_for_report AS
SELECT order_id,
       order_date,
       region_code,
       channel_code,
       order_amount,
       order_status
FROM fact_order_current
WHERE order_date >= CURRENT_DATE - 7

UNION ALL

SELECT order_id,
       order_date,
       region_code,
       channel_code,
       order_amount,
       order_status
FROM fact_order_column;

视图只统一访问入口,不会自动让两个存储层拥有相同的更新能力。历史表如果按月封存,就不要让在线应用通过这个视图随意修改历史数据;写入和归档仍应使用单独的任务和权限。

冷热分层完成后,可以按月份核对两边的数量和金额,确认归档没有漏行:

sql 复制代码
SELECT 'current' AS data_layer,
       COUNT(*) AS row_count,
       SUM(order_amount) AS amount_sum,
       MIN(order_date) AS first_date,
       MAX(order_date) AS last_date
FROM fact_order_current
UNION ALL
SELECT 'history',
       COUNT(*),
       SUM(order_amount),
       MIN(order_date),
       MAX(order_date)
FROM fact_order_column;

数量和金额对不上时,先查归档批次、时间边界和重复订单,不要直接重新导入整个月的数据。列存文件通常是批量装载的结果,重复装载会让后面更难分辨哪一批数据是正确的。

列存不是所有查询的加速按钮

列存更适合少列扫描、批量聚合和历史分析。按订单号查一行、频繁更新支付状态、需要同时返回几十列的详情接口,仍然更适合行存。即使是同一张表,报表查询和交易查询的访问方式也不同,不能只凭"列存"两个字就替业务作结论。

在 KingbaseES 中,最终要结合执行计划、实际数据量和存储设备确认结果。先看查询是否命中了目标分区,再看读取的列和聚合节点,最后比较冷缓存与热缓存下的耗时。没有这些信息时,只能说列存适合这种访问模式,不能直接写成固定倍数的性能承诺。

我会把列存落地拆成三件事:在线交易表保持稳定写入,历史分析数据按时间分层,报表 SQL 只读取需要的列。表的组织方式跟着访问方式走,改造范围也更容易控制。

相关推荐
夜雪一千2 小时前
ShardingSphere 雪花 / UUIDv7 主键分片实战 + SQL 建表示例
数据库·sql
Elastic 中国社区官方博客2 小时前
Elasticsearch:安装自行管理的 Elastic Stack 集群
大数据·运维·服务器·数据库·elasticsearch·搜索引擎·全文检索
PingCAP3 小时前
Agent 从 Demo 到生产,到底卡在哪?
数据库·tidb·agent
SelectDB4 小时前
开源替代怎么选:能力对照、三条部署路径与四类边界速查
数据库·云原生·开源
SelectDB4 小时前
自建与托管怎么选:开源自建、私有化与云上托管三条路径速查
数据库·云原生·开源
这个DBA有点耶4 小时前
深入 OLTP 锁与并发:从行锁到分布式事务的一致性权衡
数据库·架构·产品
SelectDB4 小时前
数仓 TCO 怎么算:五项成本拆解框架速查
大数据·数据库·开源
虚拟世界AI4 小时前
Kronos 时间序列数据库新手部署与实战指南
数据库
愤怒火龙果4 小时前
AI攻防 AIRT03-RAG Exploitation & Vector Database Attacks
数据库·人工智能·网络安全