之前曾今负责维护了学校的 IBM x3500 M2 塔式服务器,那是我第一次全权管理一台专业服务器,并且接触了磁盘阵列的管理,才发现 RAID 这东西远比我之前自己 DIY 组装机时所接触到的复杂许多,专业磁盘阵列卡果然不一般。当时我自底向上重新做了整个系统( Linux )的文件存储,包括 RAID 的配置,建立 LVM 以及 ext4 ,颇有些收获,尤其是意识到应该适当设置各层的“区块”的大小,互相匹配(“对齐”)以达到最佳性能,所以今天写下这么一篇笔记性质的东西来整理、分享。由于我不是专业运维,完全是自己查资料、瞎折腾,所以并不一定完全准确,希望读者能够不吝指正。

1. RAID

RAID 相关基本知识:https://zh.wikipedia.org/wiki/RAID

在除了完全镜像的 RAID1 以外的各种 RAID 系统中, striping 都是一项非常重要的技术—— RAID 期望通过同时读写多块磁盘来提高速度,但这其实无法真正做到,所以人们采取了一种变通的办法:将数据分为小块,写入时将控制器缓存中的数据按照小块分别、同时写入不同的磁盘,读取时分别、同时读出,再在控制器缓存里拼合起来(缓存是 RAM ,支持快速随机并发读写)。这便是 striping 的基本原理。而如何分块、编码数据,是否存储以及存储多少冗余(校验),如何安排数据块和冗余块的分布便是 RAID 0、2、3、4、5、6 等等的主要差别。

RAID3 采用 bit-interleaving 技术直接将数据的比特流进行编码、散布在各块硬盘,即使读写很小的数据也几乎要访问所有硬盘;除此以外上述的其他 RAID 方案基本都采用 block-interleaving 之类的技术,即将数据线性地分为一段段的序列,分别写入各个硬盘,而这个序列的大小就成为了影响性能的关键因素——若太大,则会有很多文件只会被分为一块、放入一个硬盘,无法并发读写;若太小,会造成分块过多,将 I/O 浪费在频繁切换、访问硬盘上。

这里我们会接触到两个大小:一是每块磁盘所存放的数据块的大小,二是能够正好覆盖整个阵列的数据条的大小(即前一个大小×数据盘数量,注意不包括冗余盘)。不幸的是,这两个大小并没有一个统一的术语, stripe 、 strip 、 striping unit 、 chunk size 之类的词都被不同的厂家使用,含糊不清、自相矛盾(比如这篇看起来十分正确的博客中 chunk size 的定义就和 Linux Kernel Wiki 的 RAID 设置矛盾, IBM 的 MegaRAID Software User’s Guide 更是干脆在一篇文章中出现 stripe 和 strip 两词的指代前后矛盾)。

不过只要理解了 striping 的原理,你就很容易能够搞定这些术语,时刻谨记只有单个磁盘上的数据块大小才是可以设置的!否则若设置整个阵列的数据条大小,而数据盘数量无法整除这个大小该怎么办?所以任何地方只要出现允许设置 RAID 的某种分块大小,那它指的一定是单个磁盘上的数据块大小。下文为了方便,将统称这个大小为 chunk size ,和 Linux 的习惯保持一致。

这里我并不想讨论 chunk size 到底应该设置为多少,比较常用的是 64KB 或 128KB ,小到几 KB 大到几 MB 都是可能值,要根据具体使用情况、配合一些实验来决定。不过有一个问题必须明白—— RAID 的区块不同于文件系统的簇,它并不是文件所必须占用的最小空间——如果存储一个小于簇大小的文件,那么它同样需要占用一整个簇;而 RAID 的区块并不存在这个问题,它只影响性能。所以几百 KB 的 chunk size 不会导致小文件都占用几百 KB 的空间,造成巨大的空间浪费。

2. LVM

在配置好底层的 RAID 以后,整个阵列对于系统来说就变成了一块磁盘。为了日后管理方便,我选择使用 LVM 。根据 TLDP 上的这篇文档, RAID 上的 LVM 需特别注意一个问题——对齐。类似硬盘或闪存上分区需要对齐物理扇区( 512B 或 4KB ), RAID 上的分区也需要注意这一点,以防止逻辑卷管理或文件系统中的“块”与 RAID 的区块错位,导致阵列卡大量多余运算。

首先是建立分区,使用 LVM 的话只需把整个硬盘都分成一个区就好(或者顶多再分出一个单独的 boot ,以方便 grub 引导)。如今的分区软件一般都是对齐到 MB ,因此一般也就自然和 chunk size 对齐了。

其次是建立 LVM 的物理卷( physical volume ),只需要多加一个参数--dataalignment,指定为 chunk size 即可,参数可以识别 K M 之类的单位后缀。

3. ext3/4

最后就是建立文件系统了,仍然是注意对齐。以最常见的 ext3/4 为例, mke2fs 支持这两个扩展参数: stride 和 stripe-width 。前者指定 RAID 每块磁盘上的数据块存储多少个簇(即 chunk size / ext block size ,簇大小默认是 4KB );后者指定 RAID 一个“条带”(恰好覆盖所有数据盘一次)有多少个 stride (即 stride * 数据盘个数,对于常用的 RAID5 而言就是总磁盘数减1)。

mkfs.ext4 -E stride=XX -E stripe-width=XX /dev/XXX

CentOS Wiki 上的相关 HOW-TO (主要看 RAID Math 一节):http://wiki.centos.org/HowTos/Disk_Optimization
一个简单的 mke2fs 参数生成工具(自动计算 stride 和 stripe-width ):http://busybox.net/~aldot/mkfs_stride.html