数据库应该怎样分库分表?_数据库分库分表如何实现,学习Golang开发的步骤,

118 阅读9分钟

文章目录

数据库应该怎样分库分表?

一、分库分表的目的

  将所有的数据放在一张表里,MySQL底层 B+ 树的层级结构就可能会变的很高,不同层级的数据页一般都放在磁盘里不同的地方,磁盘 I/O 就会变多,查询性能就会变差,于是我们不得不考虑数据库分表。

  分库分表就是为了解决由于数据量过大而导致数据库性能降低的问题,将原来独立的数据库拆分成若干数据库组成 ,将数据大表拆分成若干数据表组成,使得单一数据库、单一数据表的数据量变小,从而达到提升数据库性能的目的。

在这里插入图片描述

二、分库分表的分类

1、垂直分表

  垂直分表,就是把一个有很多个字段的表拆分成多个表,或者是多个库,每个库表的结构都不一样,每个库表都包含部分字段。一般来说,会将小部分访问频率很高的字段放到一个表里去,将大部分访问频率很低的字段放到另外一个表里去。简单来说,就是把旧表中的某几列拆分成一个新表,这样单行数据就会变小,B+树里的单个数据页(固定16kb)内能放入的行数就会变多,从而使单表能放入更多的数据,查询性能变快。

为什么拆几列出去查询性能就变快了?
MySQL底层使用的是 B+ 树, B+ 树本质上就是一个个 16KB 的数据页实现的,表里的一行行数据是放在数据页里面的。当查询表里的某行数据时,先将数据页从磁盘加载到内存中,就产生磁盘 I/O,磁盘 I/O 很慢。
拆分几列出去,表里的每行数据就会变少,单个 16KB 数据页就能放入更多的行,查询时需要的数据页就变少了,磁盘 I/O 也变少了,性能就变快了。

2、水平分表

  水平分表,就是把一个表的数据拆分到多个库的多个表里去,但是每个表的结构都一样,只不过每个表放的数据行数是不同的,所有库表的数据加起来就是全部数据。水平拆分的意义,就是将数据均匀放更多的库里,然后用多个库来扛更高的并发,还有就是用多个库的存储容量来进行扩容。简单来说,就是选定一个分片键,把原来的大表拆分成 N 张小张,一般一张小表保存 500 W 到 2 KW 行数据。

在这里插入图片描述

三、具体做法

1、根据 ID 取模分表

在这里插入图片描述

  • 优点:简单、读写数据都可以很均匀的分摊到每个分表上。
  • 缺点:数据迁移、资源浪费。表的数量扩展后,重新取模,对应的表变了,需要迁移数据到新表。为了避免后续扩展的问题,有些业务会在开始就将数据量预估的很大,然后分成好多张表,但到了最后,可能数据量很小,后面的表根本没用到,造成浪费。

2、根据 ID 范围分表

在这里插入图片描述

  • 优点:解决了按照 ID 取模时,数据表的扩展问题,数据少的时候,表也少,随着数据增多表会慢慢变多,数据表可以无限扩展。
  • 缺点:读写热点问题。假设用户ID是不断 +1 的,那么在某段时间内,ID会集中在某个分片范围内,比如在4kw到6kw的范围里,数据会不断写入这个特定的分表,虽然有很多个分表,但大部分时候只有一两张分表会被频繁读写,其他表很空闲,没有起到分摊数据读写压力的效果,这就是读写热点问题。

怎么解决读写热点问题?
让 ID 变得随机,这样ID就能随机分散到所有表上,分摊读写压力。

3、结合 ID 范围和 ID 取模分表

  根据 ID 取模分表最大的好处是,新写入的数据分散到了多张表上,而根据 ID 范围分表,因为 ID 是递增的,那新写入的数据一般都会落到某一张表上,如果你的业务场景写数据特别频繁,那这张表就会出现写热点的问题。这时候就可以将 ID 取模和 ID 范围分表的方式结合起来,先用 ID 范围去分表,在某个 ID 范围内引入取模的功能,再分成多个表,将写单表分摊为写多表。

在这里插入图片描述

四、中间层(proxy)

  不管是单库分表还是分库分表,都需要通过一个中间层逻辑做路由,我们把这部分逻辑封装起来,放在数据库和业务代码之间,这样对于业务代码来说,他只知道自己在读写一张 user 表,根本不知道底下还分了那么多张小表。对于数据库来说,他并不知道自己被分表了,他只知道有那么几张表,只是正好名字长得比较像而已。还真的就应了那句话,没有什么是加中间层不能解决的,如果有就多加一层

  至于这个中间层的实现方式就更灵活了,它可以像第三方 ORM 库那样加在业务代码中,但这样就需要根据不同语言实现不同的代码库,比较繁琐。所以不少大厂都选择在 MySQL 和业务代码之间加个 proxy (代理层)服务去做这个中间层分表路由逻辑,这样就不需要关心上游服务用的是什么语言了。

在这里插入图片描述

比较常用的中间件包括:

  • Sharding-jdbc 当当开源的,属于 client 层方案。优点在于不用部署,运维成本低,不需要代理层的二次转发请求,性能很高,但是如果遇到升级啥的需要各个系统都重新升级版本再发布,各个系统都需要耦合 Sharding-jdbc 的依赖。
  • Mycat 基于 Cobar 改造的,属于 proxy 层方案,支持的功能非常完善,缺点在于需要部署,自己运维一套中间件,运维成本高,但是好处在于对于各个项目是透明的,如果遇到升级之类的都是在自己中间件那里搞就行了。

五、读扩散问题

1、什么是读扩散问题?

  分库分表时,都使用了 ID 这一列作为分表的依据,这其实就是所谓的分片键,实际上我们一般也是用数据库的主键作为分片键。我们已知一个ID,不管是根据哪种规则,我们都能很快定位到该读哪个分表。

  但很多情况下,我们的查询又不是只查主键,还会查询其他字段,比如用户表里有一列是用来保存用户名字的,并且加了个普通索引。假设我现在需要查询名字叫“小李”的用户有哪些?我需要执行 SQL 语句 select * from user where name = “小李”? 由于 name 并不是分片键,我们没法定位到具体要到哪个分表去执行这条SQL,于是就会对所有的分表都并发执行上面的 SQL 语句。假设我有 100 张分表,就执行 100 次 SQL 查询,如果我有 200 张表,就执行 200 次 SQL 查,随着我的表越来越多,查询的次数也会越来越多,这就是所谓的读扩散问题。

  分库分表时,一般我们使用数据库的主键作为分片键,但大多数情况下,我们除了把主键作为查询条件外,还会把其他字段作为查询条件外,由于其他字段并不是分片键,没法定位到具体查那个分表,于是就会查所有的分表,查询次数变多,这就是读扩散问题。mysql水平分表后,对于非分片键字段的查询会有读扩散的问题。

2、怎么解决读扩散问题?

img img img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取