1. 概述
概述
-
分布式系统:分布式系统由多个通过网络互联、独立自治的计算节点组成,他们之间通过消息传递机制进行相互协作,以完成共同目标
- 独立自治:每个计算节点都有其独立的cpu,独立的时钟,发生错误的时机和模式也相互独立
- 消息传递机制:
- 数据节点之间没用公共状态,通过相互之间发消息来进行协作
- 内部运算速度比消息传递速度高出好几个数量级,因此,通信复杂度是衡量分布式系统的重要指标。
- 可能发生局部节点失效,消息延迟、丢失等问题
- 也被成为"sharing nothing" 框架
-
使用分布式系统好处:提高运算速度、存储能力、可扩展性、网络吞吐量、安全性、可靠性、实现了资源共享,实现了跨越时空的协同服务
-
分布式系统衡量标准:
- 通信复杂度
- 可扩展性
- 透明性
- 容错性
- 并发性
- 安全性
- 可维护性
- 可观测性
- 安全性
- 开放性
题目
- 简述分布式系统的定义。
- 同一主板上包含多个 CPU 的计算机系统是不是分布式系统?为什么?
- 同一个物理主机上是否可以包含多个分布式计算节点?为什么?
- 用多个计算节点构成分布式系统可以带来哪些好处?
1. 分布式系统由多个通过网络互联、独立自治的计算节点组成,他们之间通过消息传递机制进行相互协作,以完成共同目标
2. 不是,没有通过网络连接进行通信和协调,因此不能被视为分布式系统。
3. 是的,同一个物理主机上可以包含多个分布式计算节点。分布式计算节点是指在分布式系统中独立运行的计算实例,它们可以通过网络进行通信和协调。在同一个物理主机上部署多个分布式计算节点可以利用主机的资源,提高系统的利用率和性能。每个计算节点在逻辑上独立运行,可以拥有自己的计算能力和存储资源,通过网络与其他节点进行通信和协作,实现分布式系统的功能。
4. 提高运算速度,存储能力、可扩展性、网络吞吐量、可靠性、安全性,实现跨越时空的协同问题、资源共享
2. 分布式系统模型问题
概述
-
设计产品级分布式系统遇到的挑战?
- 不可靠的时钟:不同节点时钟不同
- 不可靠的计算节点:部分节点可能会失效
- 不可靠的通信网络:消息延迟、丢失,丢包,网络断裂,数据内容被修改
- 没有全局观:只能通过与其他节点的交互信息判断系统的状态
-
分布式系统模型
- 网络行为模型:网络拓扑,通信链路的行为模型
- 节点故障模型:失效停止模型,失效停止重启模型,拜占庭模型
- 时间同步模型:同步模型,异步模型,部分同步模型
-
链路
- 任意链路
- 一般损失链路:不允许数据包被篡改(可存在丢包、重复包、乱序、传输延迟抖动,链路断裂时间有限)
- 可靠链路:发送者的每个消息都能被正确接收,但是会乱序、传输抖动延迟
- 可靠FIFO链路(最强):每个发送的消息都会被正常接收且不乱序。
-
不同模式链路转换
- 任意链路 ---> 一般损失链路:纠错码、加密算法;ssl,http协议
- 一般损失链路 ---> 可靠链路 :
- 发送者尝试持续重发数据包,直到收到ack
- 利用数据包序列号等手段,去除重复包
- 可靠链路 ---> FIFO链路:利用数据包序列号,对数据进行排序
3. 分布式系统节点之间通信问题
概述
1. RPC概念,RPC中间件如何起作用
- RPC概念
- 使应用程序可以像调用本地节点上的过程(子程序)那样去调用一个远程节点上的子程序。
- RPC将面向过程的通用编程模型扩展到了分布式环境。
- 实现了跨进程、跨语言、跨网络、跨平台的过程调用
- 强化了面向接口编程的编程风格
- 实现RPC必须要有RPC中间件的支持。
- RPC中间件如何起作用
- 在调用者进程中植入stub/proxy模块,stub 模块作为远程过程的本地代理,并且暴露与远程过程相同的接口。
- 在被调用者进程中植入skeleton模块, skeleton作为调用者在远程主机中的代理,代替客户端调用本地方法,并把结果返回给客户端。
- skeleton模块相当于Client-Server通信模式中的服务器端, 要先于客户端运行,并且在某个Socket端口进行监听。
2. 什么时候使用消息队列,有什么好处?
消息队列通信模式:
— 在生产者和消费者之间建立的满足先进先出的消息队列
— 一个队列可以有多个生产者,也可以有多个消费者。
— 消息队列中的消息一旦被某个消费者取走,该消息就从队列中删除。
— 出队的消息按照某种负载均衡策略发送给特定的消费者。
— 高级队列模式:带优先级的队列;支持持久性的队列
主题/订阅通信模式:
— 支持向一个特定的消息主题发布消息。
— 多个订阅同一主题的消费者可以同时接收发布到该消息主题的消息
— 可以灵活地实现广播、组播等多对多通信模式
- 异步通信,可以减少系统响应时间,提高吞吐量
- 分布式节点之间的解耦
- 保证消息的可靠递交,实现最终一致性
- 实现广播、组播和多对多通信
- 流量削峰和流控
- 支持Push模型和Pull模型
三种接收方式:阻塞接收、轮询接收、回调|通知接收
例题
问题
- 简述远程过程调用 RPC 的实现原理和 RPC 中间件的主要作用。(4 分)
- 互联网电商平台在应对秒杀、双十一突发访问峰值等场景时如何利用分布式消息中间件提高系统的高并发服务能力?(4 分)
- 除了 RPC 和消息队列通信方式,分布式系统中不同节点之间还可以采用哪些通信方式?(列举 2 种)(4 分)
三、关于远程过程调用(RPC) 技术,回答下列问题。(共12分)
1.从应用程序员角度看,RPC 技术的主要作用是什么? (4 分)
2.简述RPC的实现原理和RPC中间件的主要作用。(6 分)
3.支持跨编程语言调用的RPC中间件系统中,如CORBA和gRPC,都会定义专用的接口定义语言(IDL: Interface Definition Language),请问IDL语言的作用是什么? (2 分)
三、关于远程过程调用(RPC) 技术,回答下列问题。(共10分)、
1.从应用程序员角度看,RPC技术的主要作用是什么?,(4分)
2.简述RPC的实现原理和RPC中间件的主要作用。(6 分)
四、关于基于消息中间件的分布式通信技术,回箐下列问题。(共11分)
1.简述发布/订阅模式和消息队列模式的区别。(4分)
2.消费者接收消息的三种方式是什么? (3 分)
3.分布式节点之间基于消息中间件进行通信有哪些优点? (4 分)
简述远程过程调用 RPC 的实现原理和 RPC 中间件的主要作用。
other
支持跨编程语言调用的RPC中间件系统中,如CORBA和gRPC,都会定义专用的接口定义语言(IDL: Interface Definition Language),请问IDL语言的作用是什么?
接口定义语言(Interface Definition Language,IDL)在跨编程语言调用的RPC中间件系统中起着关键作用。它主要有以下几个作用:
-
接口描述:IDL语言用于描述远程服务的接口。它定义了服务所提供的函数、函数参数和返回类型等接口规范。通过IDL语言,开发人员可以清楚地了解服务接口的结构和功能。
-
跨语言交互:由于不同编程语言的语法和数据类型系统存在差异,直接进行跨语言的远程调用是困难的。IDL语言提供了一种中立的、与编程语言无关的描述方式,使得不同编程语言之间可以共享和理解接口规范。通过IDL语言,RPC中间件可以生成与不同编程语言兼容的代码,以便于在不同语言之间进行远程调用。
-
代码生成:IDL语言允许开发人员在定义接口规范后,通过IDL编译器生成相应的客户端和服务器端代码。这些生成的代码可用于实现远程调用的代理、存根和序列化/反序列化等功能,从而简化了开发人员的工作,提高了开发效率。
-
数据序列化和反序列化:IDL语言通常也定义了数据类型和结构的规范,包括简单类型(如整数、字符串)和复杂类型(如结构体、数组)。通过IDL语言,开发人员可以声明和描述数据结构,使得数据在网络传输过程中可以进行序列化和反序列化操作,从而实现不同编程语言之间的数据交换。
总之,IDL语言在跨编程语言调用的RPC中间件系统中起着桥梁和约定的作用,它定义了服务接口的规范、允许跨语言交互、生成可用的代码和处理数据的序列化/反序列化,从而使得不同编程语言的应用程序能够进行统一的远程调用。
互联网电商平台在应对秒杀、双十一突发访问峰值等场景时如何利用分布式消息中间件提高系统的高并发服务能力?
-
异步处理:通过将请求异步化,将高并发的请求解耦并交给消息中间件处理,可以减轻系统的压力。当用户提交订单或进行秒杀等操作时,不需要立即响应,而是将请求消息发送到消息中间件中,让后续的处理过程异步进行。这样可以有效地平衡系统的负载,提高系统的吞吐量和并发能力。
-
订单队列:使用消息中间件作为订单队列的缓冲区,将用户提交的订单消息放入队列中进行处理。系统可以根据处理能力的情况,逐个消费订单消息进行订单处理。这种方式可以避免系统被瞬间的高并发请求压垮,并提供了订单处理的有序性。
-
分布式事务处理:在秒杀等高并发场景中,可能涉及到库存扣减、订单生成等多个操作,而这些操作需要保持一致性。分布式消息中间件可以提供分布式事务处理的支持,保证多个操作的原子性和一致性。通过将不同操作的消息放入同一个事务中,可以确保这些操作要么全部成功,要么全部回滚,避免了数据不一致的问题。
-
消息削峰填谷:当系统遇到突发的访问峰值时,可以通过消息中间件进行削峰填谷的处理。系统可以将请求消息发送到消息中间件,然后由后端的消费者按照系统处理能力的承载能力进行消费。这样可以平稳地处理高并发的请求,防止系统崩溃。
-
水平扩展:分布式消息中间件具备高可用性和可扩展性,可以方便地进行水平扩展。通过增加消息中间件的实例或增加消费者的数量,可以提升消息处理的能力,满足高并发场景下的需求。
综上所述,利用分布式消息中间件,互联网电商平台可以通过
除了 RPC 和消息队列通信方式,分布式系统中不同节点之间还可以采用哪些通信方式?
restful api,rmi,基于消息传递的通信
-
RESTful API:基于HTTP协议的RESTful API是一种常见的通信方式,它使用HTTP作为传输协议,通过HTTP请求和响应进行通信。每个节点可以暴露一组API接口,其他节点可以通过HTTP请求来调用这些接口进行通信。RESTful API具有简单、轻量级、易于扩展的特点,广泛应用于分布式系统和微服务架构中。
-
基于消息传递的通信:除了消息队列,还可以使用消息传递的通信方式,如发布-订阅(Pub-Sub)模式或事件驱动模式。节点之间可以通过消息传递来进行异步通信和事件通知。这种通信方式可以实现松耦合和可伸缩的系统架构,允许节点之间相互独立地进行通信。
-
数据库同步:在分布式系统中,不同节点之间可以通过数据库同步来进行通信。节点可以将数据写入本地数据库,并通过数据同步机制将数据复制到其他节点的数据库中。这样可以实现数据的共享和同步,不同节点之间可以通过读写数据库来进行通信和数据交换。
-
文件共享:分布式系统中的节点可以通过文件共享的方式进行通信。节点可以将需要共享的数据文件存储在共享文件系统中,其他节点可以通过访问共享文件系统来获取和操作数据文件。文件共享通信方式适用于需要大规模数据共享和批量数据交换的场景。
-
远程方法调用(RMI):类似于RPC,远程方法调用(RMI)也是一种节点之间的通信方式。RMI允许一个节点上的程序通过调用另一个节点上的方法来进行远程通信。不同于RPC的是,RMI通常在同一语言或平台中使用,它通过Java的远程接口和远程对象实现远程方法的调用和通信。
这些通信方式都可以在分布式系统中用于不同节点之间的通信和数据交换。选择适合的通信方式取决于系统的需求、场景和技术栈的选择。
4. 物理时钟和逻辑时钟
概述
物理时钟:
- 不同节点物理时钟不一定同步
- 分为单调钟、墙钟
- 单调种:按照一定频率持续自增;绝对值无意义,相对值表示延时或时间差;System.nanoTime()
- 墙钟:与日常生活日历、天文事件对应;标准:协调世界时;可能发生倒流现象;System.currentTimeMillis()
- 不同步NTP工作原理
- NTP通过时间服务器和时间客户端之间的通信,实现计算机网络中的时间同步。时间服务器提供准确的时间信息,时间客户端通过与时间服务器的通信来校准和调整自身的本地时间,以保持网络中的时间一致性。
- 向量时钟:更准确捕获因果冲突;效率较低
- 可靠广播
- 可靠广播:可靠广播协议要求发送一致性和可信达性,即消息要么被所有节点接收,要么被所有节点拒绝,且每个节点只接收一次。
(1) 如果一个节点正确接收到了广播消息m,则其它所有节点都正 确接收到了该消息m ;(除非某些节点失效)
(2) 如果消息m被正确投递(Delivered)给节点A,则消息m一定 会被被正确投递其它所有节点;(和(1)是一个意思)
(3) 如果一个节点正确接收到了广播消息m,则m一定是某个节点 发送的。(即m不能无中生有) - 全序广播协议(Total Order Broadcast): (+有序性,逻辑时钟) (1) 多个广播消息以完全相同的顺序投递给所有节点; (即所有节点以完全相同的顺序接收到了多个广播消息)
- 因果排序广播 (向量时钟)
(1)如果消息m被正确投递(Delivered)给节点A,则消息m 一定会被正确投递其它所有节点;
(2)对于具有因果关系的两个消息m1、m2 (m1 ➔ m2),在 任意节点上m1一定会先于m2投递给应用层。
- 可靠广播:可靠广播协议要求发送一致性和可信达性,即消息要么被所有节点接收,要么被所有节点拒绝,且每个节点只接收一次。
(1) 如果一个节点正确接收到了广播消息m,则其它所有节点都正 确接收到了该消息m ;(除非某些节点失效)
5 分布式存储
分区与复制
复制:在多个不同的节点存储相同数据的多个副本
分区:将一个大型数据文件/数据库,分成若干个较小子集(分区),再将不同分区指派给不同节点。
多副本分布式存储一致性问题(为什么会有一致性问题)
why? 1. 多个客户端“同时”对分布式存储系统进行读写。(并发) 2. 分布式节点之间的网络不可靠:丢包、延迟、断裂、乱序 3. 部分存储节点会失效 4. 节点的局部时钟不同步
强一致性与弱一致性 1. 强一致性:(线性一致性/原子一致性) 满足强一致性的两个条件: (1)一个写入操作返回成功应答之后,后续的读操作一定可以读出最新的值; (2)一个读操作读取到了一个更新后的变量值,发生在该读操作之后的所有读操作只能读出相同的值或者更新版本的值,不能读出旧版本的值。 在客户端看来,分布式存储系统的外在表现和单副本存储系统的外在表现完全一致。(类似于多个线程访问同一个寄存器)任意客户端看到的所有针对分布式系统的操作(读、写等原子操作)按 全局一致的顺序排列(线性化),并且该排序满足多个操作在时间维****度上的实际发生先后顺序。 1. 顺序一致性(强一致性): 任意客户端看到的所有针对分布式系统的操作(读、写等原子操作)按 全局一致的顺序排列(线性化),同一个客户端发出的多个操作的顺序与该全局一致排序并不矛盾。 3. 因果一致性: 不同客户端看到的所有针对分布式系统的操作(读、写等原子操作)排序不一定一致,但该排序不违背操作发生的因果关系。这种模型保证了按照事件的因果关系进行的操作在所有节点上具有一致性。 4. 最终一致性: 在分布式系统停止更新时,最终所有读操作都可以获得最新版本的数据。
raft协议
一种基于领导者、追随者的一致性协议。节点相互通信选出leader,由此leader统一调度。
当旧leader失效后,会重新选出一个新leader,从而解决了脑裂问题。
通过日志复制机制、leader决策解决了继承性问题,新leader会完成旧leader未完成的任务
Raft更注重可读性和易于实现,适用于更广泛的应用场景;而Paxos则更加复杂,适用于一些对性能要求更高的场景
概述
Raft协议是一种一致性协议,用于分布式系统中解决副本之间的一致性问题。它的设计目标是提供更好的可理解性和可维护性,而不是追求极致的性能。
Raft协议通过将一致性问题分解为几个较小的、易于理解和实现的组件,简化了分布式一致性的复杂性。它基于领导者-追随者模型,并通过选举机制来选择一个领导者,领导者负责接收客户端的写入请求,并将更新操作复制到其他副本节点。
Raft协议的核心机制包括:
-
领导选举:在初始状态或当前领导者失效时,Raft协议会进行领导选举。节点通过相互之间的通信来选择一个新的领导者。选举过程中,每个节点都有一个随机的超时定时器,当定时器超时时,节点会开始选举过程。
-
日志复制:Raft协议使用日志来记录所有的状态变化。当领导者接收到客户端的写入请求时,它将该请求作为一个日志条目添加到自己的日志中,并将该日志条目复制到其他节点的日志中。一旦多数节点确认接收到了这个日志条目,领导者可以将该日志条目提交并应用到状态机中,从而使得系统状态一致。
-
安全性:Raft协议通过限制谁有权决定日志内容以及如何复制和提交日志来确保安全性。只有领导者可以接受客户端的写入请求,并决定在日志中添加什么内容。其他节点只能按照领导者的指令复制和提交日志,确保数据一致性。
-
一致性检查点:为了加快节点的启动和恢复过程,Raft协议引入了一致性检查点的概念。一致性检查点是一种快照,它包含了一系列已经被提交的日志条目。当节点需要恢复时,它可以从最近的一致性检查点开始,而不是从头开始复制整个日志。
总的来说,Raft协议提供了一种简单而可靠的方法来实现分布式系统中的一致性。通过领导者选举、日志复制、安全性和一致性检查点等机制,Raft协议能够在面对节点故障和网络分区等问题时保证系统的一致性和可用性。
题目1
在一个包含 n 个节点的分布式系统中应用 Paxos 或 Raft 分布式共识协议实现不同节点间的数据同步,最多可以容忍多少个节点的失效?
Paxos协议能够容忍 (n-1)/2 个节点的失效,而Raft协议能够容忍 (n/2)-1 个节点的失效,其中 n 是总节点数量
在Paxos和Raft分布式共识协议中,能够容忍的节点失效数量取决于所采用的算法和协议的具体配置。
在Paxos协议中,基本的一致性算法(Multi-Paxos)可以容忍 n/2 - 1 个节点的失效,其中 n 是总节点数量。这意味着在一个由奇数个节点组成的系统中,最多可以容忍 (n-1)/2 个节点的失效。
在Raft协议中,为了保证正常的操作和容错能力,系统通常会配置一个主节点和多个从节点。Raft协议的容错性取决于选举过程中的多数派原则。具体来说,在一个由 n 个节点组成的系统中,至少需要 (n/2)+1 个节点正常工作才能保证系统的正常运行。因此,最多可以容忍 (n/2)-1 个节点的失效。
需要注意的是,以上提到的容忍失效节点的数量是指在系统中同时发生的失效情况。如果失效节点发生在不同的时间段或并不是同时发生的,系统可能会容忍更多的节点失效。此外,节点失效数量也可以通过增加节点数量来提高系统的容错性。
总而言之,Paxos协议能够容忍 (n-1)/2 个节点的失效,而Raft协议能够容忍 (n/2)-1 个节点的失效,其中 n 是总节点数量。
分区:哈希分区、一致性哈希分区
场景描述:假设某分布式数据库系统根据新插入数据记录的主键(key)选择不同的服务器节点存储该记录,共有 N 个存储服务器,标识号分别为 ID1,ID2,…, IDN。 另外假定存在理想的无碰撞哈希函数 H,能够将任意的字符串转化为 5bit 的正整数,H 的输出结果在区间[0, 31]上均匀分布。向该数据库共插入了 10 条记录,这10 条记录的主键的哈希值分别为 1,3,5,9,14,15,19,21,26,31。
- 假定用以下策略选择具体的存储服务器:计算 i =H(key) mod N,其中 mod 表示取余运算,然后将当前记录存储在服务器 IDi+1中。初始时 N=3,当增加一个服务器 ID4 时会有多少条数据记录需要在不同服务器节点间移动?给出计算过程。
对于问题1中的策略选择,我们先来回顾一下记录的存储过程。
1. 计算 i = H(key) mod N,其中 mod 表示取余运算,N 表示服务器节点的数量。
2. 将当前记录存储在服务器 IDi+1 中。
现在我们逐个计算记录存储的服务器编号:
1. 对于主键哈希值为1的记录:i = 1 mod 3 = 1,存储在服务器 ID2。
2. 对于主键哈希值为3的记录:i = 3 mod 3 = 0,存储在服务器 ID1。
3. 对于主键哈希值为5的记录:i = 5 mod 3 = 2,存储在服务器 ID3。
4. 对于主键哈希值为9的记录:i = 9 mod 3 = 0,存储在服务器 ID1。
5. 对于主键哈希值为14的记录:i = 14 mod 3 = 2,存储在服务器 ID3。
6. 对于主键哈希值为15的记录:i = 15 mod 3 = 0,存储在服务器 ID1。
7. 对于主键哈希值为19的记录:i = 19 mod 3 = 1,存储在服务器 ID2。
8. 对于主键哈希值为21的记录:i = 21 mod 3 = 0,存储在服务器 ID1。
9. 对于主键哈希值为26的记录:i = 26 mod 3 = 2,存储在服务器 ID3。
10. 对于主键哈希值为31的记录:i = 31 mod 3 = 1,存储在服务器 ID2。
原先的服务器分布如下:
- 服务器 ID1 上的记录:记录3、记录9、记录15、记录21
- 服务器 ID2 上的记录:记录1、记录19、记录31
- 服务器 ID3 上的记录:记录5、记录14、记录26
我非常抱歉,我的先前回答有误。在上述问题中,并没有说明重新分配记录的具体方法。请允许我来更正并解释如何进行重新分配。
当增加一个服务器 ID4(已知 H(ID4)=30)时,需要重新分配记录,具体步骤如下:
1. 首先,将现有的服务器节点和其对应的记录进行整理:
- 服务器 ID1 上的记录:记录3、记录9、记录15、记录21
- 服务器 ID2 上的记录:记录1、记录19、记录31
- 服务器 ID3 上的记录:记录5、记录14、记录26
2. 接下来,计算每个记录在新增服务器 ID4 上的存储位置:
- 对于服务器 ID1 上的记录:记录3、记录9、记录15、记录21
- 计算 i = H(key) mod 4:
- 记录3:i = 3 mod 4 = 3,存储在服务器 ID4
- 记录9:i = 9 mod 4 = 1,存储在服务器 ID2
- 记录15:i = 15 mod 4 = 3,存储在服务器 ID4
- 记录21:i = 21 mod 4 = 1,存储在服务器 ID2
- 对于服务器 ID2 上的记录:记录1、记录19、记录31
- 计算 i = H(key) mod 4:
- 记录1:i = 1 mod 4 = 1,存储在服务器 ID2
- 记录19:i = 19 mod 4 = 3,存储在服务器 ID4
- 记录31:i = 31 mod 4 = 3,存储在服务器 ID4
- 对于服务器 ID3 上的记录:记录5、记录14、记录26
- 计算 i = H(key) mod 4:
- 记录5:i = 5 mod 4 = 1,存储在服务器 ID2
- 记录14:i = 14 mod 4 = 2,存储在服务器 ID3
- 记录26:i = 26 mod 4 = 2,存储在服务器 ID3
更新服务器节点的记录分布情况:
- 服务器 ID1 上的记录:记录9、记录21
- 服务器 ID2 上的记录:记录1、记录5、记录19
- 服务器 ID3 上的记录:记录14、记录26
- 服务器 ID4 上的记录:记录3、记录15、记录31
因此,当增加服务器 ID4 后,有3条数据记录需要在不同服务器节点间移动。
- 设 N=3,并且 H(ID1)=4, H(ID2)=23, H(ID3)=13。如果采用基于一致性哈希算法的服务器选择策略,那么这三台服务器分别存储了几条记录?给出计算过程。
根据一致性哈希算法,我们需要将服务器节点和哈希空间映射到一个环上。每个服务器节点在环上占据一个位置,同时根据哈希函数的输出结果将数据记录映射到环上的一个位置。在这个问题中,服务器节点的标识号已经给出,哈希函数的输出结果也已经给出。
首先,我们将服务器节点的哈希值映射到环上,得到节点在环上的位置:
- H(ID1) = 4,节点ID1在环上的位置是4。
- H(ID2) = 23,节点ID2在环上的位置是23。
- H(ID3) = 13,节点ID3在环上的位置是13。
然后,我们将数据记录的哈希值映射到环上,找到每个数据记录应该存储的位置:
- 数据记录1的哈希值是1,对应的位置在环上是1。
- 数据记录2的哈希值是3,对应的位置在环上是3。
- 数据记录3的哈希值是5,对应的位置在环上是5。
- 数据记录4的哈希值是9,对应的位置在环上是9。
- 数据记录5的哈希值是14,对应的位置在环上是14。
- 数据记录6的哈希值是15,对应的位置在环上是15。
- 数据记录7的哈希值是19,对应的位置在环上是19。
- 数据记录8的哈希值是21,对应的位置在环上是21。
- 数据记录9的哈希值是26,对应的位置在环上是26。
- 数据记录10的哈希值是31,对应的位置在环上是31。
根据一致性哈希算法的原理,数据记录应该存储在离它最近的服务器节点处。在环上,顺时针方向离数据记录位置最近的节点即为存储该数据记录的节点。
现在我们来计算每个服务器节点存储了多少条记录:
- 节点ID1:存储了数据记录1、数据记录2、数据记录3,共3条记录。
- 节点ID2:存储了数据记录4、数据记录5、数据记录6、数据记录7、数据记录8,共5条记录。
- 节点ID3:存储了数据记录9、数据记录10,共2条记录。
所以,根据给定的一致性哈希算法和服务器节点的哈希映射,节点ID1存储了3条记录,节点ID2存储了5条记录,节点ID3存储了2条记录。
- 接上题,仍然采用基于一致性哈希算法的服务器选择策略,当增加一个服务器 ID4(已知 H(ID4)=30)时,有多少条数据记录需要在不同服务器节点间移动?
哈希分区和一致性哈希分区是在分布式系统中常用的数据分片策略,用于将数据均匀地分布到多个节点上。
- 哈希分区: 哈希分区是一种简单的数据分片方法。它使用数据的哈希值来确定数据应该被存储在哪个节点上。具体步骤如下:
- 根据数据的特征,计算数据的哈希值。
- 将哈希值映射到节点的范围,通常使用模运算将哈希值映射到节点编号。
- 将数据存储在对应的节点上。
哈希分区的优点是简单直观,适用于均匀分布的数据。但它的缺点是对于节点的增减和数据的迁移不够灵活,因为一旦节点发生变化,所有的数据都需要重新分配。
- 一致性哈希分区: 一致性哈希分区是一种解决数据分片和节点扩缩容问题的高级方法。它通过哈希环来表示节点,将数据映射到哈希环上的位置,从而决定数据应该存储在哪个节点上。具体步骤如下:
- 将节点映射到一个哈希环上的位置。
- 对于数据,计算其哈希值并映射到哈希环上的位置。
- 沿着哈希环顺时针寻找下一个节点,将数据存储在该节点上。
一致性哈希分区的优点是在节点增减和数据迁移时具有很好的扩展性和灵活性。当节点发生变化时,只需要重新分配受影响的部分数据,而不需要重新分配所有数据。这使得节点的扩缩容更加高效。但一致性哈希分区的实现相对复杂一些。
总的来说,哈希分区和一致性哈希分区都是常用的数据分片策略。哈希分区简单直观,适用于均匀分布的数据,但对节点的增减和数据的迁移不够灵活。一致性哈希分区解决了这个问题,具有更好的扩展性和灵活性,但实现较为复杂。选择使用哪种分区策略应根据具体的需求和系统设计的要求来决定。
HDFS
- 主从框架
- 高容错
- 高吞吐量
- 大文件支持 why不适合大量小文件存储?
- 简单一致性模型
概述
名称节点(NameNode) 管理HDFS的命名空间,保存了两个核心的数据解构:FsImage,EditLog。 FsImage:维护文件系统树以及文件树中所有文件和文件夹的元数据 EditLog:记录了所有针对文件的创建、删除、重命名等工作。 记录了文件各个块所在节点的位置信息。
数据节点(DataNode)HDFS的工作节点。负责数据的存储、读取。会根据client/NameNode的调度来进行数据的存储与检索,并定期向NameNode发送自己的所存储块的列表
题目
1. 简述客户端读取一个 HDFS 文件时与 NameNode 节点和 DataNode 节点的交互过程。(4 分)
2. 为了提高数据存储的可靠性,HDFS 在设计上采取了哪些技术措施?(至少给出 2 种)(4 分)
3. 为什么 HDFS 不适合存储大量的小文件?(2 分)
1. HDFS的NameNode节点和DataNode节点的主要功能分别是什么? (4 分)
2. HDFS采用什么技术提高数据存储的可靠性? (3分)
3. 简述HDFS客户端读取一个HDFS文件时与NameNode和DatanNode的交互过程。(4分)
1. why HDFS不适合大量小文件存储
- 大量小文件会导致NameNode负载过大
- HDFS采用碎片化存储,大量小文件会导致存储空间浪费
- HDFS数据元开销大,不适合大量小文件
- 处理大量小文件时,需要进行多次文件系统操作,尤其在并发环境下,会性能下降
HDFS不适合大量小文件存储的主要原因包括以下几点:
-
元数据开销:HDFS在存储小文件时,需要为每个文件分配一定的存储空间来保存元数据信息,如文件名、权限、时间戳等。对于大量小文件,这将导致巨大的元数据开销,消耗过多的存储空间和内存资源。
-
存储碎片化:小文件的存储方式会导致存储碎片化问题。HDFS的文件块大小通常为64 MB 或128 MB,如果存储的文件很小,可能会浪费大量存储空间,因为每个文件都需要至少分配一个块的空间。这将导致磁盘空间利用率低下,同时也会增加存储的管理和维护成本。
-
延迟和处理成本:在处理大量小文件时,HDFS需要进行多次文件系统操作,如文件定位、读取和关闭等。这些额外的操作会增加访问延迟,并且对于大量并发请求的情况下,可能会造成文件系统的瓶颈和性能下降。
-
NameNode的负载问题:在HDFS中,NameNode负责管理和维护文件系统的元数据信息。大量小文件会导致NameNode需要管理的文件数量巨大,使其负载过重,可能导致性能下降甚至系统崩溃。
尽管HDFS对大型文件和大数据处理具有良好的扩展性和容错性,但对于大量小文件存储,HDFS的设计并不适用。如果需要存储大量小文件,可以考虑其他专门针对小文件存储优化的文件系统,或者使用数据存储和处理框架,如Apache HBase等。
2. HDFS采用什么技术提高数据存储的可靠性
数据复制、数据块的位置感知调度、心跳机制、数据完整性检验、自动故障检测与恢复
HDFS(Hadoop Distributed File System)采用了多种技术来提高数据存储的可靠性,其中主要包括以下几个方面:
-
数据复制:HDFS将文件切分为数据块,并将每个数据块复制多次存储在不同的数据节点上。默认情况下,每个数据块会有三个副本(可以通过配置进行调整),其中一个是主副本,其他副本是冗余备份。这样即使某个数据节点出现故障,数据仍然可以从其他副本中获取。
-
数据块的位置感知调度:HDFS会尽量将副本放置在不同的机架上,以防止机架级别的故障。这样即使整个机架发生故障,数据仍然可以通过其他机架上的副本来获取,提高了系统的容错性。
-
心跳机制:HDFS中的数据节点会定期向NameNode发送心跳信号,以通知其存活状态。如果某个数据节点长时间未发送心跳信号,NameNode会将其标记为不可用,将其上的数据块复制到其他可用的节点上,以确保数据的可靠性。
-
数据完整性校验:HDFS通过使用校验和来检测数据在传输或存储过程中的损坏。每个数据块都会计算一个校验和,并与数据一起存储。当客户端读取数据时,HDFS会计算读取的数据的校验和,并与存储的校验和进行比较,以确保数据的完整性。
-
自动故障检测与恢复:HDFS具有自动故障检测和恢复机制。当数据节点或NameNode出现故障时,HDFS能够快速检测到,并自动进行故障切换和恢复,以保持系统的可靠性和可用性。
通过以上这些技术手段,HDFS能够提供高度可靠的数据存储服务,保证数据的安全性和可用性。这些技术使得HDFS在大规模数据存储和处理场景下具有优秀的容错性和鲁棒性。
3. 简述HDFS客户端读取一个HDFS文件时与NameNode和DatanNode的交互过程
- 客服端向名称节点发送读请求,参数包含:文件名、偏移量、长度
- 名称节点根据文件名、偏移量,查找"文件名—数据块对应表","数据块—物理节点对应表",并讲包含目标数据块的数据节点ip列表发送给客服端
- 客服端根据数据节点ip列表,选择最近的数据节点,进行连接,读取数据
写操作
- 客服端向名称节点发送新建文件请求
- 名称节点根据负载均衡选择三个数据节点,并将ip返回给客户端
- 客服端讲这三个数据节点构成一个流水线,装入第一个数据块
- 第一个数据块装入成功后,再向名称节点获取下一个数据块的三个数据节点
HDFS的NameNode节点和DataNode节点的主要功能分别是什么
名称节点(NameNode) 管理HDFS的命名空间,保存了两个核心的数据解构:FsImage,EditLog。 FsImage:维护文件系统树以及文件树中所有文件和文件夹的元数据 EditLog:记录了所有针对文件的创建、删除、重命名等工作。 记录了文件各个块所在节点的位置信息。
数据节点(DataNode)HDFS的工作节点。负责数据的存储、读取。会根据client/NameNode的调度来进行数据的存储与检索,并定期向NameNode发送自己的所存储块的列表
在HDFS中,NameNode节点和DataNode节点具有不同的功能和角色:
-
NameNode节点:
- 元数据管理:NameNode节点负责管理HDFS的文件系统命名空间和元数据信息。它维护着整个文件系统的目录结构、文件和目录的权限、时间戳等元数据信息。
- 块分配和命名空间操作:NameNode负责协调数据块的分配和管理。它决定将文件的数据块分配给哪些DataNode节点,并记录数据块与DataNode节点之间的映射关系。
- 故障检测和恢复:NameNode通过与DataNode节点保持心跳联系,及时检测和处理DataNode节点的故障。当一个DataNode节点失效时,NameNode负责将其上的数据块复制到其他健康的DataNode节点上,以保证数据的可靠性和可用性。
-
DataNode节点:
- 数据存储和管理:DataNode节点是HDFS的数据存储节点。它负责实际存储文件数据块,并提供数据的读取和写入服务。每个DataNode节点都保存着存储在本地磁盘上的数据块副本。
- 数据块报告和心跳:DataNode节点周期性地向NameNode发送心跳信号,以向其报告自身的状态和可用性。它还发送数据块报告,通知NameNode当前存储的数据块信息,包括数据块的ID、大小、位置等。
- 数据块复制和恢复:DataNode节点按照NameNode的指示进行数据块的复制。当一个DataNode节点上的数据块损坏或丢失时,其他DataNode节点上的副本可以被用来恢复丢失的数据块。
NameNode节点和DataNode节点之间通过网络进行通信,协同工作来实现分布式文件系统的管理和数据存储。NameNode负责管理元数据和文件系统操作,而DataNode则负责实际的数据存储和处理。这种分工使得HDFS能够提供高容错性、高可用性和高扩展性的分布式存储服务。
6 MapReduce
思想
MapReduce是一种分布式计算模型,用于处理大规模数据集的并行计算。它将数据处理过程分为两个主要阶段:映射(Map)和归约(Reduce)。
Map阶段:
- 输入数据被划分为若干个数据块,并由多个Mapper并行处理。
- 每个Mapper对数据块进行处理,将输入数据转换为键值对(Key-Value pair)的形式。
- Mapper独立处理每个键值对,生成中间结果,也称为局部结果。
Shuffle阶段:
- Map阶段产生的中间结果会经过分区和排序操作,将相同键的键值对发送到相同的Reducer进行处理。
- 中间结果按键进行分区,确保相同键的键值对被发送到同一个Reducer。
- 在分区后,键值对会按键进行排序,以便Reducer可以按顺序处理。
Reduce阶段:
- 每个Reducer独立处理一组相同键的键值对。
- Reducer对这组键值对进行归约操作,将它们合并为较少数量的键值对,产生最终的输出结果。
- Reduce操作可以进行聚合、过滤、计数等操作,根据具体需求进行数据处理。
- 最终的输出结果可以写入文件系统、数据库或其他存储介质。
2022-7
假定作为输入的 HDFS 数据文件中保存了某公司员工的工资信息,格式如下:
员工姓名 1, 部门 1, 年份 1,月份 1,工资金额 1,
(注:
为换行符)
员工姓名 2, 部门 1, 年份 1,月份 1,工资金额 2,
员工姓名 3, 部门 2, 年份 1,月份 1,工资金额 3,
员工姓名 4, 部门 3, 年份 1,月份 1,工资金额 3,
… … … …
员工姓名 1, 部门 1, 年份 1,月份 2,工资金额 4,
员工姓名 2, 部门 1, 年份 1,月份 2,工资金额 5,
… … … …
已知该公司 2021 年没有人员流动。现要求编写 MapReduce 程序,统计不同部门员
工 2021 年的年平均工资,即输出如下形式的统计数据:
部门 1,员工年平均工资 1
部门 2,员工年平均工资 2
……
用伪代码描述该 MapReduce 程序的主要设计思想。
- Mapper阶段:
- 读取输入文件的每一行。
- 解析每一行,提取部门、年份和工资金额。
- 将部门作为键,工资金额作为值发射出去。
- Reducer阶段:
- 接收Mapper阶段发射的键值对。
- 对于每个部门,将所有工资金额进行累加,并计算该部门的员工数量。
- 计算该部门的年平均工资,即累加的工资金额除以员工数量。
- 将部门作为键,年平均工资作为值发射出去。
- main:
- 创建一个MapReduce作业
- 设置输入路径为HDFS数据文件
- 设置输出路径为结果保存的位置
- 设置Mapper类和Reducer类
- 设置Mapper,Reducer的输出键值对类型
- 启动作业并等待完成
function map(key, value):
// 解析输入行的部门、年份和工资金额
let data = value.split(", ");
let department = data[1];
let salary = parseInt(data[4]);
// 发射键值对(部门, 工资金额)作为Mapper的输出
emit(department, salary);
function reduce(key, values):
// 初始化累加器和计数器
let sum = 0;
let count = 0;
// 遍历values列表,将工资金额累加到累加器中,并递增计数器
for each value in values:
sum += value;
count++;
// 计算平均工资 = 累加器 / 计数器
let averageSalary = sum / count;
// 发射键值对(部门, 平均工资)作为Reducer的输出
emit(key, averageSalary);
function main():
// 创建一个MapReduce作业
let job = createJob();
// 设置输入路径为HDFS数据文件
job.setInputPath("inputFile.txt");
// 设置输出路径为结果保存的位置
job.setOutputPath("outputFolder");
// 设置Mapper和Reducer函数
job.setMapper(map);
job.setReducer(reduce);
// 设置Mapper的输出键值对类型
job.setMapOutputKeyValueTypes(string, integer);
// 设置Reducer的输出键值对类型
job.setReduceOutputKeyValueTypes(string, float);
// 启动作业并等待完成
job.run();
例题
1
题目
输入文件的每一行为具有父子/父女/母子/母女/关系的一对人名,例如:
Tim, Andy
Harry, Alice
Mark, Louis
Andy, Joseph
........., ............
假定不会出现重名现象。
编写Hadoop平台上的MapReduce程序,找出所有具有grandchild- grandparent关系的人名组
答案
以下是使用Hadoop平台上的MapReduce程序实现找出所有具有grandchild-grandparent关系的人名组的伪代码描述:
Mapper阶段:
map(key, value):
解析输入行的父子关系,将父子关系中的父姓名作为键,子姓名作为值发射给Reducer
将父子关系中的子姓名作为键,父姓名作为值发射给Reducer
Reducer阶段:
reduce(key, values):
grandchildSet = 空集合
grandparentSet = 空集合
for each value in values:
if value starts with "child":
添加value中的姓名到grandchildSet
else if value starts with "parent":
添加value中的姓名到grandparentSet
for each grandchild in grandchildSet:
for each grandparent in grandparentSet:
发射键值对(grandchild, grandparent)作为Reducer的输出
主程序:
main():
创建一个MapReduce作业
设置输入路径为HDFS数据文件
设置输出路径为结果保存的位置
设置Mapper类和Reducer类
设置Mapper的输出键值对类型
设置Reducer的输出键值对类型
启动作业并等待完成
通过上述伪代码描述的MapReduce程序,可以实现找出所有具有grandchild-grandparent关系的人名组的功能。在Mapper阶段,我们解析输入行的父子关系,并将父姓名作为键,子姓名作为值发射给Reducer;同时,将子姓名作为键,父姓名作为值发射给Reducer。在Reducer阶段,对于每个父姓名,我们将子姓名收集到grandchildSet;对于每个子姓名,我们将父姓名收集到grandparentSet。然后,对于每个grandchild-grandparent组合,我们发射(grandchild, grandparent)键值对作为Reducer的输出。最后,在主程序中设置输入路径、输出路径以及Mapper和Reducer的相关信息,并启动作业进行执行,最终得到计算结果的输出。
2
问题
输入文件为学生成绩信息,包含了必修课与选修课成绩,格式如下: 班级1, 姓名1, 科目1, 必修, 成绩1
(注:
为换行符) 班级2, 姓名2, 科目1, 必修, 成绩2
班级1, 姓名1, 科目2, 选修,成绩3
.........., ........., ........., .........
编写Hadoop平台上的MapReduce程序,实现如下功能:
1. 计算每个学生必修课的平均成绩。
2. 按科目统计每个班的平均成绩。
答案
以下是使用Hadoop平台上的MapReduce程序实现所需功能的伪代码描述:
Mapper阶段:
map(key, value):
解析输入行的班级、姓名、科目、课程类型和成绩
如果课程类型为必修:
发射键值对(姓名, (科目, 成绩))作为Mapper的输出
发射键值对(班级, (科目, 成绩))作为Mapper的输出
Reducer阶段:
reduce(key, values):
如果key为姓名:
初始化累加器和计数器
遍历values列表,将必修课成绩累加到累加器中,并递增计数器
计算平均成绩 = 累加器 / 计数器
发射键值对(姓名, 平均成绩)作为Reducer的输出
如果key为班级:
初始化累加器和计数器
遍历values列表,将成绩累加到累加器中,并递增计数器
计算平均成绩 = 累加器 / 计数器
发射键值对(班级, 平均成绩)作为Reducer的输出
主程序:
main():
创建一个MapReduce作业
设置输入路径为HDFS数据文件
设置输出路径为结果保存的位置
设置Mapper类和Reducer类
设置Mapper的输出键值对类型
设置Reducer的输出键值对类型
启动作业并等待完成
通过上述伪代码描述的MapReduce程序,可以实现计算每个学生必修课的平均成绩和按科目统计每个班的平均成绩的功能。在Mapper阶段,我们解析输入行的班级、姓名、科目、课程类型和成绩,并根据课程类型将必修课的键值对发射给相应的Reducer,将班级的键值对发射给对应的Reducer。在Reducer阶段,对于每个学生的必修课成绩,我们计算平均成绩并发射(姓名, 平均成绩)键值对作为Reducer的输出;对于每个班级的成绩,我们计算平均成绩并发射(班级, 平均成绩)键值对作为Reducer的输出。最后,在主程序中设置输入路径、输出路径以及Mapper和Reducer的相关信息,并启动作业进行执行,最终得到计算结果的输出。
combiner类
Combiner类是MapReduce框架提供的一个中间处理步骤,用于在Mapper输出和Reducer输入之间进行局部合并操作,减少数据传输量
问题
分开假定价作为输入的HDFS数据文件中存放了大量的浮点数。现要求编写MapReduce分布式计算Combiner事程序实现TopN运算,即求输入数据集中最大的N个数。简述Mapper类、类和Reducer类的实现方法
答案
Mapper类的实现方法:
- Mapper类的输入是数据集中的每个数据项,可以是浮点数。
- Mapper类的任务是将输入数据项进行映射操作,将每个数据项映射为键值对(Key-Value pair)的形式。
- 在本例中,Mapper类可以将每个浮点数作为键,将其值设为1,表示该浮点数出现了一次。
- Mapper类的输出是键值对,其中键是浮点数,值固定为1。
Combiner类的实现方法:
- Combiner类是MapReduce框架提供的一个中间处理步骤,用于在Mapper输出和Reducer输入之间进行局部合并操作,减少数据传输量。
- 在本例中,Combiner类可以将相同键的键值对进行合并,将相同浮点数的计数值相加。
- Combiner类的输出是键值对,其中键是浮点数,值是该浮点数的计数值。
Reducer类的实现方法:
- Reducer类的输入是Mapper输出和Combiner输出的键值对。
- Reducer类的任务是对输入的键值对进行归约操作,将相同键的值进行汇总处理。
- 在本例中,Reducer类可以将相同浮点数的计数值进行累加,得到该浮点数的总出现次数。
- Reducer类可以在处理键值对时,维护一个大小为N的最大堆数据结构,用于保存出现次数最大的N个浮点数。
- 当Reducer处理完所有输入后,最大堆中的N个浮点数即为输入数据集中最大的N个数。
需要注意的是,由于题目要求求最大的N个数,因此在Reducer类中需要使用一个最大堆数据结构进行排序和筛选操作。另外,为了确保所有数据项都能经过Reducer类的归约操作,可能需要在Mapper类中进行局部聚合操作,以保证所有相同键的键值对能够发送到同一个Reducer进行处理。