数据库分表分库相关知识

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_29774479/article/details/82351929

分表的方式

垂直分表

垂直分表在日常开发和设计中比较常见,通俗的说法叫做“大表拆小表”,拆分是基于关系型数据库中的“列”(字段)进行的。通常情况,某个表中的字段比较多,可以新建立一张“扩展表”,将不经常使用或者长度较大的字段拆分出去放到“扩展表”中,如下图所示:

垂直分库

垂直分库在“微服务”盛行的今天已经非常普及了。基本的思路就是按照业务模块来划分出不同的数据库,而不是像早期一样将所有的数据表都放到同一个数据库中。如下图:

水平分表

水平分表也称为横向分表,比较容易理解,就是将表中不同的数据行按照一定规律分布到不同的数据库表中(这些表保存在同一个数据库中),这样来降低单表数据量,优化查询性能。最常见的方式就是通过主键或者时间等字段进行Hash和取模后拆分。如下图所示:


小结

水平分表,能够降低单表的数据量,一定程度上可以缓解查询性能瓶颈。但本质上这些表还保存在同一个库中,所以库级别还是会有IO瓶颈。所以,一般不建议采用这种做法。

水平分库分表

水平分库分表与上面讲到的水平分表的思想相同,唯一不同的就是将这些拆分出来的表保存在不同的数据中。这也是很多大型互联网公司所选择的做法。如下图:

某种意义上来讲,有些系统中使用的“冷热数据分离”(将一些使用较少的历史数据迁移到其他的数据库中。而在业务功能上,通常默认只提供热点数据的查询),也是类似的实践。在高并发和海量数据的场景下,分库分表能够有效缓解单机和单库的性能瓶颈和压力,突破IO、连接数、硬件资源的瓶颈。当然,投入的硬件成本也会更高。同时,这也会带来一些复杂的技术问题和挑战(例如:跨分片的复杂查询,跨分片事务等)

扫描二维码关注公众号,回复: 3041460 查看本文章

水平分库分表

在开始分库分表之前,我们首先要确定分库分表字段(也可称为“片键”)。很多常见的例子和场景中是采用ID或者时间字段进行拆分。这也并不绝对的,我的建议是结合实际业务,通过对系统中执行的sql语句进行统计分析,选择出需要分库分表的那个表中最频繁被使用,或者最重要的字段来作为分库分表字段。

常见分库分表规则

常见的分库分表策略有随机分库分表和连续分库分表这两种,如下图所示:

连续分库分表

当需要使用分库分表字段进行范围查找时,连续分库分表可以快速定位分库分表进行高效查询,大多数情况下可以有效避免跨分库分表查询的问题。后期如果想对整个分库分表集群扩容时,只需要添加节点即可,无需对其他分库分表的数据进行迁移。但是,连续分库分表也有可能存在数据热点的问题,就像图中按时间字段分库分表的例子,有些节点可能会被频繁查询压力较大,热数据节点就成为了整个集群的瓶颈。而有些节点可能存的是历史数据,很少需要被查询到。

随机分库分表

随机分库分表其实并不是随机的,也遵循一定规则。通常,我们会采用Hash取模的方式进行分库分表拆分,所以有些时候也被称为离散分库分表。随机分库分表分表的数据相对比较均匀,不容易出现热点和并发访问的瓶颈。但是,后期分库分表集群扩容起来需要迁移旧的数据。使用一致性Hash算法能够很大程度的避免这个问题,所以很多中间件的分库分表集群都会采用一致性Hash算法。离散分库分表也很容易面临跨分库分表查询的复杂问题。

数据迁移,容量规划,扩容等问题

很少有项目会在初期就开始考虑分库分表设计的,一般都是在业务高速发展面临性能和存储的瓶颈时才会提前准备。因此,不可避免的就需要考虑历史数据迁移的问题。一般做法就是通过程序先读出历史数据,然后按照指定的分库分表规则再将数据写入到各个分库分表节点中。

此外,我们需要根据当前的数据量和QPS等进行容量规划,综合成本因素,推算出大概需要多少分库分表(一般建议单个分库分表上的单表数据量不要超过1000W)。

如果是采用随机分库分表,则需要考虑后期的扩容问题,相对会比较麻烦。如果是采用的范围分库分表,只需要添加节点就可以自动扩容。

跨分库分表join

Join是关系型数据库中最常用的特性,但是在分库分表集群中,join也变得非常复杂。应该尽量避免跨分库分表的join查询(这种场景,比上面的跨分库分表分表分页更加复杂,而且对性能的影响很大)。通常有以下几种方式来避免:

全局表

全局表的概念之前在“垂直分库”时提过。基本思想一致,就是把一些类似数据字典又可能会产生join查询的表信息放到各分库分表中,从而避免跨分库分表的join。

内存计算

随着spark内存计算的兴起,理论上来讲,很多跨数据源的操作问题看起来似乎都能够得到解决。可以将数据丢给spark集群进行内存计算,最后将计算结果返回。

字段冗余

这是一种典型的反范式设计,在互联网行业中比较常见,通常是为了性能来避免join查询。

举个电商业务中很简单的场景:

“订单表”中保存“卖家Id”的同时,将卖家的“Name”字段也冗余,这样查询订单详情的时候就不需要再去查询“卖家用户表”。

字段冗余能带来便利,是一种“空间换时间”的体现。但其适用场景也比较有限,比较适合依赖字段较少的情况。最复杂的还是数据一致性问题,这点很难保证,可以借助数据库中的触发器或者在业务代码层面去保证。当然,也需要结合实际业务场景来看一致性的要求。就像上面例子,如果卖家修改了Name之后,是否需要在订单信息中同步更新呢?

猜你喜欢

转载自blog.csdn.net/sinat_29774479/article/details/82351929