疫情期间,腾讯医疗为全国人民提供了及时精准的疫情信息服务。腾讯云 kafka 作为腾讯医疗大数据架构中的关键组件。在面对业务短时间内成倍的数据存储需求的情况下,如何快速响应、快速扩容以支持业务的稳定运行的呢? 本文将从 Kafka 集群底层物理机层面硬盘的设计方案,来讲解面对不同的业务需求场景,如何选择好合适的磁盘方案。
医疗资讯场景
腾讯医疗的使用场景是典型的日志分析系统。Kafka 作为消息中间件,起到了数据聚合、流量削峰的作用。如下图所示 :
日志分析系统架构在医疗的实例中,Kafka 承载着峰值 GB/s 的数据吞吐和大量的数据存储压力。这对底层的硬盘性能提出了很大的要求。比如吞吐/存储能力、快速扩缩容能力等。
所以一般在硬盘方案设计的时候需要综合考虑下列因素:大容量存储高吞吐量的 IO 能力快速扩缩容能力数据的安全性低冗余的存储组建 Kafka 集群,常见的硬盘构建主要包括:单硬盘读写、多目录读写、硬盘阵列(RAID0,RAID10)、逻辑卷(LVM)等方案。下面将深入分析各个方案的优劣势,供读者选择参考。
硬盘方案概述
硬盘存储方案的设计使用的是现在成熟的工业化方案,并没有特殊的创新。硬盘存储方案的选择更多的是从 Apache Kafka 产品的视角出发,考虑哪种方案更贴合使用者的业务需求。
选择硬盘介质
工业界的硬盘市场基本是机械硬盘和固态硬盘(SSD)的天下。在超大规模的存储容量场景下,SSD 的价格依旧是它的硬伤。对于 Kafka 这种高 IO 的应用,固态硬盘的损坏率和使用寿命是一个很大的问题。所以,机械硬盘以其便宜的价格及大容量成为了不二之选。
机械硬盘需要解决的两个问题是:如何提高硬盘 IO 能力;在硬盘损坏成为一个常态的情况下,又该如何保持业务系统的稳定。我们先从这两个方面来分析下。
提高硬盘 IO 能力
一般使用以下指标衡量硬盘的性能:IOPS:每秒读/写次数,单位为次(计数)。存储设备的底层驱动类型决定了不同的 IOPS。吞吐量:每秒的读写数据量,单位为 MB/s。时延:I/O 操作的发送时间到接收确认所经过的时间,单位为秒。Kafka 程序本身通过顺序读写、Page Cache、零拷贝等方案,从应用层面极大的利用了硬盘的性能。但是,一旦硬盘吞吐能力不足,Kafka 集群提供服务的能力将大打折扣。因为 Kafka 的使用场景和运行方式,最关注的性能指标是吞吐量。
在自建集群的场景下,用独立主机挂载单块硬盘的方式是最常用的方案。在单硬盘读写的基础上,提高硬盘吞吐能力的方案主要有如下几种:单硬盘读写 Kafka 的多目录读写 RAID 硬盘阵列方案 Logical Volume Manage(LVM)条带化方案
方案一: 单硬盘读写
单机单硬盘部署是在自建集群当中最常见的一种方案,也是在实践当中用的最多的一种方案。如下图:
单机单硬盘集群部署方案上图是一个由三台节点构成的 Kafka 集群,集群的每个节点挂载一块 SATA/SSD 的数据盘,用来存放 Kafka 的数据。该方案的特点就是思路简单,搭建快捷。很适合自建的小规模集群。
这种方案,当发现硬盘能力不足时,最直接有效的解决思路就是垂直扩容。即提高单盘的 IO 能力,比如将 5400 转/秒的硬盘换为 7400 转/秒的,或者换为 10000 转/秒、甚至 10000 转/秒以上的更高转速。当机械硬盘的能力不足时,直接换为大容量的 SSD。
这种方案的缺点也比较明显。首先,单块的 SSD 吞吐量是有上限的,当 Kafka 流量增大,SSD 也有承受不了的一天,单块硬盘的吞吐将限制 Kafka 集群的吞吐能力。另外 SSD 的价格大约是 SATA 的好几倍。以当前腾讯云上的硬盘价格为例,SSD 价格是高性能云硬盘的 3 倍。
所以,当集群规模持续扩大时,该方案并不是一个长久的选择。
方案二: Kafka 的多目录读写
当集群的压力持续增大,单块的硬盘满足不了需求或者基于成本考虑,不想使用 SSD 了,该怎么办呢? Apache Kafka 官方在 0.8 开始,提供了多目录读写的能力。将 log.dir 属性变为 log.dirs。官方解释如下:
A comma-separated list of one or more directories in which Kafka data is stored. Each new partition that is created will be placed in the directory which currently has the fewest partitions
简单说,就是支持配置多个日志文件夹,文件夹之间用逗号隔开,这样做在实际项目中有非常大的好处,即支持多硬盘的读写能力。在 server.properties 配置文件添加如上配置:
log.dirs=/data,/data1,/data2
添加了这个配置后,有什么效果呢,看下图:
如上图,假设有一个有 9 个分区 1 个副本的 topicA。这 9 个分区会平均分布在节点 1,2,3 上。假设节点 1 上分配了 0、1、2 三个分区。那么 Kafka 会将这三个分区的数据目录分别放在/data、/data1、/data2 三个目录下。partition 的数据目录分别为: /data/topicA-0、/data1/topicA-1、/data2/topicA-2。至于为什么会均匀分布就不详细展开了,有兴趣同学可以去参阅相关资料。
此时当 Kafka 往这三个分区写入数据的时候 ,就可以利用到三块硬盘的 IO 能力。其实这是一个很好用的方案。但是多目录读写方案也有一些情况是不能处理的。
我们来看下这种情况:因为业务特点不同,有的业务会出现数据冷热明显的问题。可能出现有的 partition 的量很大,有的 partition 量很小。这就有可能出现单个 Partion 的量达到了硬盘的 IO 瓶颈,此时就又回到了单硬盘的方案遇到的问题。
所以当业务有这种场景的时候,多目录的方案可能会有一些局限。
方案三: RAID 磁盘阵列
RAID 就是磁盘阵列,由很多块独立的硬盘组合成一个容量巨大的硬盘组,利用多个硬盘产生加成效果提升整个硬盘 IO 能力。典型代表有 RAID0、RAID1、RAID5、RAID10 等。由于篇幅原因,就不展开解释 RAID 的相关知识。有兴趣的可以查阅相关资料。简单比对一下以上方案的优劣势:
RAID10 方案
使用 RAID 方案的原因就是为了解决单硬盘 IO 瓶颈的问题。经过以上方案比对,我们以 RAID10 为例,展开说明 RAID 做了什么,以及应该选择哪种 RAID 方案。先看下图:
上图中单机用四块盘组成 RAID10,即先用两块盘组成一块 RAID1 的虚拟盘,再用两块虚拟盘构建成一块虚拟的 RAID0 盘,并挂载到/data 目录下。理论上来看,假设单盘吞吐量是 100MB,那四块盘组成的 RAID10 阵列的吞吐就是 200MB,且每份数据在底层存储,是双副本的。
这里 RAID1 的作用是,即使底层有一块数据盘损坏,系统会自动读写另外一块备份盘的数据。此时系统还是能够正常使用的。RAID0 的作用是并行 IO,提高整体的 IO 能力。从理论上讲,三块硬盘的并行操作使同一时间内硬盘读写速度提升了 3 倍。但由于总线带宽等多种因素的影响,实际的提升速率肯定会低于理论值。但是,大量数据并行传输与串行传输比较,提速效果显著显然毋庸置疑。
这里需要注意一点的是,单纯从 IO 能力,从多块盘的 IO 能力相加的角度来看,RAID0 并不比多目录方案的 IO 能力强。RAID0 的优点在于,单块虚拟盘的 IO 能力比单块物理硬盘的强,单块虚拟盘容量比单块物理硬盘容量大。这两个特性是可以解决数据冷热明显、数据倾斜的情况的。
直接用 RAID0 可以吗?
细心的同学可能会发现这么一个问题?假设我们有 1 个分区 2 个副本的 topicB。两个副本分布在节点 1 和节点 2。此时当生产一条数据 messageA 时,messageA 会在集群里面存储 4 份。即节点 1 和节点 2 各存两份数据(RAID1 双副本)。如下图:
既然节点 1 和节点 2 都有 Kafka 的 Replication 副本了。为什么要在硬盘多冗余一份副本呢?这不是很浪费。是的。从数学的角度来看,是这样子没错。
我们来假设一个场景,假设我们直接用 RAID0。此时一个数据盘坏掉了,Kafka 集群会自动把影响到的 Partition 分区迁移到其他可用的机器上。如果刚好是 leader,则进行 leader 切换。看起来好像没问题。
但是假设刚好影响的 Parition 量比较大,将其切换到其他机器,会导致其他机器的压力增大,则很可能会影响到其他 Partition 的使用。这里是一个影响集群安全的重大隐患。
在一个成千上万台的大集群内,硬盘损坏是一件常事。这样就会造成分区迁移、leader 切换的过程变得相对频繁。但这点看起来不是特别大的问题,因为数据可以正常访问,也不会丢失。
但是如果客户对 leader 切换比较敏感,就会很快的感知到服务端的波动。作为服务提供商,还是希望给用户提供稳定的服务。如果发生上述情况,用户可能会觉得服务不够稳定,以至于影响厂商口碑。
由于工业发展,机械硬盘的价格持续下降,大容量的机械硬盘价格其实是极低的。所以在思考成本和稳定性的平衡中,可以考虑多付出成本,保证服务的稳定。
如果是 RAID10,坏了一块硬盘,此时系统还能正常运行。因为硬盘都有损坏告警,假设驻场更换的周期是 24 小时,则只需保证在 24 小时内,同一块 RAID1 的另一块硬盘不损坏,即可保证系统正常稳定无波动的运行。
下面分享个小技巧,先来看下面这张图:
单机不同批次硬盘部署示意图图中的硬盘为什么是不同颜色的呢?其实在做 RAID1 的时候,两块硬盘最好不要是同一个批次的。因为同一个批次硬盘具备相同的制作流程,考虑到可能会具备相同的磁盘寿命等因素,在一定程度上可能会增大两块硬盘同时损坏的概率。
到这里,似乎挺完美的了。我们再加一个因子:
假设开始规划的单机容量是 8TB,业务发展需要单机容量变为 10TB。那此时应该怎么办呢?
因为 Raid0 是不能动态扩展的。此时怎么办呢,貌似只有更换整个硬盘了。下面来看一下 LVM 方案。
方案四: LVM 逻辑卷条带化
LVM 逻辑卷的条带化原理和 RAID1 很像。都是条带化的进行数据读写。都有并行读写的能力。在实测过程中,两种方案的并行读写性能是差不多的。LVM 相对于 RAID10 的好处在于,它提供了动态扩容硬盘的能力。LVM 条带化的扩容是依赖以 lvmextend 命令实现的。扩容有一个条件:条带化的 lvm 扩容需要每个硬盘扩容大小一样的容量。如果每个硬盘容量不一样,条带化的 lvextend 会失败。
举个例子:例如当前有三块盘容量 1T,剩余空间 200GB 的盘,加入两块空间 1T 的盘,此时因为做了条带化,因为扩容需要从每块盘平均分出相等的空间来扩容,所以每块盘最多只能分出 200GB 的空间,五块盘共 1000GB 空间来扩容。此时,新加的盘就有 800GB 的空间没法利用。
如上所述,在用物理机挂载物理盘部署 Kafka 集群的的场景下,LVM 的动态扩容能力看起来没有实际用处。
我们换个场景,随着云服务时代到来。当我们在云上购买虚拟机,购买云上网盘来搭建集群的时候,LVM 的作用就凸显出来了,请看下图:
如图所示,每台 CVM 上挂三块云硬盘,三块云硬盘通过 LVM 条带化组建成一块逻辑硬盘,挂载到/data 目录下。云硬盘的特点是底层多副本,可在线扩容。这两个特点和 LVM 很搭。为什么呢?
云硬盘底层自带多副本,就不需要再做 Raid1 避免数据损坏、系统波动了。在线扩容,是指可以动态的对单块硬盘进行扩容。此时 lvm 的动态扩容能力就凸显出来了。如下图:
举个例子:假设集群一开始的时候规划每台机器只需要 600GB 的容量。此时,我们可以每台机器购买 6 块 100GB 的云硬盘,构建 LVM 条带化。挂载到/data 目录下,这样即可以利用条带化的并行写入能力,也可以得到所需的 600GB 容量。当业务发展一段时间,忽然发现,600GB 不够用了,每台 Broker 需要 1.2TB。此时我们通过控制台在线扩容硬盘容量,将每台 broker 的云硬盘扩容到 1.2TB,然后通过 lvextend 命令扩容/data 的容量,即可。
腾讯云上,单块云硬盘允许的最大容量为 16TB,单机允许最多挂载 20 块硬盘。所以,按照上述思路,一台机器理论的容量是 16*20=320TB。这对于单台机器来讲,基本是一个超大的容量了。
当然,上述毕竟是理论值。实践肯定达不到这么理想的状态。但是提供了一个思路。另外在提升条带化 IO 能力、估算挂盘的数量,都与 LVM 条带化时设置的 IO SIZE 有关。IO SIZE 取决于业务单条数据的长度和数据量。这个值并没有一个推荐的值,需要根据用户自身业务特点去评估。
如上所述,如果是部署在云上的 Kafka,LVM 是一种比 Raid10 更适合的方案。
总结
本文分析了常见的几种方案的使用场景和优劣。总结发现其实并没有一套完美的通用方案。而磁盘阵列也不是一个大力推荐的方案,自建集群的业务场景简单,单硬盘方案和多目录读写方案基本可以解决很多问题。而在业务场景复杂、规模大的物理机集群,RAID0 和 RAID10 都是可以考虑的方案。如果是部署在云上 CVM 主机的集群,LVM 方案是一个较好的选项。
综上所述,运营一个合适的 Apache Kafka 集群,需要根据业务特点、成本、数据可靠性、现有资源、所处环境等因素来考虑和权衡合适的硬盘方案。
头图:Unsplash
作者:许文强
原文:https://mp.weixin.qq.com/s/m1BzUynhDidXH3iDHt9wBg
原文:揭秘 Kafka 的硬盘设计方案,快速完成 PB 级数据扩容需求!
来源:腾讯云中间件 - 微信公众号 [ID:gh_6ea1bc2dd5fd]
转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论