进一步优化--分库分表

Posted 喵叔哟哟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进一步优化--分库分表相关的知识,希望对你有一定的参考价值。

上一篇文章中我们说到查询分离对于舆情系统来说不是很好的解决方案,这是因为查询分离虽然解决了复杂查询和查询导致的写入缓慢问题,但是如果主存储数据量越来越大时,写操作也会变的很慢,因此我们就需要引入分库分表方案。

一、案例

接上篇文章,舆情系统已经运行了三年,接入的社交平台也是越来越多,每天新增的数据量已经超过5000万,虽然查询数据的速度加快了,但是写入数据的速度越来越慢。经过开发组商讨,打算采用分库分表方案来解决这个问题。大致思路如下:

  1. 将单表进行拆分;
  2. 将拆分后的表进行分布式存储;
  3. 修改业务代码。 大致思路有了,那么我们就以原始数据表为例来看一下表的主要结构:
字段 说明
Id 数据id
Source 数据来源
CreateDate 入库日期(Date类型)
Ishandle 数据是否已处理

二、分库分表技术选型

2.1 何为分库分表

所谓分库分表可以分为两部分来讲:分表和分库。

  1. 分表:将表中的数据分成N份,存放到N个结构相同的拆分表中;
  2. 分库:将数据库拆分成类似结构的N个数据库。

2.2 为什么选择分库分表

选择分库分表的原因是,这个方案对第三方的以来较小,业务逻辑灵活可控,而且不需要复杂的底层处理和重建新的数据库结构,唯一需要变化的是要根据不同的逻辑使用不同的SQL语句和数据源。

2. 3 技术问题

在分库分表的方案中有三个问题需要处理:

  1. SQL组合,由于将一张表拆分成了多张表,因此查询的表就从固定表,变成了动态表,例如查询Id为123的数据,那么我们就需要根据分库分表规则找到数据所在的表,然后从这个表中查出来对应的数据。
  2. 数据库路由,因为将一个库拆分成了多个库,因此库名称也变成了动态的,因此需要按照不同的业务逻辑去调用不同的数据库。
  3. 执行结果合并,涉及到复杂查询或者跨表跨库查询时,就需要每个相关库将结果查询出来,然后再将所有结果合并在一起返回给使用方。

针对上述三个问题,解决方案分为两类:

  1. Proxy 模式(代理模式),将SQL组合、数据库路由和执行结果合并功能全部放在代理服务中执行,将分库分表逻辑放在另外的服务中。 优点是与客户端资源消耗解耦,而且方便升级。缺点是运维成本增加,并且多了一层服务,就多了一层服务调用,导致生产环境调试困难。
  2. Client 模式(客户端模式),将分库分表逻辑放在客户端,客户端引用库文件(例如jar包,dll文件等),SQL组合、数据库路由和执行结果合并全部由这个库文件处理。 优点是不用添加新的服务层,减少了服务调用层数,代码变得灵活可控,运维成本也减少了。缺点是省级不方便。

三、实现思路

不管你使用哪种模式,其实现分库分表的思路是一样的,都需要从如下4个方面来考虑如何实现:

  1. 使用什么字段作为分库分表的条件
  2. 分库分表的策略
  3. 业务逻辑代码怎么修改
  4. 历史数据如何迁移

每个方面的具体讲讲如下。

3.1 使用什么字段作为分库分表条件

要找到以什么字段作为分库分表的条件,首先就需要对日常业务进行分析,以舆情系统的原始数据表为例,来分析一下。

  1. 数据分析服务需要查询原始数据信息,这些原始数据中包含数据id入库日期数据来源
  2. 操作员需要查看每个平台的原始数据总量;
  3. 操作员需要查看某个时间段的原始数据总量。

以上三种操作就是针对原始数据表的常用操作,得到了常用的操作后我们再来分析一下哪个操作优先级做高。可想而知优先级最高的就是第一个操作,因为数据分析服务在无时无刻的执行这着读取新的原始数据并分析的操作。因此我们可以使用 入库日期来作为分库分表的条件,因为这样在某个拆分库的某个表中就能获取到全部最新的数据。

3.2 分库分表的策略

已经有了分科分表的条件字段,那么我们就该来指定策略了。一共有三种策略:

  1. 根据范围拆分,这个策略适合舆情系统的原始数据表。我们可以将数据库按照***年***来拆分,然后再将表按照***月***来拆分,比如将2020年的数据放在 2020DB***库中,然后将2020年1月到12月的数据分别放在***1Moth 到***12Month***表中。
  2. 根据HASH值分库分表,根据入库日期的HASH值和二的n次方进行取模运算进行分库分表(在实际开发中常用的就是2的n次方,因为这样方便后期库和表的扩展)。
  3. 根据HASH值和范围进行混合分库分表,一般是先进性范围划分,然后再根据HASH值取模划分。这个方案不适合当前的案例。

3.3 业务逻辑代码如何设计

业务逻辑代码的设计只需遵照以下原则即可:

  1. 对特定表的分库分表应该只能影响到使用该表的服务;
  2. 不要使用外键约束;
  3. 尽量避免跨库/表查询和写入。

3.4 历史数据如何迁移

历史数据迁移的思路和前面两篇文章将的查询分离和冷热分离的思路类似,在这里就不多讲了。我们需要注意的是,迁移历史数据不可能一瞬间就迁移完的,因此我们在迁移前要保证迁移程序没有问题,然后进行增量迁移(不是全量迁移),每迁移一部分数据就对该部分数据进行校验,当全部数据迁移完成且都校验通过后我们就可以停止旧的数据服务,启用新的数据服务了。

四、小结

分库分表基本上能解决大部分系统的查询和写入性能问题,但是这个方案还存在一些不足,比如涉及到复杂的跨库跨表查询的话就会很慢(当然你也可以结合上一篇文章所讲的内容,进行查询分离)

以上是关于进一步优化--分库分表的主要内容,如果未能解决你的问题,请参考以下文章

千万级数据,如何做性能优化?分库分表Oracle分区表?

千万级数据,如何做性能优化?分库分表Oracle分区表?

MySQL数据库性能优化之分区分表分库

数据库读写分离分表分库——用Mycat

用Mycat实现数据库读写分离分表分库

学会数据库读写分离分表分库——用Mycat