写在前面
为了提升数据库的处理能力,我们把单库扩展成多库,并通过更新同步机制(即Replication)来保证多份数据的一致性。如此这般,数据库的扩展难题似乎已经顺利解决了
然而,在 Replication 方案下,每个数据库都持有一份完整数据,基于全量数据提供增删改查服务,单库的性能瓶颈仍然存在,并将成为限制系统扩展性的关键因素
单机的硬件资源是有限的,因此单库的处理能力也是有限的:
单靠加机器/加库显然无法直接解决单机/单库的性能问题,除非进一步打破库的边界,把单库拆分成多库(而不只是复制多份)
P.S.理论上,Web 应用层也面临同样的问题,却不曾听说过一个 Web 服务庞大到单机无法部署,这是因为Web 服务在设计之初就会考虑职责划分与解耦,以便各部分能够独立部署、独立扩展,从 20 年前的 SOA(即面向服务架构,包括微服务架构(Microservices)等变体)起便是如此
为了避免单库性能成为系统可扩展性的瓶颈,通常把逻辑数据库(或其组成元素,例如数据表)拆分成各个独立部分,这种做法称为分区(Partitioning):
A partition is a division of a logical database or its constituent elements into distinct independent parts.
(摘自Partition (database))
就像微服务架构中把单体应用(Monolithic application)拆分成一组小型服务一样,我们通过分区把单库拆分成一组(数据规模)更小的库,各自处理一部分数据,共同分担流量,主要优势体现在:
具体的,有 3 种拆分策略:
当然,这 3 种策略并不冲突,可以结合使用
P.S.关于领域驱动设计(Domain-Driven Design),以及界限上下文的更多信息,见去中心化数据管理(Decentralized Data Management)
水平分区,即分片(Sharding)。每片(shard)都是原数据的一个子集,共同构成完整的数据集:
A database shard is a horizontal partition of data in a database or search engine. Each individual partition (or server) acts as the single source for this subset of data.
(摘自Shard (database architecture))
与垂直分区相比,水平分区最大的特点是schema 保持不变:
Each partition is a separate data store, but all partitions have the same schema.
就像把一张表横向切几刀,分成几段小表,它们的表结构(字段等)完全一致
这种横向切分减少了单库所需存储的数据量,以及所需承载的流量/操作,另一方面,还减少了资源争用(contention),有助于提升性能
具体操作上,关键在于如何选取 shard key(按哪个字段的什么特征来分片),尽可能保证负载被均匀地分散到每一片上
注意,均匀并不意味着要求每一片的数据量均等,重点是均分流量(有些片可能数据量很大,但访问量却很低)
同时还要避免产生“热点”,比如按姓氏首字母对用户信息进行分片实际上是不均匀的,因为有些字母更常见,此时按用户 ID 哈希值来分片可能更均匀些
另一种拆分方式是垂直分区,将一些列(字段)拆分到其它表中:
多用于减少 I/O、降低性能成本,比如,按使用频率把常用字段和不常用的字段分开
比起水平分区,垂直分区的关键优势在于把信息拆的更细,进而允许一些针对性的优化,比如把不经常变化的数据拆分出来,丢到缓存中,把照片等大型二进制内容拆出去单独存放,或者对部分敏感数据进行针对性的安全控制,另一方面,细粒度的数据划分也能够消除一些并发访问,降低并发访问量
此外,还可以结合具体应用场景,按业务功能拆分:
把不相干的数据剔除出去(把紧密相关的数据放到一起),有助于加强数据隔离,提升数据访问性能,比如把客户信息和商品库存信息分开
把单库拆成多库,虽然能够解决数据库的扩展性难题,但也引发了一些新问题:
诚然,其中有些问题没有非常漂亮的解决方案,实际应用中更多的是面向特定场景的权衡取舍
联系我
如果心中仍有疑问,请查看原文并留下评论噢。(特别要紧的问题,可以直接微信联系 ayqywx )