数据库的分库与分表简介


分库分表的原理

关系型数据库因为单机存储容量、连接数和处理能力都有限,所以往往成为系统的性能瓶颈。当单表的数据量达到1000万或100G以后,由于查询维度较多,即使添加从库、优化索引,但很多操作仍然会性能低下。此时就需要对其进行切分,切分的目的就在于减少数据库的负担,缩短查询时间。

数据库分布式核心内容无非就是数据切分(Sharding),以及切分后对数据的定位、整合。数据切分就是将数据分散存储到多个数据库中,使得单一数据库中的数据量变小,通过扩充主机的数量缓解单一数据库的性能问题,从而达到提升数据库操作性能的目的。

数据切分根据其切分类型,可以分为两种方式:垂直(纵向)切分和水平(横向)切分。

垂直(纵向)切分

垂直切分常见有垂直分库和垂直分表两种。

垂直分库就是根据业务耦合性,将关联度低的不同表存储在不同的数据库。做法与大系统拆分为多个小系统类似,按业务分类进行独立划分。

垂直分表是基于数据库中的 “列”进行,如果某个表的字段较多,可以新建一张扩展表,将不经常用或字段长度较大的字段拆分出去到扩展表中。在字段很多的情况下通过“大表拆小表”,更便于开发与维护,也能避免跨页问题。

垂直切分能对不同业务的数据进行分级管理、维护、监控、扩展等。在高并发场景下,垂直切分一定程度地提升了 IO性能、数据库连接数、单机硬件资源的瓶颈。但数据分散在不同的表中,有些连接查询无法实现只能通过接口聚合的方式解决,另外分布式事务处理也提升了开发的复杂度。

水平(横向)切分

当表中的数据难以再细粒度的垂直切分,或切分后数据行数巨大,存在单库读写、存储性能瓶颈,这时候就需要进行水平切分了。

水平切分分为库内分表和分库分表,是根据表内数据内在的逻辑关系,将同一个表按不同的条件分散到多个数据库或多个表中,每个表中只包含一部分数据,从而使得单个表的数据量变小,达到分布式的效果。

库内分表只解决了单一表数据量过大的问题,但没有将表分布到不同机器的库上,因此对于减轻MySQL数据库的压力来说作用有限。

水平切分不存在单库数据量过大、高并发的性能瓶颈,能够有效提升系统稳定性和负载能力。但跨分片的事务一致性难以保证,跨库的连接查询性能较差,同一张表的数据出现在多个数据库和数据表中,维护成本较高。

分库分表的问题

分库分表能有效地缓解了单机和单库带来的性能瓶颈和压力,突破网络IO、硬件资源、连接数的瓶颈,同时也带来了很多问题。

事务一致性问题

当更新内容同时分布在不同库中,不可避免会带来跨库事务问题。跨分片事务也是分布式事务,没有简单的方案,一般可使用 "XA 协议" 和 "两阶段提交" 处理。分布式事务能最大限度保证了数据库操作的原子性。但在提交事务时需要协调多个节点,推后了提交事务的时间点,延长了事务的执行时间。导致事务在访问共享资源时发生冲突或死锁的概率增高。随着数据库节点的增多,这种趋势会越来越严重,从而成为系统在数据库层面上水平扩展的枷锁。

对于那些性能要求很高,但对一致性要求不高的系统,往往不苛求系统的实时一致性,只要在允许的时间段内达到最终一致性即可,可采用事务补偿的方式。与事务在执行中发生错误后立即回滚的方式不同,事务补偿是一种事后检查补救的措施,一些常见的实现方法有:对数据进行对账检查,基于日志进行对比,定期同标准数据来源进行同步等等。

跨节点关联查询问题

切分之前,系统中很多列表和详情页所需的数据可以通过SQL的join来完成。而切分之后,数据可能分布在不同的节点上,此时join带来的问题就比较麻烦了,考虑到性能,应尽量避免使用join查询。

跨节点分页、排序、函数问题

跨节点多库进行查询时,会出现limit分页、order by排序等问题。分页需要按照指定字段进行排序,当排序字段就是分片字段时,通过分片规则就比较容易定位到指定的分片;当排序字段非分片字段时,就变得比较复杂了。需要先在不同的分片节点中将数据进行排序并返回,然后将不同分片返回的结果集进行汇总和再次排序,最终返回给用户。同样在使用Max、Min、Sum、Count之类的函数进行计算的时候,也需要先在每个分片上执行相应的函数,然后将各个分片的结果集进行汇总和再次计算,最终将结果返回。

全局主键重复问题

在分库分表环境中,由于表中数据同时存在不同数据库中,主键值平时使用的自增长将无用武之地,某个分区数据库自生成的ID无法保证全局唯一。因此需要单独设计全局主键,以避免跨库主键重复问题。


发表评论

评论数量:0