通常而言,MySQL数据库单表支持容量为1000万条记录,如果超过了这个量级,在查询/更新数据库表记录的时候,性能会受到很大的影响。在这种情况下,我们往往会采用其他的解决方案以提高数据库读写性能:
- 采用非关系型数据库如ElasticSearch、Cassandra、HBase、Mongo等,非关系型数据库提供了原生的水平扩展方式,因此往往是海量数据存储的首选;
- 对关系型数据库进行分库分表,使之能水平扩展;
本章主要讨论关系型数据库的分库分表方案。
基本概念
从拆分的维度来看,大致分为水平拆分和垂直拆分两种:
从拆分的内容来看,可以分为两类:
- 数据分片
数据分片是将所有数据按照水平拆分的方式均匀分布到多个数据库/表中,每个数据库/表持有的数据都不一样;数据分片有助于数据库存储资源和计算资源的负载均衡;
- 主从备份
主从备份分为热备和冷备,目的都是为了防止数据的丢失;热备中主数据库和从数据库都持有相同数据的副本以保证数据库存储的高可用,并可以通过读写分离提高数据库的读性能;
分库分表的痛点
- 事务性问题
将原来的一个表/库进行拆分之后,就不能使用数据库自身的事务机制保证数据操作的ACID特性,因此需要利用分布式事务如基于两阶段提交的XA协议;
- 联表查询的问题
如果表A被拆分出了两个表A1和A2,表B被拆分出了两个表B1和B2,那么原来的A INNER JOIN B查询将需要(A1 INNER JOIN B1) UNION (A1 INNER JOIN B2) UNION (A2 INNER JOIN B1) UNION (A2 INNER JOIN B2)这样的操作;
如果拆分得更多,最终的联表查询结果将是两个表拆分数量的笛卡尔积的Union操作,查询性能会变得异常低下;
- 排序分页的问题
对数据库表的查询结果进行排序分页,需要从各个分表查询出结果进行汇总之后再进行排序分页;
分库分表的架构
常用的分库分表架构大致可以分为两类:组件模式和代理模式;
- 组件模式将分库分表的逻辑以组件的方式提供给客户端使用;客户端开发人员需要以依赖的形式引用这些组件,并在客户端代码中配置分库分表的规则;Apache ShardingSphere、阿里巴巴的TDDL就是这类模式的代表;
- 代理模式是在客户端和服务器之间加入一个代理服务器,这个代理服务器负责对客户端请求的数据进行分库分表,这些分库分表规则对客户端开发人员而言是完全透明的,它屏蔽了分库分表的复杂度,减轻了客户端开发人员的学习曲线;MyCat、Sharding Proxy是这类模式的代表。
由于代理模式加入了一个中间层,不像组件模式那样客户端直连数据库管理系统,因此性能相对较差;但由于组件模式需要客户端开发人员去了解分库分表的规则,并保证这些规则在客户端都能正确配置;因此代理模式渐渐成为分库分表架构设计的主流。
水平分表的方式
- 顺序分布
顺序分布又分为时间顺序或主键顺序;时间顺序如将每年或每月的数据单独存储在一个表中;主键顺序如对于一个主键递增的表A,将主键ID为1-10000范围内的记录存储到A1表,将主键ID为10001-20000范围内的记录存储到A2表......以此类推。
- Hash分布
Hash分布可以分为三类:Hash取模、一致性Hash、虚拟槽分区;
Hash取模是使用主键ID对数据库节点数量进行取模,得到的值即是该记录存储节点的索引值;
一致性Hash和虚拟槽分区可以参考我以前写的Cassandra介绍中的数据分布部分。