尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Doris建表实战:从模型选择到分区分桶的性能优化指南

Doris建表实战:从模型选择到分区分桶的性能优化指南 1. 先搞清楚“创建Doris数据表”到底要解决什么问题如果你刚接触Doris或者从其他数据库比如MySQL、ClickHouse转过来看到“创建数据表”这个标题可能会觉得很简单——不就是个CREATE TABLE语句吗但Doris的表创建最核心的价值不在于语法本身而在于如何通过表结构设计把Doris的查询性能、数据导入效率和存储成本优势真正发挥出来。很多人踩的第一个坑就是直接套用MySQL的建表习惯结果数据导入慢、查询没快多少、存储还特别费空间。Doris是一个MPP架构的列式存储分析型数据库它的表模型Duplicate、Aggregate、Unique和分区、分桶机制是决定后续所有操作效率的基石。所以这篇文章不会只给你一个SQL模板而是会带你理解在Doris里为什么这么建表以及建完表后怎么验证它是否真的“好用”。适合看这篇的人是已经完成了Doris单机或集群部署比如用doris manager或手动部署准备开始导入业务数据进行分析的同学。我会假设你的环境已经就绪能连上Doris的MySQL客户端。2. 动手前先理清你的数据场景和资源条件建表不是凭空开始的。在写第一条CREATE TABLE语句之前我一般会先问自己几个问题把场景和条件框定好。这能避免你建完表才发现不适合又要删了重来。2.1 你的数据是什么“脾气”数据量级是每天百万、千万还是上亿条这直接决定分区策略。数据来源与更新方式是每天一次批量导入如通过Broker Load从HDFS还是实时流式写入如通过Routine Load从Kafka这影响你对数据一致性和导入性能的考量。查询模式主要是点查按某个Key快速查一行还是大范围聚合分析Group By, Sum, Count常用的过滤条件WHERE是哪几个字段这些字段就应该考虑建索引或作为分区键。查询是否通常要扫描很长的时间范围比如查一整年的数据数据特征有没有明显随时间增长的字段比如event_time或date这是天然的分区字段。数据是否有重复是否需要去重如果需要基于某些列更新数据就要用Unique模型。数值列是否需要预聚合比如相同维度下的销售额需要累加这决定了是否用Aggregate模型。2.2 你的Doris环境“家底”如何部署模式是单机部署学习测试用还是多FE、多BE的集群单机部署时分桶数要设得保守。节点配置每个BE节点的CPU、内存、磁盘尤其是SSD资源。这决定了单个BE能健康管理的数据分片Tablet上限进而影响分桶数的设置。存储介质如果用了混合存储SSDHDD建表时可以通过属性指定冷热数据的分区存储策略。把这些想清楚记下来。下面我们进入实操时每一个参数的选择都会对应回这些前提。3. 从零开始拆解一个完整的建表示例我们用一个典型的用户行为日志表来走一遍流程。假设场景是每天有数千万条用户点击日志需要按天分析不同页面的PV/UV并且偶尔需要按用户ID查询其最近行为。3.1 第一步选择表模型 – 这是Doris的“基因”Doris有三种表模型选错了后期改起来很麻烦。Duplicate 明细模型存储最原始、完整的明细数据不做任何聚合。即使两行数据一模一样也会存储两份。什么时候用需要保留原始明细用于审计、追溯、或进行任意维度分析的场景。比如我们的日志表因为要算UV需要基于用户ID去重明细模型更灵活。特点存储空间大但灵活性最高。Aggregate 聚合模型在建表时指定聚合列和聚合方式SUM, REPLACE, MAX等。导入数据时相同维度列的数据会自动聚合。什么时候用确定性的报表场景比如每天每个商品的销售总额、每个频道的最高在线人数。可以极大减少存储和加速查询。特点存储小、查询快但失去了明细且不支持非聚合列的更新。Unique 唯一模型更像是聚合模型的一个特例它通过主键唯一并指定“REPLACE”聚合方式实现类似MySQL的“ON DUPLICATE KEY UPDATE”效果。什么时候用需要实时更新数据的维度表或者有唯一性约束的场景。比如用户信息表用户的属性会变化。给新手的建议如果不确定或者业务逻辑复杂多变先用Duplicate模型。虽然占空间但不会出错。等业务稳定、模式清晰后再考虑迁移到Aggregate模型来优化。对于我们的日志表选择Duplicate模型。3.2 第二步设计分区与分桶 – 这是性能的“骨架”这是Doris建表最核心、也最容易出错的部分。分区Partitioning通常按时间范围天、月划分。目的是裁剪数据查询时带上分区条件Doris可以直接跳过无关分区的数据极大减少扫描量。管理生命周期可以方便地删除DROP或增加ADD历史分区或未来分区。-- 通常使用 Range Partition按天分区 PARTITION BY RANGE(dt) ( PARTITION p20240101 VALUES LESS THAN (2024-01-02), PARTITION p20240102 VALUES LESS THAN (2024-01-03), -- ... 可以预先创建未来几天的分区 PARTITION p20241231 VALUES LESS THAN (2025-01-01) )如果不想手动管理可以用动态分区Dynamic Partition功能自动创建和删除分区。分桶Bucketing在分区内数据进一步打散到不同BE节点上的机制。分桶列的数据会经过Hash计算决定数据去哪个桶。目的实现数据分布式存储和并行计算也用于精确查询的路由如果查询条件包含所有分桶列的等值条件。分桶列选择选择高基数值种类多、经常作为查询条件的列。比如user_id,page_id。避免选择低基数列如性别、状态会导致数据倾斜。分桶数量单机测试建议4-8个。小型集群如3个BE建议在BE数量的倍数附近比如6、9、12。通用原则单个Tablet的数据量建议在100MB-1GB之间。你可以根据“预估分区数据量 / 分桶数”来估算。分桶数一旦设置修改起来非常麻烦需要重导数据。对于日志表我们按dt日期分区按user_id分桶。3.3 第三步编写完整的建表语句现在我们把所有部分组合起来。假设我们有这些字段dt(日期)user_idpage_idactiontimestampdeviceprovince。-- 连接到Doris例如mysql -h127.0.0.1 -P9030 -uroot CREATE DATABASE IF NOT EXISTS demo_db; USE demo_db; CREATE TABLE IF NOT EXISTS user_behavior_duplicate ( dt DATE NOT NULL COMMENT 数据分区日期格式yyyy-MM-dd, user_id BIGINT NOT NULL COMMENT 用户ID, page_id INT NOT NULL COMMENT 页面ID, action VARCHAR(20) COMMENT 行为类型如click, view, timestamp DATETIME NOT NULL COMMENT 行为发生的时间戳, device VARCHAR(50) COMMENT 设备类型, province VARCHAR(20) COMMENT 省份 ) ENGINEolap -- Doris的存储引擎 DUPLICATE KEY(dt, user_id, page_id) -- 指定Duplicate模型并设置排序列 COMMENT 用户行为明细日志表 PARTITION BY RANGE(dt) -- 按日期分区 ( PARTITION p20240501 VALUES LESS THAN (2024-05-02), PARTITION p20240502 VALUES LESS THAN (2024-05-03) ) DISTRIBUTED BY HASH(user_id) BUCKETS 6 -- 按user_id哈希分桶共6个桶 PROPERTIES ( replication_num 1, -- 副本数单机部署设为1集群可设为2或3 storage_medium SSD, -- 存储介质SSD或HDD storage_cooldown_time 9999-12-31 23:59:59 -- 冷却时间用于冷热数据分离这里先不启用 );关键参数解释DUPLICATE KEY这里指定的列并不是“主键”而是前缀索引列。Doris会为这些列的前36个字节构建稀疏索引用于加速查询。务必把最常用作查询条件的列放在前面。这里把dt放第一因为大部分查询都按天过滤。replication_num数据副本数。生产环境通常设为3保证高可用但单机只能设为1。PROPERTIES可以设置很多表属性比如副本策略、存储介质、动态分区、数据压缩算法等。执行这条语句如果没有报错表就创建成功了。4. 建表后如何验证它是否“健康”表创建成功只是第一步。一个“健康”的表结构应该能高效地支持你的数据导入和查询。我一般会按以下顺序验证。4.1 查看表结构确认关键信息-- 查看建表语句确认分区、分桶等关键信息是否正确 SHOW CREATE TABLE user_behavior_duplicate\G -- 查看表的分区信息 SHOW PARTITIONS FROM user_behavior_duplicate; -- 查看表的分布情况重点关注Tablet数量、状态和副本数 SHOW TABLET FROM user_behavior_duplicate;检查SHOW TABLET的输出确保所有Tablet的State都是NORMAL且ReplicaCount符合你的预期。这是表能正常读写的基础。4.2 进行小批量数据导入测试不要一上来就导入全量数据。先用一个很小的CSV文件比如100行测试整个链路。准备测试数据(test_data.csv)2024-05-01,10001,101,click,2024-05-01 10:00:01,mobile,Beijing 2024-05-01,10002,102,view,2024-05-01 10:00:02,pc,Shanghai 2024-05-02,10001,103,click,2024-05-02 11:00:01,mobile,Beijing使用Stream Load导入适合小文件快速测试# 在终端执行注意替换文件路径和主机端口 curl --location-trusted -u root: \ -H “label:test_load_1” \ -H “column_separator:,” \ -T /path/to/your/test_data.csv \ http://127.0.0.1:8030/api/demo_db/user_behavior_duplicate/_stream_load查看返回的JSON关注“Status”: “Success”和“NumberLoadedRows”。在Doris中查询验证SELECT * FROM user_behavior_duplicate ORDER BY timestamp LIMIT 10; SELECT COUNT(*), dt FROM user_behavior_duplicate GROUP BY dt;如果数据能正确查询出来并且按dt分组计数正确说明基础导入和查询通路是好的。4.3 分析查询计划验证分区/分桶效果这是验证表设计是否合理的关键一步。使用EXPLAIN命令查看查询如何执行。-- 一个带有分区键条件的查询 EXPLAIN SELECT COUNT(*) FROM user_behavior_duplicate WHERE dt‘2024-05-01’; -- 一个带有分桶键等值条件的查询点查场景 EXPLAIN SELECT * FROM user_behavior_duplicate WHERE dt‘2024-05-01’ AND user_id10001;在EXPLAIN的结果中你需要关注partitions是否只扫描了p20240501一个分区如果是partitions1/2说明分区裁剪生效了。tabletList扫描的Tablet数量。对于第二个点查理想情况下应该只路由到1个Tablet因为user_id是分桶列。如果扫描了所有Tablet说明查询条件没有利用好分桶列。4.4 评估存储空间导入一定量数据后比如1GB查看表占用的空间是否合理。-- 查看表的数据量和副本容量 SHOW DATA FROM demo_db;对比原始文本文件如CSV的大小Doris的列式压缩通常能有3-10倍的压缩比。如果发现空间异常大可能需要检查数据类型是否合适比如用VARCHAR(65533)存很短的文本或者考虑使用更高效的压缩算法。5. 避坑指南我遇到过的常见问题与排查思路即使按照步骤做了也可能遇到问题。这里列几个我踩过的坑和排查顺序。5.1 问题数据导入失败报错“ETL_QUALITY_UNSATISFIED”现象Stream Load或Broker Load失败提示数据质量不合格。排查先看错误详情返回的JSON里会有“ErrorURL”访问这个链接能看到具体哪些行因为什么原因被过滤。检查列映射最常见的原因是CSV列数或分隔符与表结构不匹配。确认-H “column_separator:,”和实际文件一致。检查数据类型比如字符串里包含了换行符、日期格式不匹配、数值超出范围等。检查过滤条件你是否在Load作业中设置了where条件条件可能过滤了所有数据。5.2 问题查询速度很慢没有感觉到比MySQL快现象简单聚合查询也耗时很久。排查先用EXPLAIN看是否扫描了所有分区和所有Tablet。如果partitions数量远大于你预期的时间分区数说明分区裁剪没生效检查你的WHERE条件是否使用了分区列并且条件能被Doris识别。检查前缀索引你的查询条件是否用到了DUPLICATE KEY或UNIQUE KEY中靠前的列如果查询条件只用了排在后面的列索引效果会大打折扣。检查数据分布执行SHOW TABLET FROM table_name看各个Tablet的DataSize是否严重不均。如果严重倾斜说明分桶列选择不当如用了低基数列导致数据都堆在少数几个桶里无法并行计算。检查BE节点负载通过SHOW PROC ‘/backends’查看各个BE的CPU、内存、磁盘IO。可能某个BE负载过高成为瓶颈。5.3 问题建表时该选多少分桶数BUCKETS这是个经验问题但有几个原则下限至少是BE节点数量的倍数以保证数据能均匀分布。比如3个BE分桶数可以是369...上限受限于集群能力。单个BE上同一个表的Tablet数量不宜过多通常建议不超过1000否则元数据管理压力大。分桶数 BE数量 * 每个BE承载的Tablet数。数据量导向目标是让单个Tablet物理大小在100MB-1GB之间。例如你预估一个分区有30GB数据希望Tablet大小约500MB那么分桶数可以设为 30GB / 0.5GB 60。然后取一个接近的、是BE数量倍数的值比如633BE * 21。新手建议在测试环境用单个BE节点数量的10倍左右作为起始值。例如单机部署1个BE可以先设10个桶。导入一部分数据后用SHOW TABLET查看Tablet大小再进行调整。5.4 问题如何修改表结构比如增加列、修改分桶数增加列相对安全使用ALTER TABLE table_name ADD COLUMN column_name column_type;。对于Aggregate/Unique模型新增非聚合列需要指定DEFAULT value。修改分桶数无法直接修改。这是一个非常关键的限制。如果需要修改通常的做法是创建一个新表具有期望的分桶数。使用INSERT INTO new_table SELECT * FROM old_table将数据导入新表。通过原子替换ALTER TABLE rename或业务层切换将新表上线。删除旧表。修改分区可以动态添加或删除分区但不能直接修改分区范围表达式。所以初期设计分区键通常是时间列很重要。6. 进阶思考从单表到生产环境的数据管理当你成功创建并验证了一张表后下一步要考虑的是如何让它在一个持续运行的生产环境中保持高效和稳定。6.1 使用动态分区告别手动管理手动管理每天创建分区太麻烦。Doris的动态分区功能可以自动管理生命周期。-- 在建表或修改表时添加动态分区属性 ALTER TABLE user_behavior_duplicate SET ( “dynamic_partition.enable” “true”, “dynamic_partition.time_unit” “DAY”, “dynamic_partition.start” “-7”, -- 保留最近7天的分区 “dynamic_partition.end” “3”, -- 预先创建未来3天的分区 “dynamic_partition.prefix” “p”, “dynamic_partition.buckets” “6” -- 动态创建的分区分桶数可覆盖建表时的值 );设置后Doris会自动创建明天的分区并删除7天前的分区。你需要定期检查SHOW PARTITIONS来确认其运行正常。6.2 规划数据生命周期与冷热存储对于日志类数据通常只有最近几天的数据被频繁查询。你可以利用Doris的存储策略将旧数据自动从高速SSD迁移到廉价HDD甚至通过DROP PARTITION删除。设置冷却时间在PROPERTIES中设置“storage_cooldown_time”例如“2024-06-01 00:00:00”到达此时间后数据会从SSD迁移到HDD。创建多存储卷在BE节点配置中指定SSD和HDD的路径。然后在建表时通过“storage_policy”属性指定策略。6.3 建立监控与告警不要等业务方报查询慢才发现问题。至少监控以下几点导入延迟数据从产生到可查询的时间。查询延迟P95/P99查询耗时。BE节点健康度磁盘使用率、内存使用率。Tablet健康度副本缺失、版本不一致的Tablet数量。 可以通过Doris的Metrics访问FE的/metrics接口或集成PrometheusGrafana来实现。创建Doris表真正的功夫在按下回车键之前的设计和建表之后的验证。核心思路是根据数据特性和查询模式选择模型利用分区实现快速裁剪通过合理分桶实现负载均衡和并行计算。第一次建表建议在测试环境用真实数据样本哪怕只有万分之一完整跑一遍导入和典型查询用EXPLAIN反复验证你的设计这比直接上生产再折腾要稳妥得多。
返回列表