作者:phoenixxliu,腾讯 TEG 后台开发工程师
目录:
- 导语
- 一、背景
- 二、竞品分析
- 三、需求和挑战
- 四、架构和方案
- 五、总结和展望
导语
小程序云开发(Tencent CloudBase)拥有易接入、高性能、高可用等特性,其中云数据库作为核心组件之一,可以有效降低运维成本,帮助开发者实现业务快速上线与迭代。本文将简要介绍如何通过 TEG 云架构平台部的高性能分布式 NoSQL 数据库,为近百万小程序云开发用户提供完整的原生云端数据库能力支持。
一、背景
要理解小程序云开发,不妨将之从字面上拆解为小程序和云开发两个部分。本节部分我们也将尝试从这两个方面带大家一起简要梳理下相关的背景知识。
1.1 云开发
从软件工程的角度来看,软件开发经历了如下三个阶段:传统开发-->敏捷迭代-->serverless。传统的开发模式和敏捷开发模式除了需要开发者编写核心的业务逻辑外,都不可避免地需要对后端的基础设施进行管控和优化。比如,一个应用的逻辑可以很简单,可一旦涉及到应用的发布部署,就需要开发者花费大量精力进行服务器、数据库、网络等基础设施的申请和搭建,还要考虑这些后端基础设施的稳定性、可用性和监控指标。这一切耗时耗力又与产品的核心功能无关,对于需要快速开发和试错的产品,传统的模式开发速度慢、部署和运维成本较高。
开发趋势
随着 serverless 概念的火热,越来越多的开发开始转向 serverless 架构发展。“serverless”并不是指后端没有服务器,而是将后端服务器及相关运维操作变得对上层应用开发者不可见和透明。用户无需关心后端的基础设施,直接通过云 API 一键接入云函数、云数据库和云存储来获取算力、数据库、存储等基础的后端能力。这种随用随取的开发模式,不但可以让开发者能更专注于自身的业务逻辑,还具有低成本、开发速度快以及免运维等诸多优势。
1.2 小程序
小程序应该不用过多介绍,相信现在每一个使用智能手机的人都已经在日常生活中或多或少地使用到了各种各样的小程序:点餐、外卖、打车、购物等等。为了严谨起见,还是按张小龙朋友圈的介绍给出一个简单的定义:小程序是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或者搜一下即可打开应用。也体现了“用完即走”的理念,用户不用关心是否安装太多应用的问题。应用将无处不在,随时可用,但又无需安装卸载。
自 2017 年 1 月 9 日微信发布小程序以来(十年前正好是乔布斯发布首款 iphone,有媒体解读,这是张小龙的致敬以及野心的表现),小程序在这几年的发展中已经形成了完整的生态系统,用户数和小程序数量也有了非常显著的进步和发展,其他主流互联网企业也开始纷纷布局小程序平台,如支付宝小程序、百度小程序、抖音小程序、今日头条小程序等等。据微信提供的数据,在 2019 年微信小程序全球交易额达到了 8000 多亿,同比增长 160%,日活跃用户超过 3 亿,截至目前,微信上线的小程序超过 100 万个,拥有超过 150 万开发者、8200 多个第三方平台,小程序在电商,零售行业同比去年有爆发式增长。到 2020 年微信公开课 PRO(同样是 1 月 9 日),全网的小程序数量已经超过 450 万。可以说,我们已经进入了一个“全民小程序”的时代。
受到今年新冠疫情的影响,由于小程序开发相较于 app 开发更加轻量和低门槛,同时也能触达到更多的人群,各种健康码、申报、信息核实等大部分都采用小程序的形式上线。类似的,线下实体店客流受阻,越来越多的商家和店铺将自己的销售转移到线上来保证疫情期间的现金流,小程序同样也是这一场景的不二选择。
附近的小程序
越来越多样化和越来越火爆的小程序也就意味着会有越来越多的小程序开发者入场,如何服务好这些基于小程序生态的开发者们就成为了一件必须要解决的事情。于是小程序云开发问世,可以说小程序需求 + serverless 理念 = 小程序云开发。小程序云开发以微信作为小程序前端运行的依托,同时又通过接入云函数、云数据库和云存储等云服务,来达到对后端基础设施的“开箱即用”。这些特性可以在很大程度上解放小程序开发者的生产力,大大降低开发的成本和难度。
二、竞品分析
事实上,互联网巨头们很早就看上了这一块市场。以 google 为例,自从 2014 年 10 月收购 filebase,google 将自身已有的云端服务与工具一并整合进 filebase,使之成为专门为 app 开发者提供一站式的BaaS(Backend as a service,后端即服务)产品,涵盖了开发、质量、分析与发展着四个主要的模块,提供了认证、数据库、存储、云函数、机器学习等服务以及一系列性能和数据分析工具。
filebase-cloud filestore
如果重点看它的数据库产品的话,filebase 提供了两个选项:Cloud Filestore和实时数据库。虽然他们都是 NoSQL 数据库,但官方更推荐使用前者,因为它可以提供高性能、良好的可扩展性以及其他更多的高级功能。参考官网的介绍,cloud filestore的主要功能如下:
- 灵活性——支持灵活的分层数据结构,文档型;
- 富有表现力的查询——支持过滤/排序等功能,自动家索引,查询性能与结果集的大小(而不是整个数据集的大小)成比例;
- 实时更新——支持实时数据同步;
- 离线支持——缓存离线状态的写入、读取、查询,待恢复在线时,同步本地更改;
- 可扩展涉及——基础架构支持自动多区域数据复制,强大一致性保证、原子批量操作以及事务支持;
另外,filestore是支持按量收费的,可支持按数据存储量、流量以及文档的读(query),写(insert/update),删除(delete)操作次数来收费。并且对外提供了多个客户端和开发语言版本的 sdk。
其他为人熟知的 BaaS 产品还有Parse,最早被 Facebook 收编,但没多久就停止了运行。目前以开源产品的形态运行在 github 上,需要开发者自行下载源码并部署和维护,已经失去了 BaaS 的意义。
类似的,国内也有不少提供一站式后端服务(BaaS)的产品,包括:LeanCloud,Bmob,willddog野狗云服务、知晓云等。腾讯云推出的小程序云开发(Tencent CloudBase,TCB)也是属于同一赛道的产品,即采用 serverless 架构的一体化后端云服务。
三、需求和挑战
那么,小程序云开发对于数据库提出了哪些基本需求?又有哪些挑战呢?
我们认为应该主要有以下五个方面的需求:
- 安全性:对于数据库而言,数据安全是第一位的;
- 易用性:与小程序的特征类似,“开箱即用,用完即走”,简单上手,免运维;
- 低成本:按量收费,精细化成本控制;
- 高性能:Nosql,支持高并发读写;
- 灵活性:无固定的数据库表模式(no-schema),支持弹性伸缩;
显而易见,最大的挑战就是如何利用已有的技术来同时满足这五个需求并且能在它们其中达成良好的 trade-off。毕竟大多数情况下我们并不是提出了一种新的架构,而是在多种方案和组件之间进行取舍,正如分布式系统里大名鼎鼎的 CAP 理论,一致性/可用性/分区容忍性你只能选择其中的两个。
下面将首先介绍我们所使用的架构,然后阐述在这样的一个架构下有什么挑战以及我们所采取的相应对的方案。(并不一定是最优解,读者可以带着自己的思考来看待,我们是如何做取舍的)
四、架构和方案
围绕前面提到的 5 个主要需求,我们从架构设计等方面对云开发数据库进行了相应的改造及优化,其架构图如下:
云数据库架构v2
最上面是小程序的接入客户端,中间部分是接入层,底层是数据库的存储层。当然,还有周边的管控、告警、备份、元数据管理等模块。
开发者通过云开发提供的 SDK,可以在微信小程序和 qq 小程序中一键获取云数据库的登录态,然后将数据读写请求发送给接入层。接入层收到用户的读写请求后,由 keeper 和 agent 这两个无状态的模块对接入的读写请求进行相关处理。其中 keeper 主要负责请求的鉴权、认证缓存,以及读写请求数的统计,是云数据库权限校验,负载均衡和计费功能实现的核心模块。agent 模块主要有以下几个功能:1)维护接入层到底层数据库实例的连接池,通过复用已建立的连接来减少请求鉴权和连接创建的耗时;2)统计请求的并发数,对读写请求的 QPS 进行平滑处理,避免短时间的毛刺影响数据库性能和可用性;3)在热迁移切换数据库实例时,将请求挂起,切换后再将请求恢复,来实现热迁移过程对用户的全程无感知。
最后,读写请求通过了接入层,再接下来会到达存储层进行数据库实例的读写。鉴于本文的关注点主要集中在数据库层面,接下来将尝试从四个方面对其进行描述。为了避免本文篇幅过长,部分内容并不会给出细节实现,只是浅析,感兴趣的读者可以留言或者找我们讨论。
4.1 访问控制
权限控制
首先,用户只能访问自己的数据库,无法访问其他用户的数据库,不同用户的数据库之间是相互隔离的,所有连接也必须认证。默认情况下,用户是拥有数据库的读写权限的,也支持在数据库上建立多个不同权限的账户(比如一个只读的账户)。在小程序云开发的场景下,利用微信全链路免鉴权的特性,用户完全不需要太关心认证相关的问题。
访问控制
连接数控制
其次是连接数控制方面,我们会分两层进行控制:1)在接入层进行客户端连接控制,根据初始化时实例类型(免费/付费等)进行不同的初始化限制,如果超过限制则提示相应的用户;2)接入层到存储层也有相应的连接数控制,会池化到后端数据库所有主从节点的链接,避免因过多链接而导致的数据库性能问题。
流量&QPS 控制
最后是机器层面的出入流量控制以及资源使用限制,原理与连接数控制类似,用户所有的请求都会经过接入层,因此可以在接入层控制 QPS 进而实现后续的按量付费功能。QPS 超过阈值后可以提示用户或者在接入层做排队处理。
这里有人可能会质疑了,云开发数据库不是弹性伸缩的嘛?为何还有 qps 的限制呢?不应该是我 qps 越来越高,后端的数据库资源也跟着不断扩容嘛?答:是的,默认的配置下会有一定弹性扩展空间,但是会有一个限制。当然,这里限制具体多少跟产品策略有关。
4.2 数据安全
数据安全是数据库最重要的特性之一,毕竟一个存在数据丢失风险的数据库并不能够在激烈的市场竞争中存活下来。那么云数据库是如何保证数据安全的呢?
数据冗余
要解决数据不丢失的问题,首先就是要避免数据库的单点问题,也就是要有数据冗余。一般而言,工业界认为比较安全的备份数为三份。因此,云数据库默认是三副本分布式存储,即一份数据会存储三份放在不同的机器上,同时机器也要分布在不同的机房中。节点区分主从状态,主节点可以接受读写请求,从节点只能接受读请求。副本集内的存储节点之间采用 raft-like 的副本集协议来实现三副本数据的最终一致性。
高可用
当机器发生故障时副本集内的数据节点会自动切换(FailOver),从节点变为主节点继续提供服务,从而把对业务的影响降到最低。
数据安全
备份回档
云数据库的备份对用户完全透明,后台根据数据库的状态自动选择性地进行全量以及增量备份。详细来说就是用户的写入越快,后台的备份频率也会相应增高,这样做的目的是为了减少回档时需要回放的数据,提升回档性能。
支持 7 天内任意时间回档,可以选择只回档单个表,进一步减少回档所需的时间。
另外,如果节点故障需要新加一个节点到副本集中,可选择从备份文件中进行恢复,从而减少对源集群的侵入性。
基于冷备恢复
多可用区容灾
云数据库默认跨三机房(AZ, Availability Zone)部署,也对用户透明,任意一个机房挂掉也不会对服务产生任何影响;同时也可以支持跨多地域,两地三中心等模式。比如北京、上海、深圳各有一个节点,业务采取就近接入的方式来降低业务访问云数据库的时延。
两地三中心
另外,所有到数据库的连接必须认证以及所有数据均加密压缩存储,这两点保证了数据的链路安全以及存储安全。
4.3 弹性伸缩
弹性伸缩
很多时候,业务的访问模式会呈现很明显的周期性或者不均匀的特征,比如外卖类业务的高峰期出现在用餐时段,其他时段访问较少;游戏类业务的高峰期出现在晚上或周末,上班时间较少;还有一些电商类业务的高峰期出现在特殊时间点(双十一,618 等)。
周期性规律
如果按照传统的数据库运维模式,需要提前预估量级,然后运维执行扩容,等活动结束后再缩容回来(不然成本是个问题)。那么在小程序的场景,既然要做到用户对后端服务无感知,那么资源的扩缩容也应当不被感受到。
基于这个出发点,我们实现了云数据库的弹性伸缩。依赖管控系统的负载监控模块,我们可以根据数据库的实时负载情况动态地调整数据库的资源,并且自动调整敏感度,从而来有效地应对数据库负载突增的情况,在负载低的时候也可以将资源释放提供给其他更需要的实例。其次,为了避免单个大查询引起的频繁调整,我们设置了滑动窗口和“去毛刺”机制,保证了弹性伸缩尽可能平滑地进行。
数据库热迁移
当实例状态发生变更(比如免费-->付费,冷-->热)的时候,可能会需要进行数据迁移,比如从性能较差的机器迁移到性能较好的机器。有了接入层的配合,我们实现了用户无感知的数据库热迁移,可以在不停服的情况下将用户的数据从一个数据库无损迁移到另一个数据库。
热迁移
整体来看,数据库热迁移主要分为三个部分:
- 数据同步(Data Sync)
- 割接(Cutover)
- 状态变更(Status Change)
第一阶段,高性能数据同步的能力完全由底层数据库提供支持,需要先同步全量数据,再同步全量阶段新产生的操作记录(operation log),然后不断循环这个过程,直到源数据库和目标数据库的差距非常小,实现方式非常类似于一个副本集内的主从同步。在这一阶段中,源数据库是可读可写的。第二阶段,也就是当两边数据库的差距非常小时、进行热迁移的割接过程,将源数据库变为不可读不可写,接入层临时 hold 住用户的请求,并不返回错误;数据同步这边,在目标数据库补齐了所有的操作记录并且校验两边数据完全一致之后,进行割接,其中包括用户的拷贝及一系列元数据变更。第三阶段,割接完成后,将目标集群变为可用状态,之前由接入层 block 住的请求可以释放并继续执行。由于割接的过程非常迅速(秒级),这些请求并不会返回类似超时之类的失败。当然这里只考虑了最一般的情况,当某个环节出现异常时需要考虑到相应部分的回滚和应对逻辑,涉及状态机的变化,这相对比较复杂,在此不多赘述。
我们认为数据热迁移要做到用户完全无感知,主要有以下几个难点:
- 强一致性感知集群变更;
- 高性能割接;
- 割接状态持久化以及超时控制;
- 对于用户请求的临时处理;
在我们的架构中,底层数据库提供了高性能数据同步的基本能力;由于有接入层 agent 的存在,可以很方便地实现 agent 之间的强一致性感知以及对于用户请求的临时处理(比如 block 住);引入了分布式 KV 存储系统 etcd 来实现割接状态的持久化和超时控制,如下图所示:
热迁移状态转移图
经过线上环境测试,当数据库梳理维持在 3k 左右的 QPS(100% write)时,热迁移成功,从前端看用户请求在迁移过程中成功率一直维持 100%,也就是做到了热迁移对用户的完全透明。
微信读书每日一答
我们不妨举个例子来说明数据库热迁移的应用。微信读书业务就使用了小程序云开发,微信读书小程序中的“每日一答”模块完全使用云数据库作为底层支撑。“问答 PK”,“每日一答”以及不同类别的题目都分别存储存在不同的表中,峰值(夜间 0 点左右)QPS 接近 10k,自业务上线以来一直平稳运行。有一段时间我们发现共享资源池内的一个用户的访问量有明显增大的趋势,弹性伸缩后仍不能完全满足其需求,为避免其对微信读书实例产生影响,决定将其整个数据库实例通过热迁移的方式移动到我们的热资源池中。由于数据量并不大(10G),在短短几分钟之内就完成了数据的迁移和割接(秒级)工作,整个过程对用户完全透明,当然也没有对微信读书实例产生任何影响。
4.4 智能 DBA
智能 DBA 是一个非常大的话题,涉及各个方面,分层级来看主要包含以下三层:应用层,处理层和采集层。采集层负责从多个数据源采集需要的数据和指标;处理层对采集到的数据进行预处理和分析并产生相应的决策和建议;应用层则会根据不同的应用场景进行不同的处理,比如自动化运维模块需要进行异常处理,而巡检模块只需要进行巡检和告警即可。
智能DBA
自动化运维
为了进一步减少后台侧的运维操作,我们实现了自动化运维平台。通过对运行时的存储节点状态的监控,对每个节点进行探活及故障判断,然后在决策中心根据故障的统计结果进行相应的自动化运维操作(比如磁盘只读了则强制切主)同时也会告警给运维人员,确保自动化运维结果正确。
自动化运维平台
对于一部分自动化运维没办法覆盖的问题,我们有各个层面和维度(机器、实例、节点等)全套的秒级监控,各项指标共计69+项,后端可以实时感知到数据库的状态;发现问题尽早处理。
索引优化
索引是数据库中非常重要的概念,用于加速数据库的查找。在小程序的场景下,我们希望将用户对于后端的数据库的认知降低到越少越好。于是实现了一系列查询优化的功能,比如自动建索引。当我们的接入层和存储层发现用户有很多查询到后端都是全表扫描时,就会根据用户的 query 具体字段进行对应索引的添加工作。等到索引建立完成,用户就可以直接享受优化后的查询结果。重要的是,这一过程用户同样也是无感知的。
我们认为自动建索引要做到用户完全无感知需要考虑以下几个主要问题:
- 同步 VS.异步?建索引过程应该是异步的,否则将会阻塞用户的正常请求。
- 如何控制建索引的风险?主要分为两个方向:1)首先需要控制自动建索引的频率。建索引是 cpu 消耗型任务,如果数据库一直处于建索引的过程中很明显是不可接受的;2)其次需要控制自动建索引的数量,使之在一个相对合理的范围。对于一般的表不会有问题,但是如果数据库中的某个表包含多个字段,且用户会根据这些字段进行不同的查询,那么不加限制的索引会导致索引数量特别多,严重影响用户的更新效率(因为更新时除了会更新数据外还会更新相应的索引)。
- 索引该如何实现动态更新?用户的查询并不是一成不变的,伴随的业务的发展或者变换,对于同一个表的查询很有可能也会发生变化。那么之前自动建立的索引也需要自动删除,否则长期下去将成为数据库的负担和瓶颈。需要根据用户查询条件的变化来动态更新已有的索引。这里关于索引的最左匹配原则也值得考虑进来。
- 如何做到对用户透明?不仅仅是建索引时不影响用户的正常请求,而且需要让用户能直接感受到查询走索引的速度。当表内数据量比较大时,一次索引的变更操作(比如复合索引字段顺序的变化)可能会导致用户的同样一条查询存在显著的耗时差距,除了向用户解释外,我们也可以考虑将索引的变更做得更平滑一些,并提供一系列的查询优化建议。这样可以帮助用户更好地使用数据库。
自动建索引功能上线后,数据库实例的请求平均耗时大幅下降,整个小程序云开发数据库的大盘的平均耗时也减少了50%以上,如下图所示。
自动加索引上线后的效果
诚然,自动建索引还有一定的局限性,比如使用不等于和正则匹配等复杂查询方式时。此时就需要其他方式来处理,比如前台提示用户应该如何优化查询语句;或者根据云数据库提供的慢日志分析工具来分析慢日志并针对性地给出解决方案。
五、总结和展望
小程序云开发可以大大解放小程序开发者的生产力,降低开发的成本和难度。其中,云数据库扮演了举足轻重的角色。针对小程序云开发对云数据库提出的 5 大需求:安全性、易用性、低成本、高性能、灵活性,我们从数据库架构设计等方面做了诸多改造和优化,使得云数据库可以更加贴合小程序的使用场景。
面向未来,在云数据库的管控层我们也将提供更加细粒度的监控(当然,是给我们自己看的,用户无需关注),更智能的 DBA 和更高效的弹性伸缩能力(比如基于 k8s 的云数据库);在云数据库的内核层,我们将封装更多的底层存储引擎能力暴露给 API 层,并深度结合小程序的使用场景来进行定制化开发(比如调研发现很多小程序是答题相关的,那么我们可以提供更优的中文文本索引能力),进一步提升事务的性能等。
我们有理由相信,云开发数据库将在 serverless 理念的指导下不断完善自己,和小程序一起发展得越来越好。
参考资料
- Filebase
- Filebase pricing
- Cloud Filestore
- Introducing Cloud Filestore
- rtdv-vs-filestore
- Parse
- LeanCloud
- Bmob
- 知晓云
- Tencent CloudBase