大家好,我是不才陈某~

在前面的文章中讨论了架构优化的两种计划:冷热别离、查询别离

查询别离其实便是利用了非联系数据库的高功能,可是缺乏之处也很显着:当主数据量越来越多,写操作缓慢;这种问题怎么破局?可见任何一种优化计划都不是最终的银弹,只有不断的优化演化

这篇文章就来介绍一下解决计划:分库分表,将环绕以下几点介绍:

  1. 拆分后的存储选型?
  2. 分库分表的完成思路?
  3. 分库分表的缺乏?

拆分后的存储选型?

在介绍选型之前先来介绍下架构背景,笔者曾经做过电商体系的优化,该体系中包含的两个主体:

  1. 用户:数据量上千万,每日增加10W+
  2. 订单:数据量上亿,每日百万级的增加

关于如此量级的数据,单库单表的情况下,无论是IO仍是CPU都扛不住,架构上的优化是必然。

通过了屡次讨论尝试,最终挑选了分库分表。

说到分库分表首要想到的便是存储选型,关于耐久层的选型干流的无非有如下几种:

  1. 联系型数据库:MySQL、Oracle………
  2. NoSQL:MongoDB、ES……
  3. NewSQL:TiDB……..

1. 联系型数据库

联系型数据库现在市面上干流无非三种:MySQL、Oracle、SqlServer,笔者更倾向于MySQL,也是许多新型企业在用的一种数据库,因此本篇文章也将关键环绕MySQL打开

在任何体系中联系型数据库的地位都是不可或缺的,它的强约束性、事务的操控、SQL语法、锁….这些功用可谓是久经考验,因此在功用上 MySQL 能满意咱们一切的事务需求。

2. NoSQL

说到NoSQL,第一个想到便是MongoDB ,它的分片功用从并发性和数据量这两个角度现已能满意一般大数据量的需求,可是仍然需求考虑如下几点:

  1. 约束性:MongoDB 不是联系型数据库而是文档型数据库,它的每一行记载都是一个结构灵敏可变的 JSON,比方存储十分重要的订单数据时,咱们就不能运用 MongoDB,因为订单数据必须运用强约束的联系型数据库进行存储。
  2. 事务功用考量:事务的操控、SQL语法、锁以及各种千奇百怪的SQL在已有的架构上都曾久经考验,可是MongoDB在这些功用需求上并不能满意
  3. 事务改造考量:未拆分前运用联系型数据库,运用NoSQL之后关于SQL的改造比较麻烦,项目周期更长

3. NewSQL

NewSQL现在比较干流则是TiDB,该技能比较新,尽管能够满意大数据量的存储,可是在挑选上仍是需求做些考量:

  1. 熟悉程度考量:如果你地点公司架构组关于NewSQL比较数据或许现已有在运用,则能够挑选
  2. 稳定性考量:联系型数据毕竟是久经考验,在稳定性方面肯定是比较好,可是NewSQL的稳定性却无法去考量,主张初期阶段能够将一些不太重要的数据运用NewSQL存储

依据MySQL的分库分表

什么是分表分库?分表是将一份大的表数据拆分存放至多个结构相同的拆分表;分库便是将一个大的数据库拆分成多个结构相同的小库。

前面介绍的三种拆分存储技能,在我以往的项目中我都没运用过,而是挑选了依据 MySQL 的分表分库,首要是有一个重要考量:分表分库关于第三方依靠较少,事务逻辑灵敏可控,它本身并不需求十分复杂的底层处理,也不需求重新做数据库,仅仅依据不同逻辑运用不同 SQL 语句和数据源罢了。

现在市面上干流的分库分表分为两种形式:Proxy形式Client形式

Proxy形式归于事务无侵入型,直接代理数据库,关于开发者一切都是无感知的,SQL 组合、数据库路由、履行结果兼并等功用全部存放在一个代理服务中,比方MyCatShardingSphere都对Proxy形式供给了支撑

Client形式归于事务侵入型,将分库分表的逻辑放在客户端,客户端需求引入一个jar,比方Sharding-JDBC,架构图如下:

业务单表读写缓慢如何优化?

市面上关于分库分表中间件如下:

业务单表读写缓慢如何优化?

两种形式的优缺陷也很显着:

  1. Proxy形式:资源解耦,事务无侵入;缺陷则是运维成本相对较高
  2. Client形式:代码灵敏操控,运维成本低;缺陷则是言语限制,升级不方便

分库分表的完成思路

在落实分表分库解决计划时,咱们需求考虑 5 个关键。

1. 分片键怎么挑选?

针对订单这个事务,其间涉及到以下几个首要的字段:

  1. user_id:用户id
  2. order_id:订单id
  3. order_time:下单时间
  4. store_id:店肆id

通过考量,最终挑选了user_id作为ShardingKey,为什么呢?

挑选user_id作为ShardingKey需求结合事务场景,订单体系中常见的事务:

  1. C端用户需求查询一切的订单(user_id)
  2. 后台需求依据城市查询一切订单(user_city_id)
  3. B端商家需求统计自己店肆的下单量(store_id)

以上三种事务场景,判别下优先级,C端用户肯定是需求优先满意,因此运用user_id作为ShardingKey

这样在查询时需求将user_id传递过来才干定位到指定库、表

挑选字段作为分片键时,咱们一般需求考虑三点要求:数据尽量均匀分布在不同表或库、跨库查询操作尽可能少、这个字段的值不会变(这点尤为重要)。

2. 分片的战略是什么?

挑选user_id作为ShardingKey之后,需求考虑运用分片战略了,首要分为如下三种

1、规模分片

假如user_id是自增的数字,则能够依据user_id规模进行分片,每100万份分为一个库,每10万份分为一个表,此刻单个库中将分为10张表,如下表:

业务单表读写缓慢如何优化?

2、Hash取模

这种计划是依据Hash值进行分片,比方Hash函数为:hash(user_id%8),这儿是将user_id对8这个特定值取模,最终分为了8张表;这儿一般为了方便后续扩容,主张挑选2的N次方

3、规模分片和Hash取模混合

比方先依照规模对user_id拆分,每100万份分为一个库,在对这100万份数据进行Hash取模(hash(user_id%8))拆分成8个表

当然以上三种计划的优缺陷也是十分显着,这儿不再赘述了

需求留意的是:在拆分之前,为了避免频频的扩容,一定要对未来5年或许10年数据增加做个判别,预留更多的分片

3. 事务代码怎么修正

事务代码的修正这儿就不好说了,和自身的事务是强相关。

可是,在这儿我想分享一些个人观点。近年来,分表分库操作益发容易,不过咱们需求留意几个关键。

  1. 咱们现已习气微服务了,关于特定表的分表分库,其影响面只在该表地点的服务中,如果是一个单体架构的应用做分表分库,那真是伤脑筋。

  2. 在互联网架构中,咱们根本不运用外键约束。

  3. 跟着查询别离的流行,后台体系中有许多操作需求跨库查询,导致体系功能十分差,这时分表分库一般会结合查询别离一起操作:先将一切数据在 ES 索引一份,再运用 ES 在后台直接查询数据。如果订单详情数据量很大,还有一个常见做法,即先在 ES 中存储索引字段(作为查询条件的字段),再将详情数据存放在 HBase 中(这个计划咱们就不打开了)。

4. 历史数据搬迁?

历史数据的搬迁十分耗时,有时搬迁几天几夜都很正常。而在互联网职业中,别说几天几夜了,就连停机几分钟事务都无法承受,这就要求咱们给出一个无缝搬迁的解决计划。

还记得解说查询别离时,咱们说过的计划吗?咱们再来回忆下,如下图所示:

业务单表读写缓慢如何优化?

历史数据搬迁时,咱们便是选用类似的计划进行历史数据搬迁,如下图所示:

业务单表读写缓慢如何优化?

此数据搬迁计划的根本思路:存量数据直接搬迁,增量数据监听 binlog,然后通过 canal 告诉搬迁程序搬运数据,新的数据库拥有全量数据,且校验通过后逐步切换流量。

数据搬迁解决计划具体的步骤如下:

  • 上线 canal,通过 canal 触发增量数据的搬迁;

  • 搬迁数据脚本测试通过后,将老数据搬迁到新的分表分库中;

  • 留意搬迁增量数据与搬迁老数据的时间差,保证全部数据都被搬迁过去,无遗漏;

  • 第二步、第三步都运行完后,新的分表分库中现已拥有全量数据了,这时咱们能够运行数据验证的程序,保证一切数据都存放在新数据库中;

  • 到这步数据搬迁就算完成了,之后便是新版本代码上线了,至所以灰度上仍是直接上,需求依据你们的实际情况决议,回滚计划也是相同。

5. 未来的扩容计划是什么?

跟着事务的开展,如果本来的分片规划现已无法满意日益增加的数据需求,咱们就需求考虑扩容了,扩容计划首要依靠以下两点。

  • 分片战略是否能够让新表数据的搬迁源仅仅 1 个旧表,而不是多个旧表,这便是前面咱们主张运用 2 的 N 次方分表的原因;

  • 数据搬迁:咱们需求把旧分片的数据搬迁到新的分片上,这个计划与上面提及的历史数据搬迁相同,咱们就不重复啰唆了。

分表分库的缺乏

分表分库的解决计划讲完了,以上便是业界常用的一些做法,不过此计划仍然存在缺乏之处。

  • 增量数据搬迁: 怎么保证数据的一致性及高可用性
  • 短时订单量大爆发: 分表分库仍然扛不住时解决计划是什么?