揭秘Kafka的硬盘设计方案,快速完成PB级数据扩容需求!

2021-03-24 15:13:51 浏览数 (2)

导语:疫情期间,腾讯医疗为全国人民提供了及时精准的疫情信息服务。腾讯云kafka作为腾讯医疗大数据架构中的关键组件。在面对业务短时间内成倍的数据存储需求的情况下,如何快速响应、快速扩容以支持业务的稳定运行的呢? 本文将从Kafka集群底层物理机层面硬盘的设计方案,来讲解面对不同的业务需求场景,如何选择好合适的磁盘方案。(编辑:中间件小Q妹)

01

医疗资讯场景

腾讯医疗的使用场景是典型的日志分析系统。Kafka作为消息中间件,起到了数据聚合、流量削峰的作用。如下图所示 :

日志分析系统架构

在医疗的实例中,Kafka承载着峰值GB/s的数据吞吐和大量的数据存储压力。这对底层的硬盘性能提出了很大的要求。比如吞吐/存储能力、快速扩缩容能力等。

所以一般在硬盘方案设计的时候需要综合考虑下列因素:

  • 大容量存储
  • 高吞吐量的IO能力
  • 快速扩缩容能力
  • 数据的安全性
  • 低冗余的存储

组建Kafka集群,常见的硬盘构建主要包括:单硬盘读写、多目录读写、硬盘阵列(RAID0,RAID10)、逻辑卷(LVM)等方案。下面将深入分析各个方案的优劣势,供读者选择参考。

1. 硬盘方案概述

硬盘存储方案的设计使用的是现在成熟的工业化方案,并没有特殊的创新。硬盘存储方案的选择更多的是从Apache Kafka产品的视角出发,考虑哪种方案更贴合使用者的业务需求。

2. 选择硬盘介质

工业界的硬盘市场基本是机械硬盘和固态硬盘(SSD)的天下。在超大规模的存储容量场景下,SSD的价格依旧是它的硬伤。对于Kafka这种高IO的应用,固态硬盘的损坏率和使用寿命是一个很大的问题。所以,机械硬盘以其便宜的价格及大容量成为了不二之选。

机械硬盘需要解决的两个问题是:如何提高硬盘IO能力;在硬盘损坏成为一个常态的情况下,又该如何保持业务系统的稳定。我们先从这两个方面来分析下。

3. 提高硬盘IO能力

一般使用以下指标衡量硬盘的性能:

  • IOPS:每秒读/写次数,单位为次(计数)。存储设备的底层驱动类型决定了不同的 IOPS。
  • 吞吐量:每秒的读写数据量,单位为MB/s。
  • 时延:I/O 操作的发送时间到接收确认所经过的时间,单位为秒。

Kafka程序本身通过顺序读写、Page Cache、零拷贝等方案,从应用层面极大的利用了硬盘的性能。但是,一旦硬盘吞吐能力不足,Kafka集群提供服务的能力将大打折扣。因为Kafka的使用场景和运行方式,最关注的性能指标是吞吐量。

在自建集群的场景下,用独立主机挂载单块硬盘的方式是最常用的方案。在单硬盘读写的基础上,提高硬盘吞吐能力的方案主要有如下几种:

  • 单硬盘读写
  • Kafka的多目录读写
  • RAID硬盘阵列方案
  • Logical Volume Manage(LVM)条带化方案

02

方案一: 单硬盘读写

单机单硬盘部署是在自建集群当中最常见的一种方案,也是在实践当中用的最多的一种方案。如下图:

单机单硬盘集群部署方案

上图是一个由三台节点构成的Kafka集群,集群的每个节点挂载一块SATA/SSD的数据盘,用来存放Kafka的数据。该方案的特点就是思路简单,搭建快捷。很适合自建的小规模集群。

这种方案,当发现硬盘能力不足时,最直接有效的解决思路就是垂直扩容。即提高单盘的IO能力,比如将5400 转/秒的硬盘换为7400 转/秒的,或者换为10000 转/秒、甚至10000 转/秒以上的更高转速。当机械硬盘的能力不足时,直接换为大容量的SSD。

这种方案的缺点也比较明显。首先,单块的SSD吞吐量是有上限的,当Kafka流量增大,SSD也有承受不了的一天,单块硬盘的吞吐将限制Kafka集群的吞吐能力。另外SSD的价格大约是SATA的好几倍。以当前腾讯云上的硬盘价格为例,SSD价格是高性能云硬盘的3倍。

所以,当集群规模持续扩大时,该方案并不是一个长久的选择。

03

方案二: 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

添加了这个配置后,有什么效果呢,看下图:

Kafka多目录读写示意图

如上图,假设有一个有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瓶颈,此时就又回到了单硬盘的方案遇到的问题。

所以当业务有这种场景的时候,多目录的方案可能会有一些局限。

04

方案三: RAID磁盘阵列

RAID就是磁盘阵列,由很多块独立的硬盘组合成一个容量巨大的硬盘组,利用多个硬盘产生加成效果提升整个硬盘IO能力。典型代表有RAID0、RAID1、RAID5、RAID10等。由于篇幅原因,就不展开解释RAID的相关知识。有兴趣的可以查阅相关资料。简单比对一下以上方案的优劣势:

1. RAID10方案

使用RAID方案的原因就是为了解决单硬盘IO瓶颈的问题。经过以上方案比对,我们以RAID10为例,展开说明RAID做了什么,以及应该选择哪种RAID方案。先看下图:

RAID10硬盘方案构建集群示意图

上图中单机用四块盘组成RAID10,即先用两块盘组成一块RAID1的虚拟盘,再用两块虚拟盘构建成一块虚拟的RAID0盘,并挂载到/data目录下。理论上来看,假设单盘吞吐量是100MB,那四块盘组成的RAID10阵列的吞吐就是200MB,且每份数据在底层存储,是双副本的。

这里RAID1的作用是,即使底层有一块数据盘损坏,系统会自动读写另外一块备份盘的数据。此时系统还是能够正常使用的。RAID0的作用是并行IO,提高整体的IO能力。从理论上讲,三块硬盘的并行操作使同一时间内硬盘读写速度提升了3倍。但由于总线带宽等多种因素的影响,实际的提升速率肯定会低于理论值。但是,大量数据并行传输与串行传输比较,提速效果显著显然毋庸置疑。

这里需要注意一点的是,单纯从IO能力,从多块盘的IO能力相加的角度来看,RAID0并不比多目录方案的IO能力强。RAID0的优点在于,单块虚拟盘的IO能力比单块物理硬盘的强,单块虚拟盘容量比单块物理硬盘容量大。这两个特性是可以解决数据冷热明显、数据倾斜的情况的。

2. 直接用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方案。

05

方案四: LVM逻辑卷条带化

LVM逻辑卷的条带化原理和RAID1很像。都是条带化的进行数据读写。都有并行读写的能力。在实测过程中,两种方案的并行读写性能是差不多的。LVM相对于RAID10的好处在于,它提供了动态扩容硬盘的能力。LVM条带化的扩容是依赖以lvmextend命令实现的。扩容有一个条件:条带化的lvm扩容需要每个硬盘扩容大小一样的容量。如果每个硬盘容量不一样,条带化的lvextend会失败。

举个例子:例如当前有三块盘容量1T,剩余空间200GB的盘,加入两块空间1T的盘,此时因为做了条带化,因为扩容需要从每块盘平均分出相等的空间来扩容,所以每块盘最多只能分出200GB的空间,五块盘共1000GB空间来扩容。此时,新加的盘就有800GB的空间没法利用。

如上所述,在用物理机挂载物理盘部署Kafka集群的的场景下,LVM的动态扩容能力看起来没有实际用处。

我们换个场景,随着云服务时代到来。当我们在云上购买虚拟机,购买云上网盘来搭建集群的时候,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更适合的方案。

06

总结

本文分析了常见的几种方案的使用场景和优劣。总结发现其实并没有一套完美的通用方案。而磁盘阵列也不是一个大力推荐的方案,自建集群的业务场景简单,单硬盘方案和多目录读写方案基本可以解决很多问题。而在业务场景复杂、规模大的物理机集群,RAID0和RAID10都是可以考虑的方案。如果是部署在云上CVM主机的集群,LVM方案是一个较好的选项。

综上所述,运营一个合适的Apache Kafka集群,需要根据业务特点、成本、数据可靠性、现有资源、所处环境等因素来考虑和权衡合适的硬盘方案。

作者介绍

许文强, 腾讯云中间件消息队列资深研发工程师。腾讯云Ckafka核心研发,拥有多年分布式系统研发经验。主要负责腾讯云CKafka定制化开发及优化工作。专注于Kafka在公有云多租户和大规模集群场景下的性能分析和优化。

0 人点赞