

分享嘉宾:王鹏飞 PingCAP 资深专家
编辑整理:张德通 数数科技
出品平台:DataFunTalk
导读:本文将以点带面地从现有的TiDB架构触出发,为大家介绍如何设计新的云原生分布式数据库架构。
本文会围绕下面三点展开:
对云原生的朴素理解
对“云原生数据库” 架构设计理念与设计思路的思考
浅谈逃生、容灾与扩容

云原生是一个比较新的概念,TiDB有在云上为客户提供PaaS服务的业务。客户实际上需要的不是云原生,客户需要的是尽可能压低使用服务的成本。这里成本指的是综合成本,不只是资源成本。
客户想要的云原生,其真正含义是:要业务资源有足够弹性,一方面是按需计费,这个是技术上的弹性;另一方面由于客户业务SLA不同、重要性也不同,客户需要业务上有足够灵活的选择。
客户还希望PaaS服务的列表价格能做到比云服务提供商的服务器价格更便宜。而分布式数据库会有很多消耗,做到比服务器价格更便宜很有难度。但价格做到比服务器价格更便宜才能收获长尾客户。
云原生是借助云上基础设施达成目标的手段。
作为第三方数据库厂商,想要帮助客户降低成本,只有通过利用云厂商能里一个选项。
云原生的数据库一定是分布式的数据库,因此后面所有谈及云原生数据库的内容都是云原生的分布式的数据库。
与其讲云原生数据库,不如反朴归真,来谈谈分布式数据库在云上应有的表现吧。下面会以Pingcap的TiDB为例举例说明。

上图是TiDB的基本结构。TiDB进程从TiKV节点上获取到数据,完成计算。TiDB进程是无状态的进程,是负责计算的进程,可以连接任意TiKV节点。TiKV是有状态的,事务相关的操作都是在TiKV层实现的。TiKV每个节点上存储了哪些数据会被记录在元数据中。TiKV节点之间通过Raft保证数据一致性。

这个架构中计算存储分离的边界是在TiDB和TiKV之间。TiDB与TiKV计算和存储使用资源的比例并不固定。例如对计算要求不高而对存储需求大的场景中(例如知乎),TiDB节点数不多,但TiKV节点数非常大;另一些用户可能会要求计算和存储节点数量要保持1:1的关系。
把TiDB放在公有云上、让TiDB作为公有云服务的过程中,我们发现了TiDB的一些设计方面的局限性,即现有的TiDB、TiKV是面向IDC的设计而不是真正面向云的设计。
专有云和云上的应⽤并不⼀样。下面谈2点云上应用的优势:
亚马逊提供的块存储服务gp3云盘其IOPS和容量不相关,解决了EBS(块存储)IOPS底的问题。这个功能让云上数据库服务有了很大优化空间。亚马逊的S3对象存储服务价格非常便宜,它提供了11个9的数据持久性。
利用云服务的能力,可以完全实现按需付费,按量计费。另外客户也有根据业务场景定制可以使用的服务能力的需求,例如客户需求是“我的订单逻辑可以 100¥/h,但是收藏夹逻辑可以 10¥/h 吗?” ,可以通过云服务进行定制。
多个VM连接同一块云盘,每个VM读写云盘的性能和代价是一致的。前面提到的亚马逊gp3能够达到接近本地磁盘的效果,其数据持久性是3个9。可以认为gp3的表现和电脑上的普通磁盘存储已经比较接近了,从这点看来,云厂商在不断试图让云上服务达到“返祖”的效果。
既然提到“返祖”,那么TiDB的架构演进上可不可以也“返祖”呢?我们来考虑一下把“存算分离”的边界从TiDB-TiKV之间,移动到TiKV和持久化存储之间。

这个考虑是基于,从任意节点访问共享/远程存储的性能已经可以做到与访问本地磁盘的消耗接近,意味着TiDB和TiKV都变成无状态服务从技术上已经具备了可行性。
继续对这个移动存算分离边界的设计进行细化和质疑:
云存储服务用块存储EBS还是对象存储S3?能不能让成本更低?
TiDB/TiKV都已经被划分到计算那一侧了,也没有状态,分开还有意义吗?分成两个独立服务有什么好处?合并有什么好处?这一点本次分享里不再讨论,感兴趣的同学可以继续思考。
如果TiDB与TiKV合并,这个合并后的进程会负责写文件,如何解决数据持久性问题、或者说如何不丢数据?写多少份拷贝,3份、2份还是1份就够?不同拷贝之间可不可以性能不同、可靠性可不可以不同(一般客户对OLTP要求是约稳定越好)?能否在做拷贝时利用不同存储(EBS/S3)的特性对服务取长补短、达到既要高可靠性也要尽可能降低成本的效果?
EBS优点是可以和本地磁盘性能媲美,但是价格更贵;S3是对象存储,不能直接拿来和本地磁盘一样使用,优点是吞吐量和容量几乎无限、数据持久性是11个9,缺点是延迟高。虽然S3的读延迟对于一些离线分析场景是可以接受的,但是OLTP场景下对延迟容忍度低,通常OLTP场景下需要在几毫秒完成数据存取,显然单独用S3不能完全满足需求,能否组合他们的优缺点达到目的?
EBS不能跨AZ,AWS整个可用区域全部不可用并不少见,而如果同时发生了用户在其他AZ的机器也有故障,即使是用本地盘也会发生用户数据丢失的事故。当一个AZ挂掉,会导致集群内数据要重新分布,集群也要补副本,这时客户集群负载高,用户体验在这段时间不易保证,如何解决?
如何满足客户希望按不同应用场景的重要程度付费、且愿意忍受因为预算减少带来的使用体验降低的场景需求?
如何设计架构,紧跟云厂商存储服务快速迭代的步伐?
根据提出的问题,我们对“云原生分布式数据库”的设计进行细化。
下图中TiDB作为TiDB与TiKV合并后的进程,WAL是事务日志。TiDB进程都来读写S3。

为了兼顾可靠性和价格,也为了防止AZ故障问题,我们选择S3作为默认的存储选项。当然不排除部分客户更愿意选择EBS这类价格更高的存储。
但S3存储读写速度慢,同步读写会导致用户体验差,需要引入异步读写机制。异步读写的过程不与用户读写的流程耦合。读过程首选读缓存内数据,尽量避免直接读S3;写过程是先写入缓存,当条件合适时异步地把数据写入S3。为了保持读写的高性能,缓存进程和TiDB进程应该部署在一起。
有了缓存,就意味着数据存储了两份,一份是缓存数据一份在S3中持久化存储。OLTP数据库的特征是需要保持数据的持久性,在发生故障时可以恢复数据,因此要记录事务日志。当一个TiDB节点挂掉后,利用S3上持久化存储的数据配合事务日志就可以做到数据恢复。
事务日志与S3上持久化存储的数据不同,必须同步写,其安全性需要得到保障。任何事务写操作执行前,需要保障事务日志写成功。参考其他的关系型数据库和分布式数据库的实现是:在事务日志写成功、缓存更新完毕后,向用户返回日志写成功的消息。日志需要保障跨AZ可用。
有了跨AZ的事务日志,S3存储本身就可以跨区访问的,数据非常安全,这时可以考虑只写一份。
上面我们粗略地细化了“云原生分布式数据库”的设计,真正决定成败的是细节,我们继续打磨这个架构。
这个抽象已经几乎完全利用到了云存储服务的所有优点。除非云厂商可以提供比缓存性价比更高的服务,这个架构几乎不会被动摇。
异步写的操作让任意吞吐量满足要求的存储介质都能够成为持久化存储,甚至可以使用磁带机作为持久化存储。但如果真的使用磁带机,由于其读性能很差,如果有缓存节点宕机,重新建立缓存的过程会非常漫长。但如果所有节点正常运行的情况下,是可以把使用磁带机做持久化存储作为极端情况考虑的。这个架构让持久化存储的下限可以接受磁带机,上限可以是一种缓存。
这个架构也满足了紧跟云厂商步伐、可以快速迭代持久化存储层存储介质的需求,不论使用EBS还是哪一种新存储都可以很方便地替换。
这个架构的风险点在于缓存击穿。如果遇到缓存击穿,查询延迟会很高。但是这个问题可以转化为商业问题解决:让客户选择不同价位的服务,服务提供不同级别击穿率保证。例如订单和计费类业务购买100%不击穿的服务,收藏夹类业务购买70%击穿率服务、但是减少收费。
我们来考虑一下击穿率,如果用户要求0%击穿率,其实现是缓存中多了一份数据拷贝。这时返回来看Cache选型,用户可以选择全部数据放在内存、追求极致速度,但内存价格更高。本地SSD的读写速度也足够满足缓存场景需求。甚至也可以考虑使用EBS、用EBS也能够加快节点故障后恢复速度加。
使用缓存这个设计使系统失去了原有的TiDB与TiKV节点可以按任意比例配比的灵活性。但这个灵活性并不一定在所有场景下对客户都非常重要,在这个架构中,客户对于缓存命中率的需求比系统节点数配比的需求强烈更多。
关于存储格式,如果迭代到上图中这个架构模式,就不必须要KV格式存储了。TiDB使用Percolator实现分布式事务,Percolator是基于KV实现的;而这个架构中,我们可以用更紧凑的方式实现事务。KV格式中,Value是一行数据,而我们可以把这一行数据设计得非常紧凑。当前其他KV引擎大多使用LSM-Tree数据结构实现,LSM-Tree在试图用读时开销换取写时的开销。到底要用LSM-Tree组织数据还是用其他格式组织数据,需要根据客户使用场景判断、也要根据使用场景做针对性优化。在S3上的LSM-Tree做Compaction时,由于存算分离的架构、外加异步读写的设计,Compaction过程几乎对主计算进程无影响。
在一些场景中,客户会希望能够分析到几秒/几分钟之内的数据,但同时分析的操作量也很重。为了达到这种HTAP的效果,异步写S3也可以考虑直接写成列式存储。LSM-Tree加列存加Compaction,在TiFlash读数据时候控制好Checkpoint就可以达到实时读数据的效果,为整个系统横向扩展提供了可能。
TiDB和缓存合并后,在EKS和K8S存储中会作为一个Pod部署。这时如果利用基于共享资源池的多租户体系就可以实现顺畅地实现超卖,而不需要在单一TiDB集群内做多租户的逻辑。多租户需要安全隔离、需要性能隔离,在进程级别做这两项隔离的复杂度远远高于基于虚拟化实现多租户。
这个新架构的分布式事务实现、数据分⽚、failover 流程、死锁检测、扩缩容等等,今天我们没有讨论。
1. 逃生
单 AZ 部署⽆论如何也解决不了 AZ 挂掉的问题,多 AZ 容灾是必选。旧架构中(现有架构下),多AZ容灾的实现是3份拷贝分别放到不同AZ内。如果客户需要TiDB数量是N,为了实现容灾,最终TiDB的机器数量是3N。改进后的架构使用的S3存储本身就是跨AZ的服务,外加跨AZ的WAL(可以依靠Raft协议保证其一致性)。
假设⽀撑⽤户业务需要 n 台服务器并⾏⼯作,使用新架构后不再需要购买3n台服务器做高可用,az1购买n台机器、az2购买m台机器、az3购买k台机器,其中 m,k ≤ n 且 m + k ≥ n,就能保证相当不错的可⽤性。这个特性可以大幅降云数据库运营成本,同时降低用户需要付出的费用。
有用户需要具备跨 Region 逃生⽅案。Region1上需要一套系统、Region2也需要一套同样的系统。如果使用现有的架构,实现起来代价很高很重;新架构能简单的多地实现这个需求,直接通过事物日志WAL做跨Region的复制。WAL复制其实现可以参考RDBMS的主从复制,不论是同步复制、半同步复制、完全异步复制都可以借鉴;也可以通过Raft协议直接向其他Region复制事务日志。一旦事务日志到达对应Region,计算进程就可以处理这些日志并对数据做同步。
新架构大大降低了云上数据库逃生与容灾的复杂度。
2. 扩容

现有架构在业务高峰期时为了解决集群能力不足的问题扩容节点,此时扩容的结果可能很严重:
TiKV节点都很忙。
新增TiKV节点会导致数据重新分布。
在TiKV节点都很忙的情况下,有一个或多个节点因为数据重新分布宕机、需要重新分布的数据量增大,引发整个集群的雪崩。
现有架构下有什么解决问题的办法?
不搬迁数据,结果节点负载不会降低,不能根本上解决问题。
搬迁更少的数据,对整个集群的调度和性能监控系统考验很大、难以实现。
能否在流量控制之下温柔的搬迁数据?时间足够的情况下可以,但线上问题救火的场景下很难有充足时间做这类操作。例如双十一错误估计了负载,那么此时留给技术人员做数据迁移的时间非常短。
集群负载高的情况下做停机扩容是业务上不能接受的。
要求⽤户别在集群健康边界内做扩容,但客户可能会因为系统账单和业务预估量不买单。
用上新架构后似乎可以更好地做扩容了:
首先数据都在S3上存储,不再需要在节点之间搬迁数据。
扩容节点后新增的节点可从S3存储中异步地把数据加载到缓存。这个操作不会对正在运行的节点造成任何影响。
缓存加载完成、事务日志同步数据过程执行完成后,扩容的节点就可以正常工作。虽然事务日志同步数据的过程需要占用WAL集群的IO,但WAL系统资源开销单一、性能消耗也更容易预测,可控性方面相比于直接控制TiKV集群好很多。

新提出的架构对非云客户有帮助吗?是的。
新架构可以用HDFS替换S3作为持久化存储、也可以根据客户定制化需求和定制化硬件方便地进行改造。新架构不仅能根据不同场景提升用户对延迟的要求,也可以大幅提升整个系统性能。比如激活了HDFS EC功能后只需要存1.5份空间的数据即可保证数据可靠性,相比于传统的3副本,在空间上减少了很多。
这个新架构并没有实际落地,落地过程中的细节决定了整个云原生分布式数据库能否成功。
今天的分享就到这里,谢谢大家。
在文末分享、点赞、在看,给个3连击呗~
分享嘉宾:

福利时刻

《大数据典藏版合集》电子书目录如上,感兴趣的小伙伴,欢迎识别二维码,添加小助手微信,回复『大数据典藏版合集』,即可下载。

关于我们:
🧐分享、点赞、在看,给个3连击呗!👇




