分库分表后全局ID生成方案(上)

简介: 分库分表后全局ID生成方案(上)

依据数据库的第二范式,数据库中每一个表中都需要有一个唯一的主键,其他数据元素和主键一一对应。

那么关于主键的选择就成为一个关键点了,一般有如下方案:


使用业务字段作为主键

比如说对于用户表来说,可以使用手机号,email或者身份证号作为主键。对大部分场景,这并不适用,像评论表,你很难找到一个业务字段主键。而对于用户表,考虑的是业务字段是否能够唯一标识人,一人可有多个email和手机号,一旦出现变更email或手机号,就需要变更所有引用的外键信息,所以使用email或者手机作为主键不行。

身份证号码确实是用户唯一标识,但由于过于私密,并非用户系统的必须属性,你的系统如果没有要求做实名认证,肯定不会要求用户填写身份证号。

而且已有身份证号码也会变化,比如1999年时身份证号从15位变为18位,但主键一变更,以该主键为外键的表也都要随之变更,影响很大。


使用生成的唯一ID作为主键

因此,更推荐使用生成的ID作为数据库主键。不仅是因为其唯一性,且一旦生成就不会变更,可随意引用。

1 数据库自增id

提供一个专门用于生成主键的库,这样服务每次接收请求都

  1. 先往单点库的某表里插入一条没啥业务含义的数据
  2. 然后获取一个数据库自增id
  3. 取得id后,再写入对应的分库分表

优点

简单,是个人都会

缺点

因为是单库生成自增id,所以若是高并发场景,有性能瓶颈。

若硬是要改进,那就专门开个服务:

  • 该服务每次就拿到当前id最大值
  • 然后自己递增几个id,一次性返回一批id
  • 然后再把当前最大id值修改成递增几个id之后的一个值

但无论怎么说都只是基于单库。

适用场景

分库分表原因其实就俩:

  1. 单库的并发负载过高
  2. 单库的数据量过大

除非并发不高,但数据量太大导致的分库分表扩容,可用该方案,因为可能每秒最高并发最多就几百,那么就走单独的一个库和表生成自增主键即可。

并发很低,几百/s,但是数据量大,几十亿的数据,所以需要靠分库分表来存放海量数据。


当数据库分库分表后,使用自增字段就无法保证 ID 的全局唯一性了吗?

1.使用数据库的自增,设置起始值和步长不一样,不是一样可以实现吗?

2.预估每天的数据量,预先生成ID存入缓存(比如Redis)里面,然后去取,这种方法也简单?

但是这其实很难预估数据量,某一天有活动咋办?不同的起始值也可,只是增加人工成本,增加了库表咋办?忘了设置咋办?


2 UUID(Universally Unique Identifier,通用唯一标识码)

2.1 优点

本地生成,不依赖任何第三方系统,所以在性能和可用性上都比较好。

2.2 缺点

2.2.1 无序

生成的ID做好具有单调递增性,即有序。

为什么ID要有序呢?

因为在系统设计时,ID可能成为排序字段。

比如实现评论系统,一般会设计两个表:

  • 评论表
    存储评论的详细信息,其中有ID字段,有评论的内容,还有评论人ID,被评论内容的ID等等,以ID字段作为分区键
  • 评论列表
    存储着内容ID和评论ID的对应关系,以内容ID为分区键


获取内容的评论列表时,需按照时间序倒排,因为ID时间上有序,所以可按评论ID倒序排列。

若评论ID不在时间上有序,就得在评论列表中再冗余createTime列以排序,假设内容ID、评论ID和时间都8字节,就要多出50%存储空间存储时间字段,浪费存储空间。


ID有序会提升数据的写性能

MySQL InnoDB主键也是一种索引。索引数据在B+树中有序排列。当插入的下一条记录ID递增时,DV只需将其追加到后面。

但若插入数据无序,则DB查找数据应该插入的位置,再挪动该数据后面的数据,造成多余数据移动开销。

导致 B+ 树索引写时有着过多的随机写操作,而机械磁盘:


  • 随机写时,需先“寻道”找到要写入位置,即让磁头找到对应磁道,很耗时
  • 顺序写就无需寻道,大大提升索引写性能


写时不能产生有顺序的 append 操作,而需要 insert,将会读取整个 B+ 树节点到内存,在插入这条记录后会将整个节点写回磁盘,这种操作在记录占用空间较大情况下,性能下降明显

2.2.2 过长

由32个16进制数字组成的字符串,若作为DB主键使用,较耗费空间。

2.2.3 不具备业务含义

现实使用的ID中都包含有一些有意义数据,这些数据会出现在ID的固定位置。

如身份证:

  • 前6位地区编号
  • 7~14生日
    不同城市电话号码的区号不同,前三位即可看出所属运营商。


而若生成的ID可被反解,则从反解出的信息中即可验证ID,从而知道该ID生成时间、从哪个机房发号器生成、为哪个业务服务,这都有助问题排查。

Snowflake算法则可完美弥补UUID缺点。

适用场景

随机生成文件名、编号等,生成Request ID标记单次请求。

3 系统时间

获取当前时间即可。但问题是高并发时,会有重复,这肯定不合适啊,而且还可能修改系统时间!

适用场景

若用该方案,一般将当前时间跟很多其他的业务字段拼接起来,作为一个id。若业务上你可以接受,那也行。

你可以将别的业务字段值跟当前时间拼接起来,组成一个全局唯一的编号,比如订单编号:

时间戳 + 用户id + 业务含义编码

目录
相关文章
|
9月前
|
存储 缓存 算法
[转]分布式唯一ID生成方案
分布式唯一ID生成方案
166 0
[转]分布式唯一ID生成方案
|
6月前
|
算法 NoSQL 关系型数据库
分布式系统第三讲:全局唯一ID实现方案
分布式系统第三讲:全局唯一ID实现方案
176 0
|
3月前
|
设计模式 算法 Java
面试官:分库分表后如何生成全局ID?
面试官:分库分表后如何生成全局ID?
115 1
|
5月前
|
NoSQL 算法 关系型数据库
分布式系列教程(43) -高并发情况下生成分布式全局id策略
分布式系列教程(43) -高并发情况下生成分布式全局id策略
44 0
|
10月前
|
SQL 存储 缓存
聊聊分库分表后非Sharding Key查询的三种方案~(建议收藏)
聊聊分库分表后非Sharding Key查询的三种方案~(建议收藏)
509 0
|
存储 NoSQL 算法
分布式唯一 ID 的 7 种生成方案
在互联网的业务系统中,涉及到各种各样的ID,如在支付系统中就会有支付ID、退款ID等。那一般生成ID都有哪些解决方案呢?特别是在复杂的分布式系统业务场景中,我们应该采用哪种适合自己的解决方案是十分重要的。下面我们一一来列举一下,不一定全部适合,这些解决方案仅供你参考,或许对你有用。
分布式唯一 ID 的 7 种生成方案
|
11月前
|
存储 算法 NoSQL
9种 分布式ID生成方案,让你一次学个够
一、为什么要用分布式ID? 在说分布式ID的具体实现之前,我们来简单分析一下为什么用分布式ID?分布式ID应该满足哪些特征?
|
11月前
|
存储 缓存 算法
短链系统设计性能优化-分片键选型及全局自增 ID 策略
若一个 long 可对应多个 short 使用 cache 缓存所有 long2short 在为一个 long url 创建 short url 时,若 cache miss,则创建新 short
54 0
|
算法 Scala 数据库
4. 分库分表之后, id 主键如何处理?
4. 分库分表之后, id 主键如何处理?
85 0
4. 分库分表之后, id 主键如何处理?
|
SQL 中间件 关系型数据库
MyCat - 分片 - 垂直拆分 - 全局表配置 | 学习笔记
快速学习 MyCat - 分片 - 垂直拆分 - 全局表配置
112 0
MyCat - 分片 - 垂直拆分 - 全局表配置 | 学习笔记