聊聊数据库分库分表

151 阅读7分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情

从业务层面来看一看,分库分表究竟是个什么。

分表

分表其实有两个维度的分表,分别是垂直分表和水平分表。

垂直分表

垂直分表这里怎么理解呢?举个列子,比如一个电商平台,它有个商品业务,在商品对应的数据库表中有 name、price、img、desc 等等这样的字段,其中 img 是商品的主图,desc 商品的副文本或详情这样的大字段。

这些字段对于垂直分表的含义是什么呢?把一个表按照字把它分到不同的表中。每一个表中存储其中的一部分字段,比如经常查询到的字段 name、price 可以把它放到商品的主表当中。对于一些不经常查询,并且非常占用 IO 的这样一些字段把它单独抽出来。

对于大部分业务来说,你只用到商品的主表当中就能获取到这些信息。这个就叫做分表的垂直拆分。

在实践领域当中,大字段和 IO 效率比较低的字段,把它分到单独的表中,与此同时,一些相对冷门的字段也可以把它给拆分出来。垂直分表相对看起来非常好理解,实现起来也非常容易,没有什么技术难度。

水平分表

那水平分表又是个什么呢?在商品表中,分了 t_product_0、t_product_1、t_product_2,这三个表当中他们的字段都是完全一样的。但是不同的数据、不同的商品会落在不同的表中。

怎么来区分哪些商品该落在哪些表中呢?这里就要谈到水平分表的路由规则。比如我们常用的有两种,第一种,我们对于商品表是经常采用这种手段,你的商品 id 取模,假设我们有三张商品表,ID % 3 之后的值,这个值是多少,就把对象商品的信息存储该商品表中。显而易见好处,就是单表的数据量会显著的降低,这样就能整体地提高你的查询效率。第二种,我们通常在业务中会应用在订单表中,比如时间分片,你过去一年的订单、过去三个月的订单、过去 10 天的订单,它会根据你的时间片来分到不同的数据库当中。

缺点

这两种分表的维度都有自己的优点和缺点。比如我们通过商品 id 取模的形式这样来分表,显而易见的缺点是不好水平扩展,假设现在是三台机器,大家相安无事,但是当增加到第四台的时候,ID 取模变,这样一来,相同的 id 取模也变,取模的时候就可能被路由到不同的表当中,而这个表原本可能并不存在要查找的商品信息。因此对于水平分表,通过 id 取模的这种路由方式,每次水平扩展都要伴随着数据的迁移。

时间分片是根据时间分表,没有 ID 取模这样的问题,随着时间往前推移,每次都是新加一个表,比方当前表1 月份的订单,到了 2 月份,再添加一张表示 2 月份的订单,看似没有这个问题,但是根据时间分片,有可能在某一个时间段你的订单量非常的多,比如双十一,当月的这个数据有可能比过去 6 个月的数据都多。因此时间片分片它也有一个缺点,就是数据分布可能会导致不均匀。

分库

垂直分库

垂直分库其实和垂直分表意思是相近的。根据不同的业务维度,将不同的业务信息分到不同的数据库当中。

水平分库

水平分库和水平分表有一曲同工之妙,比如 DB_0、DB_1,每个库当中也同样的都有水平分表,因此它是一个树状结构,你的库是树的两个结点,你这两个节点之下还有各个分表的叶子节点。

对于一个查询请求来说,前面我们根据 ID 可以直接路由到表,但是对于分库不能这样做,因为在表之上还有一个分库。

比如要查询 ID 为 100 的商品信息,商品信息我们把它存在哪里呢?就存在这个两各水平库当中,每一个水平库下面有三个水平分表,ID 为 100 的商品,我们具体要到哪个表中去查呢?这里就需要用到取模,所以分库、分表和分表其实有一曲同工之妙。

第一步都是要取模,如何摸,模 = 商品的 ID / (分库数 X 分表数),分库 = 模 / 分表数,分表 = 模 % 分表数。以商品 ID 为 100 举例,模 = 100 / (3X2)=4,分库= 4 / 3 = 1,这样就能确定在那个数据库中了,在通过取模来确定分表,分表 = 4 % 3 =1,确定数据库表。

业务层面

通常来讲业务层面哪些数据维度需要做分库分表?

在一个电商平台中,用户信息、订单信息以及商品信息,这些大数据量的业务,通常来讲我们在互联网场景下面是一定要去做分库、分表的。

如果没事在这个场景中使用分库分表的原因是:一是业务量目前还不大,还没有达到分库分表的数据量。二是可能公司比较有钱,使用的是 Oracle 数据库。

不过我们在互联网场景下面这种海量数据的面前,你即使用再好的关系型数据库,总归是需要做分库分表的,不然单表的体量完全会拖垮查询性能。

在真正的业务场景中,我们通常还要做一些数据异构。什么叫数据异构呢?举一个例子,比如我们根据订单的 id 做了一个分库分表,一切都没问题,根据订单 id 搜索这都是正常的。但是我们现在还有一个查询的维度,查询一个用户下面所有的订单,这就麻烦了。如果你根据它的订单 id 做分库分表,那同一个用户的订单它可能会分散在很多的表当中。如果你要把他们全部查询出来,那你肯定要在每一个分库、分表里面做扫描查询,这个是完全不可想象的一个性能上的瓶颈。

通过数据异构,我们把一个订单表的数据不光根据订单 id 来做分库分表,同时我们还会根据用户的 id 做一个分库分表。它在不同的分库、分表策略下面都可以查询到订单的数据,也就是说你的一条数据可能去落在了多个数据库当中,那通过这种数据异构的方式完成业务。

数据异构听起来非常的简单,但是实现起来其实还有很多的技术瓶颈。比如数据之间的同步,甚至要跨存储介质来进行同步。例如底层数据库 Oracle 或者 MySQL,同步到一些搜索引擎,比如 ES 或者是 Redis 这样的缓存。