Vitess TabletManager 模型重构:从「记录驱动轮询」到「vttablet 自述权威状态」
Vitess TabletManager 模型重构从「记录驱动轮询」到「vttablet 自述权威状态」【免费下载链接】vitessVitess is a database clustering system for horizontal scaling of MySQL.项目地址: https://gitcode.com/gh_mirrors/vi/vitess导读本文基于 Vitess 仓库中的设计文档 TabletManagerModel.md剖析 Vitess tablet 状态管理模型的一次关键演进将 tablet 记录topo 中的 Tablet record从「权威事实」降级为「发现用缓存」让 vttablet 进程成为自身状态的唯一权威发布者。读完本文你将理解为何单次 RPC 往返即可完成状态变更、为何主库选举必须把 topo 当作例外权威、vttablet 启动时信任哪些 topo 字段以及当前仓库源码tm_state.go、tm_init.go是如何落地这套模型的。背景旧模型的脆弱之处设计文档首先描述了旧模型的运行方式tablet 记录被视为权威authoritative。具体表现为vttablet 进程持续轮询poll自己在 topo 中的 tablet 记录并对记录的变化做出反应代码中散布着大量「先更新 tablet 记录再调用RefreshTablet或RefreshState」的调用点。这套「改记录 主动刷新」的两段式流程存在两个问题不符合实际运维方式更新 tablet 记录并期望 tablet 自己刷新并不能带来任何实际收益链路脆弱每一次状态变更都把多余的组件topo 存储 轮询观察者拉进动作链中链路越长失败概率越高。因此文档提出的新模型是tablet 进程vttablet是自己当前状态的权威来源它把状态「发布」到 tablet 记录中而该记录仅用于发现discovery。换句话说topo 中的 tablet 记录不再是「命令源头」而是「状态展示板」。新模型核心单次 RPC 往返 尽力而为发布vttablet 存活时直接 RPC不再轮询在新模型下所有需要改变 tablet 状态的流程直接向 vttablet 发起一次 RPC 请求tablet 立即执行该请求执行成功后vttablet尽力而为best-effort地把新状态写回 tablet 记录如果写回失败vttablet 会持续重试直到成功。这一设计带来的核心收益是请求只需一次到 tablet 的往返即可成功场景结果RPC 请求失败操作视为失败直接返回错误RPC 成功、但 tablet 记录更新失败操作仍然成功记录稍后会被 vttablet 的发布重试补齐这里的关键哲学是操作的成功不依赖 topo 写入的即时成功。因为 tablet 记录迟早会收敛到真实状态调用方无需为了「写记录」而多等一次 topo 往返。源码印证publishStateLocked 与 retryPublish在 tm_state.go 中publishStateLocked正是这套「尽力而为发布」的实现func (ts *tmState) publishStateLocked(ctx context.Context) { // ... _, err : ts.tm.TopoServer.UpdateTabletFields(ctx, ts.tm.tabletAlias, func(tablet *topodatapb.Tablet) error { if err : topotools.CheckOwnership(tablet, ts.tablet); err ! nil { // ... return topo.NewError(topo.NoUpdateNeeded, ) } proto.Reset(tablet) proto.Merge(tablet, ts.tablet) return nil }) if err ! nil { // ... log.Error(fmt.Sprintf(Unable to publish state to topo, will keep retrying: %v, err)) ts.isPublishing true // Keep retrying until success. go ts.retryPublish() } }注意几个细节写入使用UpdateTabletFields并经过topotools.CheckOwnership校验确保只有 tablet 自己或拥有者能改写自己的记录发布失败后进入retryPublish()后台重试循环tm_state.go每轮间隔由--publish-retry-interval控制默认30 秒见 tm_state.go一个特殊分支如果UpdateTabletFields返回NoNode有人把 tablet 记录删了vttablet 会认为自己的身份已不存在主动向servenv.ExitChan发送 SIGTERM 优雅退出——因为记录被删意味着它已不属于任何拓扑。这套「先执行、后发布、失败续传」的结构与设计文档描述的模型完全一致。vttablet 宕机或不可达时操作直接失败如果 vttablet 不可达操作直接失败。设计文档特别指出这种失败模式并不比「我们无法更新 tablet 记录」更糟。理由很朴素无论采用哪种模型vttablet 不可达时状态变更都无法完成而且代码本来就默认 tablet 记录可能与 vttablet 实际状态不同步——这是 Vitess 长期存在并已被各方代码如 health check、vtgate 的发现逻辑处理过的前提新模型只是把这个前提显式化了。RefreshState保留 API但语义收窄RefreshState仍然作为一个 API 存在但它的用途被明确限制为针对 global topo 中状态变化的刷新例如 shard 记录、SrvKeyspace 的变化而不再用于「刷新自身 tablet 记录」。源码中的实现印证了这一点——rpc_actions.go 中RefreshState只是转调tmState.RefreshFromTopo而后者读取的是shard 记录和 SrvKeyspace见 tm_state.gofunc (ts *tmState) RefreshFromTopo(ctx context.Context) error { // ... shardInfo, err : ts.tm.TopoServer.GetShard(ctx, ts.Keyspace(), ts.Shard()) // ... srvKeyspace, err : ts.tm.TopoServer.GetSrvKeyspace(ctx, ts.tm.tabletAlias.Cell, ts.Keyspace()) // ... return ts.RefreshFromTopoInfo(ctx, shardInfo, srvKeyspace) }RefreshFromTopoInfo会从中提取分片级信息是否处于 reshardingSourceShards、TabletControls 中的 denied tables 规则、SrvKeyspace 各 partition 中本分片是否 serving 等并据此调用updateLocked调整本地查询服务状态tm_state.go。也就是说刷新不再是为了「同步自己的记录」而是为了感知全局拓扑分片、keyspace、serving 状态的变化。两个例外哪里仍然信任 topo设计文档明确列出了两个 topo 依然作为权威的例外场景这两处也正是新模型边界的关键。例外一集群主库选举Cluster Leadership对于「谁是这个分片的主库primary」这类流程topo 是权威。对于这类请求tablet 会先尝试更新自己的记录成功后才算操作成功——顺序与常规流程相反。原因在于新的集群主库选举cluster leadership redesign机制的依赖分片记录shard record中的PrimaryAlias与PrimaryTermStartTime是全体组件vtgate、vttablet、vtorc判定当前主库的唯一依据必须先落盘才能避免「DB 层已是双主、而 topo 层毫不知情」的脑裂窗口。源码中可以看到这一顺序被严格保证——tm_state.go 的ChangeTabletTypeif tabletType topodatapb.TabletType_PRIMARY { primaryTermStartTime protoutil.TimeToProto(time.Now()) // Update the tablet record first. _, err : topotools.ChangeType(ctx, ts.tm.TopoServer, ts.tm.tabletAlias, tabletType, primaryTermStartTime) if err ! nil { // 读取验证或持续重试直到确认写入成功 // ... } } err : ts.updateTypeAndPublish(ctx, tabletType, primaryTermStartTime, action)转为 PRIMARY 时先通过topotools.ChangeType写 topo若写失败代码会反复读取 topo 验证写入是否真的发生了因为不确定是「没写进去」还是「写了但响应丢失」确认Type与PrimaryTermStartTime都匹配后才继续本地状态切换。updateTypeAndPublish中还注释解释了顺序的必要性只有当 topo 更新完成之后才调用SetReadOnly(false)避免出现「DB 层双主、Vitess 层只认一个主」的情况tm_state.go。例外二vttablet 启动时vttablet 启动时会把来自 topo 的以下信息当作权威KeyspaceShardTablet TypeDBName设计文档要求如果这些信息与 init 参数不匹配进程应直接退出exit而不是带着错误的身份进入服务。源码中这一校验体现在 tm_init.go 的initTablet里——当 tablet 记录已存在NodeExists时oldTablet, err : tm.TopoServer.GetTablet(ctx, tablet.Alias) // ... // Sanity check the keyspace and shard if oldTablet.Keyspace ! tablet.Keyspace || oldTablet.Shard ! tablet.Shard { return fmt.Errorf(initTablet failed because existing tablet keyspace and shard %v/%v differ from the provided ones %v/%v, oldTablet.Keyspace, oldTablet.Shard, tablet.Keyspace, tablet.Shard) }keyspace与shard不一致时直接返回错误、拒绝启动。而这些值正是通过一系列init-*启动参数注入的注册于 tm_init.go参数说明--init-keyspace该 tablet 所属 keyspace--init-shard该 tablet 所属 shard--init-tablet-type初始 tablet 类型合法值仅REPLICA、RDONLY、EXPERIMENTAL、SPARE默认REPLICA构建时校验见 tm_init.go--init-db-name-override覆盖 vttablet 使用的数据库名缺省为vt_keyspacename--init-tablet-type-lookup实验性重启时从已有 topo 记录中恢复 tablet 类型使 RDONLY/DRAINED 等角色在重启后保持见 tm_init.go--init-timeoutinit 阶段超时默认 1 分钟--init-tags逗号分隔的key:value标签列表写入 tablet 记录BuildTabletFromInputtm_init.go会基于这些参数构造初始 tablet 记录checkMysql还会把探测到的 MySQL 地址/端口合并进记录tm_init.go。设计文档还提出一个可选项如果 tablet 类型是 primary可以在确认自己是主库之前强制与 shard 记录做一次同步force a sync。源码中的checkPrimaryShiptm_init.go正是这类逻辑——它会读取 shard 记录中的PrimaryAlias与PrimaryTermStartTime与已有 tablet 记录交叉比对shard 记录指向本 tablet 且旧记录也同意 → 带上旧记录的PrimaryTermStartTime恢复为 PRIMARYshard 记录指向本 tablet 但旧记录不是 primary → 采用 shard 记录的PrimaryTermStartTime升主旧记录是 primary 但 shard 不认 → 仅当本 tablet 的PrimaryTermStartTime更新时才接管否则保持 replica。这套启动期逻辑确保「主库身份」在极端重启场景下如旧主先重启、新主还在接管中不会产生双主或丢主。新模型的收益设计文档总结了四个层面的收益vttablet 成为 tablet 记录的权威所有者不再需要持续轮询记录也无需处理记录中可能出现的意外或非法变更复杂度大幅下降自由覆盖本地记录既然假设没有别人会改这条记录vttablet 就可以放心地用本地副本整体覆盖 tablet 记录而无需做字段级合并或同步协商源码中proto.Resetproto.Merge的整记录覆盖正是这一假设的体现流程从两步变一步大量流程从「改记录 刷新」两段式变成「单次 RPC」调用链变短、失败面变窄减轻 topo 负载tablet 不再轮询topo 的读压力显著下降。过渡路径Transition当前仓库的落地现状设计文档给出的过渡分两步第一步把所有调用点改成对 tablet 的单次往返 RPC此时 tabletmanager 仍然更新 tablet 记录并继续依赖现有的轮询器poller第二步当 tabletmanager 已成为其记录的唯一所有者sole owner后把行为切换为非轮询non-polling。从当前仓库源码结构看这套演进仍在推进中且已经能看到大量第一步的痕迹所有状态变更ChangeType、SetServingType引发的状态调整等都经由 rpc_actions.go 的 RPC 入口直接驱动配合actionSema重量级信号量保证同一时刻只有一个动作在执行记录写入统一收敛到tmState.publishStateLockedretryPublish的「发布 重试」模式同时shard_sync.go 中仍然存在shardSyncLoop它维护对 shard 记录的 topo watch仅 primary 时监听并通过notifyChan接收 tablet 本地状态变化通知保持 tablet 记录与 shard 记录如 PrimaryTermStartTime的一致性重试间隔由--shard-sync-retry-delay控制默认 30 秒。这说明当前实现正处于「tablet 已自述状态、但 shard 级同步与轮询辅助机制仍在」的过渡阶段——主库选举对 topo 的强依赖例外一决定了 shard 记录的 watch 在短期内不可完全移除。小结TabletManager 模型的这次重构本质是职责归位vttablet 负责「我是谁、我处于什么状态」topo 中的 tablet 记录退化为「给 vtgate 等组件做发现用的缓存视图」。单次 RPC 往返、尽力而为发布与后台重试、启动时的字段强校验、主库选举时 topo 优先——这四个支柱构成了新模型的完整轮廓。对于想在 Vitess 上做二次开发或运维调优的读者tm_state.go状态发布与刷新、tm_init.go启动与初始化和 shard_sync.goshard 记录同步是理解这套模型最直接的三个入口。【免费下载链接】vitessVitess is a database clustering system for horizontal scaling of MySQL.项目地址: https://gitcode.com/gh_mirrors/vi/vitess创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

EC2302触摸芯片调试实战:电容传感校准与PCB物理设计要点

EC2302触摸芯片调试实战:电容传感校准与PCB物理设计要点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/21 1:51:59 阅读更多 →
树莓派SSH免密登录实战:VScode远程开发高效配置指南

树莓派SSH免密登录实战:VScode远程开发高效配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/21 1:51:59 阅读更多 →
OpenCore EFI自动生成:黑苹果新手如何十分钟装出macOS

OpenCore EFI自动生成:黑苹果新手如何十分钟装出macOS

OpenCore EFI自动生成:黑苹果新手如何十分钟装出macOS 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 装一次黑苹果,最劝退的不…

2026/9/21 1:51:59 阅读更多 →

最新新闻

RSA算法原理图解:3个步骤搞定加密完整示例

RSA算法原理图解:3个步骤搞定加密完整示例

RSA算法原理图解:3个步骤搞定加密完整示例 你从网上复制了一段 RSA 加密代码,导入项目后直接报错 ValueError: b'...' is not a valid base64 string…

2026/9/22 3:59:22 阅读更多 →
3步搞定快刀乱麻:程序员项目架构完整示例

3步搞定快刀乱麻:程序员项目架构完整示例

3步搞定快刀乱麻:程序员项目架构完整示例 刚毕业写代码,是不是常觉得单看每个函数都懂,一搭项目就懵?别慌,这是典型的“快刀乱麻”状态。…

2026/9/22 3:59:22 阅读更多 →
实习总结及体会:手写实现3个核心模块,搞定毕业项目

实习总结及体会:手写实现3个核心模块,搞定毕业项目

实习总结及体会:手写实现3个核心模块,搞定毕业项目 看了一堆教程还是不会写项目?别慌。我带过5届应届生,发现90%的人卡在“能跑通Demo”和“能交付产品”之间。今天不讲虚的,直接拆解我实习期间主导的订单系统重构项目。通过 手写实现…

2026/9/22 3:59:22 阅读更多 →
面试必问大容量存储器,3个坑点避开配置卡半天

面试必问大容量存储器,3个坑点避开配置卡半天

面试必问大容量存储器,3个坑点避开配置卡半天 刚入职的小张,为了准备大厂后端面试,对着文档配置本地测试环境。他下载了 SSD 驱动,装好了 RAID 卡,结果代码一跑,磁盘 I/O 直接卡死,日志刷出几千行报错。他盯着屏幕抓头发,心想:…

2026/9/22 3:59:22 阅读更多 →
大整数加法速查手册:拆解源码彻底搞定

大整数加法速查手册:拆解源码彻底搞定

大整数加法速查手册:拆解源码彻底搞定 看了一堆教程还是不会写项目?别慌,很多人卡在“看懂了逻辑”和“能独立实现”之间的鸿沟。大整数加法看似简单,实则是考察字符串处理、数组操作及边界条件的经典入门题。本文不玩虚的,直接通过一份…

2026/9/22 3:58:21 阅读更多 →
qvod视频搜索实战项目踩坑:API全变后的3个致命错误

qvod视频搜索实战项目踩坑:API全变后的3个致命错误

qvod视频搜索实战项目踩坑:API全变后的3个致命错误 qvod视频搜索接口在2023年Q4版本升级后,底层数据结构彻底重构,导致大量基于旧版API开发的实战项目直接报错。很多开发者盯着控制台里满屏的 JSON Parse Error…

2026/9/22 3:58:21 阅读更多 →

日新闻

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天 配置环境就卡半天?别怪机器慢,多半是你没选对工具链。在Java、Go或Python的项目现场, 手写实现…

2026/9/22 0:00:41 阅读更多 →
剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑 面试被问原理答不上来,是不是常态?别慌。很多开发者对着 GitHub 开源仓库里的代码发呆,看似简单实则暗藏玄机。今天这份【剑帝加点】速查手册,直接带你拆解核心实现,把面试必考的原理讲透。…

2026/9/22 0:00:41 阅读更多 →
手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优 复制来的代码跑不通不知道怎么调?别慌,这种“复制粘贴地狱”在开发圈太常见了。尤其是做 图片压缩网站…

2026/9/22 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/21 3:13:20 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/21 2:19:36 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/21 4:51:05 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/22 2:43:42 阅读更多 →