Storm1.0版本重要知识点梳理

本文详细介绍了Storm的集群部署流程、高可用测试方法及其实现原理,包括Nimbus、Supervisor和Worker的角色职责,以及消息的可靠性处理方式。

一、Storm topology提交到集群分析 

   storm目前1.x版本支持nimbus的高可用(其实也可以不需要高可用,因为nimbus是无状态的,只要运行的topology没有故障且没有新的任务需要提交到storm集群,那么也可以不要nimbus,因为是worker在运行任务,nimbus只是负责任务分配,资源调度且和supervisor保持心跳我们可以做好整个集群的监控即可,当nimbus挂了后,直接重启它,不会影响正在运行的topology)。
    当我们将topology提交到storm集群的时候,如果你搭的环境是一套高可用的环境,首先需要找到leader nimbus节点,因为需要向leader节点提交我们的拓扑,当调用submitTopology的时候,首先会进行相关的配置校验,然后找到配置的nimbus的ip,循环找到为leader的ip地址将其构建成一个NimbusClient返回,然后开始提交jar,我们可以把这个过程抽象成,我们是thift client通过RPC向server提交jar,jar上传完成后,通知Nimbus上传任务已经完成,nimbus接收到jar后将jar重命名后保存到inbox目录下,然后进行配置检查,任务分配。
   nimbus首先会检查它的配置信息,以及整个storm集群中可用的slots(可用理解为worker),
   第一步:检查整个集群中可用的worker,然后根据配置的worker数分配
   第二步:在zk中创建任务的心跳检测节点/storm/workbeats/,storm对zookeeper的一个重要应用就是利用zk的临时节点做存活检测。任务将定时刷新节点的时间戳,然后nimbus会检测这个时间戳是否超过timeout设置。
   第三步:开始分配任务,根据topology配置的task数和worker数,进行分配
     3.1 如果task数目比worker数多
          比如task 为4个,worker为2个
          task1,task2,taks3,task4
          worker1,woker2, 那么效果就是task1到worker1,task2到woker2,这样依次轮询
     3.2 如果worker数比task数目多
         比如task5个,worker 8 个 基本会轮询保证task不会全部分配到同一个worker上
   第四步:supervisor下载分配给自己的topology下载完成后然后运行。


二、高可用测试


       storm nimbus的单点问题解决了在1.版本,主要理由zk的Ledaer选举实现,其实主要是依靠分布式互斥锁来实现,我们可以将zk的一个数据节点代表一个锁,当多个客户端同时调用create()节点创建节点的时候,zookeeper会保证只会有一个客户端创建成功,那么我们就可以让这个创建成功的客户端让其持有锁,而其它的客户端则注册Watcher监听,当持有锁的客户端释放锁后,监听的客户端就会收到Watcher通知,然后再去试图获取锁。
   1、直接停掉nimbus leader :已经运行的任务不会有任何问题  
  2、任务启动过程中,立刻停掉nimbus leader 
     如果此时新Leader还没选举出来,任务提交会失败
     如果新的Leader已经产生了,任务提交成功
   3、关闭worker所在机器的supervisor守护进程(默认3秒检测一次worker进程)--> Kill -9 $workerPid ---> 关闭Nimbus Leader --->worker没有重新启动/飘走-->1分钟后启动supervisor --->worker飘移走
   4、storm的高可用利用分布式缓存API来实现数据的备份,实现文件在多个topology之间的共享。


三、storm的主要特点


       1、水平扩展:可以直接通过加机器来提高整个集群处理任务的速率,不仅可以直接添加机器作为supervisor节点,而且可以动态调整并发度来改变运行某个topology的线程数
    2、容错性比较好:当Nimbus挂了,可以通过高可用来选举出一个新的Leader(当然nimbus挂了也不是什么大问题,因为运行任务不在nimbus节点上),当supervisor挂了,nimbus会通过心跳检测到,然后会将运行在这个supervisor节点上的所有任务转移到其它节点上,当worker挂了,即运行任务的工作进程挂了,首先supervisor会尝试重启它,如果没启动成功,那么nimbus就会将运行在worker上的任务转移到其它可用的worker节点上运行。
    3、消息的可靠性处理:storm默认提供3种类型,
      仅仅一次:原生storm api无法实现,需要用的storm 高级部分trident实现 
      最多一次:也就是消息最多只发一次,不管这条消息有没有被成功处理
      至少一次:消息如果处理失败后,会重发
    4、节点的无状态:状态都保存在zk里面,当我们使用kill -9 $nimbus_pid的时候,不影响整个集群的运行。

四、运行Storm的相关术语

      1、topology:拓扑,运行一组spout/bolt构成的计算逻辑组件的总称
   2、spout:storm消息来源,一般的话是消息队列
   3、bolt:处理消息逻辑的组件,它的上游可以是spout也可以是Bolt
   4、tuple:运行的消息单元,比如句子 zhangsan is a man 这个消息就是一个tuple
   5、Stream:流,由一条条消息组织的,抽象成流
   6、Stream Grouping:流分组,定义各个计算组件之间流的连接,分组,分发策略等


五、看一个简单的例子(测试storm的容错性)


      从消息队列Kafka接收消息,然后进行单词拆分,然后统计单词出现的次数.起一个线程从消息队列收消息,3个线程进行单词拆分,1个线程进行全局计数
    1、如果nimbus挂了怎么办?
    2、如果supervisor 挂了怎么办?
    3、如果worker挂了怎么办?
    4、节点挂了怎么办?
    第一个问题:nimbus是无状态的,nimbus挂了不影响整个集群中已经在运行的topology的运行。
   第二个问题:supervisor守护进程挂了,没有影响。
   第三个问题:worker挂了怎么办? supervisor会尝试重启,如果没有启动成功,则发生worker转移,转移到其它可用的节点上运行。
   第四个问题:Node节点挂了怎么办? 这个实际上也可用理解为worker挂了,也会发生worker转移。


六、storm的并发度


      一个topology可用跑在多个worker进程上,自然可用想的在一个进程里面,我们可以跑多个线程,再一次加快数据的处理效率,默认情况下,如果我们不设置线程数,就是1,且默认情况下,一个线程对应一个task任务(task可以理解为spout/bolt实例)
   其中,每一个worker默认起一个acker,也就是说每一个worker默认起一个线程来执行acker,而acker实际上就是一个特殊的bolt,所以在计算整个集群线程数的时候,需要加上acker


七、流分组


       流分组定义了上层tuple如何路由到下一层tuple,storm里面默认有以下几种。注意,首先下层必须有多个线程,不然一个线程定义流分组没有意义。
   1、shuffleGrouping : 轮询
   2、filedGrouping:按照上一层的输出属性,相同属性的会被路由到同一个线程处理
   3、allGrouping:广播分组,对于每一个tuple,所有的Bolt都会收到
   4、globalGrouping:全局分组,只会到bolt里面其中task-id最小的那一个
   5、noGrouping:不分组,随机
   6、DirectGrouping:直接分组上一层可以直接指定想把该消息路由到下一层的哪个bolt
   7、自定义分组
   8、localorshuffleGrouping:如果上层bolt和下层bolt运行在同一个进程里面,优先在进程内通信,使用Disruptor有界队列


八、storm的可靠性测试

  8.1: 消息的ack

      当一个spout数据源发射了消息后,一个tuple经过了所有的Bolt处理之后,这个tuple才被认为是处理完了,而acker就是一个用来追踪这些消息的组件,
acker内部类似于一个map,维护了消息id和顶层spoutid直接的关系 
    1、如果task挂了,一个tuple没有被ack,那么会在超时之后(30s)spout负责重发
    2、如果acker挂了,那么由这个tuple追踪的所有的tuple都会超时,也会被重发
    3、那么spout挂了,第三方消息源负责重发

   8.2 如何实现可靠的spout

     storm的bolt和spout有很多接口和抽象类,我们可以实现ISpout接口,首先需要注意的是spout里面,nextTuple,ack,fail是运行在同一个线程里面,所有不要在nextTuple里面执行一些比较耗时的处理,默认30s超时,如果acker-30s还没有收到那么就会超时重发,且nextTuple不能阻塞,如果没有消息发射,他会sleep一下,释放cpu,而如果我们想实现可靠的spout,需要自己维护msgId和消息直接的对应关系,可以放在map,redis里面,并且自己实现fail函数进行消息重发。

   8.3 如何实现可靠的bolt

    storm提供两种不同类型的 Bolt,分别是 BaseRichBolt 和 BaseBasicBolt都可以实现消息的可靠性处理,其中BaseBasicBolt,storm已经帮我们内部自己实现了消息的可靠处理,BasicOutputCollector在emit数据的时候,会自动和输入的tuple相关联,而在execute方法结束的时候那个输入tuple会被自动ack 。
   而BaseRichBolt则需要我们自己维护msgid和tuple之间的关系并手动实现ack或者fail
   下面来分析一下BaseBasicBolt是怎么实现了,当我们调用emit的时候,由于没有streamId和tuple之间的对应关系,storm会给我们自己生成一个默认的流id,最后会起一个BasicBoltExecutor,这里面的excetue方法自己给我们实现了ack和失败后的fail

   8.4 针对storm提交的3种消息保证语义,在ack的基础上来看一下如何实现

    8.4.1、如何实现最多一次(下面几个条件只要有一个满足,都只能实现最多一次语义)
       1)将ack的个数设置为0
       2)Spout不实现可靠的消息处理
          不带msgId或者不实现fail函数
       3)bolt不把处理的消息发送给acker
    8.4.2 如何实现至少一次
       1)开启ack机制,即ack数目大于0(默认每个worker一个ack)
       2)Spout 实现可靠性传输保证
         Spout 发送消息时附带 message 的 ID
         如果收到 Acker 的处理失败反馈,需要进行消息重传,即实现 fail 函数
       3)Bolt 在处理成功或失败后需要调用相应的方法通知 Acker  
    8.4.3 实现仅仅一次
    storm原生api不能做到,要实现仅仅一次,需要存储tuple的状态,用来防止重复发送的数据被重复处理,在 Storm 中使用 Trident API 实现


九、思考?
   1、storm消息重发,重发的消息从哪来?重复的消息怎么处理?
      storm需要维护msgId和具体tuple之间的关系  
   2、如果一个bolt总是处理失败导致spout重发,维护的数据越来越多,最后OOM,怎么办?  
       storm在以前的版本里面可以设置max.pending数(前提条件是必须开启ack),即如果spout组件发现还有这么多消息没有给他ack,当达到这个阀值的时候,就不会在接着发tuple给bolt
     PS:这种设置方法个人感觉值得商榷,首先这个值应该设置多少,设置小了,那么整个集群的吞吐量上不去,设置大了很有可能导致内存溢出。


十、事物


     storm事物在0.7版本后封装到trident中,虽然原生的事物api已经废弃,但是对于我们理解高级部分trident还是非常有帮助的。事物在storm里面主要分为2个节点,事物的处理阶段和提交阶段,事物的处理阶段是可以并行处理的,但是是提交阶段必须按照事物id依次排队处理。
   storm里面事物主要分为3种,一种是普通的事物,一种是分区事物,一种是不透明事物(事物的更高级封装)
  1、普通事物ITransactionSpout接口,事物处理阶段起多个线程,提交阶段起1个线程
     Coordinator:初始化事物
     Emitter:emitBatch接收初始化事物生成的事物元数据,发送出去
  2、分区事物IPartitionTransactionSpout接口,主流的事物Spout
      Coordinator:isReady和返回分区个数
      Emitter: emitPartitionBatchNew 发送新分区的元数据
               emitPartitionBatch 事物处理失败后重发
  3、不透明事物IOpaqueTransactionSpout,容错性最好的事物Spout
      它不区分每一批是不是发送的tuple是一样的,因为即使某个分区不可用,它还会继续发送可用的分区,等其他不可用的分区可用后,这些分区里面的数据被放到下一个事物batch里面执行

<think>首先,用户要求梳理数据库分片的相关知识点,包括核心概念、实践方法和知识点梳理。我需要根据系统级指令来构建回答。 系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 - 回答结构清晰,帮助用户逐步解决问题,尽量保证回答真实可靠。 - 参考提供的引用内容。 提供的引用内容: - [^1]: 分片键是用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段。SQL中如果无分片字段,将执行全路由,性能较差。除了对单分片字段的支持,ShardingSphere也支持根据多个字段进行分片。 - [^2]: ER分片:关系型数据库中,如果可以先确定表之间的关联关系,并将那些存在关联关系的表记录存放在同一个分片上,那么就能较好的避免跨分片join问题。在1:11:n的情况下,通常按照主表的ID主键切分。 - [^3]: ShardingJDBC核心概念知识梳理。官方网站:http://shardingsphere.apache.org/index_zh.html 官网的学习文档超级好。 - [^4]: 数据量太大?用数据库水平切分搞定!好友业务是一个典型的多对多关系,数据冗余是一个常见的多对多业务数据水平切分实践;冗余数据的常见三种方案;实现一致性要实践的常见三种方案。 用户的问题:梳理数据库分片的核心概念、实践方法、知识点梳理。 我需要构建一个结构清晰的回答: 1. **核心概念**:解释数据库分片是什么、为什么需要它、关键术语如分片键、分片策略等。 2. **实践方法**:讨论如何实现分片,包括分片策略(如取模、范围)、工具(如ShardingSphere)、常见实践(如ER分片)。 3. **知识点梳理**:总结关键点,可能包括优缺点、注意事项。 在回答中,自然地引用提供的引用内容。例如,当提到分片键时,引用[^1];当提到ER分片时,引用[^2];当提到ShardingSphere时,引用[^3];当提到多对多关系的数据冗余时,引用[^4]。 由于系统级指令要求生成相关问题,我需要在回答末尾添加一个“相关问题”部分。 回答结构: - **引言**:简要介绍数据库分片。 - **核心概念**:定义和解释关键概念。 - **实践方法**:描述实现方法。 - **知识点梳理**:总结核心知识点。 - **相关问题**:生成几个相关问题。 确保使用中文。 现在,脑力 storm 内容: - **核心概念**: - 数据库分片:水平拆分数据库,将数据分布到多个物理服务器上。 - 分片键(Shard Key):用于决定数据如何分片的字段,如订单ID。引用[^1]。 - 分片策略:如取模(mod)、范围(range)、哈希等。 - ER分片:基于实体关系的分片,避免跨分片join。引用[^2]。 - **实践方法**: - 使用工具如ShardingSphere(Apache项目)。引用[^3]。 - 数据冗余:用于多对多关系,如好友业务。引用[^4]。 - 一致性维护:如通过冗余数据的一致性方案。 - **知识点梳理**: - 优点:提高性能、可扩展性。 - 缺点:复杂性增加、跨分片操作(如join、事务)困难。 - 最佳实践:选择合适的分片键、避免热点数据、使用ER分片。 在回答中,自然地添加引用标识。例如: - 分片键是水平拆分的关键字段[^1]。 - ER分片可以避免跨分片join问题[^2]。 生成相关问题: 1. 如何选择合适的分片键? 2. 数据库分片分库分表的区别是什么? 3. ShardingSphere 的主要功能有哪些? 4. 如何处理分片环境下的分布式事务? 最后,确保回答真实可靠,基于标准知识。</think>### 数据库分片核心概念、实践方法知识点梳理 数据库分片是一种水平拆分数据库的技术,用于解决大数据量下的性能瓶颈和可扩展性问题。它将一个大型数据库(或表)拆分成多个较小的片段(称为“分片”),并分布到不同的物理服务器上。这样,查询和写入操作可以并行化处理,提升系统吞吐量。下面我将从核心概念、实践方法到知识点梳理,逐步解释这一技术。 #### 一、核心概念 1. **分片键(Shard Key)**: 分片键是用于决定数据如何水平拆分的数据库字段。例如,在订单表中,以订单ID的尾数取模作为分片键,可以将数据均匀分布到多个分片。SQL查询中如果不包含分片键,会导致全路由扫描(即查询所有分片),性能大幅下降。除了单字段分片,也支持多字段组合分片[^1]。 2. **分片策略**: 分片策略定义了数据如何分配到分片。常见策略包括: - **取模分片(Mod)**:基于分片键的哈希值取模分配,例如 $h(k) \mod n$,其中 $k$ 是分片键值,$n$ 是分片数量。这种方式确保数据均匀分布。 - **范围分片(Range)**:按分片键的范围划分,如订单ID从1-1000分配到分片11001-2000分配到分片2。适合时间序列数据。 - **哈希分片(Hash)**:使用哈希函数计算分片位置,公式为 $s = \text{hash}(k) \mod n$,避免数据倾斜。 3. **ER分片(Entity-Relationship Sharding)**: 在关系型数据库中,ER分片通过分析表之间的关联关系(如1:11:n),将关联记录存储在同一个分片上。例如,用户表和订单表按用户ID分片,确保同一用户的订单都在同一分片,避免跨分片JOIN操作[^2]。 4. **跨分片操作**: 分片后,跨分片操作(如JOIN、事务)变得复杂。例如,跨分片JOIN需要从多个分片聚合数据,增加延迟。解决方案包括使用ER分片或数据冗余。 #### 二、实践方法 1. **分片实现步骤**: - **选择分片键**:优先选择高基数字段(如用户ID),避免热点数据。例如,在好友业务中,用户ID作为分片键可均匀分布数据[^4]。 - **确定分片策略**:根据业务场景选择策略。取模分片适合均匀负载,范围分片适合范围查询。 - **部署分片工具**:使用开源工具如Apache ShardingSphere,它支持自动分片路由、SQL重写等功能。其官方网站提供详细文档,适合学习实践[^3]。 - **测试优化**:通过压力测试验证分片效果,监控热点分片。 2. **数据冗余一致性**: 在多对多关系(如好友系统)中,数据冗余是常见实践。例如,存储好友关系的冗余副本到不同分片。一致性维护方案包括: - **最终一致性**:通过异步复制保证数据最终一致。 - **分布式事务**:如使用两阶段提交(2PC)。 - **版本控制**:为数据添加版本号,处理冲突[^4]。 3. **避免跨分片问题**: - 使用ER分片减少JOIN跨分片[^2]。 - 在SQL中强制包含分片键,避免全路由查询[^1]。 - 对于聚合查询,使用中间件(如ShardingSphere)自动合并结果。 4. **工具推荐**: Apache ShardingSphere 是主流分片中间件,支持分库分表、读写分离等。其核心流程包括SQL解析、路由、重写、执行和结果合并[^3]。示例分片配置: ```yaml # ShardingSphere 配置示例 rules: - !SHARDING tables: orders: actualDataNodes: ds_${0..1}.orders_${0..1} tableStrategy: standard: shardingColumn: order_id shardingAlgorithmName: mod_hash shardingAlgorithms: mod_hash: type: MOD props: sharding-count: 4 # 分片数量 ``` #### 三、知识点梳理 - **优点**: - 提升性能:读写操作分散到多节点,降低单点压力。 - 高扩展性:通过添加分片服务器,轻松扩容。 - 高可用性:分片间故障隔离,避免全库宕机。 - **缺点**: - 复杂性:跨分片事务和JOIN难以实现。 - 运维成本:需要管理多个分片,监控和备份更复杂。 - 数据倾斜:分片键选择不当会导致热点问题。 - **关键注意事项**: 1. 分片键应具有高区分度(高基数),避免如性别字段导致数据不均。 2. 优先使用ER分片处理关联表,减少跨分片操作[^2]。 3. 在NoSQL数据库(如MongoDB)中,分片是内置功能;在SQL数据库(如MySQL),需借助中间件。 4. 性能分析:分片后,查询延迟公式可表示为 $T = T_{\text{route}} + \max(T_{\text{shard}_i})$,其中 $T_{\text{route}}$ 是路由时间,$T_{\text{shard}_i}$ 是各分片查询时间。 - **最佳实践总结**: - 小表不分片:维度表(如城市代码)可全局存储。 - 分片粒度:根据数据量动态调整分片数量。 - 一致性优先:在金融场景,使用强一致性方案;在社交业务,可接受最终一致性[^4]。 数据库分片是处理大数据量的核心手段,但需权衡利弊。推荐参考ShardingSphere官网深入学习[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值