搭建阿里云 TiDB 的灾备,让我安欣睡个好觉
作者: anxin 原文来源:https://tidb.net/blog/07ee8049
云原生数据库 TiDB 上阿里云了,依托的平台是阿里云计算巢,阿里云计算巢是一个服务管理平台,一方面方便第三方开发商交付服务,另一方面可以更充分的保障用户的信息安全和使用体验,两周前我申请到了试用,对于这次试用活动,PingCAP 的诚意十足,最基础的集群配置也要几百块一天。

初始状态下包含了如下资源:
ControlServer 控制服务器、除了 tiup 外还部署了 Grafana 和 Promethues 服务、一台 2C4GiB 的 ECS。
控制服务器 EIP、为控制服务器提供公网 IP 地址。
SLB 负载均衡、规格为 slb.s3.small 。
TiDB 服务器 、两台 4C8GiB 的 ECS。
PD 服务器、三台 2C4GiB 的 ECS。
TiKV 服务器、三台 8C64GiB 的 ECS,是本地盘类型的实例,挂了一个 1.8TB 的 NVME SSD 本地盘,这样能够提供更强的磁盘 IO 能力。
TiFlash 节点初始环境并没有提供,要通过计算巢的扩容方式生成。
在确定了包括登录密码等配置参数后,集群开始部署。

参数配置和基础使用可以参考阿里云计算巢服务的 TiDB 用户指南:
[https://help.aliyun.com/document_detail/433034.html]
在按照上述文档的指引登录 ControlServer 以后,首先确认了一下联网带宽:

大概 5Mbps,这个带宽用来做管理肯定是够了,传输大量数据的话肯定不成。正常情况下云上的 ECS 要访问 TiDB 数据库要通过 SLB 负载均衡,这个负载的最大连接数可以达到 20 万,以这个性能绝大多数场景下都是足够的。

因为涉及到一些 VPC 之间的安全隔离问题,用户 VPC 下的服务器要访问计算巢中的资源需要经过 privatelink 服务,在计算巢中的叫法是虚拟互联网。

为了更好的配合测试,我在华北二开通了一台测试服务器和一个云桌面,并在华北三地域开通了一台服务器用以测试跨地域连接。云桌面的好处是可以与数据库集群保持 7X24 的稳定本地连接,执行一些压测、备份、恢复等长耗时操作的时候不用耽心网络中断。所有的云桌面、用户侧 ECS 均通过 SLB 访问数据库集群。
测试环境的拓扑:

TiFlash 节点需要通过计算巢控制台的运维菜单下的弹性扩缩容实现,除了弹性扩缩容之外,通过运维功能还可以修改控制服务器的安全组,从而实现直接通过 SSH 客户端登录。

在完成扩容 TiFlash 节点后,终于获得了完全体的 TiDB 集群。

利用 tiup 自带的 bench 工具简单测试了一下,自带的 bench 工具可以测试 OLTP 或者 HTAP 的性能。

以上数据未经优化,仅供参考,另外我本次的测试重点主要是备份容灾架构方面的内容,因为数据库不同于一般应用,承载着的都是用户的身家性命,不容有失,备份和容灾的架构不解决,晚上会睡不着觉的。
先从最简单的本地备份开始,tidb 的备份工具是 br,可以从 tidb 的官网下载社区工具包获得,也可以通过 tiup 直接下载,但我用下来觉得 tiup 的下载下来的 br 有些问题,具体什么问题我会在后面提到,所以建议单独下载社区工具包。
我所使用的 br 版本(6.5)是不支持阿里云 OSS 的,所以要实现本地备份也不是那么容易,需要找一块足够大、足够可靠的临时存储空间,找来找去,我盯上了 TiFlash 节点,基本上 TiFlash 的空间使用是处于比较受控的状态的,因为具体哪张表要放在 TiFlash 上需要手工指定,而计算巢环境下的 TiFlash 给的空间配置和 TiFlash 是完全一样的 1.8TB SSD 本地盘,本地盘的优势是速度快,缺陷是缺乏高可用保护,需要额外想办法,比较极端的做法就是直接搞一个分布式文件系统上去,我觉得对于一个备份来说有些过于夸张了,我决定还是用多个 TiFlash 节点做 NFS 进行轮转使用,并在备份完成后立即上传阿里云 OSS。
考虑到一周 7 天,这样需要 7 个 TiFlash 节点,然后日志的备份再需要一个额外的 Tlash 节点,我就通过计算巢的控制台扩容出了 8 个 TiFlash 节点出来,将这 8 个节点做成 NAS 并 mount 到控制节点、TiKV、TiFlash 节点。

其中 nfs21 我用来进行日志备份,nfs244、nfs245、nfs246、nfs17、nfs18、nfs18 分别用来备份从周一到周日的全备份。
先把日志备份搞定,日志备份使用 tiup br log start 然后指定一个存储位置和 PD 节点就可以创建成功,可以用 tiup br status 查询状态。

日志备份的主要问题不在于备份的创建,而在于备份日志的及时清理,假如不进行清理,很快就会把 1.8TB 的空间撑爆。而我在测试过程中发现 tiup 所带的 br 在清理日志的时候有些问题,一直不能成功的完成清理,而社区工具包中的独立 br 工具则没有这个问题。
写一个日志清理脚本,每天执行一次。这里可以根据数据的变化情况选择保留当天的日志、或者 6 天前的日志,我目前的策略是选择保留两天的日志,这样可以在第二天还能恢复前一天任意时间点的数据。

使用社区工具包中的 br 工具终于可以成功清理日志了。

然后,我们需要从周一到周日每天执行一个全备份,每一个全备份对应不同的 NAS 共享。这是周一的脚本,对应的 NAS 共享是 nfs244。

这里还用到了阿里云 OSS 的命令行工具 ossutil,可以从阿里云 OSS 的产品页面下载,下载后要进行配置,将 OSS AK,endpoint 等相关信息进行存储,这样就不需要每次输入了。在设置的时候务必注意使用 OSS 的内网 endpoint,这样既不需要额外支付流量费用也能充分保证备份和恢复速度。
假如对数据的安全性比较看重,可以在创建 OSS bucket 的时候选择多可用区的同城冗余存储,这样就不怕单个可用区发生故障时无法访问备份数据。

在权限控制上,我使用了 OSS 的 bucket 授权策略,只对单个 bucket 进行完全授权,这样这个子用户就只能访问这个备份用的 bucket 而不能访问其他 bucket。

日志的同步同样需要写一个脚本:

这里用到了 ossutil 的同步功能,每次只同步变化的数据到 OSS,同时删除在本地已经删除的日志,这样可以防止 OSS 上的日志数据无限扩张。
我完整的 crontab 配置是这样的

从周一到周日每天执行一个全备份,然后把全备份传输到 OSS,每天清理一次日志备份,只保留两天的日志备份,每 15 分钟同步一次日志备份到 OSS。
在这个配置下即便当天的全备份无法使用,也可以依靠昨天的全备份和日志进行还原,假如日志备份所在的节点发生故障也能从 OSS 下载到最近的日志。
其实,假如 br 能够支持 OSS,以上这些操作全都不必要,直接在备份的时候指定 OSS 的 URI 就可以。
本来到这一步就是数据恢复的测试了,但我还想增加一点儿难度,利用 OSS 的跨地域复制功能将备份数据复制到异地,然后在异地做恢复测试,这样就直接能获得一套异地灾备架构。

第一步,在灾备地域创建 OSS bucket,这里选择华北三。

在生产中心地域(华北二)配置跨区域复制。

复制的速度非常快。

在灾备中心购买所需的 ECS,配置拓扑文件。测试的过程中我用到了 4 台服务器,其中 3 台 tikv,一台 pd/tidb,实际业务环境建议增加更多的 pd/tidb 节点。

使用 tiup 部署非常简单快捷,这非常有利于灾备环境的快速创建和拉起

为灾备中心的 oss bucket 授权,同样只给一个 bucket 的权限。

ossutil 配置,注意这里要使用灾备地域的 oss 内网 endpoint

同步备份数据,为了加速恢复速度,这里我就不搭 NAS 了,因此需要在每一个 tikv 节点上都保存一份备份数据,这里依然使用了 sync 命令,因为每隔 15 分钟都会有一份新数据,而在日志备份的数据量较大的情况下 15 分钟内可能无法完成复制,连续执行两次 sync 就以最快速度获得最新的数据。

在其中一台 tikv 节点上执行恢复,毕竟 tikv 节点的配置较高,恢复的速度能更快一些。

首先是全备份的恢复,这一部分的速度比较快,然后是日志恢复,速度相对慢一些,期间还报了一个告警,看起来像是 PD 相关的,可能是我的 PD 节点的配置太低吧。另外总的恢复时长要两个小时以上了,这相当于 RTO。

为了能对这套架构的 RPO 有个更直观的感知,我启动一个数据库压测,利用命令行向数据库插入一千万行数据,命令行插入的效率比较低,这一千万行要插入好久,到时候我就可以通过对比生产站点和灾备站点之间的数据行数来感知 RPO 了。

在恢复之前生产站点的数据行数,可以看到数据在不断增加。

在恢复之后灾备站点的数据行数。

引起数据延迟的点有两个,一个是日志备份本身的延迟有大概两三分钟,一个是每隔 15 分钟才同步一次日志到 oss,理论上 RPO 大概能做到 20 分钟,要是 br 能够直接写 oss,RPO 就能做到 5 分钟内。
在现有架构下进一步提高 RPO 的可能性不大,因为同步一次日志至少要几分钟,数据变化的速度越快,日志的同步负担越重,设置成 15 分钟是为了防止同步任务重叠堆积。那就研究提高一下 RTO,我决定再鼓捣一下 cdc 做个数据同步。
在生产环境,利用 7 个 tiflash 节点部署 cdc 节点,实际用不到,下面是拓扑文件:

扩容以后的全家福,挺壮观

再利用 tiflash 节点部署一个备用集群:

在备用集群恢复一份数据出来。

建立一个 changefeed,就实现实时复制了。

建立 changefeed 成功

查询一下状态,确认是 normal

重新向生产库压数据

数据的同步延迟很小(上面的是生产库,下面的是备用库),可能是因为我给的压力也不是很大。

想给一点儿压力,结果在清理数据的时候把复制给玩坏了。

删除 changefeed

重新建立一个,注意这里的 URI 用的是 tidb

发现备用库缺了一段儿数据(上面的是生产库,下面的是备用库)

这种补数据的操作在 cdc 上下游均为 tidb 的情况下不需要重新同步,也不需要停业务,只要用 sync_diff_inspector 进行在线对比生成补数据脚本,就可以利用脚本补齐缺失的数据。
在线对比需要开启三个 cdc 配置,生成一个 cdc 配置文件

要更新配置需要先暂停 changefeed

更新配置

恢复复制

在 cdc 下游(备用数据库),查询两个 tso。

将两个 tso 分别填充到 sync_diff_inspector 配置文件中的上游数据库和下游数据库配置中。

使用配置文件进行对比

对比结果的概要,上游多 8500 行

SQL 脚本

执行脚本

数据已经补齐

现在还有最后一个问题:如何完成主备切换?
集群的业务入口是 slb,slb 下面挂了两个 tidb 节点,切换其实就是切 tidb,将这两个节点从生产数据库移除,再挂载到备用数据库即可完成切换。
先给生产库加一个额外的 tidb 节点

扩容生产库

生产库的 tidb 节点

缩容 ,将挂在 slb 下的两个 tidb 节点移除

扩容配置 ,将 slb 下挂的节点 作为 tidb 节点重新挂载到备用集群

扩容备用集群

备用集群各节点

登录备用数据库,压测还在继续。

RTO 在分钟级,RPO 约等于 0。
我终于可以安心睡一个好觉了。最后,标题不是笔误,因为我真的叫安欣。
版权声明: 本文为 InfoQ 作者【TiDB 社区干货传送门】的原创文章。
原文链接:【http://xie.infoq.cn/article/fe93fd76e4c950f54ba0878a0】。文章转载请联系作者。
评论