架构师训练营第二期 第 8 周总结
文件系统与磁盘 IO
RAID 独立硬盘冗余阵列
多个硬盘同时进行读写,提升性能
RAID 0 把多份磁盘平均分成几份,同时进行读写。一份数据会分成多片分别写入。读写新能提升很多,安全性不足。如果一块硬盘失效了,导致一份数据中的部分内容丢失,数据整体不可用了。意味着整体的寿命,取决于最快损坏的硬盘的使用寿命。宕机概率也急剧增加,系统可用性下降。
RAID 1 一份数据同时向两块硬盘写入,降低部分性能,以此提升可用性与安全性。
RAID 10 即结合 RAID 0 与 RAID 1,一份数据拆分为多个数据块,每个数据块向两块硬盘写入(复制),多个数据块同时写入。这样有一半的数据是备份数据,磁盘空间浪费太严重。
RAID 5 业界用的最广泛的。不是每份数据都存储备份。多个数据的分片,计算一个校验位,只记录一个校验位。以 8 个磁盘举例,一份数据分成 7 片,记在七个磁盘上,第 8 张磁盘记录前面 7 个磁盘的数据校验信息。如果 7 个磁盘中其中一个磁盘损坏了,通过校验信息,和以它存在的,在其他 6 张磁盘上的信息。进行反计算,计算出来,丢失的数据是什么。提高数据利用率,保证良好的可用性。
1、这个是怎么计算出丢失的数据呢 ?== 检验位是怎么算出来的?
校验位就是所有数据异或出来的结果。因此,同一位置,当且仅当一份数据分片的丢失,校验位与其余数据进行异或操作 ,就能算出来丢失的那份分片数据。这个异或操作甚至不需要 CPU 参与,RAID 自己就可以做到。
2、关于校验位的记录,上面只是个举例。
实际上,校验位不是单独的记录在一块磁盘上。比如在第一块磁盘上在位置 1,第二块磁盘在位置 2,依次类推,是一种螺旋的形式。(所有的校验位记录在一块磁盘上是 RAID3)任何数据的修改,都需要更改校验位,这样校验位所在磁盘压力很大,磁盘的读写次数是有限的,这样会较快得减少磁盘的寿命。因此 RAID 5 实际是为了分摊 RAID 3 下 磁盘上校验位的写入压力,所做的改进。
RAID 6 解决 RAID 5 两块硬盘上数据分片丢失的数据还原问题。它计算两个校验信息。也就是一个校验信息会存两份,并且有两种校验信息。
RAID 的提升 是单台服务器级别的提升。如需要进入更大的提升层次需要下面的方案。
分布式文件系统 HDFS
数据分片存在多个服务器上,每个服务器并发进行读写操作。
核心角色: NAMENODE 充当文件控制块的角色,记录文件名,创建时间,副本数,权限,记录地址等等
数据存储: DATANODES 里存放数据,以块为单位。
客户端如何写入数据?
客户端想要写入文件时,通过 NAMENODE 创建一个文件控制块,记录相关的信息,分配数据块,再把交互信息返回给客户端,客户端,拿着这些信息进行数据写入请求。
如何保持高可用?
NAMENODE 存储着所有 DATANNODES 上存储的数据编号(块 ID)。当 NAMENODE 失去一块 DATANODE 的心跳,断定它已经宕机不可用的时候,就会检查这些块 ID 有哪些,同时检查还在哪些 DATANODE 存储着这些块,然后就给这些 DATANODE 发指令,高诉它把这些数据重新复制一遍(秒级)。
数据结构与算法
时间复杂度:不是计算程序具体运算的时间,而是算法执行语句的次数。
空间复杂度:一个算法在运行过程中临时占用存储空间大小的量度。
数组: 创建数组要内存中一块连续的空间。数组中存放相同的数据类型,每个数据占用的空间是相同的。支持快速读写,时间复杂度 O(1),不易增删改元素。
链表:每个元素必须包含存放指向下一个数据元素的内存地址指针。链表的查找复杂度 O(N),增删改元素容易。
HASH 表:(KEY, VALUE) 用 KEY 计算 HASH 值,获取数组的下标,访问数组该下标的数据。因此访问速度是 O(1)。上面提到数组每个数据占用相同大小的空间,HASH 涉及的数组实际存放的是一个指向 entry 的地址的指针。这个 entry 实际是链表的头节点,存放了指向下一个 entry 的指针,用于解决 HASH 冲突问题。这种解决 HASH 冲突的方法容易被攻击者利用,他们构造会命中同一个位置的 key,导致链表长度过长。
堆栈:线性表(数组与链表)的基础上,满足后进先出。如线程调用栈保证了在任何时候,程序执行使用的局部变量,总是在栈顶元素的。
队列:线性表(数组与链表)的基础上,满足先进先出。应用场景:生成者消费者,阻塞等待的线程放入队列(公平锁)
网络通信基本原理与性能优化
主机 A 数据一层一层按照协议加包头,主机 B 一层一层(校验满足条件)去掉已经处理过的协议的包头。
叠加的数据格式如下
数据封装为数据帧,以帧为单位通过物理层进行通讯。有了数据帧,可以进行流量控制。链路层会定义帧的大小,也称为最大传输单元。
基于链路层的负载均衡:
负载均衡服务器收到请求,需要分发到应用服务器上去。它在收到了数据帧包以后,修改数据帧的帧头的 MAC 地址(修改前当然就是负载均衡服务器 MAC 地址)后,把这个数据包重新丢在自己的局域网里,各个服务器收到后检查 MAC 地址是否与自己一致,检查结果为一致的服务器就把这个数据留下来,接着去掉帧头,交给自己的网络层的协议去处理。注意里面修改的仅 MAC 地址,意味着想要通讯成功,负载均衡服务器与这个应用服务器集群的各台服务器必须得是同一个 IP 地址(设置的时候,它们都设置为了一个虚拟的共享的 IP),并且因此响应的时候不需要经过负载均衡服务器,直接链路返回就行。用户请求的初始的 MAC 地址是属于负载均衡服务器的。
网络层:网络层 IP 协议使得互联网应用根据 IP 地址就能访问目标服务器。
基于网络层的负载均衡(IP 负载均衡):
负载均衡服务器通过修改 IP 地址的方式(源改成负载均衡服务器的地址,目标地址改成待接收的服务器的地址),实现请求的转发。返回响应需通过负载均衡服务器,负载均衡服务器再次修改源地址与目标地址,把结果返回用户。
基于链路与网络层的负载均衡的不同在于是否修改了请求头上的 目标 IP,目标 IP 改变,意味着改变网络通讯的路由选择,无法复用发请求时的路由表中存放的路由路径,把响应发送给浏览器。性能差距差在负载均衡服务器的压力。
传输层(TCP 协议):保证通信的稳定可靠
应用层协议: web 通讯常用 http 协议
非阻塞网络/IO
阻塞式网络通讯:多少个请求,就建立多少个连接(每一个 socket 都绑定着一个线程,这个线程大部分时间是阻塞状态)。
阻塞式网络通讯 socket 的处理过程:
1、一个网络通讯的数据包,到达网卡,网卡调用解析协议,主要解析链路层的数据(MAC 地址是否为当前网卡),解析完后,去掉链路层协议头,把最外是 IP 层协议头的数据包通知 CPU(有个数据包来了),中断 CPU。2、CPU 会把数据包拷贝到对应的 socket 接收缓冲区中。(如果接收缓冲区满了,无法写入,通知数据传输慢一点)
3、缓冲区接收到数据后,会唤醒阻塞在 socket 上的线程,让线程去处理数据,处理完后,线程无法在缓冲区读取到数据,重新会被阻塞在这里。
非阻塞式网络通讯:
Java NIO (一个线程复用到所有的网络通讯上去)
多个客户端并发的建立连接,进行数据请求等待响应。每一个客户端到达服务器的时候(由一直监听着是否有新连接的,服务器的 ServerChannel,通过 channel.accept())建立一个 channel 通道,其中各自有自己的 socket。
selector 选择器,监听这些 channel,观察上面是不是有新的事件发生,有就去处理,可读、可写到 buffer 缓存区中,而应用程序交互的是 buffer,只要应用缓存区够大,写的时候,隔离,不因请求的数据帧多大而阻塞。
selector 工作机制
有 socket 上有事件(读、写、建立新连接)发生时,通知 selector,但 selector 并不知道哪个 socket 发生了事件,因此它会遍历 socket 检查,收集要处理的 key。遍历消耗资源,性能不是很好。
epoll 下 操作系统提供了 eventpoll。当有事件,会构建这么一个事件列表,同时指定是属于哪些 socket 上的事件到达。这种映射关系的存在,使得 selector 不需要遍历 socket,它只遍历 eventpoll,根据列表,去检查 socket 就行。
当没有任务事件时,整个 selector 多路复用还是会被阻塞
评论