对于计算机里超级多的 I/O 设备和一个又一个的硬件,应该怎么设计逻辑结构让物理硬件的实际性能提高呢?
解耦的艺术:操作系统 SPOOLing 技术在分布式消息队列中的重生
SPOOLing 技术和分布式消息队列的产生都是为了解决生产者(快)与消费者(慢/独占)之间的速度不匹配。
SPOOLing
SPOOLING(外部设备联机并行操作),全称Simultaneous Peripheral Operations On-Line,又称假脱机技术或排队转储技术,是解决低速外设与主机数据交互的技术。该技术通过在输入输出环节增加磁盘缓冲(输入井和输出井),将独占设备虚拟化为共享设备,以联机方式实现脱机效果。
在此之前我们需要了解一下什么是独占,什么是共享
独占和共享是一个相对概念
- 从宏观上来看
-
- 独占式设备:一段时间内只有一个进程在使用
- 共享式设备:一段时间内可以有多个进程使用
- 从微观上来看
-
- 独占式设备:一段时间内只有一个进程使用
- 共享式设备:一段时间内是多个进程交替使用(就像 进程共享处理机一样)
而 SPOOLing 就是使用在独占式设备上的,例如打印机
图解
画的有点抽象,内容就是
当进程运行在 CPU 上时,向 CPU 请求使用 I/O 设备输出数据时,CPU 会在磁盘中的输出井中申请一个缓冲区,将该进程的请求输出的数据放置在缓冲区内,然后再根据放置的位置以及进程请求的 I/O 设备等信息整合成一个请求输出表,CPU 会把这张表挂载到假脱机文件队列中后会“骗”进程说:“我已经帮你输出了”,接下来进程就可以无需阻塞,可以去执行其他的操作(如果进程需要获取 I/O 设备的输入,也就是需要输入井中的数据,则需要阻塞等待该数据到达),第二个进程想使用同一个 I/O 设备,第三个进程都是使用相同的方法,在输出井中将数据放好了,在假脱机文件队列中挂起了,交由 DMA 去进行操作, DMA 会根据队列内容将每一个进程的输出信息发送给打印机等输出 I/O 设备,就这样完成所有的输出请求
如果没有使用 SPOOLing 技术,那么第一个请求输出的进程在使用打印机时,第二个进程,第三个进程就需要被阻塞...
SPOOLing 的好处就是
- 提高了 I/O的速度 —— 后来的进程不需要等待前面的进程输出,CPU 直接交给输出井
- 将独占设备改造为共享设备 —— 相当于独占的洗衣机增加了一个管理员,所有人的衣服都交给他,他会帮你一个一个地调用洗衣机洗,你就不用等前面的同学洗完了。
- 实现 了虚拟设备功能 —— 在进程看来,就是自己独享了一台 I/O 设备,但是在实际上只有一台 I/O 设备。
如果一台计算机连接多台相同的 I/O 设备,比如打印机,那么 DMA 就会将请求分发给不同的打印机进行打印,提高效率
SPOOLing 把“独占的物理设备”虚拟成了“共享的逻辑设备”。
分布式消息队列和 SPOOLing 技术有异曲同工之妙
分布式分为集群和微服务,在这里的分布式更贴合集群,即相同但大量的消息分发给不同的服务器
消息队列相当于一个蓄水池,雨水可以非常多,但是会被蓄水池接住,真正流向后方的会是一条小河流
分布式消息队列的完整定义:
- 分片(Sharding/Partitioning): 确实是为了分担压力,把消息分到多台机子处理。
- 副本(Replication): 每一条消息,不仅存这一台,还会自动同步拷贝到另外两台机子上做备份。
分布式消息队列可以将大量的信息请求集合起来,分发给多台服务器,再由多台服务器去逐步处理,简直就和 SPOOLing 技术一模一样
内容大致为
大量的信息需要处理 -> 分发给多台服务器处理,每台服务器处理与消息之间又插入了一段消息队列,同时会将处理的信息备份在其他的服务器上
消息队列 把“同步的阻塞调用”虚拟成了“异步的非阻塞调用”。
高性能网络编程基石:深入理解 DMA、中断与 Java NIO 的零拷贝
- DMA - 磁盘和内存之间的“搬运工”
- 中断 - CPU 并发处理的重要逻辑
- 零拷贝 - CPU 可以不同将东西搬给用户进程,而是直接给用户进程一段内核空间,让他当成自己的使
CPU 作为一个读写速度极快的设备,相交而来磁盘的读写速度就太慢了,如果让 CPU 去执行数据拷贝,由于磁盘较慢的读写速度,CPU需要等待,这样子会极大程度上浪费了 CPU 的性能。
于是在 20 世纪 50 年代诞生了 DMA 技术,使用硬件去读写磁盘的数据,而 CPU 只需要下达命令即可,在下达命令之后就可以去执行其他的操作了,这样子解放了 CPU 的性能,当 DMA 传输完成后就会向 CPU 发出中断,让 CPU 继续执行对应的操作,DMA 释放了 CPU 的性能,使得 CPU 不用再花费性能在数据传输(拷贝)上。
如果要把数据发送给其他的 I/O 设备虽然 DMA 负责搬运硬件到内核的数据,但在内核态(Kernel Space)和用户态(User Space)之间,数据被 CPU 来回拷贝了 4 次,上下文切换了 4 次(中断 -> 进入内核态 -> 将数据拷贝进入用户空间 -> 转变为用户态 -> 用户需要传递给另一个 I/O 设备 -> 转变为内核态 -> 将数据从用户态拷贝到内核态 -> 转变为用户态)
这样子也极大地降低了 CPU 的性能,所以诞生了 零拷贝技术 ,零拷贝技术可以实现使用 DMA 就直接将进程输出的信息直接发送给 I/O 设备,说到零拷贝就不得不提内存映像文件技术了,通过允许用户进程访问内核空间内的部分数据来减少拷贝次数。
Netty/Kafka 底层大量使用了 FileChannel.transferTo,利用 OS 的零拷贝特性,让数据在磁盘和网卡之间“直通”,CPU 占用率极低。
高性能网络编程的核心在于高效利用系统资源、减少拷贝、优化事件处理机制。
数据库调优的物理底色:为何 SSD 时代我们依然需要“顺序写”?
曾经的我们使用 HDD(机械硬盘)由于是机械结构,为了保障读写效率,我们使用了“顺序写”。
而当下产生了 SSD(固态硬盘)属于电子结构,为什么还需要使用“顺序写”呢?
SSD 的物理结构:
player.bilibili.com/player.html…
由物理结构我们能知道:在 SSD 中无法对数据进行覆盖,只能进行一块数据的擦除,所以当 SSD 使用随机写时会查看当前位置是否有数据,如果有则会找到一个新的地方进行数据的写入。
如果使用顺序写则不会有这类麻烦,顺序写比随机写好的原因就四个字:垃圾回收,写相同数据量的情况下,顺序写制造更少的垃圾Block,所以比随机写有更高的性能。
相比于传统的机械硬盘HDD,SSD除了性能大幅度提升外,还有两个显著特性:一是没有寻道时间,相比于机械硬盘,随机读性能有很大的提升;二是SSD基于闪存进行存储的,但闪存不能覆盖写,如果要修改一个块的内容的话,只能把整个闪存块擦除后才能写入。闪存的使用寿命就是和擦除次数直接关联的,如果能减少擦除次数,就相当于延长了SSD的使用寿命。
SSD 的这两个特性,契合了 out-of-place update 结构。SSD随机读取性能的提升,在很大程度上弥补了LSM树的读放大这一短板;而LSM树追加写的写入模式,有助于节省SSD擦除的损耗,并提升SSD的使用寿命。