核心内容摘要
【课程设计/毕业设计】基于spring boot的汽车4s店管理系统基于SpringBoot的汽车服务管理系统【附源码、数据库、万字文档】
快速参考主键表 vs 非主键表配置速查快速决策工具一页纸搞定主键表和非主键表的选择和配置 30 秒快速决策是否是否需要 UPDATE/DELETE?主键表数据可能重复?非主键表 核心差异速查表维度主键表 ✋非主键表 定义PRIMARY KEY (id) NOT ENFORCED不定义主键操作INSERT, UPDATE, DELETE仅 INSERTSplit 策略1 Bucket ≈ 1 Split1 Bucket 多 Splits批处理并行度≈ Bucket 数
Bucket 数
读取方式Merge慢Concat快吞吐量
MB/s
MB/s适用场景CDC、维度表日志、指标⚡ 配置速查主键表快速配置-- 建表CREATETABLEmy_pk_table(idBIGINT,dataSTRING,PRIMARYKEY(id)NOTENFORCED-- 定义主键)PARTITIONEDBY(dt STRING)WITH(bucket64,-- 并行度关键Bucket 数要足够compaction.max-file-num50-- 频繁 Compaction);-- 批处理查询配置SETexecution.runtime-modebatch;SETscan.infer-parallelismtrue;SETscan.infer-parallelism.max128;-- 略大于 Bucket 数SETscan.split-assign-modefair;-- 推荐 FAIRSETsplit.target-size128mb;-- 默认即可-- 预期并行度 ≈ 64非主键表快速配置-- 建表CREATETABLEmy_append_table(log_id STRING,dataSTRING-- 不定义主键)PARTITIONEDBY(dt STRING)WITH(bucket16,-- Bucket 可以少一些compaction.max-file-num
;-- 批处理查询配置SETexecution.runtime-modebatch;SETscan.infer-parallelismtrue;SETscan.infer-parallelism.max500;-- 可以很高SETscan.split-assign-modepreemptive;-- 两种都可以SETsplit.target-size64mb;-- 可以更小增加并行度-- 预期并行度 16取决于数据量 并行度速算公式主键表批处理并行度 ≈ Bucket 数量 流处理并行度 Bucket 数量固定示例Bucket 32批处理并行度 ≈ 32流处理并行度 32非主键表批处理并行度 ≈ 总数据量 / split.target-size 流处理并行度 Bucket 数量Fixed Bucket 流处理并行度 BucketUnaware Bucket示例Bucket 16每个 Bucket 500MBsplit.target-size 128MB批处理并行度 ≈ (16 × 500MB) / 128MB ≈ 63️ 参数速查核心参数对比参数主键表推荐非主键表推荐bucket
split.target-size128mb-256mb64mb-128mbscan.infer-parallelism.maxBucket ×
scan.split-assign-modefairpreemptivecompaction频率每天每周 性能速查读取速度对比100GB 数据指标主键表非主键表倍数执行时间8 分钟3 分钟
7x吞吐量200 MB/s550 MB/s
8xCPU 利用率80%50%
6x内存占用16GB8GB
5x结论非主键表读取性能约为主键表的
2.
倍️
常见问题速查Q1: 为什么主键表读取这么慢A:并行度低受 Bucket 数限制需要 Merge 操作CPU 密集多层级文件IO 开销大解决方案增加 Bucket 数量提高并行度定期 Compaction减少 Merge 开销考虑改用非主键表如果不需要更新Q2: 我的主键表只有 10 个 Buckets批处理很慢怎么办A:-- 方案 1增加 Bucket 数需要重建表ALTERTABLEmy_tableSET(bucket
;-- 方案 2等待 Compaction 完成临时方案CALLsys.compact(database.my_table);-- Compaction 后如果都是高 Level 文件可以切分-- 方案 3改为非主键表如果不需要更新CREATETABLEmy_new_tableASSELECT*FROMmy_table;Q3: 非主键表如何去重A:-- 方案 1在查询时去重SELECTDISTINCT*FROMmy_append_tableWHEREdt
;-- 方案 2在 INSERT 时去重INSERTINTOtarget_tableSELECTDISTINCT*FROMsource_table;-- 方案 3改为主键表自动去重CREATETABLEmy_pk_table(idBIGINT,dataSTRING,PRIMARYKEY(id)NOTENFORCED)ASSELECT*FROMmy_append_table;Q4: 应该选择多少个 BucketsA:主键表Bucket 数 期望的并行度 推荐 - 小规模 100GB
- 中规模100GB - 1TB
- 大规模 1TB
非主键表Bucket 数 期望的流式并行度批处理不受限 推荐 - 小规模
- 中规模
- 大规模
原因非主键表批处理并行度不依赖 Bucket 数Q5: 如何判断我的表是主键表还是非主键表A:-- 查看表定义SHOWCREATETABLEmy_table;-- 输出示例 1主键表-- PRIMARY KEY (id) NOT ENFORCED---- 输出示例 2非主键表-- 没有 PRIMARY KEY-- 或者查看元数据SELECT*FROMmy_table$schemas; 配置模板主键表 - 批处理作业-- 主键表批处理模板 --
建表CREATETABLEmy_pk_table(idBIGINT,dataSTRING,update_timeTIMESTAMP,PRIMARYKEY(id)NOTENFORCED)PARTITIONEDBY(dt STRING)WITH(bucket64,compaction.max-file-num50,compaction.min-file-num
;--
配置SETexecution.runtime-modebatch;SETscan.infer-parallelismtrue;SETscan.infer-parallelism.max128;SETscan.split-assign-modefair;--
查询SELECT*FROMmy_pk_tableWHEREdt
;--
优化建议-- - 增加 Bucket 数到 128如果并行度不够-- - 定期执行CALL sys.compact(db.my_pk_table);非主键表 - 批处理作业-- 非主键表批处理模板 --
建表CREATETABLEmy_append_table(log_id STRING,dataSTRING,timestampBIGINT)PARTITIONEDBY(dt STRING)WITH(bucket16,compaction.max-file-num
;--
配置SETexecution.runtime-modebatch;SETscan.infer-parallelismtrue;SETscan.infer-parallelism.max500;SETsplit.target-size64mb;SETscan.split-assign-modepreemptive;--
查询SELECT*FROMmy_append_tableWHEREdt
;--
优化建议-- - 减小 split.target-size 到 32mb如果需要更高并行度-- - 定期合并小文件CALL sys.compact(db.my_append_table);主键表 - 流处理作业-- 主键表流处理模板 --
建表CREATETABLEmy_pk_stream_table(idBIGINT,dataSTRING,PRIMARYKEY(id)NOTENFORCED)WITH(bucket128,-- 流处理并行度 Bucket 数changelog-producerinput,scan.modelatest);--
配置SETexecution.runtime-modestreaming;SETscan.parallelism128;SETscan.snapshot-time-interval10s;SETexecution.checkpointing.interval60s;SETstate.backendrocksdb;--
查询SELECTCOUNT(DISTINCTid)asuser_cntFROMmy_pk_stream_table;非主键表 - 流处理作业-- 非主键表流处理模板 --
建表CREATETABLEmy_append_stream_table(log_id STRING,dataSTRING)WITH(bucket64,-- 可以比主键表少scan.modelatest);--
配置SETexecution.runtime-modestreaming;SETscan.parallelism64;SETscan.snapshot-time-interval10s;SETexecution.checkpointing.interval60s;--
查询SELECTCOUNT(*)aslog_cntFROMmy_append_stream_table; 可视化速查Split 数量对比场景1 个分区10 个 Buckets每个 1GB 主键表有 Level 0 ├─ Split 数量10 个 Bucket 数 ├─ 并行度10 └─ 原因Key Range 重叠不能切分 非主键表 ├─ Split 数量80 个10GB / 128MB ├─ 并行度80 └─ 原因可以自由切分性能对比读取 100GB 数据 主键表需要 Merge: [████████░░] 8 分钟 非主键表顺序拼接: [███░░░░░░░] 3 分钟 性能提升
7x 常见错误❌ 错误 1主键表 Bucket 太少-- 错误配置CREATETABLEmy_table(idBIGINT,PRIMARYKEY(id)NOTENFORCED)WITH(bucket4-- ❌ 太少批处理并行度只有
;-- 正确配置CREATETABLEmy_table(idBIGINT,PRIMARYKEY(id)NOTENFORCED)WITH(bucket64-- ✅ 并行度可以达到
;❌ 错误 2非主键表 Bucket 太多-- 低效配置CREATETABLEmy_append_table(log_id STRING)WITH(bucket256-- ❌ 太多会产生大量小文件);-- 推荐配置CREATETABLEmy_append_table(log_id STRING)WITH(bucket16-- ✅ 适中批处理并行度通过 Split 切分实现);❌ 错误 3主键表使用小 Split-- 错误配置SETsplit.target-size32mb;-- ❌ 对主键表无效无法切分-- 正确做法-- 主键表增加 Bucket 数量而不是减小 SplitALTERTABLEmy_pk_tableSET(bucket
; 优化技巧速查主键表优化
提高并行度 └─ 增加 Bucket 数量核心手段
减少 Merge 开销 └─ 定期 Compaction └─ 减少 Level 0 文件
批处理优化 └─ 选择 Compaction 完成后的快照 └─ 使用 FAIR 分配模式
流处理优化 └─ Bucket 数 期望并行度非主键表优化
提高并行度 └─ 减小 split.target-size核心手段 └─ 提高 scan.infer-parallelism.max
减少小文件 └─ 定期 Compaction └─ 适当减少 Bucket 数
批处理优化 └─ 充分利用高并行度 └─ 使用 PREEMPTIVE 分配模式
流处理优化 └─ 考虑使用 Unaware Bucket如果允许 决策热线我应该用哪种表问自己 3 个问题
是否需要 UPDATE 或 DELETE └─ 是 → 主键表 └─ 否 → 继续
数据是否可能重复需要去重 └─ 是 → 主键表 └─ 否 → 继续
读取性能是否非常关键 └─ 是 → 非主键表 └─ 否 → 都可以推荐非主键表性能更好我的配置合理吗检查清单 主键表 ☐ Bucket 数 32 ☐ scan.split-assign-mode fair ☐ 定期执行 Compaction ☐ scan.infer-parallelism.max Bucket × 2 非主键表 ☐ split.target-size 128mb ☐ scan.infer-parallelism.max 200 ☐ Bucket 数不要太多 64 ☐ 定期合并小文件 相关文档链接详细对比《Paimon 主键表 vs 非主键表核心差异》Split 机制《Paimon Split 机制深度解析》配置指南《Paimon Flink 配置实战指南》读取流程《Paimon 分布式读取数据完整流程》如果你喜欢这篇文章请转发、点赞。
扫描下方二维码关注我们您会收到更多优质文章推送在这里插入图片描述关注「Java源码进阶」获取海量java大数据机器学习资料