全文内容主要来自对课程《深入浅出计算机组成原理》的学习笔记。
46 | SSD硬盘(上)
SSD 的读写原理
没有寻道,随机读写快。
缺点:耐用性(重复擦写)差。
SLC、MLC、TLC 和 QLC
一个电容,有无电压代表0、1,即1bit信息。这种方式为 SLC
(Single-Level Cell) 颗粒。与 CPU Cache 类似,存储有限。
如果一个单元存储超过1bit呢?
于是发明了 MLC
(Multi-Level Cell)、TLC
(Triple-Level Cell)以及 QLC
(Quad-Level Cell),分别可以存储2、3、4bit。
因为有一个电压计
,可以通过不同的电压值表示不同的bit。比如,15个不同的电压位,加0电压总共16个值则可以表示4bit。但对电压精度要求很高,会导致充放电慢。
P/E 擦写问题
SSD 由多个裸片
(Die)叠在一起组成,每个如下所示。
- 一个裸片会分成多个
平面
(Plane),容量约GB级; - 一个平面会分很多
块
(Block),容量约MB级; - 一个块分多个
页
(Page),容量约4KB。
SSD 写入叫 Program
,不是复写来完成,需要先擦除
(Erase)。单位是一个页(Page),擦除是按照块(Block)来进行。硬盘寿命是每个块的可擦除的次数,物理材料原因。
SLC 一般在10万次,MLC约1万次,TLC和QLC约几千次。
SSD 读写的生命周期
下图是SSD硬盘读写的流程:
- 绿色:有效数据;
- 红色:已删除数据;
- 白色:无数据。
因为写入最小单位是页,擦除最小单位是块,数据一多容易出现红色空洞占的地方也会越来越多,因为绿色页的存在,整块不能擦除,白色页就越来越少。
所以需要磁盘碎片整理
,如下图所示,即把有效的零散的绿色数据集中搬移到新空间,然后擦除原来的块,就可以空出容量了。
厂商为了“磁盘碎片整理”,往往有一些预留空间(Over Provisioning),一般在7%-15%。
47 | SSD硬盘(下)
作为系统盘的时候,大多部分只读不写。擦除会反复发生在其他用来存放数据的地方,容易变成坏块,即容量变小了。
磨损均衡、TRIM 和写入放大效应
FTL 和磨损均衡
为了减少坏块,我们尽量平摊擦除到每个块上,即磨损均衡
(Wear-Leveling)。实现方式就是FTL
这个闪存转换层
。
与内存管理类似,在 FTL 里面,存放了逻辑块地址
(Logical Block Address,简称 LBA)到物理块地址
(Physical Block Address,简称 PBA)的映射。系统都是访问前者,实际操作是后者。
FTL 能够记录物理块被擦写的次数。如果某块被擦写的次数多了,可以将它挪到一个擦写次数少的物理块上。
TRIM 指令的支持
操作系统的逻辑层和 SSD 的逻辑层里的块状态,是不匹配的。
因操作系统删除数据的时候,是把对应的 inode 里面的元信息清理掉,物理层面仅标记成可写入,没有实际擦除。所以日常的文件删除,都只是一个操作系统层面的逻辑删除,有被恢复的可能。
为了磨损均衡,很多时候在都在搬运很多已经删除了的数据。解法是 TRIM 命令,在文件被删除的时候,让操作系统去通知 SSD 硬盘,对应的逻辑块已经标记成已删除了。
写入放大
SSD 硬盘容易越用越慢。
空间被占用多了之后,空白不足,需要经常进行整理,于是变慢。
写入放大 = 实际的闪存写入的数据量 / 系统通过 FTL 写入的数据量,该值越大(说明为了完成操作多了很多整理),性能越差。
AeroSpike
SSD 虽然有劣势,但可以充分利用其优势,比如 AeroSpike
。
AeroSpike 这个专门针对 SSD 硬盘特性设计的 Key-Value 数据库(键值对数据库)
利用SSD物理特性:
- 不通过操作系统,直接操作 SSD 中的块和页;
- 写入的时候尽可能写较大的数据块,一般128KB;
- 读取的时候可以读512 字节(Bytes)这样的小数据。
AeroSpike 需要响应时间短,所以写入放大严重,优化:
- 持续地进行磁盘碎片整理。(块碎片>50%)
- 建议只用到容量最大额的50%。(降低写放大)
48 | DMA: Kafka 快速原因
CPU 的主频 2GHz 意味每秒20亿次操作,诸如大文件复制等操作,CPU 大部分时间都空闲等待。DMA
技术,直接内存访问
(Direct Memory Access)来减少 CPU 等待的时间。
理解 DMA,一个协处理器
主板上一块芯片,在内存和 I/O 设备的数据传输时,不通过 CPU 控制,直接通过 DMA 控制器
(DMA Controller,简称 DMAC),也是协处理器。
DMAC 使用价值:
- 传输的数据极大、速度快,减少 CPU 依赖;
- 传输的数据极小、速度慢,等数据到齐再发送,减少 CPU 忙等待。
DMAC 也是一个特殊的 I/O 设备,链接总线进行传输。总线上设备分主设备
、从设备
。
只有主设备可以主动发起数据传输,如 CPU。I/O 设备只能发送控制信号,告知 CPU 有数据要传,再由 CPU 拉数。
DMAC 既是一个主设备(对于IO设备),又是一个从设备(对于CPU)。
DMAC 进行数据传输的过程(硬盘加载数据为例):
- CPU 向 DMAC 发起请求,即修改其配置寄存器;
- 提供给 DMAC 的信息:
- 源地址的初始值(硬盘IO地址)以及传输时候的地址增减方式(是否大往小地址传);
- 目标地址初始值(目的地地址)和传输时候的地址增减方式;
- 要传输的数据长度。
- 设置后,DMAC 变为空闲(Idle);
- 硬盘向 DMAC 发起数据传输请求;
- DMAC 响应;
- DMAC 向硬盘接口发起总线读请求并读取;
- DMAC 向内存发起总线写请求并写入;
- 反复上述6、7,直到指定长度数据传输完成,回到第 3 步空闲。
因显示器、网卡、硬盘对于数据传输的需求都不一样,所以各个设备里面都有自己的 DMAC 芯片了。
Kafka 的实现原理
Kafka 项目是通过利用 DMA 的方式实现了非常大的性能提升,是目前实时数据传输管道的标准解决方案。
Kafka 两种常见海量数据传输:
- 从网络中接收数据并落盘;
- 从本地磁盘读取并通过网络发送出去。
从本地磁盘读取并通过网络发送出去,这个过程数据一共发生四次传输,2次 DMA 传输,2次 CPU 控制传输。
整个过程:
- 从硬盘读到系统内核缓冲区,DMA 搬运;
- 内核缓冲区复制到内存,CPU 搬运;
- 从内存写到操作系统的 Socket 缓冲区,CPU 搬运;
- 从 Socket 缓冲区写到网卡缓冲区,DMA 搬运。
在 Kafka 中,则只有上述的 1、4 步骤。数据绕过内存,直接通过 Channel,写入到对应的网络设备里。并且,对于 Socket 的操作,也不是写入到 Socket 的 Buffer 里面,而是直接根据描述符(Descriptor)写入到网卡的缓冲区里面。
没有在内存层面去“复制(Copy)”数据,所以也称上述方法是
零拷贝
(Zero-Copy)。
传输同样数据的时间,可以缩减为原来的 1/3
49 | 数据完整性(上)
单比特翻转
作者举例遇到的硬件错误,由于定制的硬件没有使用 ECC 内存,内存中出现了单比特翻转
(Single-Bit Flip)。
一个 ASCII 码二进制表示是 0010 0100,可能是 0011 0100 在第4位发生单比特反转变来的。随机,不可复现,而 ECC
内存的全称是 Error-Correcting Code memory
,中文名字叫作纠错内存
。
奇偶校验和校验位
奇偶校验,即内存里面的 N 位比特当成是一组,1 的个数是奇数还是偶数,并记录在校验码位
。
如,101010111,前8位数数据位,最后一位就是校验码位
该算法速度快,并且能判断内存数据是否出错。缺陷:
- 只能解决遇到奇数个的错误;
- 只能发现错误,不能纠正错误。
ECC 内存所使用的解决方案,既可以发现,也可以纠错。2个版本:
- 纠错码(Error Correcting Code),发现并纠正;
- 纠删码(Erasure Code),不能纠正时,直接删除。
50 | 数据完整性(下)
纠错码,需要既能判断是否有错,还要找出错误位置。
海明码
海明码
(Hamming Code)是最知名的纠错码,上世纪四十年代发明,沿用至今。
最基础的是 7-4 海明码,即 7bit 数据位,4bit 校验位。因为,4bit 可以表示 16 个数,假设全部正确状态占用一个数,那么剩余 15 个数可以用来纠正 15 中单比特反转错误。
这里需要注意,如果纠错位少一位,变成3bit,就只能表达7个校验位,而校验位本身也会出现错误,所以7-4实际上有11种单比特反转错误。
数据位有 K 位,校验位有 N 位,需要满足 K + N + 1 <= 2^N
一个简单的理解:数据位+纠错位总共有 K+N 位,那么就有 K+N 种单比特错误位置,外加1个全部正确状态,故 N 位的校验位需要能够表达至少 K + N + 1 个数值,于是有了上述关系。
海明码的纠错原理
校验位数的原理清楚了,那么怎么构建校验位不同数值对应的错误位映射关系呢?
这里直接以7-4海明码为例:
- 在 11 位中将 $2^k$ 位作为校验位,即 1、2、4、8 位,记为 p1-p4;
- 剩下的 7 位作为数据位,记为 d1-d7,来存储有效信息;
- 我们需要将数据位分组到每个校验位上,分组方式是根据二进制各个位置的1值:
如下图,如:
- 针对位置1的校验位p1,分到该组的数据位就是二进制下第0位为1的所有,包括1(自身)、3、5、7、9、11;
- 针对位置4的校验位p3,分到该组的数据位就是二进制下第2位为1的所有,包括4(自身)、5、6、7;
- 分好组了,校验位的值可以根据组内位置奇偶校验法得到。
任何一个数据码出错了,就至少会有对应的两个或者三个校验码对不上,这样我们就能反过来找到是哪一个数据码出错了。
实际上找出错位方式很简单,即校验位按照 p4-p1 排列二进制的数值。
首先校验位需要按照 p4-p1 的方式来排列成二进制;比如 11 位中,第 3 位bit(即d1)反转了,那么对应上述表格,p1、p2校验位会为 1,即 0011,刚好是第 3 位。再比如第 7 位,那么就是 0111,刚好是7。
这里很像一道 coding 问题:1000 瓶药水只有 1 瓶有毒,有 10 个试验用的小白鼠,如何定位有毒的那瓶?
解法:
- 10 个小白鼠作为 10 个 bit 位,可以表示 0 到 $2^{10}-1$,能够涵盖 1000;
- 1000 瓶药水按照顺序编码,编码对应 bit 位为 1 的药水要给对应位置的小白鼠喝;
- 死亡的小白鼠,将对应 bit 位置为 1,那么 10 个 bit 位对应的值就是第几瓶药水有毒。
海明距离
对于两个二进制表示的数据,他们之间有差异的位数,我们称之为海明距离。
51 | 分布式计算
数据中心里一台计算机一般不够,面临3个问题:
- 垂直扩展和水平扩展的选择问题;
- 如何保持高可用性(High Availability);
- 一致性问题(Consistency)。
从硬件升级到水平扩展
假设采买了一台云服务器:1 个 CPU 核心、3.75G 内存以及一块 10G 的 SSD 系统盘。
当请求增长,性能不足时需要增加资源:
垂直扩展
(Scale Up):换成 2 个 CPU 核心、7.5G 内存;水平扩展
(Scale Out):买 2 台 1 个 CPU 核心、3.75G 内存。
垂直扩展总有上限,因为物理机的原因。水平扩展,则需要软件层面改造,进入分布式,需要负载均衡
。
分布式,即通过
消息传递
(Message Passing)而不是共享内存
(Shared Memory)的方式,让多台不同的计算机协作起来共同完成任务
理解高可用性和单点故障
系统的可用性(Avaiability):系统可以正常服务的时间占比。
如可用性是99.99%,则表示服务计划外的宕机时间<=4.32分钟/月。
水平扩展具有天然的优势,因为负载均衡能够通过健康检测
(Health Check)发现坏掉的服务器,自动把其上的流量切换到其他正常服务器上,即故障转移
(Failover)。
单点故障问题(Single Point of Failure,SPOF):任何一台服务器出错了,整个系统就没法用了。
解决单点故障问题就是移除单点,通过水平扩展,使得单台服务器挂了不影响其他正常使用。
但是,单点可能存在多处,比如虽然服务器水平扩展了,但物理机在一个机房,交换机容易成为单点。这时候就需要异地多活
的系统设计和部署。即,服务器分地区采购后部署服务,很多公司都会采用。
故障转移(Failover)机制的生效,需要服务进行健康监测
(Health Check),即每隔很短时间检查一次各服务器是否正常运行,如果有,则将流量转移到其他正常机器。