要回答清楚这个问题,我们首先要详细分析下 web 服务的水平扩展原理,web 服务的水平扩展是基于 http
协议的无状态,http 的无状态是指不同的 http 请求之间不存在任何关联关系,因此如果后台有多个 web 服务处
理 http 请求,每个 web 服务器都部署相同的 web 服务,那么不管那个 web 服务处理 http 请求,结果都是等
价的。这个原理如果平移到数据库,那么就是每个数据库操作落到任意一台数据库服务器都是等价的,那么这个
等价就要求每个不同的物理数据库都得存储相同的数据,这么一来就没法解决读写失衡,解决海量数据的问题了,
当然这样做看起来似乎可以解决连接数的问题,但是面对写操作就麻烦了,因为写数据时候我们必须保证两个数
据库的数据同步问题,这就把问题变复杂了,所以 web 服务的水平扩展是不适用于数据库的。这也变相说明,
分库分表的数据库本身就拥有很强的状态性。
不过 web 服务的水平扩展还代表一个思想,那就是当业务操作超出了单机服务器的处理能力,那么我们可
以通过增加服务器的方式水平拓展整个 web 服务器的处理能力,这个思想放到数据库而言,肯定是适用的。那
么我们就可以定义下数据库的水平扩展,具体如下:
数据库的水平扩展是指通过增加服务器的方式提升整个存储层的性能。
数据库的读写分离方案,垂直拆分方案还有水平拆分方案其实都是以表为单位进行的,假如我们把数据库的
表作为一个操作原子,读写分离方案和垂直拆分方案都没有打破表的原子性,并且都是以表为着力点进行,因此
如果我们增加服务器来扩容这些方案的性能,肯定会触碰表原子性的红线,那么这个方案也就演变成了水平拆分
方案了,由此我们可以得出一个结论:
数据库的水平扩展基本都是基于水平拆分进行的,也就是说数据库的水平扩展是在数据库水平拆分后再进行
一次水平拆分,水平扩展的次数也就代表的水平拆分迭代的次数。因此要谈好数据库的水平扩展问题,我们首先
要更加细致的分析下水平拆分的方案,当然这里所说的水平拆分方案指的是狭义的水平拆分。
数据库的水平扩展其实就是让被水平拆分的表的数据跟进一步的分散,而数据的离散规则是由水平拆分的主
键设计方案所决定的,在前文里我推崇了一个使用 sequence 及自增列的方案,当时我给出了两种实现手段,一
种是通过设置不同的起始数和相同的步长,这样来拆分数据的分布,另一种是通过估算每台服务器的存储承载能
力,通过设定自增的起始值和最大值来拆分数据,我当时说到方案一我们可以通过设置不同步长的间隔,这样我
们为我们之后的水平扩展带来便利,方案二起始也可以设定新的起始值也来完成水平扩展,但是不管哪个方案进
行水平扩展后,有个新问题我们不得不去面对,那就是数据分配的不均衡,因为原有的服务器会有历史数据的负
担问题。而在我谈到狭义水平拆分时候,数据分配的均匀问题曾被我作为水平技术拆分的优点,但是到了扩展就
出现了数据分配的不均衡了,数据的不均衡会造成系统计算资源利用率混乱,更要命的是它还会影响到上层的计
算操作,例如海量数据的排序查询,因为数据分配不均衡,那么局部排序的偏差会变得更大。解决这个问题的手
段只有一个,那就是对数据根据平均原则重新分布,这就得进行大规模的数据迁移了,由此可见,除非我们觉得
数据是否分布均匀对业务影响不大,不需要调整数据分布,那么这个水平扩展还是很有效果,但是如果业务系统
不能容忍数据分布的不均衡,那么我们的水平扩展就相当于重新做了一遍水平拆分,那是相当的麻烦。其实这些
还不是最要命的,如果一个系统后台数据库要做水平扩展,水平扩展后又要做数据迁移,这个扩展的表还是一个
核心业务表,那么方案上线时候必然导致数据库停止服务一段时间。
数据库的水平扩展本质上就是水平拆分的迭代操作,换句话说水平扩展就是在已经进行了水平拆分后再拆分
一次,扩展的主要问题就是新的水平拆分是否能继承前一次的水平拆分,从而实现只做少量的修改就能达到我们
的业务需求,那么我们如果想解决这个问题就得回到问题的源头,我们的前一次水平拆分是否能良好的支持后续
的水平拆分,那么为了做到这点我们到底要注意哪些问题呢?我个人认为应该主要注意两个问题,它们分别是:
水平扩展和数据迁移的关系问题以及排序的问题。
问题一:水平扩展和数据迁移的关系问题。在我上边的例子里,我们所做的水平拆分的主键设计方案都是基
于一个平均的原则进行的,如果新的服务器加入后就会破坏数据平均分配的原则,为了保证数据分布的均匀我们
就不能不将数据做相应的迁移。这个问题推而广之,就算我们水平拆分没有过分强调平均原则,或者使用其他维
度来分割数据,如果这个维度在水平扩展时候和原库原表有关联关系,那么结果都有可能导致数据的迁移问题,
因为水平扩展是很容易产生数据迁移问题。