小程序开发的架构和方案

从架构设计等方面对云开发数据库进行了相应的改造及优化,其架构图如下:

最上面是小程序的接入客户端,中间部分是接入层,底层是数据库的存储层。当然,还有周边的管控、告警、备份、元数据管理等模块。

开发者通过云开发提供的 SDK,可以在微信小程序和 qq 小程序中一键获取云数据库的登录态,然后将数据读写请求发送给接入层。接入层收到用户的读写请求后,由 keeper 和 agent 这两个无状态的模块对接入的读写请求进行相关处理。其中 keeper 主要负责请求的鉴权、认证缓存,以及读写请求数的统计,是云数据库权限校验,负载均衡和计费功能实现的核心模块。agent 模块主要有以下几个功能:1)维护接入层到底层数据库实例的连接池,通过复用已建立的连接来减少请求鉴权和连接创建的耗时;2)统计请求的并发数,对读写请求的 QPS 进行平滑处理,避免短时间的毛刺影响数据库性能和可用性;3)在热迁移切换数据库实例时,将请求挂起,切换后再将请求恢复,来实现热迁移过程对用户的全程无感知。

最后,读写请求通过了接入层,再接下来会到达存储层进行数据库实例的读写。鉴于本文的关注点主要集中在数据库层面,接下来将尝试从四个方面对其进行描述。为了避免本文篇幅过长,部分内容并不会给出细节实现,只是浅析,感兴趣的读者可以留言或者找我们讨论。

1 访问控制

权限控制

首先,用户只能访问自己的数据库,无法访问其他用户的数据库,不同用户的数据库之间是相互隔离的,所有连接也必须认证。默认情况下,用户是拥有数据库的读写权限的,也支持在数据库上建立多个不同权限的账户(比如一个只读的账户)。在小程序云开发的场景下,利用微信全链路免鉴权的特性,用户完全不需要太关心认证相关的问题。

连接数控制

其次是连接数控制方面,我们会分两层进行控制: 1)在接入层进行客户端连接控制,根据初始化时实例类型(免费/付费等)进行不同的初始化限制,如果超过限制则提示相应的用户; 2)接入层到存储层也有相应的连接数控制,会池化到后端数据库所有主从节点的链接,避免因过多链接而导致的数据库性能问题。

流量&QPS 控制

最后是机器层面的出入流量控制以及资源使用限制,原理与连接数控制类似,用户所有的请求都会经过接入层,因此可以在接入层控制 QPS 进而实现后续的按量付费功能。QPS 超过阈值后可以提示用户或者在接入层做排队处理。

这里有人可能会质疑了,云开发数据库不是弹性伸缩的嘛?为何还有 qps 的限制呢?不应该是我 qps 越来越高,后端的数据库资源也跟着不断扩容嘛?
答:是的,默认的配置下会有一定弹性扩展空间,但是会有一个限制。当然,这里限制具体多少跟产品策略有关。

2 数据安全

数据安全是数据库最重要的特性之一,毕竟一个存在数据丢失风险的数据库并不能够在激烈的市场竞争中存活下来。那么云数据库是如何保证数据安全的呢?

数据冗余

要解决数据不丢失的问题,首先就是要避免数据库的单点问题,也就是要有数据冗余。一般而言,工业界认为比较安全的备份数为三份。因此,云数据库默认是三副本分布式存储,即一份数据会存储三份放在不同的机器上,同时机器也要分布在不同的机房中。节点区分主从状态,主节点可以接受读写请求,从节点只能接受读请求。副本集内的存储节点之间采用 raft-like 的副本集协议来实现三副本数据的最终一致性。

高可用

当机器发生故障时副本集内的数据节点会自动切换(FailOver),从节点变为主节点继续提供服务,从而把对业务的影响降到最低。

备份回档

云数据库的备份对用户完全透明,后台根据数据库的状态自动选择性地进行全量以及增量备份。详细来说就是用户的写入越快,后台的备份频率也会相应增高,这样做的目的是为了减少回档时需要回放的数据,提升回档性能。

支持 7 天内任意时间回档,可以选择只回档单个表,进一步减少回档所需的时间。

另外,如果节点故障需要新加一个节点到副本集中,可选择从备份文件中进行恢复,从而减少对源集群的侵入性。

多可用区容灾

云数据库默认跨三机房(AZ, Availability Zone)部署,也对用户透明,任意一个机房挂掉也不会对服务产生任何影响;同时也可以支持跨多地域,两地三中心等模式。比如北京、上海、深圳各有一个节点,业务采取就近接入的方式来降低业务访问云数据库的时延。

另外,所有到数据库的连接必须认证以及所有数据均加密压缩存储,这两点保证了数据的链路安全以及存储安全。

3 弹性伸缩

弹性伸缩

很多时候,业务的访问模式会呈现很明显的周期性或者不均匀的特征,比如外卖类业务的高峰期出现在用餐时段,其他时段访问较少;游戏类业务的高峰期出现在晚上或周末,上班时间较少;还有一些电商类业务的高峰期出现在特殊时间点(双十一,618 等)。

如果按照传统的数据库运维模式,需要提前预估量级,然后运维执行扩容,等活动结束后再缩容回来(不然成本是个问题)。那么在小程序的场景,既然要做到用户对后端服务无感知,那么资源的扩缩容也应当不被感受到。

基于这个出发点,我们实现了云数据库的弹性伸缩。依赖管控系统的负载监控模块,我们可以根据数据库的实时负载情况动态地调整数据库的资源,并且自动调整敏感度,从而来有效地应对数据库负载突增的情况,在负载低的时候也可以将资源释放提供给其他更需要的实例。其次,为了避免单个大查询引起的频繁调整,我们设置了滑动窗口和“去毛刺”机制,保证了弹性伸缩尽可能平滑地进行。

数据库热迁移

当实例状态发生变更(比如免费-->付费,冷-->热)的时候,可能会需要进行数据迁移,比如从性能较差的机器迁移到性能较好的机器。有了接入层的配合,我们实现了用户无感知的数据库热迁移,可以在不停服的情况下将用户的数据从一个数据库无损迁移到另一个数据库。

整体来看,数据库热迁移主要分为三个部分:

  1. 数据同步(Data Sync
  2. 割接(Cutover
  3. 状态变更(Status Change

第一阶段,高性能数据同步的能力完全由底层数据库提供支持,需要先同步全量数据,再同步全量阶段新产生的操作记录(operation log),然后不断循环这个过程,直到源数据库和目标数据库的差距非常小,实现方式非常类似于一个副本集内的主从同步。在这一阶段中,源数据库是可读可写的。第二阶段,也就是当两边数据库的差距非常小时、进行热迁移的割接过程,将源数据库变为不可读不可写,接入层临时 hold 住用户的请求,并不返回错误;数据同步这边,在目标数据库补齐了所有的操作记录并且校验两边数据完全一致之后,进行割接,其中包括用户的拷贝及一系列元数据变更。第三阶段,割接完成后,将目标集群变为可用状态,之前由接入层 block 住的请求可以释放并继续执行。由于割接的过程非常迅速(秒级),这些请求并不会返回类似超时之类的失败。当然这里只考虑了最一般的情况,当某个环节出现异常时需要考虑到相应部分的回滚和应对逻辑,涉及状态机的变化,这相对比较复杂,在此不多赘述。

我们认为数据热迁移要做到用户完全无感知,主要有以下几个难点:

  • 强一致性感知集群变更;
  • 高性能割接;
  • 割接状态持久化以及超时控制;
  • 对于用户请求的临时处理;

在我们的架构中,底层数据库提供了高性能数据同步的基本能力;由于有接入层 agent 的存在,可以很方便地实现 agent 之间的强一致性感知以及对于用户请求的临时处理(比如 block 住);引入了分布式 KV 存储系统 etcd 来实现割接状态的持久化和超时控制,如下图所示:

经过线上环境测试,当数据库梳理维持在 3k 左右的 QPS(100% write)时,热迁移成功,从前端看用户请求在迁移过程中成功率一直维持 100%,也就是做到了热迁移对用户的完全透明。

我们不妨举个例子来说明数据库热迁移的应用。微信读书业务就使用了小程序云开发,微信读书小程序中的“每日一答”模块完全使用云数据库作为底层支撑。“问答 PK”,“每日一答”以及不同类别的题目都分别存储存在不同的表中,峰值(夜间 0 点左右)QPS 接近 10k,自业务上线以来一直平稳运行。有一段时间我们发现共享资源池内的一个用户的访问量有明显增大的趋势,弹性伸缩后仍不能完全满足其需求,为避免其对微信读书实例产生影响,决定将其整个数据库实例通过热迁移的方式移动到我们的热资源池中。由于数据量并不大(10G),在短短几分钟之内就完成了数据的迁移和割接(秒级)工作,整个过程对用户完全透明,当然也没有对微信读书实例产生任何影响。

4 智能 DBA

智能 DBA 是一个非常大的话题,涉及各个方面,分层级来看主要包含以下三层:应用层,处理层和采集层。采集层负责从多个数据源采集需要的数据和指标;处理层对采集到的数据进行预处理和分析并产生相应的决策和建议;应用层则会根据不同的应用场景进行不同的处理,比如自动化运维模块需要进行异常处理,而巡检模块只需要进行巡检和告警即可。

自动化运维

为了进一步减少后台侧的运维操作,我们实现了自动化运维平台。通过对运行时的存储节点状态的监控,对每个节点进行探活及故障判断,然后在决策中心根据故障的统计结果进行相应的自动化运维操作(比如磁盘只读了则强制切主)同时也会告警给运维人员,确保自动化运维结果正确。

对于一部分自动化运维没办法覆盖的问题,我们有各个层面和维度(机器、实例、节点等)全套的秒级监控,各项指标共计**69+**项,后端可以实时感知到数据库的状态;发现问题尽早处理。

索引优化

展开阅读全文

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java