关于java:为什么要分库分表

42次阅读

共计 4521 个字符,预计需要花费 12 分钟才能阅读完成。

前言

在高并发零碎当中,分库分表是必不可少的技术手段之一,同时也是 BAT 等大厂面试时,常常考的热门考题。

你晓得咱们为什么要做分库分表吗?

这个问题要从两条线说起:垂直方向 和 程度方向。

1 垂直方向

垂直方向次要针对的是业务,上面聊聊业务的倒退跟分库分表有什么关系。

1.1 单库

在零碎初期,业务性能相对来说比较简单,零碎模块较少。

为了疾速满足迭代需要,缩小一些不必要的依赖。更重要的是缩小零碎的复杂度,保障开发速度,咱们通常会应用单库来保留数据。

零碎初期的数据库架构如下:

此时,应用的数据库计划是:一个数据库蕴含多张业务表。用户读数据申请和写数据申请,都是操作的同一个数据库。

1.2 分表

零碎上线之后,随着业务的倒退,一直的增加新性能。导致单表中的字段越来越多,开始变得有点不太好保护了。

一个用户表就蕴含了几十甚至上百个字段,治理起来有点凌乱。

这时候该怎么办呢?

答:分表。

将用户表拆分为:用户根本信息表 和 用户扩大表。

用户根本信息表中存的是用户最次要的信息,比方:用户名、明码、别名、手机号、邮箱、年龄、性别等外围数据。

这些信息跟用户非亲非故,查问的频次十分高。

而用户扩大表中存的是用户的扩大信息,比方:所属单位、户口所在地、所在城市等等,非核心数据。

这些信息只有在特定的业务场景才须要查问,而绝大数业务场景是不须要的。

所以通过分表把外围数据和非核心数据离开,让表的构造更清晰,职责更繁多,更便于保护。

除了按理论业务分表之外,咱们还有一个罕用的分表准则是:把调用频次高的放在一张表,调用频次低的放在另一张表。

有个十分经典的例子就是:订单表和订单详情表。

1.3 分库

人不知; 鬼不觉,零碎曾经上线了一年多的工夫了。经验了 N 个迭代的需要开发,性能曾经十分欠缺。

零碎功能完善,意味着零碎各种关联关系,盘根错节。

此时,如果不赶快梳理业务逻辑,前面会带来很多暗藏问题,会把本人坑死。

这就须要按业务性能,划分不同畛域了。把雷同畛域的表放到同一个数据库,不同畛域的表,放在另外的数据库。

具体拆分过程如下:

将用户、产品、物流、订单相干的表,从原来一个数据库中,拆分成独自的用户库、产品库、物流库和订单库,一共四个数据库。

在这里为了看起来更直观,每个库我只画了一张表,理论场景可能有多张表。

这样按畛域拆分之后,每个畛域只用关注本人相干的表,职责更繁多了,一下子变得更好保护了。

1.4 分库分表

有时候按业务,只分库,或者只分表是不够的。比方:有些财务零碎,须要按月份和年份汇总,所有用户的资金。

这就须要做:分库分表了。

每年都有个独自的数据库,每个数据库中,都有 12 张表,每张表存储一个月的用户资金数据。

这样分库分表之后,就能十分高效的查问出某个用户每个月,或者每年的资金了。

此外,还有些比拟非凡的需要,比方须要依照地区分库,比方:华中、华北、华南等区,每个区都有一个独自的数据库。

甚至有些游戏平台,按接入的游戏厂商来做分库分表。

2 程度方向

水分方向次要针对的是数据,上面聊聊数据跟分库分表又有什么关系。

2.1 单库

在零碎初期,因为用户非常少,所以零碎并发量很小。并且存在表中的数据量也非常少。

这时的数据库架构如下:

此时,应用的数据库计划同样是:一个 master 数据库蕴含多张业务表。

用户读数据申请和写数据申请,都是操作的同一个数据库,该计划比拟适宜于并发量很低的业务场景。

2.2 主从读写拆散

零碎上线一段时间后,用户数量减少了。

此时,你会发现用户的申请当中,读数据的申请占据了大部分,真正写数据的申请占比很少。

家喻户晓,数据库连贯是无限的,它是十分贵重的资源。而每次数据库的读或写申请,都须要占用至多一个数据库连贯。

如果写数据申请须要的数据库连贯,被读数据申请占用完了,不就写不了数据了?

这样问题就重大了。

为了解决该问题,咱们须要把读库和写库离开。

于是,就呈现了主从读写拆散架构:

思考刚开始用户量还没那么大,抉择的是一主一从的架构,也就是常说的一个 master 一个 slave。

所有的写数据申请,都指向主库。一旦主库写完数据之后,立马异步同步给从库。这样所有的读数据申请,就能及时从从库中获取到数据了(除非网络有提早)。

读写拆散计划能够解决下面提到的单节点问题,绝对于单库的计划,可能更好的保证系统的稳定性。

因为如果主库挂了,能够降级从库为主库,将所有读写申请都指向新主库,零碎又能失常运行了。

读写拆散计划其实也是分库的一种,它绝对于为数据做了备份,它曾经成为了零碎初期的首先计划。

但这里有个问题就是:如果用户量的确有些大,如果 master 挂了,降级 slave 为 master,将所有读写申请都指向新 master。

但此时,如果这个新 master 基本扛不住所有的读写申请,该怎么办?

这就须要一主多从的架构了:

上图中我列的是一主两从,如果 master 挂了,能够抉择从库 1 或从库 2 中的一个,降级为新 master。如果咱们在这里降级从库 1 为新 master,则原来的从库 2 就变成了新 master 的的 slave 了。

调整之后的架构图如下:

这样就能解决下面的问题了。

除此之外,如果查问申请量再增大,咱们还能够将架构降级为一主三从、一主四从 … 一主 N 从等。

2.3 分库

下面的读写拆散计划的确能够解决读申请大于写申请时,导致 master 节点扛不住的问题。但如果某个畛域,比方:用户库。如果注册用户的申请量十分大,即写申请自身的申请量就很大,一个 master 库根本无法接受住这么大的压力。

这时该怎么办呢?

答:建设多个用户库。

用户库的拆分过程如下:

在这里我将用户库拆分成了三个库(实在场景不肯定是这样的),每个库的表构造是截然不同的,只有存储的数据不一样。

2.4 分表

用户申请量上来了,带来的势必是数据量的成本上升。即便做了分库,但有可能单个库,比方:用户库,呈现了 5000 万的数据。

依据经验值,单表的数据量应该尽量管制在 1000 万以内,性能是最佳的。如果有几千万级的数据量,用单表来存,性能会变得很差。

如果数据量太大了,须要建设的索引也会很大,从小到大检索一次数据,会十分耗时,而且十分耗费 cpu 资源。

这时该怎么办呢?

答:分表,这样能够管制每张表的数据量,和索引大小。

表拆分过程如下:

我在这里将用户库中的用户表,拆分成了四张表(实在场景不肯定是这样的),每张表的表构造是截然不同的,只是存储的数据不一样。

如果当前用户数据量越来越大,只需再多分几张用户表即可。

2.5 分库分表

当零碎倒退到肯定的阶段,用户并发量大,而且须要存储的数据量也很多。这时该怎么办呢?

答:须要做分库分表。

如下图所示:

图中将用户库拆分成了三个库,每个库都蕴含了四张用户表。

如果有用户申请过去的时候,先依据用户 id 路由到其中一个用户库,而后再定位到某张表。

路由的算法挺多的:

  • 依据 id 取模,比方:id=7,有 4 张表,则 7%4=3,模为 3,路由到用户表 3。
  • 给 id 指定一个区间范畴,比方:id 的值是 0-10 万,则数据存在用户表 0,id 的值是 10-20 万,则数据存在用户表 1。
  • 一致性 hash 算法

这篇文章就不过多介绍了,前面会有文章专门介绍这些路由算法的。

3 实在案例

接下来,废话不多说,给大家分享三个我参加过的分库分表我的项目经验,给有须要的敌人一个参考。

3.1 分库

我之前待过一家公司,咱们团队是做游戏经营的,咱们公司提供平台,游戏厂商接入咱们平台,推广他们的游戏。

游戏玩家通过咱们平台登录,胜利之后跳转到游戏厂商的指定游戏页面,该玩家就能失常玩游戏了,还能够充值游戏币。

这就须要建设咱们的账号体系和游戏厂商的账号的映射关系,游戏玩家通过登录咱们平台的游戏账号,胜利之后转换成游戏厂商本人平台的账号。

这里有两个问题:

  1. 每个游戏厂商的接入形式可能都不一样,账号体系映射关系也有差别。
  2. 用户都从咱们平台登录,胜利之后跳转到游戏厂商的游戏页面。过后有 N 个游戏厂商接入了,沉闷的游戏玩家比拟多,登录接口的并发量不容小觑。

为了解决这两个问题,咱们过后采纳的计划是:分库。即针对每一个游戏都独自建一个数据库,数据库中的表构造容许存在差别。

咱们过后没有进一步分表,是因为过后思考每种游戏的用户量,还没到大到离谱的境地。不像王者光荣这种景象级的游戏,有上亿的玩家。

其中有个比拟要害的中央是:登录接口中须要传入游戏 id 字段,通过该字段,零碎就晓得要操作哪个库,因为库名中就蕴含了游戏 id 的信息。

3.2 分表

还是在那家游戏平台公司,咱们还有另外一个业务就是:金钻会员。

说白了就是打造了一套跟游戏相干的会员体系,为了放弃用户的活跃度,开明会员有很多福利,比方:送游戏币、充值有折扣、积分兑换、抽奖、专属客服等等。

在这套会员体系当中,有个十分重要的性能就是:积分。

用户有很多种路径能够获取积分,比方:签到、充值、玩游戏、抽奖、推广、加入流动等等。

积分用什么用处呢?

  1. 退换实物礼物
  2. 兑换游戏币
  3. 抽奖

说了这么多,其实就是想说,一个用户一天当中,获取积分或生产积分都可能有很屡次,那么,一个用户一天就可能会产生几十条记录。

如果用户多了的话,积分相干的数据量其实挺惊人的。

咱们过后思考了,程度方向的数据量可能会很大,然而用户并发量并不大,不像登录接口那样。

所以采纳的计划是:分表。

过后应用一个积分数据库就够了,然而分了 128 张表。而后依据用户 id,进行 hash 除以 128 取模。

须要特地留神的是,分表的数量最好是 2 的幂次方,不便当前扩容。

3.3 分库分表

起初我去了一家从事餐饮软件开发的公司。这个公司有个特点是在每天的中午和早晨的就餐高峰期,用户的并发量很大。

用户吃饭前须要通过咱们零碎点餐,而后下单,而后结账。过后点餐和下单的并发量挺大的。

餐厅可能会有很多人,每个人都可能下多个订单。这样就会导致用户的并发量高,并且数据量也很大。

所以,综合思考了一下,过后咱们采纳的技术计划是:分库分表。

通过调研之后,感觉应用了当当网开源的基于 jdbc 的中间件框架:sharding-jdbc。

过后分了 4 个库,每个库有 32 张表。

4 总结

下面次要从:垂直和程度,两个方向介绍了咱们的零碎为什么要分库分表。

说实话垂直方向(即业务方向)更简略。

在程度方向(即数据方向)上,分库和分表的作用,其实是有区别的,不能一概而论。

  • 分库:是为了解决数据库连贯资源有余问题,和磁盘 IO 的性能瓶颈问题。
  • 分表:是为了解决单表数据量太大,sql 语句查问数据时,即便走了索引也十分耗时问题。此外还能够解决耗费 cpu 资源问题。
  • 分库分表:能够解决 数据库连贯资源有余、磁盘 IO 的性能瓶颈、检索数据耗时 和 耗费 cpu 资源等问题。

如果在有些业务场景中,用户并发量很大,然而须要保留的数据量很少,这时能够只分库,不分表。

如果在有些业务场景中,用户并发量不大,然而须要保留的数量很多,这时能够只分表,不分库。

如果在有些业务场景中,用户并发量大,并且须要保留的数量也很多时,能够分库分表。

好了,明天的内容就先到这里。

是不是有点意犹未尽?

没关系,其实分库分表相干内容挺多的,本文作为分库分表系列的第一弹,作为一个开胃小菜吧,分享给大家。

在文章开端顺便提几个问题:

  1. 分库分表的具体实现计划有哪些?
  2. 分库分表后如何平滑扩容?
  3. 分库分表后带来了哪些问题?
  4. 如何在我的项目中实现分库分表性能?
正文完
 0