导读:近年来,云原生化、全用户态、软硬协同等技术对 KV 存储服务产生了微小的影响,上述技术在极大晋升了服务的性能和升高服务老本的同时,也对系统的架构和实现提出了新的要求。百度在信息流和搜寻业务中大量应用了 KV 存储服务,服务每天响应近千亿次各类拜访申请,如何使用上述技术晋升零碎的性能、稳定性和运维人效是咱们重点思考的问题。本文通过介绍咱们利用上述技术打造高性能 KV 存储系统的实际过程,为大家分享了咱们在单机性能优化,大规模集群设计、治理等方面的思路和实践经验。
全文 7854 字,预计浏览工夫 21 分钟。
自 2016 年起,百度进入『搜寻 + 信息流』双引擎驱动构建内容生态的“信息散发 2.0 时代”,搜寻、举荐的内容也不再局限于网页,而是引入越来越多的视频、图片、音频等多媒体资源。KV 存储作为在搜寻和举荐中台中被宽泛应用的在线存储服务,更是受到了存储规模和拜访流量的双重考验。
至 2018 年初,咱们投入各类 KV 存储服务的服务器数量便超过万台,数据规模超过百 PB,承接了每天近千亿次的各类拜访申请。集群规模的增长除了资源老本的晋升,还加剧了运维治理的难度。作为有状态服务,集群的故障机处理、服务器降级、资源扩缩容都须要专人跟进,运维人力随集群规模呈反比增长。彼时又逢举荐业务实现了微服务化革新,业务资源交付和上线都能当天实现,存储资源动辄周级的交付能力也成了业务上线效率的瓶颈。
这些都促使咱们对原来的零碎架构进行彻底降级,通过晋升单机引擎性能和云原生化无效升高资源老本和运维人力老本。同时咱们还要满足业务对服务的敏捷性要求,通过云基础设施提供的资源编排能力,使零碎具备小时级服务交付能力。
一、问题与挑战
1)性能挑战
单机引擎性能是 KV 零碎的要害指标,个别咱们通过读写性能(OPS、延时(latency))和空间利用率来掂量引擎的性能,因为以后引擎架构和存储设备硬件性能的制约,咱们在引擎中往往只能抉择读性能、写性能和空间利用率中某个方向进行重点优化,比方就义空间利用率晋升写吞吐,或者就义写吞吐、晋升空间利用率和读吞吐。
这类优化对于业务繁多的场景比拟无效,之前咱们的零碎就针对大业务场景进行逐个调参优化,针对其余业务则应用读写性能和空间利用率平衡的『平衡型』引擎接入。
然而百度的信息流和搜寻业务规模都十分宏大、业务场景极其简单,咱们接入的数千个业务中有每天更新 PB 级数据的业务,也有读写比超过 100:1 的业务,还有要求强一致性、存储规模数十 PB 的业务,不少业务还会体现出潮汐个性,全天流量都集中在某个特定时段。
因而咱们通过引擎优化,既要解决如何在升高读写放大的同时,尽可能均衡空间放大的问题;又要在引擎内实现自适应机制,解决业务充沛混布场景下,吞吐模式多变的问题。
2)云原生化挑战
云原生架构的次要价值在于效率的晋升,包含 资源利用效率和研发效率两个方面。
百度信息流和搜寻业务对其业务模块制订了对立的云原生化规范:
- 微服务化:每个服务粒度应该在限定的范畴内。
- 容器化封装:一个服务的部署,应该只依赖基础架构以及本容器内的组件,而不应该依赖其余业务服务。
- 动静治理:每个服务应该能够动静调整部署,而不影响本身对外承诺的 SLA。
KV 服务在对齐上述规范的过程中,次要难点在于 容器化革新和动静治理两个方面。
容器化革新方面,单机时代的 KV 服务以用满整机资源为指标,对内存资源和存储介质 IO 的应用往往不加任何限度。引擎的容器化革新,要求咱们精细化管制对上述资源的应用:
- 内存资源:存储引擎除了显式应用零碎内存,更多的是对 page cache 的应用,文件系统中诸如 Buffered I/ O 和文件预读都会产生 page cache。咱们须要在引擎中对其加以控制,防止超过容器配额触发硬限。
- 存储介质 I /O:KV 服务的次要介质是 SSD,咱们不少业务也须要应用 SSD 晋升读写性能,这些业务自身往往只须要不到 100GB,因而为了晋升 SSD 的使用率,KV 服务须要和这些业务进行混布。这也要求咱们岂但能无效利用 SSD I/O,还要能对 I / O 加以控制,防止影响混布业务。
动静治理则要求业务具备肯定的容错能力和弹性伸缩能力,因为 KV 是典型的有状态服务,兼具了数据长久化、多分片、多正本等特点,咱们在动静治理中须要关注:
- 服务可用性:与无状态服务不同,多分片服务不能看服务的整体可用性,而要确保每个分片都可用。比方一个服务有 10 个分片,每个分片有 3 个容器,服务整体可用度为 90%。从无状态服务视角 2 个容器故障不会影响服务可用性,然而从 KV 服务视角,如果这两个容器正好服务同一个分片的两个数据正本,那么该服务就会呈现数据不可用问题。因而服务在继续部署时,须要确保每个数据分片的可用性。
- 数据可靠性:云原生化后为了晋升资源利用率,在线数据迁徙和动静伸缩频率将远高于物理机时代,数据的动静伸缩过程须要对业务通明,也不能呈现数据失落或一致性问题。
- 管理效率:确保治理服务能即时响应治理操作,对系统的稳定性起着关键作用。随着集群规模的减少,并发的治理操作数量也会减少,如果响应操作的工夫也逐步减少,最终将导致系统雪崩,因而咱们须要零碎响应治理操作的能力也能程度伸缩。
- 部署效率:KV 服务的部署包含部署利用和实现数据迁徙,因而咱们岂但要在性能上做到可迁徙,还要确保数据迁徙的效率。比方一个有 100 个实例的服务,如果迁徙一个实例要 12 个小时,且每轮只容许迁徙 1 个实例,遇到内核降级、操作系统降级等须要重启的操作,全服务迁徙一轮就须要 1 个半月,这样的效率还不如人工操作。这样的服务就不能算反对动静治理,因为没有在线操作能够容忍这样低的迁徙效率。
3)满足业务的特化需要
之前也提到百度的信息流和搜寻业务规模都十分宏大,一些大业务依据本身场景曾经做了单机引擎的特化,有些特化还波及批改 linux 内核,通用引擎在性能上无奈超过这些特化引擎,这就要求能从业务中提取共性,同时容许其保留个性,使业务团队能在 资源利用效率和研发效率两个方面都取得收益。
为此咱们提出了 UNDB – NoSQL 联结存储系统(United NoSQL Database)的概念,兼顾对立通用能力与放弃业务个性:
- 对立框架:应用对立的云原生存储框架,打平各零碎的运维、集群治理差别,同时突破原有的资源屏障。
- 通用接口:各 KV 服务剥离业务协定,对齐根本 KV 接口协议,对用户提供基于根本 KV 接口封装的 SDK,使业务能够在各服务间平滑迁徙,升高学习老本。
- 通用引擎:自研高性能通用 KV 引擎,便于其余 NoSQL 服务在此之上实现高性能的存储服务。
- 分层可插拔架构:通过可插拔的接口层、数据模型层、数据同步层、引擎层设计,使接入零碎能疾速实现业务个性化差别。
二、引擎优化
引擎是 KV 零碎的外围组件,鉴于 RocksDB 在开源社区和工业界的广泛应用,咱们一开始便间接应用 RocksDB 作为单机引擎。基于 LSM-Tree 实现,RocksDB 在 HDD 介质上有良好的性能,但在 SSD 介质上的性能体现却并不出众,次要问题在于:
- RocksDB 在设计实现中, 为了防止随机 I /O,减少了大量程序 I / O 开销(读放大、写放大),而 SSD 介质的随机 I / O 尤其是随机读性能和程序 I / O 差距不大,因而针对 HDD 介质的优化在 SSD 介质中反而造成了读写带宽节约。
- 上述额定的 I / O 开销所产生的高写放大,还减少了 SSD 介质的寿命损耗,在高吞吐环境中,新盘的均匀使用寿命有余 2 年。
- LSM-Tree 构造对业务数据长度也非常敏感,因为每层 SST 文件大小是固定的,数据长度越大,越容易触发 Compaction,从而造成写放大;同时数据长度越大,每层 SST 文件能记录的数据条目数就越少,读申请向上层拜访概率就越高,从而造成了读放大。
咱们业务场景中的 value 个别在 KB ~ 百 KB 级别,为了升高 LSM-Tree 的写放大,咱们在 RocksDB 根底上实现了 Key-Value 拆散的单机存储引擎,如下图左侧引擎构造所示:
图 1:一般引擎与基于 OpenChannel SSD 的软硬协同引擎的架构比照
- 在 RocksDB 中存储 Key 和 Value 的地址索引(BlockID+Offset),RocksDB 的 Compaction 只影响到索引,不会引起 Value 的变动。
- Value 独自长久化,咱们称之 Data Block 文件,Data Block 文件独自进行 Compaction。
为了进一部晋升引擎的 I / O 效率,咱们又对 Compaction 策略和压缩形式进行了优化:
- 自适应 Compaction 机制:综合引擎 I / O 和残余存储空间,调节对 Block 文件空洞率的抉择阈值,实现流量躲避、动静调节空间放大能力。
- 冷热分层:缩小冷热混合导致的不必要 Compaction,并抉择流量闲暇时段对冷数据进行 Compaction,升高触发 SSD 动态磨损平衡频率。
- 全局压缩:在 Block File 粒度反对了 zstd-dict 压缩形式,进一步晋升了 Block 文件的压缩率。
此外,咱们在引擎中为同步框架封装了 Log View,实现数据同步与引擎复用,WAL 升高了数据同步造成的写放大。
通过上述优化,在软件层面,咱们在空间放大 <1.6x 的状况下,将写放大管制到了 < 1.5x。在业务继续以 30MB/ s 更新数据的场景下,单盘寿命由之前的半年内晋升至 3 年左右。
然而,SSD 的写放大并不限于软件层,物理个性决定其不反对笼罩写,只能先擦除旧数据再写入新数据,大部分 SSD 按 4KB(Page)写入、按 256KB ~ 4MB(Block)擦除数据。SSD 在擦除一个 Block 时,须要迁徙 Block 中依然无效的 Page,这个迁徙动作导致了 SSD 硬件层的写放大,硬件层写放大又与 SSD 残余空间密切相关,个别状况下,当 SSD 容量应用达 90% 时,写放大会超过 3.5x。
仔细的同学或者会发现,咱们之前在计算 SSD 寿命时并没有提到这部分放大,这里其实蕴含了 SSD 厂商的优化:SSD 介质的理论容量单位是 GiB(Gibibyte),1GiB = 230bit,提供给用户的指标则是 GB(Gigabyte),1GB = 109 bit,前者比后者多了 7.374% 的空间,被厂商用作了外部操作空间。加之咱们在理论应用时,也会放弃磁盘用量不超过 80%,因而能够管制写放大。然而这些策略其实就义了 SSD 的容量。
为了进一步挖掘设施潜能,咱们和百度的基础架构部门单干,基于 Open Channel SSD 实现了一款软硬协同设计的引擎,如上图右侧引擎构造所示与传统用法相比:
- 实现了全用户态 I / O 操作,升高了引擎读写提早;
- 引擎间接治理 Flash 物理地址,防止了文件系统、LBA、PBA 三层映射造成的性能损失和空间节约;
- 将 FTL 中 Wear Leveling、GC、PLP、Error Handling 上移至引擎,和 KV 原有的 Compaction,Crash Recovery 逻辑合并,合并了软、硬两层操作空间。
软硬协同引擎在性能上超过软件引擎 > 30%,软硬整体放大率 < 1.1x。
图 2:引擎性能比照,顺次为:数据加载性能、读写吞吐(读写 1:1)、99 分位写延时、99 分位读延时
上图是咱们实现的 KV 拆散引擎(Quantum)、软硬协同引擎(kvnvme)和开源 Rocksdb 在数据加载、随机读写场景下的性能比照。
测试中咱们选用:NVME 1TB SSD(硬件指标:4KB 随机写 7 万 IOPS,随机读 46.5 万 IOPS)。数据集选用:1KB、4KB、16KB 和 32KB 共 4 组,每组数据集都随机预构建 320GB 初始数据,再采纳齐夫散布(Zipf)进行读写测试,读写测试时放弃读写比为 1:1。
从测试后果能够发现:
- Value 越大,KV 拆散引擎和软硬协同引擎的读写劣势就越为显著,在 32KB 时软硬协同引擎的吞吐是 RocksDB 的近 5x。
- 软硬协同引擎在读写延时管制上,尤其是写延时管制上也显著优于其余引擎。
三、云原生实际
上节中咱们通过引擎优化重构解决了业务混布性能和容器化问题,这节将介绍一下咱们是如何解决动静治理问题。
UNDB 服务整体框架如下图所示:
图 3:UNDB 零碎框架
架构上咱们将服务分成了 Operator(数据调度)、管制面和数据面三局部:
- Operator:负责向 PaaS 传递管制面和数据面中所有容器的状态信息和进行用户数据调度
- 管制面:包含元信息服务、集群管制服务、接入治理服务、路由服务和用户数据中心,负责管理一个 IDC(Internet Data Center)中的所有存储集群(Store Clusters)和用户发现、接入存储集群的路由映射关系,不同 IDC 间部署不同的管制面服务。
- 数据面:IDC 中 KV 服务治理的所有存储集群的汇合。存储集群按存储设备类型(比方:NVMe SSD、SATA SSD、OpenChannel SSD、HDD、……),存储机制的容量,引擎类型,以及引擎应用的 CPU、内存资源数量进行划分。
咱们在零碎设计、实现中次要思考如何实现数据全局调度能力和海量存储实例的动静治理能力。
3.1 数据全局调度能力
数据全局调度指:
- 用户数据能够在数据面中的不同存储集群间任意迁徙、扩缩容。
- 用户数据能够在不同数据中心间任意迁徙、复制。
这种能力的意义在于:
- 当业务状态、引擎技术和存储设备发生变化时,咱们能用对业务无感的办法将数据迁徙到适合的集群中。
- 当部门新建数据中心,业务新增、切换数据中心,或是数据中心数据恢复时,咱们能以最低的运维老本施行数据迁徙、复原。
图 4:Table 在 UNDB 集群间迁徙示意
如上图(图 4)所示,全局调度由 Operator 发动,通过管制面协调数据面中的存储实例实现操作,其中:
- 元信息服务和集群管制服务,为 Operator 对用户数据进行全局调度从底层机制上提供了无损迁徙和无损伸缩能力。
- 数据中心:通过剖析存储集群的流量、容量散布和业务数据的流量、容量增长趋势,向 Operator 提供了全局调度计划。
- Operator:基于 K8S Operator 框架实现,通过申明式原语,疏导管制面实现数据迁徙。
基于上述能力,咱们除了反对即时集群容量平衡和即时业务容量调整,还实现了周级机房建设、搬迁。
3.2 海量存储实例的动静治理能力
之前提到咱们在动静治理中须要关注服务可用性、数据可靠性、管理效率和部署效率,上节中咱们通过引擎优化实现了小时级实现 TB 数据的迁徙、复原。这里咱们将关注如何在动静治理中确保服务的可用性、数据可靠性和管理效率。
业务拜访 KV 服务的过程能够简略概括为:
- 业务通过路由发现数据所在的存储节点。
- 拜访存储节点获取数据。
- 数据和存储节点映射关系产生变更时,告诉业务更新路由。
咱们在数据面中:
- 按 3 -2- 1 准则确保数据安全。
- 应用 Multi-Raft 确保数据一致性。
- 采纳多地多活确保重要业务的可用性。
因为数据中心间的元信息不尽相同,尤其是拓扑信息齐全不同,且拓扑信息具备极强的时效性,冷备成果并不好,因而对于管制面咱们采纳了利用数据面,集群管制服务多级兜底的思路,如下图(图 5)所示:
- 冷备机制:对于低频更新的元信息,诸如业务数据属性、配额实时备份 SQL 数据库。
- 反向恢复机制:利用集群管制服务和存储集群都有拓扑镜像的特点,反对通过上述服务反向恢复拓扑信息。
- 异变拦挡机制:在管制面、数据面及业务端(Client)拦挡异样变更,防止拓扑异样时,服务迅速异样雪崩。
图 5:元信息多级拦挡、反向恢复
另外咱们还通过独立的路由服务向业务屏蔽了元信息服务,业务间通过多组路由服务进行物理隔离。并通过接入治理服务治理业务和路由服务间的映射关系,这样能够无效避免因为某个业务的异样拜访(比方:连贯泄露)影响其余业务的路由拜访。
在晋升管理效率方面,咱们次要采纳了元信息和集群管制拆散的设计,因为元信息服务须要确保节点间数据一致性,咱们的数据批改操作只能在 Leader 节点上进行,如果不采纳拆散设计,所有管制操作只有 Leader 节点能力进行,当集群规模和数量减少后,无奈通过程度扩容节点减少管制面算力,因而咱们选用了两种模块拆散的办法,使管制面具备程度伸缩管制算力的能力以应答超大规模集群。
四、多模型存储架构
NoSQL 概念倒退至今,业界曾经呈现了数百种不同的开源和商业 NoSQL 数据库,当业务倒退到肯定水平,对规范数据库进行革新,使其更适宜业务模型的需要也变得越来越广泛。因而咱们在整合 KV 零碎时,提出了整合通用性能保留业务个性的设计思路。在这个思路领导下,咱们对立了管制面,用于实现对立的运维治理,数据面则分成了 3 个功能模块、6 个分层向业务凋谢了对服务接口、数据模型、存储模式、以及对同步框架的定制化能力,如下图所示:
图 6:UNDB 多模型存储架构
- DbProxy 模块:整个架构的第一层,是一个独立部署的模块,DBProxy 是对所有代理存储节点(Store Node)的 Proxy 服务的总称,按性能不同还有诸如 KvProxy,GraphProxy 等,该模块次要用于:
- 分担存储节点(Store Node)拜访压力:当业务存在大量的 Fanout 读,或离线工作(map reduce 工作)存在大量写连贯时,通过 proxy 能够缩小对存储节点的压力;
- 缩小上下游连贯开销:次要用于超大规模业务,比方搜寻业务的一张索引表就散布在上万个实例中,这也象征拜访该表的每个上游都要保护上万个连贯,因而须要通过 proxy 缩小客户端的连贯开销。
- 存算拆散:以后通过 NVMe-OF、RDMA 技术网络曾经不是零碎的次要瓶颈,像图数据库这样的服务,一次申请须要拜访多个存储节点能力实现,还须要通过本地缓存构建子图减速业务查问速度,整合在存储节点中简直无奈带来的性能收益,还容易呈现热点查问,因而咱们也通过 Proxy 实现存算拆散。
- libNode 模块:该模块承当了存储节点的业务逻辑、节点治理和数据同步:
- 分布式管理层:通过 CtrlService 向业务提供了一组响应分布式状态治理的外围原语,并提供了默认原语实现。
- 接口协议层 & 数据模型层:提供 KvService 作为所有服务独特恪守的最简 KV 协定,凋谢服务注册和模型层(Module)供业务组织治理本人的定制化服务接口以及数据结构。为了能让模型应用适应不同引擎,咱们对立 KV 接口作为模型层的序列化反序列化接口。
- 同步框架层:不同业务对服务的可用性、数据一致性的要求并不相同,比方用户模型关注可用性和吞吐能力,内容模型则关注读写延时和数据的强一致性。咱们也针对业务的不同需要提供了最终一致性(高可用、高吞吐)、程序统一(braft 实现的一致性,介于最终统一和强一致性之间)和线性统一(强统一)三个层级的一致性保障,其中线性统一是咱们在 braft 根底上,通过 ReadIndex 和 LeaseRead 算法满足的一致性语义。
- 引擎模块:也是 6 层架构中的最初一个分层『引擎层』,次要负责单机存储性能,与 libnode 独特组成了一个存储节点(Store Node)。上节提到的引擎优化,就是对这个模块的一系列优化工作。
五、总结
UNDB 零碎自落地以来,曾经笼罩了百度信息流和搜寻次要业务场景,波及集群规模数十万实例,每天承接了超过万亿次的各类拜访流量,老本相较原先升高近 50%。同时零碎还放弃着月级频率的全集群业务无感更新。在运维方面则实现了高度自动化,集群无专职运维,全副由研发团队自主治理。目前团队还在打造多模型数据库、单机性能优化、存储架构优化等方向继续致力,力求使 UNDB 具备更欠缺的业务生态。
举荐浏览:
|难看视频 Android 重构——围绕于播放器的重构实际
|浅谈百度浏览 / 文库 NA 端排版技术
|云原生架构下的继续交付实际
|一年数十万次试验背地的架构与数据迷信
———- END ———-
百度 Geek 说
百度官网技术公众号上线啦!
技术干货 · 行业资讯 · 线上沙龙 · 行业大会
招聘信息 · 内推信息 · 技术书籍 · 百度周边
欢送各位同学关注