GBase 8a 宽表查询里的压缩和行存列取舍

GBase 8a 宽表查询里的压缩和行存列取舍

我最近看 GBase 8a 存储优化资料时,对"列存天然快"这句话更谨慎了一些。GBase 8a 的优势确实很大程度来自 MPP、列存、压缩、智能索引和并行执行,但真正落到现场时,并不是所有宽表查询都会自然变快。尤其是 select *、返回列很多、命中记录很离散的场景,列存的 I/O 优势可能被大量离散读取抵消。

这类问题和单纯 SQL 慢不一样。SQL 本身可能不复杂,过滤条件也正常,但业务就是要一次拉很多列做明细展示。此时只盯分布键、并行度、连接顺序,容易漏掉存储形态本身。我的理解是,GBase 8a 的列存压缩和行列混存要一起看:前者解决存储和扫描效率,后者解决特定宽行返回场景的离散 I/O。

列存压缩不是越高越合适

GBase 8a 支持实例级、表级、列级压缩策略,资料里也提到列级优先级高于表级,表级高于实例级。这个设计很适合做精细化治理,但现场如果只追求压缩比,很容易把热表查询和加载性能拖慢。

压缩策略要结合访问模式,而不是只看磁盘节省多少。比如历史归档表很少更新,查询也多是按日期过滤后做聚合,高压缩比更有价值;但高频加载的近实时明细表,如果压缩和解压成本明显影响入库窗口,就要更谨慎。

数据类型 访问特点 压缩倾向 备注
热明细表 持续加载,频繁查询最近数据 均衡或高性能压缩 优先保证写入窗口和响应
冷历史表 基本不改,偶尔查询 高压缩比 节省空间和 I/O 更重要
高基数字符列 唯一值多,长度不稳定 单独评估 压缩收益可能不稳定
状态码/枚举列 重复值高 适合压缩 列存优势明显
宽文本字段 查询频率低 可列级策略 避免拖累常用列

示例写法可以按实例、表、列三个粒度考虑。不同版本压缩算法名称和取值要以当前环境为准,下面只展示治理思路。

sql 复制代码
-- 表级指定压缩策略示例
CREATE TABLE dwd.t_user_action_202605 (
    user_id      bigint,
    action_time  datetime,
    action_type  varchar(32),
    province     varchar(32),
    remark       varchar(512)
) COMPRESS(5,5);

-- 对特殊列单独指定压缩,示例语法需结合当前版本验证
CREATE TABLE dwd.t_user_profile (
    user_id      bigint COMPRESS(5),
    city_code    varchar(16) COMPRESS(5),
    profile_txt  varchar(2048) COMPRESS(3)
);

我在现场更喜欢先拿一份真实数据压测,而不是凭感觉设置。压缩比、加载耗时、查询耗时、CPU 使用率这几个指标要一起看。

sql 复制代码
-- 压测表:默认策略
CREATE TABLE test.t_action_default AS
SELECT * FROM dwd.t_user_action_202605 WHERE 1=0;

-- 压测表:调整策略
CREATE TABLE test.t_action_compress AS
SELECT * FROM dwd.t_user_action_202605 WHERE 1=0;

实际压测时导入同一批数据,再对比存储占用和典型 SQL 耗时。压缩策略一旦用于 TB 级历史表,后面再改会牵涉重建和数据搬迁,前期多测几轮更划算。

select 星号是宽表最常见的坑

GBase 8a 的列存对"只访问少量列"的分析查询非常友好,因为没涉及的列不需要参与读取。但如果业务大量使用 select *,列存需要把很多列都读出来,再进行物化,优势就会下降。资料里对这个限制也有说明:访问列数较多、记录又很离散时,会造成大量离散 I/O。

我会先把宽表 SQL 分成两类:

查询类型 示例 优化重点
聚合分析 select city, count(*) ... group by city 分布键、过滤列、智能索引、列裁剪
明细拉取 select * from t where user_id in (...) order by time 返回列控制、行存列、分页策略

明细拉取不要盲目套聚合查询的优化方法。它的问题常常不是计算,而是把很多分散行的很多列拼出来。

sql 复制代码
-- 不建议作为默认接口写法
SELECT *
FROM dwd.t_user_action
WHERE user_id = 1008611
ORDER BY action_time DESC
LIMIT 100;

-- 更可控的写法:先明确投影列
SELECT
    user_id,
    action_time,
    action_type,
    province,
    channel
FROM dwd.t_user_action
WHERE user_id = 1008611
ORDER BY action_time DESC
LIMIT 100;

如果业务确实需要大量列,比如客服明细页、监管导出、交易流水回放,就要评估行列混存能力,而不是要求开发永远不查宽列。

行存列适合解决特定明细查询

GBase 8a 支持行列混存,通过冗余行存储来提升典型 SELECT * 场景性能。我的理解是,它不是替代列存,而是在列存表上给某些高频明细访问准备一条更适合物化的路径。资料里提到,行存列以更小粒度 Data Page 读取数据,减少无效 I/O;也提到 gbase_hybrid_store 可以控制查询时是否使用行存数据。

我会在下面几类场景考虑行存列:

场景 是否适合 原因
单表明细查询,返回列多 适合评估 行存列可减少离散物化成本
聚合统计为主 不适合优先 列存本身更有优势
join/group 复杂计算 谨慎 行存列不用于所有运算阶段
高频 order by 明细页 适合评估 资料示例更接近这类物化场景
超宽字段全部冗余 谨慎 冗余空间和维护成本高

参数上我一般会先保持自动判断,再用会话级强制对比验证。

sql 复制代码
-- 自动判断是否使用行存列
SET gbase_hybrid_store = 1;

-- 排查或压测时强制不使用
SET gbase_hybrid_store = 0;

-- 压测时强制使用,有则使用
SET gbase_hybrid_store = 2;

还有两个参数需要关注:行存列页大小 gbase_hybrid_store_page_size,以及平均每个 DC 返回记录数上限 _gbase_hybrid_store_limit。这些参数不要直接生产调整,先在测试环境用真实 SQL 对比。

sql 复制代码
SHOW VARIABLES LIKE 'gbase_hybrid_store%';
SHOW VARIABLES LIKE '_gbase_hybrid_store_limit';

行存列不是越多越好

行存列本质上有冗余,冗余就会带来空间和维护成本。INSERT、快速 UPDATE、DELETE、LOAD 等动作会维护冗余数据,资料里提到这部分对用户透明,但透明不等于没有代价。宽表上如果随便把所有列都放进行存列,可能把空间、省 I/O、维护成本之间的平衡打破。

我更倾向于按"接口画像"建行存列,而不是按表字段全量冗余。

text 复制代码
接口A:客服查询最近100条行为
常用列:user_id, action_time, action_type, channel, province, device_type
排序列:action_time
过滤列:user_id

接口B:监管导出全字段
频率:每月一次
处理方式:不为它单独设计行存列,走离线导出窗口

这种分析之后,行存列只覆盖接口 A 的高频列,接口 B 不因为偶发需求拖累整个表。

决策项 建议
行存列字段 选高频投影列,不盲目全列
宽文本字段 低频时不纳入
多个接口 可以拆多个行存列,但要控制数量
建立时机 先压测,再对大表实施
删除调整 旧定义不合适时按规范重建

资料里还提到行存列不支持直接修改定义,只能先删除再重建;原始列定义也有一些限制。这个在变更评审里要提前说明,不要等业务上线后频繁改字段。

压缩和行存列要一起算账

一个典型例子:用户行为明细表 300 列,日增 2 亿行。大部分报表只看 10 到 20 列,列存压缩效果很好;但客服系统经常按用户查最近行为,需要返回 80 列。如果只按报表视角设计,客服查询会慢;如果只按客服视角全量行存冗余,存储成本又会上去。

我会用下面这张表做方案比较。

方案 优点 缺点 适用情况
纯列存 + 默认压缩 存储省,聚合快 宽行明细物化慢 报表分析为主
纯列存 + 调整压缩 可平衡加载和查询 不能根治离散宽查 热数据压力明显
列存 + 精简投影 改造成本低 需要应用配合 接口可改字段
列存 + 行存列 明细宽查改善明显 有冗余和维护成本 高频单表明细
拆宽表 模型更清晰 改造成本大 访问模式分层明显

真正落到现场时,我会先推动应用去掉无意义的 select *,再用行存列解决剩下的刚性宽查。顺序不能反。否则行存列会变成替开发兜底的万能选项,后面很难管理。

小结

GBase 8a 的列存、压缩、智能索引确实适合分析型负载,但宽表明细查询要单独看。我的经验是,压缩策略先按冷热和列特征分层,行存列按高频接口画像设计,gbase_hybrid_store 用来压测和排查,不要在没有对比数据时直接拍脑袋。

列存解决的是大规模分析效率,行存列解决的是特定明细物化效率。两者不是谁替代谁,而是要按查询形态取舍。

参考资料

text 复制代码
南大通用GBase 8a MPP Cluster性能优化之存储优化 https://www.gbase.cn/community/post/4439
数据分布式存储 https://www.gbase.cn/docs/gbase-8a/%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8C/product-overview/product-enterprise-enhance-feature/product-data-distributed-storage
南大通用GBase 8a MPP Cluster产品技术简介(一) https://www.gbase.cn/community/post/4209
南大通用GBase 8a不同压缩算法对表和列的数据压缩的使用方法 https://www.gbase8.cn/5087
相关推荐
派大星的日常1 小时前
64位windo系统安装ODBC链接工具并进行EXCEL数据连接
数据库·excel
我有医保我先冲1 小时前
【无标题】
java·大数据·人工智能
辰尘_星启1 小时前
【ROS2】 Python 节点的开发流程
开发语言·python·机器人·系统·控制·ros2
小徐学编程-zZ1 小时前
拆解业务逻辑分析
数据库·学习
计算机安禾1 小时前
【c++面向对象编程】第12篇:继承(二):构造与析构顺序,继承中的构造函数
开发语言·c++
染指11101 小时前
2.AI大模型-链式思考TOC让AI实现思考-优秀提示词设计技巧
大数据·数据库·人工智能
知识分享小能手1 小时前
R语言入门学习教程,从入门到精通,R语言获取数据 (7)
开发语言·学习·r语言
m0_624578591 小时前
SQL数据更新时如何减少锁表时间_合理控制事务边界与并发
jvm·数据库·python
XiYang-DING2 小时前
【Java EE】UDP(User Datagram Protocol)协议
java·udp·java-ee