分库分表之分表

前言

上篇我们对分库、分表有了一定得认知,本篇我们继续研究如何分表。

深入理解分库、分表、分库分表-CSDN博客文章浏览阅读1.1k次,点赞49次,收藏24次。分库分表,是企业里面比较常见的针对高并发、数据量大的场景下的一种技术优化方案,所谓"分库分表",根本就不是一件事儿,而是三件事儿,他们要解决的问题也都不一样,这三个事儿分别是"只分库不分表"、"只分表不分库”、以及"既分库又分表"。本文我们一起理解分库、分表的奥秘。https://blog.csdn.net/weixin_44543482/article/details/136293241

​​​​​​​分区和分表有什么区别?

数据库中数据量过多,表太大的时候,不仅可以做分库分表,还可以做表分区,分区和分表类似,都是按照一定的规则将一张大表进行分解。

听上去好像也差不多,不就是将表拆分吗?那具体有什么差别呢?

主要是分区和分表后数据的数据存储方式有变化。

在Innodb中(8.0之前),表存储主要依赖两个文件,分别是,frm文件和.ibd文件。.frm文件用于存储表结构定义信息,而.ibd文件则用于存储表数据。

假如我们有一张users表,想要对他进行分区和分表,区别如下:

MySOLInnoDB存储引擎在分区表时,会将每一个分区分别存放在一个单独的 ibd 文件中,所有的 .ibd 文件组合构成表的物理结构,即 Table Space。

对于上面分区的 users表,存储时会在 MySOL的 data 目录下创建一个用户名+表名+分区名.ibd 的文件(如:users p1.ibd),用来存储 users 表中第一个分区的数据,同样会有 users p2.ibd 和 users p3.ibd 来存储第和第三个分区的数据:

users_pl.ibd
users_p2.ibd
users_p3.ibd
users_p4.ibd
users.frm

MySQL InnoDB存储引擎在分表时,会将每一个分表分别存放在一个单独的 .frm 文件中,所有的 .frm 文件组合构成表的逻辑结构,即 Table Definition。

对于上面分表的users表,存储时会在 MySOL的data目录下创建后缀名为“users 1.frm”的表格文件,存储users 表中第一个分表的数据,同样会有 users 2.frm 和 users 3.frm 来存储第二和第三个分表的数据:

users_1.ibd
users_1.frm
users_2.ibd
users_2.frm
users_3.ibd
users_3.frm
users_4.ibd
users_4.frm

在做了分区后,表面是还是只有一张表,只不过数据保存在不同的位置上了(同一个.frm文件)在做数据读取的时候操作的表名还是users表,数据库会自己去组织各个分区的数据。

而在做了分表之后,不管是表面上,还是实际上,都已经是不同的表了(多个.frm文件)数据库操作的时候,需要去指定具体的表名。

一般来说,数据量变大时,我们应该先考虑分区,分区搞不定再考虑分表,

因为分表可以在分区的基础上,进一步减少査询时的系统开销。因为分表后,单表数据量小,页缓存率更高,I/0读写性能更优,另外分表也能降低了锁带来的阻塞,也可以提高事务处理效率。还有就是小的表可以提升备份和恢复的速度、并且具有更好的横向扩展性。

分表算法都有哪些?

如何基于这个分表字段来准确的把数据分表到某一张表中?

这就是分表算法要做的事情了,但是不管什么算法,我们都需要确保一个前提,那就是同一个分表字段,经过这个算法处理后,得到的结果一定是一致的,不可变的。

通常情况下,当我们对order表进行分表的时候,比如我们要分成128张表的话,那么得到的128表应该是:order 0000、order 0001、order 0002.....rder 0126、order 0127

通常的分表算法有以下几种:

1、直接取模

在分库分表时,我们是事先可以知道要分成多少个库和多少张表的,所以,比较简单的就是取模的方式。
比如我们要分成128张表的话,就用一个整数来对128取模就行了,得到的结果如果是0002,那么就把数据放到order 0002这张表中。

2、按照关键字

有的时候,我们在分表的时候,可以给予一定的关键字做拆分,比如按照时间,比如某个月份或者年份的数据单独放在某一个表中,或者可以按照地区分表也比较常见。

3、Hash取模

那如果分表字段不是数字类型,而是字符串类型怎么办呢?有一个办法就是哈希取模,就是先对这个分表字段取Hash,然后在再取模.
但是需要注意的是,Java中的hash方法得到的结果有可能是负数,需要考虑这种负数的情况。

4、一致性Hash

前面两种取模方式都比较不错,可以使我们的数据比较均匀的分布到多张分表中。但是还是存在一个缺点。

那就是如果需要扩容二次分表,表的总数量发生变化时,就需要重新计算hash值,就需要涉及到数据迁移了

为了解决扩容的问题,我们可以采用一致性哈希的方式来做分表,

致性哈希可以按照常用的hash算法来将对应的key哈希到一个具有2^32次方个节点的空间中,形成成一个顺时针首尾相接的闭合的环形。所以当添加一台新的数据库服务器时,只有增加服务器的位置和逆时针方向第一台服务器之间的键会受影响。

分表后全局ID如何生成?

涉及到分库分表,就会引申出分布式系统中唯一主键ID的生成问题,因为在单表中我们可以用数据库主键来做唯一ID,但是如果做了分库分表,多张单表中的自增主键就一定会发生冲突。那就不具备全局唯一性了。
那么,如何生成一个全局唯一的ID呢?有以下几种方式:

1、UUID

很多人对UUID都不陌生,它是可以做到全局唯一的,而且生成方式也简单,但是我们通常不推荐使用他做唯ID,首先UUID太长了,其次字符串的查询效率也比较慢,而且没有业务含义,根本看不。

2、基于某个单表做自增主键

多张单表生成的白增主键会冲突,但是如果所有的表中的主键都从同一张表生成是不是就可以了。
所有的表在需要主键的时候,都到这张表中获取一个自增的ID。

这样做是可以做到唯一,也能实现自增,但是问题是这个单表就变成整个系统的瓶颈,而且也存在单点问题,一旦他挂了,那整个数据库就都无法写入了,

3、基于多个单表+步长做自增主键

为了解决单个数据库做白增主键的瓶颈及单点故障问题,我们可以引入多个表来一起生成就行了
但是如何保证多张表里面生成的Id不重复呢?如果我们能实现以下的生成方式就行了:

实例1生成的ID从1000开始,到1999结束。实例2生成的ID从2000开始,到2999结束。实例3生成的ID从3000开始,到3999结束。实例4生成的ID从4000开始,到4999结束:

这样就能避免ID重复了,那如果第一个实例的ID已经用到1999了怎么办?那就生成一个新的起始值:
实例1生成的ID从5000开始,到5999结束。实例2生成的ID从6000开始,到6999结束。实例3生成的ID从7000开始,到7999结束。实例4生成的ID从8000开始,到8999结束。

我们把步长设置为1000,确保每一个单表中的主键起始值都不一样,并且比当前的最大值相差1000就行了

4、雪花算法

雪花算法也是比较常用的一种分布式ID的生成方式,它具有全局唯一、递增、高可用的特点。
雪花算法生成的主键主要由4部分组成,1bit符号位、41bit时间戳位、10bit工作进程位以及 12bit 序列号位。

时间戳占用41bit,精确到毫秒,总共可以容纳约69年的时间。

工作进程位占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,做多可以容纳1024个节点。

序列号占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID.

所以,一个雪花算法可以在同一毫秒内最多可以生成1024X4096=4194304个唯一的ID

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小徐很努力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值