云数据库供应商主要分为三类。
① 传统的数据库厂商,如 Teradata、Oracle、IBM DB2 和 Microsoft SQL Server 等。
② 涉足数据库市场的云供应商,如 Amazon、Google、Yahoo!、阿里、百度、腾讯等。
③ 新兴厂商,如 Vertica、LongJump 和 EnterpriseDB 等。
市场上常见的云数据库产品见表 6-3。
Amazon 是云数据库市场的先行者。Amazon 除了提供著名的 S3 存储服务和 EC2 计算服务以外,还提供基于云的数据库服务 SimpleDB 和 Dynamo。
SimpleDB 是 Amazon 公司开发的一个可供查询的分布式数据存储系统,AWS(Amazon Web Service)上的第一个 NoSQL 数据库服务,集合了 Amazon 的大量 AWS 基础设施。顾名思义,SimpleDB 的目的是作为一个简单的数据库来使用,它的存储元素(属性和值)是由一个 id 字段来确定行的位置。这种结构可以满足用户基本的读、写和查询功能。SimpleDB 提供易用的 API来快速地存储和访问数据。但是,SimpleDB 不是一个关系型数据库,传统的关系型数据库采用行存储,而 SimpleDB 采用了“键/值”存储,它主要是服务于那些不需要关系数据库的 Web 开发者。但是,SimpleDB 存在一些明显缺陷,如存在单表限制、性能不稳定、只能支持最终一致性等。
Dynamo 吸收了 SimpleDB 以及其他 NoSQL 数据库设计思想的精华,旨在为要求更高的应用设计,这些应用要求可扩展的数据存储以及更高级的数据管理功能。Dynamo 采用“键/值”存储,其所存储的数据是非结构化数据,不识别任何结构化数据,需要用户自己完成对值的解析。Dynamo系统中的键(key)不是以字符串的方式进行存储的,而是采用 md5_key(通过 md5 算法转换后得到)的方式进行存储,因此它只能根据 key 去访问,不支持查询。DynamoDB 使用固态硬盘,实现恒定、低延迟的读写时间,旨在扩展大容量同时维持一致的性能,虽然这种性能伴随着更为严格的查询模型。
Amazon RDS(Amazon Relational Database Service)是 Amazon 开发的一种 Web 服务,它可以让用户在云环境中建立、操作关系型数据库(可以支持 MySQL 和 Oracle 等数据库)。用户需要关注应用和业务层面的内容,而不需要在繁琐的数据库管理工作上耗费过多的时间。
此外,Amazon 和其他数据库厂商开展了很好的合作,Amazon EC2 应用托管服务已经可以部署很多种数据库产品,包括 SQL Server、Oracle 11g、MySQL 和 IBM DB2 等主流数据库平台,以及其他一些数据库产品,比如 EnerpriseDB。作为一种可扩展的托管环境,开发者可以在 EC2 环境中开发并托管自己的数据库应用。
Google Cloud SQL 是谷歌公司推出的基于 MySQL 的云数据库,使用 Cloud SQL 的好处显而易见,所有的事务都在云中,并由谷歌管理,用户不需要配置或者排查错误,仅仅依靠它来开展工作即可。由于数据在谷歌多个数据中心中复制,因此它永远是可用的。谷歌还将提供导入或导出服务,方便用户将数据库带进或带出云。谷歌使用用户非常熟悉的 MySQL,带有 JDBC 支持(适用于基于 Java 的 App Engine 应用)和 DB-API 支持(适用于基于 Python 的 App Engine 应用)的传统 MySQL 数据库环境,因此多数应用程序不需过多调试即可运行,数据格式对于大多数开发者和管理员来说也是非常熟悉的。Google Cloud SQL 还有一个好处就是与 Google App Engine集成。
2008 年 3 月,微软通过 SQL Data Service(SDS)提供 SQL Server 的关系数据库功能,这使得微软成为云数据库市场上的第一个大型数据库厂商。此后,微软对 SDS 功能进行了扩充,并且重新命名为 SQL Azure。微软的 Azure 平台提供了一个 Web 服务集合,可以允许用户通过网络在云中创建、查询和使用 SQL Server 数据库,云中的 SQL Server 服务器的位置对于用户而言是透明的。对于云计算而言,这是一个重要的里程碑。SQL Azure 具有以下特性。
SQL Azure 的体系架构中包含了一个虚拟机簇,可以根据工作负载的变化,动态增加或减少虚拟机的数量,如图 6-2 所示。每台虚拟机 SQL Server VM(Virtual Machine)安装了 SQL Server2008数据库管理系统,以关系模型存储数据。通常,一个数据库会被分散存储到 3~5 台 SQL Server VM 中。每台 SQL Server VM 同时安装了 SQL Azure Fabric 和 SQL Azure 管理服务,后者负责数据库的数据复写工作,以保障 SQL Azure 的基本高可用性要求。不同 SQL Server VM 内的 SQL Azure Fabric 和管理服务之间会彼此交换监控信息,以保证整体服务的可监控性。
Yahoo! PNUTS 是一个为网页应用开发、大规模并行、地理分布式的数据库系统,它是 Yahoo!云计算平台重要的一部分。Vertica Systems在2008年发布了云数据库。10Gen公司的Mongo、AppJet公司的 AppJet 数据库也都提供了相应的云数据库版本。IBM 投资的 EnerpriseDB 也提供了一个运行在 Amazon EC2 上的云数据库。LongJump 是一个与 Salesforce 竞争的新公司,它推出了基于开源数据库 PostgreSQL 的云数据库产品。Intuit QuickBase 也提供了自己的云数据库系列。麻省理工学院研制的 Relational Cloud 可以自动区分负载的类型,并把类型近似的负载分配到同一个数据节点上,而且采用了基于图的数据分区策略,对于复杂的事务型负载也具有很好的可扩展性,此外,它还支持在加密的数据上运行 SQL 查询。阿里云 RDS 是阿里云提供的关系型数据库服务,将直接运行于物理服务器上的数据库实例租给用户。百度云数据库可以支持分布式的关系型数据库服务(基于 MySQL)、分布式非关系型数据库存储服务(基于 MongoDB)、键/值型非关系型数据库服务(基于 Redis)。
不同的云数据库产品采用的系统架构存在很大差异,下面以阿里集团核心系统数据库团队开发的 UMP(Unified MySQL Platform)系统为例进行介绍。
UMP 系统是低成本和高性能的 MySQL 云数据库方案,关键模块采用 Erlang 语言实现。开发者通过网络从平台上申请 MySQL 实例资源,由平台提供的单一入口来访问数据。UMP系统把各种服务器资源划分为资源池,并以资源池为单位把资源分配给 MySQL 实例。系统中包含了一系列组件,这些组件协同工作,以对用户透明的形式提供主从热备、数据备份、迁移、容灾、读写分离、分库分表等一系列服务。系统内部划分为 3 种规格的用户,分别是数据量和流量比较小的用户、中等规模用户以及需要分库分表的用户。多个小规模用户可以共享同一个 MySQL 实例,中等规模用户独占一个 MySQL 实例,需要分库分表的用户的多个MySQL 实例共享同一个物理机,通过这些方式实现了资源的虚拟化,降低了整体成本。UMP通过“用 Cgroup 限制 MySQL 进程资源”和“在 Proxy 服务器端限制 QPS(Query Per Second)”两种方式,实现了资源隔离、按需分配以及限制 CPU、内存和 IO 资源;同时,还支持在不影响提供数据服务的前提下根据用户业务的发展进行动态扩容和缩容。系统还综合运用了 SSL数据库连接、数据访问 IP 白名单、记录用户操作日志、SQL 拦截等技术,来有效保护用户的数据安全。
总的来说,UMP 系统架构设计遵循了以下原则。
UMP 系统架构如图 6-3 所示,UMP 系统中的角色包括 Controller 服务器、Proxy 服务器、Agent服务器、Web 控制台、日志分析服务器、信息统计服务器、愚公系统;依赖的开源组件包括Mnesia、LVS、RabbitMQ 和 Zookeeper。
Mnesia 是一个分布式数据库管理系统,适合于电信及其他需要持续运行和具备软实时特性的Erlang 应用,是构建电信应用的控制系统平台——开放式电信平台(Open Telecom Platform OTP)的一部分。Erlang 是一个结构化、动态类型编程语言,内建并行计算支持,非常适合于构建分布式、软实时并行计算系统。使用 Erlang 编写出的应用,在运行时通常由成千上万个轻量级进程组成,并通过消息传递相互通信,Erlang 的进程间上下文切换要比 C 程序高效得多。Mnesia 与 Erlang编程语言是紧耦合的,其最大的好处是在操作数据时,不会发生由于数据库与编程语言所用的数据格式不同而带来阻抗失配问题。Mnesia 支持事务,支持透明的数据分片,利用两阶段锁实现分布式事务,可以线性扩展到至少 50 个节点。Mnesia 的数据库模式(schema)可在运行时动态重配置,表能被迁移或复制到多个节点来改进容错性。Mnesia 的这些特性,使其在开发云数据库时被用来提供分布式数据库服务。
RabbitMQ 是一个用 Erlang 开发的工业级的消息队列产品(功能类似于 IBM 公司的消息队列产品 IBM WEBSPHERE MQ),作为消息传输中间件来使用,可以实现可靠的消息传送。UMP 集群中各个节点之间的通信,不需要建立专门的连接,都是通过读写队列消息来实现的。
Zookeeper 是高效和可靠的协同工作系统,提供分布式锁之类的基本服务(如统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等),用于构建分布式应用,减轻分布式应用程序所承担的协调任务(关于 Zookeeper 的工作原理可以参考相关书籍或网络资料)。在 UMP 系统中,Zookeeper 主要发挥 3 个作用。
(1)作为全局的配置服务器。UMP 系统需要多台服务器运行,它们运行的应用系统的某些配置项是相同的,如果要修改这些相同的配置项,就必须同时到多个服务器上去修改,这样做不仅麻烦,而且容易出错。因此,UMP 系统把这类配置信息完全交给 Zookeeper 来管理,把配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的服务器对这个目录节点设置监听,也就是监控配置信息的状态,一旦配置信息发生变化,每台服务器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息。
(2)提供分布式锁。UMP 集群中部署了多个 Controller 服务器,为了保证系统的正确运行,对于有些操作,在某一时刻,只能由一个服务器去执行,而不能同时执行。例如,一个 MySQL实例发生故障后,需要进行主备切换,由另一个正常的服务器来代替当前发生故障的服务器,如果这个时候所有的 Controller 服务器都去跟踪处理并且发起主备切换流程,那么,整个系统就会进入混乱状态。因此,在同一时间,必须从集群的多个 Controller 服务器中选举出一个“总管”,由这个“总管”负责发起各种系统任务。Zookeeper 的分布式锁功能能够帮助选出一个“总管”,让这个“总管”来管理集群。
(3)监控所有 MySQL 实例。集群中运行 MySQL 实例的服务器发生故障时,必须及时被监听到,然后使用其他正常服务器来替代故障服务器。UMP 系统借助于 Zookeeper 实现对所有MySQL 实例的监控。每个 MySQL 实例在启动时都会在 Zookeeper 上创建一个临时类型的目录节点,当某个 MySQL 实例挂掉时,这个临时类型的目录节点也随之被删除,后台监听进程可以捕获到这种变化,从而知道这个 MySQL 实例不再可用。
LVS(Linux Virtual Server)即 Linux 虚拟服务器,是一个虚拟的服务器集群系统。LVS 集群采用 IP 负载均衡技术和基于内容请求分发技术。调度器是 LVS 集群系统的唯一入口点,调度器具有很好的吞吐率,将请求均衡地转移到不同的服务器上执行,且调度器自动屏蔽掉服务器的故障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器。整个服务器集群的结构对客户是透明的,而且无需修改客户端和服务器端的程序。UMP 系统借助于 LVS 来实现集群内部的负载均衡。
Controller 服务器向 UMP 集群提供各种管理服务,实现集群成员管理、元数据存储、MySQL实例管理、故障恢复、备份、迁移、扩容等功能。Controller 服务器上运行了一组 Mnesia 分布式数据库服务,其中存储了各种系统元数据,主要包括集群成员、用户的配置和状态信息,以及用户名到后端 MySQL 实例地址的映射关系(或称为“路由表”)等。当其他服务器组件需要获取用户数据时,可以向 Controller 服务器发送请求获取数据。为了避免单点故障,保证系统的高可用性,UMP 系统中部署了多台 Controller 服务器,然后由 Zookeeper 的分布式锁功能来帮助选出一个“总管”,负责各种系统任务的调度和监控。
Web 控制台向用户提供系统管理界面。
Proxy 服务器向用户提供访问 MySQL 数据库的服务,它完全实现了 MySQL 协议,用户可以使用已有的 MySQL 客户端连接到 Proxy 服务器,Proxy 服务器通过用户名获取到用户的认证信息、资源配额的限制[如 QPS、IOPS(I/O Per Second)、最大连接数等],以及后台 MySQL 实例的地址,然后用户的 SQL 查询请求会被转发到相应的 MySQL 实例上。除了数据路由的基本功能外,Proxy 服务器中还实现了很多重要的功能,主要包括屏蔽 MySQL 实例故障、读写分离、分库分表、资源隔离、记录用户访问日志等。
Agent 服务器部署在运行 MySQL 进程的机器上,用来管理每台物理机上的 MySQL 实例,执行主从切换、创建、删除、备份、迁移等操作,同时还负责收集和分析 MySQL 进程的统计信息、慢查询日志(Slow Query Log)和 bin-log。
日志分析服务器存储和分析 Proxy 服务器传入的用户访问日志,并支持实时查询一段时间内的慢日志和统计报表。
信息统计服务器定期将采集到的用户的连接数、QPS 数值以及 MySQL 实例的进程状态用RRDtool 进行统计,可以在 Web 界面上可视化展示统计结果,也可以把统计结果作为今后实现弹性的资源分配和自动化的 MySQL 实例迁移的依据。
愚公系统是一个全量复制结合 bin-log 分析进行增量复制的工具,可以实现在不停机的情况下动态扩容、缩容和迁移。
UMP 系统是构建在一个大的集群之上的,通过多个组件的协同作业,整个系统实现了对用户透明的容灾、读写分离、分库分表、资源管理、资源调度、资源隔离和数据安全功能。
云数据库必须向用户提供一直可用的数据库连接,当 MySQL 实例发生故障时,系统必须自动执行故障恢复,所有故障处理过程对于用户而言是透明的,用户不会感知到后台发生的一切。
为了实现容灾,UMP 系统会为每个用户创建两个 MySQL 实例,一个是主库,一个是从库,而且这两个 MySQL 实例之间互相把对方设置为备份机,任意一个 MySQL 实例上面发生的更新都会复制到对方。同时,Proxy 服务器可以保证只向主库写入数据。
主库和从库的状态是由 Zookeeper 负责维护的,Zookeeper 可以实时监听各个 MySQL实例的状态,一旦主库宕机,Zookeeper 可以立即感知到,并通知给 Controller 服务器。Controller 服务器会启动主从切换操作,在路由表中修改用户名到后端 MySQL 实例地址的映射关系,并把主库标记为不可用,同时,借助于消息队列中间件 RabbitMQ 通知所有 Proxy服务器修改用户名到后端 MySQL 实例地址的映射关系。通过这一系列操作后,主从切换完成,用户名就会被赋予一个新的可以正常使用的 MySQL 实例,而这一切对于用户自己而言是完全透明的。
宕机后的主库在进行恢复处理后需要再次上线。在主库宕机和故障恢复期间,从库可能已经发生过多次更新。因此,在主库恢复时,会把从库中的这些更新都复制给自己,当主库的数据库状态快要达到和从库一致的状态时,Controller 服务器就会命令从库停止更新,进入不可写状态,禁止用户写入数据,这个时候用户可能感受到短时间的不可写。等到主库更新到和从库完全一致的状态时,Controller 服务器就会发起主从切换操作,并在路由表中把主库标记为可用状态,然后通知 Proxy 服务器把写操作切回主库上,用户写操作可以继续执行,之后再把从库修改为可写状态。
由于每个用户都有两个 MySQL 实例,即主库和从库,因此可以充分利用主从库实现用户读写操作的分离,实现负载均衡。UMP 系统实现了对于用户透明的读写分离功能,当整个功能被开启时,负责向用户提供访问 MySQL 数据库服务的 Proxy 服务器,就会对用户发起的 SQL 语句进行解析,如果属于写操作,就直接发送到主库,如果是读操作,就会被均衡地发送到主库和从库上执行。但是,有可能发生一种情况,那就是,用户刚刚写入数据到主库,数据还没有被复制到从库之前,用户就去从库读这个数据,导致用户要么读不到数据,要么读到数据的旧版本。为了避免这种情况的发生,UMP 系统在每次用户写操作发生后都会开启一个计时器,如果用户在计时器开启的 300ms 内读数据,不管是读刚写入的这些数据还是其他数据,都会被强行分发到主库上去执行读操作。当然,在实际应用中,UMP 系统允许修改 300ms 这个设定值,但是一般而言,300ms 已经可以保证数据在写入主库后被复制到从库中。
UMP 支持对用户透明的分库分表(Shard / Horizontal Partition),但是用户在创建账号的时候需要指定类型为多实例,并且设置实例的个数,系统会根据用户设置来创建多组 MySQL 实例。除此以外,用户还需要自己设定分库分表规则,如需要确定分区字段,也就是根据哪个字段进行分库分表,还要确定分区字段里的值如何映射到不同的 MySQL 实例上。
当采用分库分表时,系统处理用户查询的过程如下:首先,Proxy 服务器解析用户 SQL 语句,提取出重写和分发 SQL 语句所需要的信息;其次,对 SQL 语句进行重写,得到多个针对相应MySQL 实例的子语句,然后把子语句分发到对应的 MySQL 实例上执行;最后,接收来自各个MySQL 实例的 SQL 语句执行结果,合并得到最终结果。
UMP 系统采用资源池机制来管理数据库服务器上的 CPU、内存、磁盘等计算资源,所有的计算资源都放在资源池内进行统一分配,资源池是为 MySQL 实例分配资源的基本单位。整个集群中的所有服务器会根据其机型、所在机房等因素被划分为多个资源池,每台服务器会被加入到相应的资源池。对于每个具体 MySQL 实例,管理员会根据应用部署在哪些机房、需要哪些计算资源等因素,为该 MySQL 实例具体指定主库和从库所在的资源池,然后系统的实例管理服务会本着负载均衡的原则,从资源池中选择负载较轻的服务器来创建 MySQL 实例。在资源池划分的基础上,UMP 还在每台服务器内部采用 Cgroup 将资源进一步地细化,从而可以限制每个进程组使用资源的上限,同时保证进程组之间相互隔离。
UMP 系统中有 3 种规格的用户,分别是数据量和流量比较小的用户、中等规模用户以及需要分库分表的用户。多个小规模用户可以共享同一个 MySQL 实例。对于中等规模的用户,每个用户独占一个 MySQL 实例。用户可以根据自己的需求来调整内存空间和磁盘空间,如果用户需要更多的资源,就可以迁移到资源有空闲或者具有更高配置的服务器上。对于分库分表的用户,会占有多个独立的 MySQL 实例,这些实例既可以共存在同一台物理机上,也可以每个实例独占一台物理机。
UMP 通过 MySQL 实例的迁移来实现资源调度。借助于阿里集团中间件团队开发的愚公系统,UMP 可以实现在不停机的情况下动态扩容、缩容和迁移。
当多个用户共享同一个 MySQL 实例或者多个 MySQL 实例共存在同一个物理机上时,为了保护用户应用和数据的安全,必须实现资源隔离,否则,某个用户过多消耗系统资源会严重影响到其他用户的操作性能。UMP 系统采用表 6-4 所示的两种资源隔离方式。
数据安全是让用户放心使用云数据库产品的关键,尤其是企业用户,数据库中存放了很多业务数据,有些属于商业机密,一旦泄露,会给企业造成损失。UMP 系统设计了多种机制来保证数据安全。
(1)SSL 数据库连接。SSL(Secure Sockets Layer)是为网络通信提供安全及数据完整性的一种安全协议,它在传输层对网络连接进行加密。Proxy 服务器实现了完整的 MySQL 客户端/服务器协议,可以与客户端之间建立 SSL 数据库连接。
(2)数据访问 IP 白名单。可以把允许访问云数据库的 IP 地址放入“白名单”,只有白名单内的 IP 地址才能访问,其他 IP 地址的访问都会被拒绝,从而进一步保证账户安全。
(3)记录用户操作日志。用户的所有操作记录都会被记录到日志分析服务器,通过检查用户操作记录,可以发现隐藏的安全漏洞。
(4)SQL 拦截。Proxy 服务器可以根据要求拦截多种类型的 SQL 语句,比如全表扫描语句“select *”。