1 数据库拆分的兴起
在过去几年中,随着商业应用数据库事务量的大幅增长和数据库体积的增大,数据库拆分的概念已日益普及。许多在线服务供应商,软件即服务供应商(SaaS)和社交网站的成功更说明了这一点。
数据库拆分可以简单定义为针对跨多个服务器的大型数据库设计的一种“零共享”分区方案,这种方案使数据库性能和扩展性提高到一个新的水平变得可行。想象一下碎玻璃,您就能理解什么是sharding(碎片)——将数据库分成较小块所谓的“碎片”,散布在众多分布式服务器上。
术语“sharding”是谷歌工程师们首创,并通过他们的大表架构的公布得到推广的。然而,“shared-nothing(零分享)”这一数据库分区的思想已经存在了十年以上。在此期间已经产生了许多实例,特别是一些知名的在线服务提供商的著名的内部解决方案,如eBay、Amazon、Digg、Flickr、Skype、YouTube、Friendster和Wikipedia。
本文的重点是关于数据库拆分的需求、数据库分区的可选方案和成功进行数据库拆分的一些关键考虑因素。
1 大表:一个结构化数据分布式存储系统,由Fay Chang,Jeffrey Dean及其他Google员工提出。
2 是什么推动了对数据库拆分的需求?
数据库拆分是一个高可扩展性的方法,用于提高高度事务化、大型以数据库为中心的商业应用程序的数据吞吐和整体性能。自从关系数据库产生以来,显而易见,商业数据库一般随着时间增长而增长,对此应用工程师和设计师们要求不断地提高数据库的性能和容量。除此之外,网络经济的发展、信息时代的大背景、大量电子商务的普及使得商业数据急剧膨胀,导致了这一趋势更加明显。
正如任何一位有经验的数据库管理员或应用程序开发人员所深知,当数据层的大小和事务规模呈线性增长的话,响应时间往往呈对数增长,这是不言而喻的。如下图所示:
图1. 数据库大小和事务数的增长对响应时间有着巨大影响。
数据库性能和扩展性面临挑战的内在原因就是数据库管理系统本身的基础设计。任何计算机的数据库主要依赖于其三个部件:
- CPU
- 内存
- 磁盘
通过进行基准测试,我们知道,单个服务器上的某个部件只能扩展到一定的限度,然后必须采取其他措施。很明显,磁盘I / O是主要瓶颈,因为即使数据库管理系统得到改善,它依然保持对CPU和内存的高占用率。事实上,我们已经注意到,正是这三个因素的匹配程度决定了数据库的最大性能。换句话说,你不能通过单纯无限制增加CPU(或处理核心)的数量,而不提高内容容量,也不改善磁盘驱动子系统的性能,来使整个数据库系统整体性能得到相应的提高。显而易见,将资源投入到单个数据库服务器上的回报会逐渐减小。尤其是在混合使用的商业事务系统上,在执行大量读写事务的系统上,以及支持广义的商业报表任务方面,这些因素会更加明显。
因此,随着商业应用程序日益成熟,对它的需求也持续增长。架构师、开发人员和数据库管理员一直面临着维护关键任务系统的数据库性能的挑战。这一前景推动着对数据库拆分的需求。
3 数据库分区的可选项
人们早就知道数据库分区能够改善关系数据库的性能和扩展性。演变至今的技术包括:
- 主/从服务器:这是被许多组织使用的最简单的一个选择。使用一个主服务器处理所有的写(创建、更新或删除,或增删改查)操作,同时使用一个或多个从服务器处理只读操作。主服务器使用标准地、近似实时的复制功能将数据复制到各个从服务器。主/从模式可以将数据库整体性能提升到一定程度,允许读密集型的操作被分离到从服务器进行处理,但此种方法也有如下局限性:
l 单个主服务器处理写操作,在扩展性上有明显的局限性,并且会很快产生瓶颈。
l 主/从服务器的复制机制是“近于实时”的,这意味着从服务器不能保证获得主服务器上的即时快照。这种机制对于某些应用来说是可行的,但如果您的应用需要的是最新数据,这种方法是不可取的。
l 许多组织同样采用主/从服务器的方法来实现高可用性,但同样受限于主从服务器并不完全同步。如果主服务器发生灾难性故障时,任何事务都将在复制之前丢失,这种情况是大多商业事务应用所不能接受的。
- 集群运算:利用多台服务器同组集群计算,服务器之间通过群集的节点共享信息。大多数情况下这要依赖于一个集中的共享磁盘设备,通常是一个存储区域网络(SAN)。集群中的每个节点运行数据库服务器的单一实例,且以不同的模式工作:
l 对于高可用性,集群中的多个节点可用于读取,但只有一个处理写(增删改查)操作。这虽然可以使读取速度更快,而写操作却得不到任何改善。如果一个节点失败时,则群集中的另一个节点接替它,继续在共享磁盘设备上进行工作。由于一个增删改查操作瓶颈,这种做法限制了其扩展性。即使是读取操作也最终会达到一个性能极限,因为集中共享的磁盘设备在性能增幅递减之前只能分担这么大的负载。当一个应用程序需要复杂的联接或包含未优化的SQL语句时,其读操作所受的限制就是一个有力的证明。
l 更先进的集群技术依靠节点之间实时内存复制,该技术通过一种实时信息系统来保持在集群节点的内存镜像是实时的。这样使得每个节点具备既可工作在读取模式也可工作在写入模式,但最终还是会被通信节点之间可以传输的流量大小所限制(采用一个典型的网络或其他高速通信机制)。因此,随着节点的增加,通讯和内存复制的开销呈几何级数倍增,从而严重限制了扩展性,通常只好采用相对较少的节点数。此方案遇到了和传统集群一样的共享磁盘的限制,即不断增大的单一的大型数据库会产生越来越密集的磁盘读写。
- 表分区: 许多数据库管理系统支持表分区,如在一个大表中的数据可以跨多个磁盘以提高磁盘I / O利用率。这种分区通常是做横向(跨磁盘分区分行),但某些系统也可以垂直分区(在不同的分区上放置不同的列)。这种方法可以帮助减少对于某个特定表的磁盘I / O瓶颈,但往往使联接和其他操作变慢。此外,由于这种方法依赖于数据库管理系统下的单个数据库实例,所有其他对于CPU和内存的争夺所造成的限制进一步限制了其扩展性。
- Federated表:表分区技术的一个分支就是Federated表方法。使用这种方法,表可以在跨多个服务器被访问。这种做法管理起来非常复杂,且由于Federated表必须通过网络访问,效率也不高。这种做法可能适合某些报道性或分析性工作,但对于一般的读/写事务来说并非一个很好的选择。
这些方法的共同缺点是依赖于共享设备和资源。无论是依赖于共享内存,集中磁盘,还是处理器,扩展性都会受到限制,更不用提其他缺点了,包括复杂的管理,缺乏对关键业务需求的支持以及高可用性方面的限制。
4 数据库拆分,一种“零共享”的方法
数据库拆分提供了一个跨多个独立的服务器实现可扩展性的方法。每个服务器有自己的CPU,内存和磁盘。与传统的增强数据库性能的方法相比,它没有其他方法所遇到的典型限制。对于“零共享”数据库如何实现的研究和探讨已超过15年之久,但直到最近几年因为应用数据量的大增,才在商业领域找到较为广泛的市场需求。
数据库拆分的基本概念非常直白:将一个大的数据库,跨服务器分解成许多更小的数据库。如下图所示:
图2:数据库拆分就是将大数据库拆分成若干个小数据库。
很明显,“零共享”数据库拆分的优势就是大大提高扩展性。当更多的服务器被添加到网络中时,扩展性以近线性的方式增长。不过,在考虑一个拆分方案时,拆分成若干小数据库的方式还有其他几个优点不容忽视:
l 较小的数据库更易于管理。生产数据库必须进行全面的管理:定期备份、数据库优化和其他常规任务。使用一个大数据库的话,如果仅就完成操作所需的时间而言,实现这些日常任务非常困难。常规表与索引优化可以持续到几小时或几天,某些情况下会导致定期维护变得不那么灵活。通过拆分的方法,每个单独的“子库”可以单独维护。这样,管理更为简单,可以并行执行多个维护任务。
l 较小的数据库速度更快。拆分的扩展性是显而易见的,它通过在网络中跨子库和服务器的分布式处理得以实现。还有一个较不明显的事实是,每个子库库因其较小的尺寸从而在性能上胜过单个大的数据库。每个子库都有自己的服务器,这样内存和磁盘之间比率大为提高,从而减少磁盘的I/O。这样带来的后果是更少的资源争夺,更优秀的联接操作的性能,更快的索引搜索,以及更少的数据库锁定。因此,不仅拆分后的系统可以扩展到更高级别的容量,而且单个事务的性能也得到了提高。
l 数据库拆分能够降低成本。大多数数据库拆分方案可以从成本较低的开源数据库中受益,甚至可以从“工作组”版的商业数据库中受益。此外,拆分数据库在商用多核心服务器硬件上工作的很好,而这种硬件的花费远低于昂贵的高端多处理器服务器和昂贵的存储区域网络(SAN)。在许可证、软件维护和硬件投资上节约的综合成本是非常可观的,相比其他解决方案,有时可以节约70%或者更多。毫无疑问,数据库拆分对许多组织而言是一个可行的方案,这已由不少大型在线销售商和软件即服务(SaaS)供应商的实践证明过了(巨头如亚马逊,易趣,当然还有谷歌)。
5 数据库拆分的实用性
如果数据库拆分具有高扩展性,花费更低的成本,并且提高了性能,为什么该技术还没被广泛采用?它是否适合你的组织?
事实上,数据库拆分是一项非常有用的技术,但像其他方案一样,要成功实施需要考虑很多因素。此外,还存在一些限制,并且数据库拆分并不能在所有类型的商业应用上良好运行。本章讨论了这些关键因素以及它们如何能得到解决。
5.1 数据库拆分面临的挑战
鉴于单个数据库分布的性质,一些关键因素必须加以考虑:
- 可靠性。首先,任何生产经营性的商业应用都必须是可靠、容错的,且不能遭受频繁的断电。数据层通常是任一可靠性设计方案中最为关键的因素,数据库拆分的实施也不例外。事实上,鉴于多个拆分数据库分布的性质,一个设计优秀的方案显得尤为重要。为确保可靠性和容错性,需要具备以下几点:
n 单个子库的自动备份
n 子库冗余,确保每个子库至少有2个实时的备份可在断电或服务器发生故障时接替其工作。这就需要一个高性能、高效率、可靠的复制机制。
n 经济的硬件冗余,不管是服务器内部的硬件,还是跨服务器的硬件。
n 断电或服务器发生故障时自动故障切换。
n 灾难恢复的站点管理
- 分布式查询。 如果使用分布式查询并行处理模式,每个子库单独进行查询,再将每个子库的处理结果进行合并,那么许多类型的查询的处理速度会快上很多。这种技术使数据库性能获得数量级的提升,在很多情况下性能提高都在10倍或者更多。为了在应用程序上无缝地进行分布式查询,很重要的一点是需要一个设备对每个子库的查询进行处理,然后将结果合并成一个结果集返回到应用层。能从这种分布式处理模式中受益的常见查询有:
n 统计汇总,需要对整个系统的数据进行全面扫描。例如产品销售量计算通常要对整个数据库进行评估。
n 支持复杂报表的查询,如给出某一指定商品的前一天、前一周或前一月的所有顾客的列表。
- 避免跨子库的联接。在拆分系统中,跨子库使用内联的查询或其他语句效率很低,执行起来也很困难。在大多数情况下,如果采用的方法正确,实际上并不需要使用内联。主要技巧就是复制全局表,即那些相对不变化,通常用来与大型主表进行联接的对照表。那些包含状态代码、国家、类型甚至产品的表都属此类。我们需要的是一个自动化的复制机制,以确保在全局表中的值在所有子库中是同步的,尽量减少或消除跨子库联接。
- 自增长键管理。数据库管理系统所提供的典型的自增长功能对每一条插入数据库的新行生成一个序号键。这对一个单数据库的应用程序来说没有问题,但当使用数据库拆分技术时,必须对这些键值进行跨子库的协调管理。对此,我们需要为应用程序提供一个跨子库运行的、无缝的、自动的方法来生成键值,以确保整个系统的键值都是唯一的。
- 支持多个拆分方案。有一点很重要,就是据库拆分技术之所以有效,是因为它提供了一个面向应用的大规模扩展和性能改进技术。事实上,可以说拆分效果与拆分算法本身和应用程序面临的问题有多贴切是直接挂钩的。我们需要的是一套多样、灵活的、的拆分方案,其中每一个方案针对一个应用程序面临的特定问题。每一个方案都具备固有的性能以及针对应用程序的特质和优势,或者其中之一。事实上,使用错误的拆分方案会限制性能,达不到预期效果。单个应用采用多个拆分方案,每个方案用于应用程序的特定部分,从而获得优化的情况并不常见。以下列出了一些常见的拆分方案:
n 基于会话的拆分。如果单个用户或进程在整个用户或进程的会话期间内,与一个具体的子库进行交互,则采用这种方案。这是最容易实现的拆分技术,对整体性能来说几乎没有额外的开销,这是因为每一会话期只做一次拆分。从中受益的应用通常是以客户为中心的商业应用,每个客户相关的所有数据都放在一个子库上。
n 基于事务的拆分。判定子库的依据是检查给定事务的第一个SQL语句。这通常是通过评估语句中 “拆分键”的键值来完成(如订单号)。然后,将事务中其他所有的语句直接导向同一子库。
n 基于语句的拆分。以语句为基础的拆分是所有拆分类型中最为进程密集型的一种,它评估每一个SQL语句来确定这条语句应该导入哪个正确的子库。同样的,它同样需要对“拆分键”键值进行评估。这种拆分方案适合于数量大粒度小的事务,如记录通话记录。
- 决定如何拆分数据的最佳方法。这是另一领域,变化繁多,不同的应用有不同的选择。这与上述几种拆分方案的选择有很大的关系。有很多方法可以确定如何拆分您的数据,但重要的是要知道您的事务频率,表的大小量,键值如何分布,以及您的应用的其他特性。知道了这些数据就可以确定最优化的拆分策略:
n 根据表的主键进行拆分。这是最直截了当的选择,也是映射到一个应用的最容易的方法。不过,只有当您的数据分布合理才会有效。例如,如果您按客户ID(这是一个顺序的数字型值)拆分数据库,而大多数事务是针对新客户的,那么拆分效果只是微乎其微。另一方面,如果您选择一个能将用来合理自然的分发事务的键,则可以获得巨大的收益。
n 按一个键的键值的模数拆分。这种方法应用非常广泛,它对键值取模,并根据模数分发事务。实际上,您可以预先设定任意数量的子库,然后取模函数会基于“循环”规则处理键值,使得新键值能够非常均衡地分布于整个数据库。
n 维护子库索引主表。此项技术使用一个单独的主表,给不同的子库分配不同的值。这种方法非常灵活,适用面很广。但是,这种方法经常导致数据库性能不高,因为它需要对每一个拆分后的SQL语句进行额外查询。
由上可知,有很多因素需要考虑,也需要满足许多条件,才能确保数据库拆分能够成功而且有效,以达到提供经济的、更高级别的扩展能力和性能的目标。
5.2 什么时候进行数据库拆分合适
数据库拆分对许多类型的、有常规的数据库需求的商业应用来说非常合适。它同样可以有效地应用于数据仓库应用,不过因为有很多产品和技术可以实现这方面应用,我们就不在此详细讨论了。
适合对数据库进行拆分的常规数据库需求如下:
- 高度事务化的数据库应用
- 混合任务的数据库应用
n 频繁的读操作,包括复杂的查询和联接
n 写操作密集型的事务(增删改查语句,包括插入、更新、删除)
n 对于公共表和公共行,或者两者之一的资源争夺
- 常规商业报表
n 典型的“重复分段”报表的生成
n 一些数据分析(混合了其他任务)
要确定数据库拆分是否适合您特定应用或环境,最重要的事情是评估您的数据库结构能拆分的多好。从本质上讲,数据库拆分是一种“横向”分区的方法,即单个表的行集(与列相反)分布在多个子库上。为了搞清楚针对于特定情况下判定拆分的好坏的依据,以下一些事情非常重要:
- 找出您的数据库结构中所有事务密集型的表
- 确认您的数据库目前处理的事务数量(或者是预期需要处理的事务数量)
- 找出所有公用的SQL语句(选择、插入、更新、删除),确认每个语句的使用量
- 理解您的数据库结构的“表层次”,换句话说就是表之间的从属关系。
- 确定基于大容量表的事务的“键分布”情况,以确定他们是否均匀地分布还是集中于狭窄的区域内。
有了以上信息,您可以对拆分您的应用的价值和适用性做一个快速的评估。举一个例子,这有一个简单的书店系统的数据库结构,显示了数据是如何进行拆分的:
图3. 图例 书店系统数据库结构,显示了数据是如何进行拆分的
在书店系统这个例子当中,主拆分表是“顾客”表。这是用来拆分数据的表。“顾客”表是子库的父表,“顾客订单”表和“订单书目详情”表是其子表。这些数据根据“顾客ID”属性进行拆分,所有子表中与指定“顾客ID”的行集都拆分的很好。那些全局表是公用的对照表,它们相对来说操作较少,并被复制给所有的子库,以避免跨子库的联接操作。
虽然这个例子非常简单,但它提供了在决定如何对一个指定的数据库应用进行拆分时,应该考虑的基本因素。通过这样的评估方式,您就可以确定拆分是否适用于您的特定环境,以及数据库拆分后所能带来的好处。
6 结束语
本文对数据库拆分做了一个概述,包括对数据拆分所面临挑战的讨论,以及完成一个数据拆分方案的基本方法。数据库拆分已经在许多大型组织中得到了证明,也会很好的适用于您的应用中所碰到的具体问题。只要正确使用,数据库拆分一定会帮助大量的商业事务应用实现获得低成本的、近于线性的扩展性能的目标。