Categories
程式開發

数据库如何弹性伸缩?


1.写在前面

在之前文章“中,探讨过到底为什么要云原生,以及云原生的核心应该是【弹性伸缩】和【按需计费】。并且简单描述了下应用层做到【弹性伸缩】所需要解决的问题。但是在上篇文章中,有同学在评论区留言,没有谈数据层怎么【弹性伸缩】

所以本文主要是继续探讨一下,弹性伸缩如何应用在数据库上。以及目前业界一些案例。

2.弹性伸缩前提

要实现【弹性伸缩】的前提是什么?

答:通过一些监控指标,自动化快速拉起一个服务,无需人工介入

而要做到这点,业界提出一个概念:服务无状态

既然有无状态,那么就会存在有状态,两者的区别是什么呢?

无状态:如果发布一台新机器无需任何操作,直接打包代码,编译,部署即可完成,那么就是无状态

有状态:需要配置上游IP,白名单,本地磁盘存储了数据等

可以看出如果服务是有状态的,是无法做到自动化快速拉起一个服务的,最少得人为配置IP,白名单,或者迁移数据。可以很明显的看出数据库MySQL天然是有状态服务。因为本地磁盘存储数据。

无状态服务天然适应【弹性伸缩】,完全可以自动化快速拉起一个新的无状态服务进程。

而以数据库为例的有状态服务,如果要【弹性伸缩】,那么动态拉起一个Slave实例,必须等待漫长的主从同步,才可以生效。此时不符合我们的应用场景。

3.数据库的弹性问题的本质

我们先回到问题的本质,【弹性伸缩】解决的问题是什么,其主要解决的是资源利用率的问题。

对于应用层,资源利用率可以认为是CPU核数,内存数,机器数等,应用层的弹性伸缩通过动态的扩容机器(可以扩容高配置机器,也可以扩容低配置机器)完成对这些资源的调整。

对于数据库层,资源利用率可以任务是CPU核数,内存数,机器数,硬盘容量等,概括来说可以分为两类,计算资源(CPU+内存)+存储资源(硬盘容量)。

思考一下,我们遇见什么场景需要扩容MySQL实例?

数据库如何弹性伸缩? 1

我们简单分析一下,遇见下述两种情况会扩容MySQL

流量过大,增加MySQL实例,承担更多的流量压力【计算资源】磁盘空间不足,单表过大,增加MySQL实例,分库分表【存储资源】

综上,我们可以得出问题本质:数据库需要伸缩的是计算资源和存储资源

想明白了问题的本质,所以我们可以针对不同的场景,得出不同的解决方案。

4.实现逻辑

计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。

当下,大多数公司的数据库架构都如下图所示:

数据库如何弹性伸缩? 2

通过增加从库增强读能力,提升可用性(主从灾备),通过分库分表扩展写能力和数据规模。

根据上述分析,我们只在两个场景对MySQL进行扩容,流量过大,磁盘容量不足;这两者又可以概括为计算和存储两个不同的纬度。

此时,我们可以直接想到,将其分层,划分为两个不同的纬度,从而分别解决其存在的问题。

所以,我们可以将数据库划分为【计算层】+【存储层】

数据库如何弹性伸缩? 3

下面,我将从上至下,简单的解释下两个分层的作用

计算层

首先,我们思考一个问题,我们搭建MySQL集群一主多从是为什么?

答:灾备+提升读能力

我们把关注点锁定在读能力上,我们是通过增加机器数量,让更多的机器可以同时的接受应用层请求过来的读接口。因为一台机器的计算资源(CPU,内存,线程)是有限的,除非更换更好的配置,否则只能水平增加机器数来增强。

所以我们可以在计算层,抽象出Writer节点+Reader节点两个不同的概念

Writer节点:处理写SQL请求Reader节点:处理读SQL请求

此时,我们可以通过动态的扩展节点数量,增强整个集群的读写能力。

要做到Writer节点+Reader节点,和现在的MySQL读写分离其实差不多。

区别是Writer节点+Reader节点只关注SQL执行,MySQL不仅仅关注SQL执行,还关注数据存储

存储层

首先,我们先抛弃一切MySQL的知识,别让其干扰你的思维。

思考一个问题,平时软件项目开发过程中,我们最关心数据的什么?

答:数据存储,数据查询,数据可靠

再思考一个问题,如果你电脑硬盘不够,你将会做什么操作缓解一下

答:加一块硬盘

那么,对应在存储层,应该要做到以下三点:

如果数据量快占满整个集群的容量了,那么存储层得必须做到动态的增加“硬盘”的能力,提供读写接口,以供上层(计算层)调用。当一块“硬盘”损坏后,必须做到还能保证数据不丢失的能力,那么必须做到同一份数据,多保存几份在不同的“硬盘”上。

如何才能做到上述三点呢?

第一:抽象出Region节点的概念(可以类比Redis,Kafka,ElasticSearch当中的分片的概念),将数据水平切分在一个一个的Region(分片)上。当需要扩展容量的时候,动态增加Region即可。

第二:无论是Redis还是MySQL,它们存储数据时候都给数据提供唯一标示,Redis当中的Key,MySQL当中的主键,查询过程中,无论是直接还是间接都是通过主键去检索到指定数据的。所以Region在保存数据的时候,也得为数据添加上Key或者主键,这种唯一标示的概念,才能做到对外提供读写接口

第三:分布式的基础操作,冗灾备份

综上:如下图所示

数据库如何弹性伸缩? 4

其实换个角度来看,MySQL可以分为逻辑层和引擎层

在这里可以把计算层看作逻辑层,存储层看作引擎层

到这里,我们通过划分计算层,存储层,即完成了可以动态完成数据库的【弹性伸缩】

当流量过大,计算层的Reader节点和Writer节点已经是无状态服务,天然适合【弹性伸缩】当容量不足,存储层完全可以通过新增Region(分片)的方式,增大容量

5.存在的问题

看到这里,你或许已经发现了一个特别明显的的问题,那就是查询操作是通过RPC操作来完成,一次范围查询岂不是得许多次RPC操作才可以完成,网络传输的耗时都难以接受!

数据库如何弹性伸缩? 5

举个例子,有下面一条SQL

Select * from user where id > 5 and id < 10

如果计算层,每次都调用存储层next接口获取下一行元组,提取出来,然后判断是否符合,符合加入结果集,不符合丢弃,然后继续调用next接口。

假设User表中有100条元组,那么将发生100次RPC调用。

有什么办法可以优化吗?思考下ElasticSearch是怎么做检索的呢?

ElasticSearch是将检索请求下发到所有的分片上,让每个分片执行检索操作,返回符合逻辑的值,然后统一起来再进行处理后,返回给Client。

同理,我们可以照葫芦画瓢。

数据库如何弹性伸缩? 6

我们将过滤逻辑下沉到存储层,这样返回你的数量是减小了很多,这样就能减小无意义的网络传输,而且减少了许多无谓的RPC调用。

但是:无论怎么优化,SQL性能比MySQL是降低了的

降低不意味着无法使用,针对不同的业务场景,其实部分的性能损耗是可以接受的

6.写在最后

将MySQL分解成上述模式,其实还存在很多的问题,例如,MySQL中的事务,MVCC,Join查询等等,这些都是需要解决的问题,但是这些问题总是有许多的方案解决的。

业界对于云数据库的【弹性伸缩】实现方案,大体上是一致的,落在细节上的实现却大相径庭。例如阿里的POLARDB,腾讯的CynosDB,PingCAP的TiDB等,有兴趣大家可以了解更多细节。

本文主要是带领大家快速了解数据库这种有状态服务如何实现【弹性伸缩】