一张图 + 五条时间线,彻底拆透 StarRocks 主键表 UPSERT 链路

你好,我是 shengjk1,多年大厂经验,努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注!你会有如下收益:

  1. 了解大厂经验
  2. 拥有和大厂相匹配的技术等

希望看什么,评论或者私信告诉我!

一、 前言

"为什么 StarRocks 主键表可以毫秒级 UPSERT,还能保证点查性能不崩,数据不丢?"

社区里 90% 的调优踩坑,归根结底是没把 dumpapply 这两件事分开。

今天用"一张图 + 五条时间线"把完整读写链路拆到代码级,让你 10 分钟变成"主键表懂王"。


二、 鸟瞰图:把 4 个核心组件先印在脑子里

sql 复制代码
┌────────────┐  1  write  ┌──────────────┐  2  dump   ┌──────────────┐
│ Flink/Java │ ────────►  │ Memory       │ ────────►  │ Disk       │
│ Clients    │  INSERT/   │ DeltaBuffer  │  Mem→Disk  │ Delta RS * │
└────────────┘  UPDATE/   │ (跳表+索引)  │            │ (尚未合并) │
              DELETE      └──────────────┘            └──────────────┘
                                                        │
                                                        │ 3  apply
                                                        ▼
┌────────────┐  4  read   ┌──────────────┐            ┌──────────────┐
│ SQL        │ ◄────────  │ Index        │ ◄────────  │ Base RS      │
│ SELECT     │            │ PrimaryKey   │            │ (已合并)     │
└────────────┘            │ + DelVec     │            └──────────────┘
                          └──────────────┘

一句话注解:
写只碰 DeltaBuffer → dump 只落盘不合并 → apply 才真正把 Delta 合并进 Base → 读永远三路归并。

把这张图截屏当桌面,后面所有概念都能一一映射回来。


2 五条时间线:写 → dump → apply → 读 → 后台维护

2.1 写入:内存里完成 UPSERT,不到 1 ms

步骤 动作 并发保证
① Write Ahead Log 追加写 _wal/xxx.log 崩溃后可重放
② DeltaBuffer 插入 跳表(skiplist)按 pk 排序,同 key 覆盖 行级锁仅怼跳表,无 IO
③ 更新 PK Index 内存 B+ 树直接覆盖 RowLocation 无锁 CAS
④ 返回客户端 纯内存,延迟 P99 < 1 ms ---

关键点:

  • 跳表只保留 最新版本,天然 UPSERT。
  • Base 数据文件零写入,因此没有随机写放大。

2.2 dump:内存 → 磁盘,只写不合并

触发三件套:

  1. 时间间隔:pk_dump_interval_seconds(默认 1 h)。
  2. 内存水位:update_memory_limit_percent 30%。
  3. 文件个数:Delta Rowset ≥ 1000。

内部三步:

  1. 把跳表顺序扫一遍 → 按主键排好序的列存块(含 DELETE 标记)。
  2. 写临时文件 .dat/.idx → 原子 rename 成 DeltaRowsetID
  3. 释放整块 DeltaBuffer,内存瞬间掉回 0。

结果:

  • 查询需要 Union 所有 Delta RS + 残留内存
  • 不会去重 Base,所以 dump 越快,文件越碎,读放大越高。

2.3 apply:磁盘 Delta + Base → 新 Base,真正合并

StarRocks 里叫 update compaction

触发阈值:

  • 总 Delta 大小 ≥ 256 MB(update_compaction_size_threshold)。
  • Delta 文件数 ≥ 1000。
  • 手动 ADMIN COMPACT TABLE tbl;

四步完成:

  1. 选集:挑一个 Base + 若干 Delta(主键区间对齐)。
  2. 多路归并:顺序扫描,同 key 只保留最新一条;遇到 DELETE 就丢弃。
  3. 写新 Base :全新 .dat/.idx/.col/.del 四件套。
  4. 原子切换:元数据版本号 +1,老文件引用计数减 1,GC 线程 5 min 后物理删除。

副作用:

  • CPU 密集 + 读放大(要读旧 Base),但 查询无阻塞(快照隔离)。

2.4 读取:点查 / 范围扫都靠"三路归并"

可见性顺序(从新到旧):

复制代码
内存 DeltaBuffer → 磁盘 Delta RS → Base RS

点查流程:

  1. PK Index 先定位 <RowSetId, RowId>。
  2. 按类型取行:
    • DeltaBuffer → 直接返回。
    • Delta RS → 读主键索引页解析。
    • Base RS → 先查 DelVec,bit=1 表示已删,否则返回。
      范围扫:对三路建迭代器 → 小顶堆归并 → 输出最新版本。

性能关键:

  • PK Index 全内存,点查 O(logN) 毫秒级。
  • Delta RS 越多,堆归并层数越高 → 范围查询 CPU 飙升;因此需要 apply

2.5 后台维护:DelVec / GC / PIndex 重建

  • DelVec
    每份 Base 一份位图,apply 时把删除标记合并进新 Base,旧 DelVec 直接扔。
  • GC
    引用计数为 0 后延迟 5 min 删除,保证正在跑的查询快照不踩空。
  • PK Index 重建
    老版本:BE 重启要扫 全量 Base + Delta 重建 B+ 树,10 亿行≈5 min。
    3.1+:支持 持久化 pindex 快照 ,重启增量恢复,秒级

三、 dump vs apply 对照表:别再混淆!

误区 真相
dump 就是合并 ❌ 只落盘,不去重 Base
dump 越频繁越好 ❌ 文件更碎,读放大,IOPS 爆炸
apply 会锁表 ❌ 快照隔离,读写无锁
DelVec 会无限膨胀 ❌ 每次 apply 生成新 Base,旧位图丢弃
主键表写放大严重 ❌ 写只进内存,零随机写 Base

四、 一张时序图再串一遍(文字版)

ini 复制代码
时间轴 ──►
| 写入 ─┐                    ┌─ dump ─┐               ┌─ apply ─┐
|       │ DeltaBuffer        │ 新 Delta RS            │ 新 Base RS
| key=1 │ v1 → v2 → v3       │ v3 落盘                │ v3 进 Base
| key=2 │ delete             │ delete 标记落盘        │ 丢弃 key=2
| 查询  │ 看 v3              │ 看 v3                   │ 仍看 v3(无锁切换)

五、 总结

StarRocks 主键表 = 内存 DeltaBuffer + 磁盘 Delta/Base + 内存 PKIndex + DelVec 四件套:
写只进内存,dump 不落基线,apply 真正合并,读三路归并,全程无锁。

dump(落盘) vs apply(合并) 这条分界线刻进脑子,

以后任何"UPSERT 延迟高 / 点查毛刺 / 磁盘 IOPS 爆炸"的报警,

你都能 30 秒内说出根因和参数。

相关推荐
源代码•宸17 分钟前
分布式缓存-GO(分布式算法之一致性哈希、缓存对外服务化)
开发语言·经验分享·分布式·后端·算法·缓存·golang
It's now29 分钟前
Spring AI 基础开发流程
java·人工智能·后端·spring
计算机毕设VX:Fegn089532 分钟前
计算机毕业设计|基于springboot + vue图书商城系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
夕颜1112 小时前
BeeAI 框架学习记录
后端
极市平台2 小时前
骁龙大赛-技术分享第5期(上)
人工智能·经验分享·笔记·后端·个人开发
程序员爱钓鱼2 小时前
Node.js 编程实战:路由处理原理与实践
后端·node.js·trae
hhzz4 小时前
Spring Boot整合Activiti的项目中实现抄送功能
java·spring boot·后端
Victor3565 小时前
Netty(7)如何实现基于Netty的TCP客户端和服务器?
后端
Victor3565 小时前
Netty(8)什么是Netty的ChannelPipeline和ChannelHandler?
后端
乘风!6 小时前
NSSM启动tomcat部署Java程序
java·服务器·后端·tomcat