为了提升数据库的处理能力,我们把单库扩展成多库,并通过更新同步机制(即Replication)来保证多份数据的一致性。然而,在 各种复制方案下,每个数据库都持有一份完整数据,基于全量数据提供增删改查服务,单库的性能瓶颈仍然存在,并将成为限制系统扩展性的关键因素。
单库性能瓶颈
单机的硬件资源是有限的,因此单库的处理能力也是有限的:
- 容量有限:数据量可能大到单库无法容纳
- 性能有限:单库的读写性能同样受数据量影响,查询/更新越来越慢
单靠加机器/加库显然无法直接解决单机/单库的性能问题,除非进一步打破库的边界,把单库拆分成多库(而不只是复制多份)
理论上,Web 应用层也面临同样的问题,却不曾听说过一个 Web 服务庞大到单机无法部署,这是因为Web 服务在设计之初就会考虑职责划分与解耦,以便各部分能够独立部署、独立扩展,从 20 年前的 SOA(即面向服务架构,包括微服务架构(Microservices)等变体)起便是如此。
分区 Partitioning
把逻辑数据库(或其组成元素,例如数据表)拆分成各个独立部分,这种做法称为分区(Partitioning)。
就像微服务架构中把单体应用(Monolithic application)拆分成一组小型服务一样,我们通过分区把单库拆分成一组(数据规模)更小的库,各自处理一部分数据,共同分担流量,主要优势体现在:
- 可扩展性:把单库数据拆分到多库后,系统的可扩展性不再受限于单库性能,数据库层“无限”扩展成为了可能
- 性能:单库数据量减少,数据操作更快,甚至允许多库并行操作
- 安全性:可以针对(拆出去的)敏感数据,采取更强的安全控制
- 灵活性:可以对不同的库(比如按数据重要性)采用不同的监控、备份策略,以缩减成本,提升管理效率。或者对不同类型的数据选用不同的存储服务,比如大型二进制内容放到 blob 存储中,更复杂的数据可以存放在文档数据库中
- 可用性:把数据分散放到多个篮子里,能够避免单点故障,并且单库故障仅影响一部分数据
具体的,有 3 种拆分策略:
- 水平分区(Horizontal partitioning,也叫 Sharding):按行拆分,把不同的行放入不同的表中
- 垂直分区(Vertical partitioning):按列拆分,把一些列放到其它表中
- 按功能分区(Functional partitioning,有时也叫 Federation):按业务功能拆分,把业务领域中属于相同界限上下文(Bounded Context)的数据放在一起
当然,这 3 种策略并不冲突,可以结合使用
水平分区 Horizontal partitioning
水平分区,即分片(Sharding)。每片(shard)都是原数据的一个子集,共同构成完整的数据集。
与垂直分区相比,水平分区最大的特点是schema 保持不变:每个分区都是一个单独的数据存储,但所有分区都有相同的模式。
就像把一张表横向切几刀,分成几段小表,它们的表结构(字段等)完全一致。
这种横向切分减少了单库所需存储的数据量,以及所需承载的流量/操作,另一方面,还减少了资源争用(contention),有助于提升性能
它的关键在于shard key
的选取:
按哪个字段的什么特征来分片,尽可能保证负载被均匀地分散到每一片上。注意,均匀并不意味着要求每一片的数据量均等,重点是均分流量(有些片可能数据量很大,但访问量却很低)。同时还要避免产生“热点”,比如按姓氏首字母对用户信息进行分片实际上是不均匀的,因为有些字母更常见,此时按用户 ID 哈希值来分片可能更均匀些。
垂直分区 Vertical partitioning
多用于减少 I/O、降低性能成本,比如,按使用频率把常用字段和不常用的字段分开。
比起水平分区,垂直分区的关键优势在于把信息拆的更细,进而允许一些针对性的优化,比如把不经常变化的数据拆分出来,丢到缓存中,把照片等大型二进制内容拆出去单独存放,或者对部分敏感数据进行针对性的安全控制,另一方面,细粒度的数据划分也能够消除一些并发访问,降低并发访问量。
按功能分区
把不相干的数据剔除出去(把紧密相关的数据放到一起),有助于加强数据隔离,提升数据访问性能,比如把客户信息和商品库存信息分开。
分区的代价
把单库拆成多库,虽然能够解决数据库的扩展性难题,但也引发了一些新问题:
- 连表查询慢:尽量避免跨分区 join、或者考虑并行查询
- 全表查询慢:对于需要扫描全量数据的查询操作,即便有并行优化也慢,可以通过垂直分区、按功能分区来定位目标分区,避免全表查询,至于水平分区,可以在应用层维护一张映射表,加快分区定位
- 不支持事务操作:将事务操作交由应用层来处理
- 负载不匀导致分区效果大打折扣:考虑增加监控,并根据分析预测定期调整
参考
http://www.ayqy.net/blog/database-partitioning/