TongSearch中分片从何而来,又解决了什么问题

一、为什么 TongSearch 必须引入"分片"这一抽象

在理解 TongSearch 分片机制之前,必须先回到它最底层的事实:TongSearch 并不是一个"原生分布式存储引擎",而是一个分布式的 Lucene 管理系统。

Lucene 的核心设计决定了一个非常重要的前提条件:

一个 Lucene Index 在物理上并不是无限可扩展的。

Lucene 的索引文件需要维护倒排表、词典、段(Segment)以及大量随机访问的数据结构。当索引规模不断扩大时,无论是文件句柄数量、内存映射压力,还是段合并成本,都会呈现出明显的非线性增长。Lucene 官方长期以来的工程经验都表明,单个索引做到"无限大"在工程上不可行。

TongSearch 在设计之初就接受了这一现实,并选择了一条非常明确的路线:

与其让一个 Lucene Index 无限膨胀,不如从逻辑层面把一个索引拆成多个 Lucene Index,并在系统层统一管理。

于是,"Shard(分片)"这一抽象诞生了。

在 TongSearch 中,每一个 Primary Shard 或 Replica Shard,本质上就是一个独立的 Lucene Index。分片并不是逻辑概念,而是物理存在的索引实体。

从这个角度看,分片并不是为了"分布式而分布式",而是为了控制 Lucene 的工程复杂度,并在此基础上实现横向扩展能力。

二、索引创建阶段:分片数量是如何被"固化"的

在 TongSearch 中,分片的生命周期起点,永远发生在索引创建这一刻。

当用户通过 PUT /index_name 创建索引时,请求最终会被路由到当前集群的主节点。主节点并不会立刻去创建任何物理分片,而是先完成一件非常关键的事情:

生成索引级别的元数据(IndexMetadata)并写入集群状态。

在这个元数据中,有一个字段具有决定性意义:

number_of_shards

一旦索引被创建成功,这个值就会被永久写入集群状态,并且在 TongSearch 中不可更改。这是一个非常典型的"设计即约束"的工程决策。

原因并不复杂:

分片数量直接参与 document → shard 的路由计算

分片 ID 会被写入倒排结构、事务日志和 translog

Lucene 并不支持在不重写索引的情况下重新切分索引

因此,TongSearch 选择了一条相对保守但极其稳定的道路:

分片数一旦确定,就成为索引的"物理边界条件"。

这也是为什么在工程实践中,分片设计往往被称为"索引设计中最重要的一次性决策"。

三、Shard ID 的来源:分片并不是随机编号

在索引元数据被写入集群状态后,TongSearch 会为这个索引生成一组固定的 Primary Shard。

每一个 Primary Shard 都有一个不可变的整数 ID:

ShardId = [indexUUID, shardNumber]

其中:

indexUUID 是索引级别的唯一标识

shardNumber 的取值范围是 [0, number_of_shards - 1]

这里有一个非常重要但容易被忽略的事实:

Shard ID 的编号顺序,与节点、磁盘、甚至是否真的创建完成都没有关系。

Shard ID 是一个纯逻辑编号空间,它只用于路由和一致性计算,而不是用于表达"第几个被创建"。

这为后续的路由算法提供了一个稳定、可预测的映射基础。

四、Document 到 Shard 的路由:一次确定、终身有效

当一个文档被写入 TongSearch 时,它必须被路由到某一个确定的 Primary Shard。这一过程在 中遵循一个极其稳定的计算链路。

  1. routing 值的确定

每一个文档在写入时,都会先确定一个 routing 值:

如果用户显式指定了 _routing,则使用该值

否则默认使用 _id

这一步的设计目标非常明确:确保同一 routing 值的文档,永远落到同一个分片上。

  1. hash 计算

TongSearch 使用 Murmur3 哈希函数对 routing 值进行计算:

hash = murmur3(routing)

Murmur3 的特点在于:

速度快

分布均匀

跨版本稳定

这保证了分片负载在统计意义上的均衡性。

  1. 分片定位公式

最终的分片编号通过取模计算得到:

shard = hash % number_of_shards

这一行代码,几乎决定了 TongSearch 分片模型的全部工程约束。

因为:

number_of_shards 一旦变化

所有历史文档的路由结果都会发生改变

这也是为什么 TongSearch 禁止在线修改主分片数量 的根本原因。

五、分片信息如何进入集群状态

当索引创建完成后,分片并不会立刻出现在某个节点上。

主节点首先会在内存中构建一个新的 ClusterState,其中包含:

IndexMetadata(索引元数据)

RoutingTable(分片路由表)

在这个 RoutingTable 中,每一个 Primary Shard 会被标记为:

未分配(UNASSIGNED)

此时,"分片"在逻辑上已经存在,但在物理上尚未落地。

只有在后续的 Shard Allocation 阶段,主节点根据节点信息、磁盘水位、分配规则计算出目标节点后,分片才会真正被创建为 Lucene Index。

这意味着:

分片的"存在"是先于其物理文件的。

这是 TongSearch 能够实现复杂重分配、延迟分配和失败恢复的基础。

六、总结

在 TongSearch 中:

分片不是运行时随意切分的单元,而是索引创建时确定的物理边界

分片 ID 是稳定的、与节点无关的逻辑编号

document → shard 的映射在第一次写入时就已经被永久确定

集群状态是分片存在的第一载体,而不是磁盘文件

这也直接解释了很多现象,例如:

为什么分片设计错误只能通过重建索引修复

为什么 routing 使用不当会导致严重的数据倾斜

为什么索引数量和分片数量会直接影响主节点稳定性

相关推荐
进阶小白猿2 小时前
Java技术八股学习Day26
java·开发语言·学习
余瑜鱼鱼鱼2 小时前
synchronized总结
java·开发语言
小宇的天下2 小时前
Calibre :SVRF rule file example
java·开发语言·数据库
码农水水2 小时前
大疆Java面试被问:使用Async-profiler进行CPU热点分析和火焰图解读
java·开发语言·jvm·数据结构·后端·面试·职场和发展
我真的是大笨蛋2 小时前
MVCC解析
java·数据库·spring boot·sql·mysql·设计模式·设计规范
秃头续命码农人2 小时前
谈谈对Spring、Spring MVC、SpringBoot、SpringCloud,Mybatis框架的理解
java·spring boot·spring·mvc·maven·mybatis
ahauedu2 小时前
SpringBoot 3.5.10引入springdoc-openapi-starter-webmvc-ui版本
java·spring boot·后端
我是咸鱼不闲呀2 小时前
力扣Hot100系列15(Java)——[二叉树]总结(有效的括号,最小栈,字符串解码,每日温度,柱状图中最大的矩形)
java·算法·leetcode
沉默-_-2 小时前
MyBatis 学习笔记
java·开发语言·tomcat