天天看点

大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库

5 数据库拆分

  • 数据库如何拆分
  • 大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库

5.1 水平拆分

把一个表的数据给弄到多个库的多个表里,但每个库的表结构都一样,只不过每个库中表放的数据不同,所有库表的数据加起来就是全部数据。关注点在数据的特点。

水平拆分的意义

  • 将数据均匀放更多的库,然后用多个库抗更高并发
  • 多个库的存储进行扩容

5.2 垂直拆分(拆库)

解决问题

  • 服务不能复用
  • 连接数不够

将一个数据库,拆分成多个提供不同业务数据处理能力的数据库,关注点在于业务相关性。

例如拆分所有订单的数据和产品的数据,变成两个独立的库,数据结构发生了变化,SQL 和关联关系也必随之改变。

原来一个复杂 SQL 直接把一批订单和相关的产品都查了出来,现在得改写 SQL 和程序。

先查询订单库数据,拿到这批订单对应的所有产品 id

再根据产品 id list去产品库查询所有产品信息

最后再业务代码里进行组装把一个有很多字段的表给拆分成多个表或库

每个库表的结构都不一样,每个库表都包含部分字段。

一般将较少的访问频率很高的字段放到一个表,然后将较多的访问频率很低的字段放到另外一个表。

因为数据库有缓存,访问频率高的行字段越少,可在缓存里缓存更多行,性能就越好。这个一般在表这个层面做的较多。

现有中间件都可实现分库分表后,根据你指定的某个字段值,比如userid,自动路由到对应库,然后再自动路由到对应表。

8 分库分表的方式

按range分

就是每个库一段连续的数据,一般按比如时间范围来的,但是这种一般较少用,因为很容易产生热点问题,大量的流量都打在最新的数据上了

  • 好处

    后面扩容的时候,就很容易,因为你只要预备好,给每个月都准备一个库就可以了,到了一个新的月份的时候,自然而然,就会写新的库了

  • 缺点

    但是大部分的请求,都是访问最新的数据。实际生产用range,要看场景,你的用户不是仅仅访问最新的数据,而是均匀的访问现在的数据以及历史的数据

按某字段hash

均匀分散,最为常用。

  • 可以平均分配没给库的数据量和请求压力
  • 坏处

    扩容起来比较麻烦,会有一个数据迁移的过程

1 Sharding

把数据库横向扩展到多个物理节点的一种有效方式,主要是为了突破数据库单机服务器的 I/O 瓶颈,解决数据库扩展问题。

Sharding可简单定义为将大数据库分布到多个物理节点上的一个分区方案。每一个分区包含数据库的某一部分,称为一个shard,分区方式可以是任意的,并不局限于传统的水平分区和垂直分区。

一个shard可以包含多个表的内容甚至可以包含多个数据库实例中的内容。每个shard被放置在一个数据库服务器上。一个数据库服务器可以处理一个或多个shard的数据。系统中需要有服务器进行查询路由转发,负责将查询转发到包含该查询所访问数据的shard或shards节点上去执行。

垂直切分/水平切分

MySQL的扩展方案

  • Scale Out 水平扩展

    一般对数据中心应用,添加更多机器时,应用仍可很好利用这些资源提升自己的效率从而达到很好的扩展性

  • Scale Up 垂直扩展

一般对单台机器,Scale Up指当某个计算节点添加更多的CPU Cores,存储设备,使用更大的内存时,应用可以很充分的利用这些资源来提升自己的效率从而达到很好的扩展性

MySQL的Sharding策略

  1. 垂直切分:按功能模块拆分,以解决表与表之间的I/O竞争

e.g. 将原来的老订单库,切分为基础订单库和订单流程库。数据库之间的表结构不同

大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库
  1. 水平切分:将

    同个表

    的数据分块,保存至不同的数据库

    以解决单表中数据量增长压力。这些数据库中的表结构 完全相同

2.3 表结构设计案例

垂直切分

  1. 大字段

    单独将大字段建在另外的表中,提高基础表的访问性能,原则上在性能关键的应用中应当避免数据库的大字段

  2. 按用途

例如企业物料属性,可以按照基本属性、销售属性、采购属性、生产制造属性、财务会计属性等用途垂直切分

3、 按访问频率

例如电子商务、Web 2.0系统中,如果用户属性设置非常多,可以将基本、使用频繁的属性和不常用的属性垂直切分开

水平切分

  1. 比如在线电子商务网站,订单表数据量过大,按照年度、月度水平切分
  2. 网站注册用户、在线活跃用户过多,按照用户ID范围等方式,将相关用户以及该用户紧密关联的表做水平切分
  3. 论坛的置顶帖,因为涉及到分页问题,每页都需显示置顶贴,这种情况可以把置顶贴水平切分开来,避免取置顶帖子时从所有帖子的表中读取

3 分表和分区

分表:把一张表分成多个小表;

分区:把一张表的数据分成N多个区块,这些区块可以在同一个磁盘上,也可以在不同的磁盘上。

3.1 分表和分区的区别

  • 实现方式
  • MySQL的一张表分成多表后,每个小表都是完整的一张表,都对应三个文件(MyISAM引擎:.MYD数据文件,.MYI索引文件,.frm表结构文件)

数据处理

分表后数据都存放在分表里,总表只是个外壳,存取数据发生在一个个的分表里

分区则不存在分表的概念,分区只不过把存放数据的文件分成许多小块,分区后的表还是一张表,数据处理还是自己完成。

性能

分表后,单表的并发能力提高了,磁盘I/O性能也提高了。分表的关键是存取数据时,如何提高 MySQL并发能力

分区突破了磁盘I/O瓶颈,想提高磁盘的读写能力,来增加MySQL性能

实现成本

分表的方法有很多,用merge来分表,是最简单的一种。这种方式和分区难易度差不多,并且对程序代码透明,如果用其他分表方式就比分区麻烦

分区实现比较简单,建立分区表,跟建平常的表没区别,并且对代码端透明

3.2 分区适用场景

  1. 一张表的查询速度慢到影响使用
  2. 表中的数据是分段的
  3. 对数据的操作往往只涉及一部分数据,而不是所有的数据
  4. 大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库

3.3 分表适用场景

  1. 频繁插入或连接查询时,速度变慢
  2. 分表的实现需要业务结合实现和迁移,较为复杂

4 分库

分表能解决

单表数据量过大带来的查询效率下降

问题,但无法给数据库的并发处理能力带来质的提升。面对高并发的读写访问,当数据库主服务器无法承载写压力,不管如何扩展从服务器,都没有意义了。

换个思路,对数据库进行拆分,

提高数据库写性能

,即分库。

4.1 分库的解决方案

一个MySQL实例中的多个数据库拆到不同MySQL实例中:

大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库
  • 缺陷

    有的节点还是无法承受写压力。

4.1.1 查询切分

  • 将key和库的映射关系单独记录在一个数据库。
  • 大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库
  • 优点

    key和库的映射算法可以随便自定义

  • 引入了额外的单点

4.1.2 范围切分

按某字段的区间拆分,比较常用的是时间字段。

在内容表里有“创建时间”的字段,而我们也是按时间来查看一个人发布的内容。我们可能会要看昨天的内容,也可能会看一个月前发布的内容,这时即可按创建时间的区间来分库分表。比如可以把一个月的数据放入一张表,查询时即可根据创建时间先定位数据存储在哪个表,再按查询条件查询。

按照时间区间或ID区间切分。

大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库

适用场景

一般列表数据可使用这种拆分方式。

比如一个人某时间段的订单、发布的内容。但这种方式可能存在明显热点,因为肯定会更关注最近用户买了啥,发了啥,所以查询的 QPS 会更多,对性能有一定影响。

使用这种拆分规则后,数据表要提前建立好,否则如果时间到了次年元旦,DBA却忘记了建表,那么次年的数据就没有库表可写了。

  • 单表容量可控,水平扩展很方便。
  • 无法解决集中写入的瓶颈问题。

4.1.3 Hash切分

按照某字段的哈希值做拆分,适用于实体表,比如用户表,内容表,一般按这些实体表的 ID 字段拆分。

比如把用户表拆分成 16 个库,64 张表,可先对用户 ID 做哈希将 ID 尽量打散,然后再对 16 取余,这样就得到了分库后的索引值;对 64 取余,就得到了分表后的索引值。

一般都是采用hash切分。

大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库

数据水平切分后我们希望易于水平扩展,所以推荐采用mod 2^n这种一致性Hash。

比如一个订单库,分库分表方案是32*32,即通过UserId后四位mod 32分到32个库中,同时再将UserId后四位Div 32 Mod 32将每个库分为32个表,共计分为1024张表。

线上部署情况为8个集群(主从),每个集群4个库。

大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库

为什么说这易于水平扩展?分析如下场景:

数据库性能达到瓶颈

  1. 现有规则不变,可直接扩展到32个数据库集群。
大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库
  1. 如果32个集群也无法满足需求,那么将分库分表规则调整为(32*2n)*(32⁄2n),可以达到最多1024个集群。
大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库

单表容量达到瓶颈

或1024都无法满足。

大厂原来都这么对MySQL分库分表!(中)5 数据库拆分8 分库分表的方式1 Sharding垂直切分/水平切分3 分表和分区4 分库

假如单表都突破200G,200*1024=200T

没关系,32 * (32 * 2^n),这时分库规则不变,单库里的表再裂变,当然,在目前订单这种规则下(用userId后四位 mod)还是有极限的,因为只有四位,所以最多拆8192个表。