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