IoTDB集群扩容:从慌乱到从容的经验分享

一、IoTDB集群架构:不只是"分布式"那么简单

我刚开始接触IoTDB那会儿,以为它就是个简单的分布式数据库,多加几个节点就能提升性能。但真正深入了解之后才发现,IoTDB的集群架构设计有它自己的考虑。

1.1 ConfigNode与DataNode:管事的和干活的

IoTDB集群把计算和存储给分开了。啥意思呢?就是说管数据这事儿和数据实际存在哪儿、怎么去查,是两拨"人"在干【turn0search37】。

  • ConfigNode:这个角色,其实就是干管理活儿的。管啥呢?管元数据、管权限、管分区这些"行政"上的事
  • DataNode:这是干活的。数据怎么存、查询怎么执行,都归它管

这么分的好处是管理节点不会变成性能瓶颈。就算元数据操作特别频繁,也不会影响到数据读写的性能。我们的环境里,ConfigNode放在3台配置比较高的机器上,DataNode则放在多台性价比更高的机器上。
#mermaid-svg-OfsTZ87UGyMzuFUD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OfsTZ87UGyMzuFUD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OfsTZ87UGyMzuFUD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OfsTZ87UGyMzuFUD .error-icon{fill:#552222;}#mermaid-svg-OfsTZ87UGyMzuFUD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OfsTZ87UGyMzuFUD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OfsTZ87UGyMzuFUD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OfsTZ87UGyMzuFUD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OfsTZ87UGyMzuFUD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OfsTZ87UGyMzuFUD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OfsTZ87UGyMzuFUD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OfsTZ87UGyMzuFUD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OfsTZ87UGyMzuFUD .marker.cross{stroke:#333333;}#mermaid-svg-OfsTZ87UGyMzuFUD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OfsTZ87UGyMzuFUD p{margin:0;}#mermaid-svg-OfsTZ87UGyMzuFUD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OfsTZ87UGyMzuFUD .cluster-label text{fill:#333;}#mermaid-svg-OfsTZ87UGyMzuFUD .cluster-label span{color:#333;}#mermaid-svg-OfsTZ87UGyMzuFUD .cluster-label span p{background-color:transparent;}#mermaid-svg-OfsTZ87UGyMzuFUD .label text,#mermaid-svg-OfsTZ87UGyMzuFUD span{fill:#333;color:#333;}#mermaid-svg-OfsTZ87UGyMzuFUD .node rect,#mermaid-svg-OfsTZ87UGyMzuFUD .node circle,#mermaid-svg-OfsTZ87UGyMzuFUD .node ellipse,#mermaid-svg-OfsTZ87UGyMzuFUD .node polygon,#mermaid-svg-OfsTZ87UGyMzuFUD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OfsTZ87UGyMzuFUD .rough-node .label text,#mermaid-svg-OfsTZ87UGyMzuFUD .node .label text,#mermaid-svg-OfsTZ87UGyMzuFUD .image-shape .label,#mermaid-svg-OfsTZ87UGyMzuFUD .icon-shape .label{text-anchor:middle;}#mermaid-svg-OfsTZ87UGyMzuFUD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OfsTZ87UGyMzuFUD .rough-node .label,#mermaid-svg-OfsTZ87UGyMzuFUD .node .label,#mermaid-svg-OfsTZ87UGyMzuFUD .image-shape .label,#mermaid-svg-OfsTZ87UGyMzuFUD .icon-shape .label{text-align:center;}#mermaid-svg-OfsTZ87UGyMzuFUD .node.clickable{cursor:pointer;}#mermaid-svg-OfsTZ87UGyMzuFUD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OfsTZ87UGyMzuFUD .arrowheadPath{fill:#333333;}#mermaid-svg-OfsTZ87UGyMzuFUD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OfsTZ87UGyMzuFUD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OfsTZ87UGyMzuFUD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OfsTZ87UGyMzuFUD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OfsTZ87UGyMzuFUD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OfsTZ87UGyMzuFUD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OfsTZ87UGyMzuFUD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OfsTZ87UGyMzuFUD .cluster text{fill:#333;}#mermaid-svg-OfsTZ87UGyMzuFUD .cluster span{color:#333;}#mermaid-svg-OfsTZ87UGyMzuFUD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OfsTZ87UGyMzuFUD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OfsTZ87UGyMzuFUD rect.text{fill:none;stroke-width:0;}#mermaid-svg-OfsTZ87UGyMzuFUD .icon-shape,#mermaid-svg-OfsTZ87UGyMzuFUD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OfsTZ87UGyMzuFUD .icon-shape p,#mermaid-svg-OfsTZ87UGyMzuFUD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OfsTZ87UGyMzuFUD .icon-shape .label rect,#mermaid-svg-OfsTZ87UGyMzuFUD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OfsTZ87UGyMzuFUD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OfsTZ87UGyMzuFUD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OfsTZ87UGyMzuFUD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} IoTDB集群
ConfigNode集群
DataNode集群
元数据管理
权限控制
分区管理
集群配置
数据存储
查询执行
数据合并
数据迁移

1.2 Region:数据是怎么分片存的

IoTDB集群里,Region说白了就是个数据分片【turn0search37】。每个Region本质上是个逻辑上的数据文件夹,负责存一部分数据。Region有两种:

  • SchemaRegion:存元数据信息(比如时间序列定义、模板这些)
  • DataRegion:存实际的时间序列数据

Region会自动分布在多个DataNode上,这样能保证数据不会因为一个节点坏了就丢掉,负载也能均衡。每个Region默认有多个副本(通常是3个),放在不同的DataNode上,这样单个节点坏了数据也不会丢。

💡 踩坑提示 :维护集群的时候,必须保证正常运行的DataNode数量不能少于数据复制因子(通常是2)或者元数据复制因子(通常是3)【turn0search37】。不然的话,数据可能会丢,集群也可能用不了。

1.3 集群之间怎么通信

IoTDB集群内部通过好几种协议来通信:

  • Raft协议:用在ConfigNode和SchemaRegion上,保证元数据是一致的
  • IoTConsensus协议:用在DataRegion上,保证数据是一致的
  • 心跳机制:节点之间靠心跳来感知彼此的状态

二、分区机制:数据如何分布到不同节点

理解了Region是什么,下一个关键问题是:数据是怎么被分到不同Region里的? 这就涉及到IoTDB的分区机制。

2.1 序列分区:基于设备的哈希分区

IoTDB用哈希分区的策略,把不同时间序列的数据分布到不同的DataRegion里【turn0search11】。具体怎么做的呢?就是拿序列的存储路径做个哈希计算,把哈希值映射到不同的序列槽,每个序列槽对应一个DataRegion。
#mermaid-svg-oDe5mOfRe5pzzIVm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oDe5mOfRe5pzzIVm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oDe5mOfRe5pzzIVm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oDe5mOfRe5pzzIVm .error-icon{fill:#552222;}#mermaid-svg-oDe5mOfRe5pzzIVm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oDe5mOfRe5pzzIVm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oDe5mOfRe5pzzIVm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oDe5mOfRe5pzzIVm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oDe5mOfRe5pzzIVm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oDe5mOfRe5pzzIVm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oDe5mOfRe5pzzIVm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oDe5mOfRe5pzzIVm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oDe5mOfRe5pzzIVm .marker.cross{stroke:#333333;}#mermaid-svg-oDe5mOfRe5pzzIVm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oDe5mOfRe5pzzIVm p{margin:0;}#mermaid-svg-oDe5mOfRe5pzzIVm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oDe5mOfRe5pzzIVm .cluster-label text{fill:#333;}#mermaid-svg-oDe5mOfRe5pzzIVm .cluster-label span{color:#333;}#mermaid-svg-oDe5mOfRe5pzzIVm .cluster-label span p{background-color:transparent;}#mermaid-svg-oDe5mOfRe5pzzIVm .label text,#mermaid-svg-oDe5mOfRe5pzzIVm span{fill:#333;color:#333;}#mermaid-svg-oDe5mOfRe5pzzIVm .node rect,#mermaid-svg-oDe5mOfRe5pzzIVm .node circle,#mermaid-svg-oDe5mOfRe5pzzIVm .node ellipse,#mermaid-svg-oDe5mOfRe5pzzIVm .node polygon,#mermaid-svg-oDe5mOfRe5pzzIVm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oDe5mOfRe5pzzIVm .rough-node .label text,#mermaid-svg-oDe5mOfRe5pzzIVm .node .label text,#mermaid-svg-oDe5mOfRe5pzzIVm .image-shape .label,#mermaid-svg-oDe5mOfRe5pzzIVm .icon-shape .label{text-anchor:middle;}#mermaid-svg-oDe5mOfRe5pzzIVm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oDe5mOfRe5pzzIVm .rough-node .label,#mermaid-svg-oDe5mOfRe5pzzIVm .node .label,#mermaid-svg-oDe5mOfRe5pzzIVm .image-shape .label,#mermaid-svg-oDe5mOfRe5pzzIVm .icon-shape .label{text-align:center;}#mermaid-svg-oDe5mOfRe5pzzIVm .node.clickable{cursor:pointer;}#mermaid-svg-oDe5mOfRe5pzzIVm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oDe5mOfRe5pzzIVm .arrowheadPath{fill:#333333;}#mermaid-svg-oDe5mOfRe5pzzIVm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oDe5mOfRe5pzzIVm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oDe5mOfRe5pzzIVm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oDe5mOfRe5pzzIVm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oDe5mOfRe5pzzIVm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oDe5mOfRe5pzzIVm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oDe5mOfRe5pzzIVm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oDe5mOfRe5pzzIVm .cluster text{fill:#333;}#mermaid-svg-oDe5mOfRe5pzzIVm .cluster span{color:#333;}#mermaid-svg-oDe5mOfRe5pzzIVm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oDe5mOfRe5pzzIVm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oDe5mOfRe5pzzIVm rect.text{fill:none;stroke-width:0;}#mermaid-svg-oDe5mOfRe5pzzIVm .icon-shape,#mermaid-svg-oDe5mOfRe5pzzIVm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oDe5mOfRe5pzzIVm .icon-shape p,#mermaid-svg-oDe5mOfRe5pzzIVm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oDe5mOfRe5pzzIVm .icon-shape .label rect,#mermaid-svg-oDe5mOfRe5pzzIVm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oDe5mOfRe5pzzIVm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oDe5mOfRe5pzzIVm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oDe5mOfRe5pzzIVm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 时间序列路径

root.sg.d1.s1
哈希计算
映射到序列槽

0-999
分配到DataRegion

Region1
时间序列路径

root.sg.d2.s2
哈希计算
映射到序列槽

1000-1999
分配到DataRegion

Region2

序列分区有几个关键点:

  • 哈希函数保证相同设备的数据尽量放在同一个Region,这样查询起来快
  • 序列槽的数量通过series_slot_num参数配置,默认是1000【turn0search2】
  • 没法手动指定序列的分区位置,系统自动分配

注意:如果你的业务经常要跨设备查询,可能需要调整序列槽数量,避免数据倾斜。

2.2 时间分区:基于时间范围的分区

除了序列分区,IoTDB还会按时间范围 对数据进行分区【turn0search11】。时间分区通过time_partition_interval参数配置,默认是604800000毫秒(也就是1周)【turn0search2】。

时间分区的好处:

  1. 时间范围查询快:查特定时间段的数据,只需要访问相关的Region
  2. 数据生命周期管理:可以基于时间分区设置TTL(生存时间),自动删掉过期数据
  3. 并行处理:不同时间分区的数据可以并行处理,查询效率高

2.3 分区与Region的关系

每个DataRegion实际上是由序列分区和时间分区组成的矩阵里的一个格子。比如,一个集群可能有100个序列槽和10个时间分区,那理论上最多会有1000个DataRegion(但实际上会根据数据量和负载动态调整)。

数据分布计算公式:

复制代码
Region数量 ≈ 序列槽数量 × 时间分片数量

三、集群扩容

当 IoTDB 集群因数据量或访问压力激增,出现 CPU、内存、磁盘或网络等资源瓶颈时,可通过本指南进行横向扩容,以提升集群整体性能与承载力。

3.1 实现原理

IoTDB 集群扩容的核心是增加 DataNode 节点,因为 DataNode 是处理读写请求的主要节点。其内部的数据分区(DataRegion)是承载负载的关键单元,扩容的本质即通过增加数据分区组来提升集群并发能力与吞吐量。

默认情况下,假设集群的元数据副本数为三,数据副本数为二,初次初始化时会创建集群总核数除以二的数据分区。如下图一个由3台2核服务器组成的集群,假设每个数据副本组的处理能力为1,那么该集群(包含3个数据副本组)的总吞吐能力为3。因此,将服务器数量由三台扩展为六台,集群由原来的三数据分区组扩容为六数据分区组,总吞吐能力也相应实现翻倍。

扩容后,客户端读写负载的均衡规则是旧节点可能同时处理老设备(集群中已存在的 DeviceId 的数据)和新设备(集群中不存在的 DeviceId 的数据)的数据读写,而新节点仅接收新增数据的读写,旧节点不会对原有副本组进行自动重均衡。数据负载均衡通常在每个新时间分区创建时进行,默认时间为每周四上午八点。其核心原理是通过序列分区槽和时间分区槽进行负载均衡算法运算,从而决定数据由哪个节点的哪个分区处理。

如果希望在扩容后进一步均衡历史数据,可按需执行 Region 迁移或 Region 重均衡:当需要精确指定 Region、源 DataNode 和目标 DataNode 时,可参考【4. 手动 Region 迁移】;当希望系统自动计算迁移方案并完成整体均衡时,可参考【5. 自动 Region 重均衡】

3.2 操作步骤

1、前置准备

1 服务器环境检查,建议操作系统版本与原集群保持一致。

2 数据库版本检查,建议版本与原集群版本保持一致。

3 操作系统环境检查,保证所需端口不被占用,DataNode 会占用端口 6667、10730、10740、10750 、10760、9090、9190、3000请确保这些端口未被占用。

bash 复制代码
lsof -i:6667  或  netstat -tunp | grep 6667
lsof -i:10710  或  netstat -tunp | grep 10710
lsof -i:10720  或  netstat -tunp | grep 10720

4 数据目录检查,确认服务器已挂载数据盘,建议将安装包放在数据盘目录下。

5 软件依赖:安装 Java 运行环境 ,Java 版本 >= 1.8,请确保已设置 jdk 环境变量。

6 域名配置:部署时推荐优先使用 hostname 进行 IP 配置,可避免后期修改主机 IP 导致数据库无法启动的问题。设置 hostname 需要在目标服务器上配置/etc/hosts,如本机 IP 是192.168.1.3,hostname 是 iotdb-1,则可以使用以下命令设置服务器的 hostname,并使用 hostname 配置 IoTDB 的 cn_internal_address、dn_internal_address、dn_rpc_address。

2、扩容操作

1 为确保您获取的 IoTDB 安装包完整且正确,在执行安装部署前建议您进行 SHA512 校验。

2 解压安装包并进入安装目录

bash 复制代码
unzip  iotdb-{version}-bin.zip
cd  iotdb-{version}-bin

3 修改相应配置

bash 复制代码
cd  iotdb-{version}-bin/conf
vim iotdb-system.properies

4 启动 DataNode 节点

进入 IoTDB 的 sbin 目录下,启动 Datanode:

bash 复制代码
./sbin/start-datanode.sh -d    #"-d"参数将在后台进行启动

5 通过 CLI 命令连接原集群,进行扩容后验证

bash 复制代码
# Linux 或 macOS系统
  ./iotdb-{version}-bin/sbin/start-cli.sh
  
  # Windows 系统
  ./iotdb-{version}-bin/sbin/start-cli.bat

6 执行命令进行验证

执行show datanodes命令进行验证,预期结果列表中出现新加入的节点,且节点状态为 Running。

test 复制代码
IoTDB> show datanodes
+------+-------+----------+-------+-------------+---------------+
|NodeID| Status|RpcAddress|RpcPort|DataRegionNum|SchemaRegionNum|
+------+-------+----------+-------+-------------+---------------+
|     1|Running|   0.0.0.0|   6667|            0|              0|
|     2|Running|   0.0.0.0|   6668|            1|              1|
|     3|Running|   0.0.0.0|   6669|            1|              0|
|     4|Running|   0.0.0.0|   6669|            1|              0| # 新加入节点
+------+-------+----------+-------+-------------+---------------+
Total line number = 4
It costs 0.110s

7 其他节点重复以上操作。新节点能够成功加入原集群的前提是原集群允许加入的 DataNode 节点数量足够

3、手动负载均衡(按需选择)

默认情况下,扩容后历史数据不会自动迁移。若需进一步均衡各节点的数据分布,可选择以下方式:

手动 Region 迁移:适用于已明确需要迁移的 Region、源 DataNode 和目标 DataNode 的场景。

自动 Region 重均衡:适用于扩容后希望系统自动选择迁移方案、整体均衡历史数据的场景。

四、集群缩容与节点移除:安全下线的艺术

比起扩容,缩容(移除节点)更要小心,必须保证数据安全。

4.1 移除DataNode的安全流程

我第一次试着移除DataNode时,差点搞丢数据,后来总结出这么一套安全流程:
#mermaid-svg-pN4gol6IukTaSjMc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pN4gol6IukTaSjMc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pN4gol6IukTaSjMc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pN4gol6IukTaSjMc .error-icon{fill:#552222;}#mermaid-svg-pN4gol6IukTaSjMc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pN4gol6IukTaSjMc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pN4gol6IukTaSjMc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pN4gol6IukTaSjMc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pN4gol6IukTaSjMc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pN4gol6IukTaSjMc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pN4gol6IukTaSjMc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pN4gol6IukTaSjMc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pN4gol6IukTaSjMc .marker.cross{stroke:#333333;}#mermaid-svg-pN4gol6IukTaSjMc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pN4gol6IukTaSjMc p{margin:0;}#mermaid-svg-pN4gol6IukTaSjMc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pN4gol6IukTaSjMc .cluster-label text{fill:#333;}#mermaid-svg-pN4gol6IukTaSjMc .cluster-label span{color:#333;}#mermaid-svg-pN4gol6IukTaSjMc .cluster-label span p{background-color:transparent;}#mermaid-svg-pN4gol6IukTaSjMc .label text,#mermaid-svg-pN4gol6IukTaSjMc span{fill:#333;color:#333;}#mermaid-svg-pN4gol6IukTaSjMc .node rect,#mermaid-svg-pN4gol6IukTaSjMc .node circle,#mermaid-svg-pN4gol6IukTaSjMc .node ellipse,#mermaid-svg-pN4gol6IukTaSjMc .node polygon,#mermaid-svg-pN4gol6IukTaSjMc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pN4gol6IukTaSjMc .rough-node .label text,#mermaid-svg-pN4gol6IukTaSjMc .node .label text,#mermaid-svg-pN4gol6IukTaSjMc .image-shape .label,#mermaid-svg-pN4gol6IukTaSjMc .icon-shape .label{text-anchor:middle;}#mermaid-svg-pN4gol6IukTaSjMc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pN4gol6IukTaSjMc .rough-node .label,#mermaid-svg-pN4gol6IukTaSjMc .node .label,#mermaid-svg-pN4gol6IukTaSjMc .image-shape .label,#mermaid-svg-pN4gol6IukTaSjMc .icon-shape .label{text-align:center;}#mermaid-svg-pN4gol6IukTaSjMc .node.clickable{cursor:pointer;}#mermaid-svg-pN4gol6IukTaSjMc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pN4gol6IukTaSjMc .arrowheadPath{fill:#333333;}#mermaid-svg-pN4gol6IukTaSjMc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pN4gol6IukTaSjMc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pN4gol6IukTaSjMc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pN4gol6IukTaSjMc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pN4gol6IukTaSjMc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pN4gol6IukTaSjMc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pN4gol6IukTaSjMc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pN4gol6IukTaSjMc .cluster text{fill:#333;}#mermaid-svg-pN4gol6IukTaSjMc .cluster span{color:#333;}#mermaid-svg-pN4gol6IukTaSjMc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pN4gol6IukTaSjMc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pN4gol6IukTaSjMc rect.text{fill:none;stroke-width:0;}#mermaid-svg-pN4gol6IukTaSjMc .icon-shape,#mermaid-svg-pN4gol6IukTaSjMc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pN4gol6IukTaSjMc .icon-shape p,#mermaid-svg-pN4gol6IukTaSjMc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pN4gol6IukTaSjMc .icon-shape .label rect,#mermaid-svg-pN4gol6IukTaSjMc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pN4gol6IukTaSjMc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pN4gol6IukTaSjMc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pN4gol6IukTaSjMc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 开始移除DataNode
检查节点状态

确保没有Region Leader
把Region迁到其他节点
等数据同步完成
执行REMOVE DATANODE命令
验证节点移除成功
清理下线节点环境

详细操作步骤:

  1. 看看要移除的节点上有什么Region:

    sql 复制代码
    -- 看看DataNode 2上有什么Region
    SHOW REGIONS WHERE DataNodeId=2;
  2. 把Region迁到其他节点:

    sql 复制代码
    -- 把所有Region从DataNode 2迁到DataNode 3
    MIGRATE REGION ALL FROM 2 TO 3;
  3. 等迁移完成:

    sql 复制代码
    -- 检查迁移状态,直到所有Region都迁完了
    SHOW REGIONS WHERE DataNodeId=2;
  4. 移除DataNode:

    sql 复制代码
    REMOVE DATANODE 2;
  5. 验证移除成功:

    sql 复制代码
    SHOW DATANODES;

4.2 移除ConfigNode的注意事项

移除ConfigNode相对简单,但要保证剩下的ConfigNode数量是奇数(通常是3或5),维护Raft共识。

sql 复制代码
REMOVE CONFIGNODE 1;

⚠️ 重要提示 :永远不要把集群里最后一个ConfigNode移掉,不然元数据就丢了,集群都起不来!

五、集群监控与日常维护:预防胜于治疗

折腾了几次集群维护后,我深刻体会到日常监控和定期维护有多重要。下面是我们的监控体系:

5.1 关键监控指标

指标类别 关键指标 告警阈值 说明
CPU CPU使用率 >80% 持续5分钟高于阈值告警
内存 JVM堆内存使用率 >85% 可能导致频繁GC
磁盘 磁盘使用率 >80% 得考虑扩容或清理了
磁盘 磁盘I/O等待时间 >20% 磁盘I/O有瓶颈
网络 网络带宽使用率 >70% 可能成为查询瓶颈
Region Region数量 >1000/节点 Region太多影响性能
查询 查询延迟P99 >5秒 用户体验受影响

5.2 日常维护操作

下面是我们日常做的维护操作:

1. 定期检查集群健康状态

bash 复制代码
# 每天跑一次的健康检查脚本
#!/bin/bash
# 检查节点状态
NODES=$(iotdb-cli -e "SHOW DATANODES;" | grep -c "Running")
if [ "$NODES" -lt 3 ]; then
  echo "警告:DataNode数量不足3个,当前数量:$NODES"
fi

2. 定期合并数据

sql 复制代码
-- 手动触发合并(建议在低峰期执行)
MERGE;
FULL MERGE;

3. 清理缓存

sql 复制代码
-- 清理查询缓存
CLEAR QUERY CACHE;

4. 检查并修复数据

sql 复制代码
-- 启动数据修复任务
START REPAIR DATA;

六、性能调优:从集群配置到查询优化

集群维护不光是管节点,还包括性能调优。下面是我总结的性能调优经验:

6.1 集群参数调优

根据我们的硬件配置和业务负载,调了这些参数:

properties 复制代码
# conf/iotdb-common.properties
# 增加查询线程数,提高并发查询能力
max_query_worker_threads=32
# 增加查询队列大小,避免查询被拒绝
max_query_queue_size=100
# 优化内存分配,根据硬件调整
storage_query_schema_consensus_free_memory_proportion=3:3:1:1:2

6.2 查询优化技巧

  1. 用索引加速查询:

    sql 复制代码
    -- 给时间序列建索引
    CREATE INDEX ON root.sg.d1.s1 USING BLOOM_FILTER;
  2. 合理设计查询语句:

    sql 复制代码
    -- 别全表扫描,加上时间过滤条件
    SELECT * FROM root.sg.d1 WHERE time > 2026-01-01 AND s1 > 100;
    
    -- 用降采样减少数据量
    SELECT AVG(s1) FROM root.sg.d1 GROUP BY ([2026-01-01, 2026-01-31], 1h);
  3. 用缓存:

    properties 复制代码
    # 启用查询缓存
    enable_query_cache=true
    query_cache_size=1073741824

七、结语

IoTDB这个集群吧,它把管事的和干活的给分开了。ConfigNode就负责管元数据、管权限这些事,DataNode就负责数据怎么存、查询怎么跑。这么分的好处是管理不会拖累干活,系统就稳当多了。

数据怎么分是靠Region这个分片。Region说白了就是个数据文件夹,有存元数据的,有存实际数据的。每个Region默认有3个副本,放在不同节点上,这样坏一个也不怕。

分区主要看两个东西:按设备做哈希 ,还有按时间范围切。这两个一交叉,就成了个矩阵,查询快,负载也均匀。

想扩容就加DataNode。加了之后数据不会马上过去,得等新数据写进来,或者手动迁移Region,负载才能平衡。

相关推荐
写后端的胖头鱼1 小时前
详解索引下推
数据库·索引失效·索引·icp·索引下推
Arcai10242 小时前
PostgreSQL数据备份和恢复完全指南
数据库·postgresql·ffmpeg·备份一体机·备份软件·smartstor·智备科技
闲云自留地4 小时前
主库倒下 30 秒自动扶正:MySQL MHA 高可用实战
数据库·mysql
许彰午12 小时前
01-Oracle数据库简介与版本选择
数据库·oracle
菜鸟~noob23313 小时前
【电子战】第三季预告—波武器与电子系统强力毁伤
开发语言·数据库·matlab·电子战
Seraphina3614 小时前
SQL注入(二)盲注
网络·数据库·经验分享·笔记·sql
盟接之桥16 小时前
AI助手:你的7×24小时智能管家——让异常预警无处不在
大数据·网络·数据库·人工智能·制造·ai编程
waoooqwe16 小时前
问卷样本真实吗
大数据·数据库·算法·数据分析
可乐ea16 小时前
Agent 评测换判据:用终态数据库状态判定任务是否完成
数据库·ui·oracle·mcp工具·agent评测·终态断言·智能体可靠性