分库分表后全局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月前
|
人工智能 JSON 数据可视化
集成500+多模态现实任务!全新MEGA-Bench评测套件:CoT对开源模型反而有害?
多模态模型在处理图像、文本、音频等数据方面能力不断提升,但其性能评估一直是个挑战。为此,研究团队推出了MEGA-Bench评测套件,集成505个现实任务,涵盖广泛领域和数据类型,由16位专家标注。它采用灵活输出格式,提供多维度评估指标,并配有交互式可视化工具,为模型优化提供了重要支持。然而,评估过程复杂且耗时,COT方法对开源模型性能的影响也值得探讨。论文链接:https://arxiv.org/abs/2410.10563
203 29
|
存储 Java 数据库
密码专辑:对密码加盐加密,对密码进行md5加密,封装成密码工具类
这篇文章介绍了如何在Java中通过加盐和加密算法(如MD5和SHA)安全地存储密码,并提供了一个密码工具类PasswordUtils和密码编码类PasswordEncoder的实现示例。
403 10
密码专辑:对密码加盐加密,对密码进行md5加密,封装成密码工具类
|
9月前
|
监控 Java 中间件
8G的容器Java堆才4G怎么就OOM了?
本文记录最近一例Java应用OOM问题的排查过程,希望可以给遇到类似问题的同学提供参考。
|
11月前
|
Ubuntu Java Linux
如何检查 Java 版本是否兼容
要检查Java版本是否兼容,可在命令行输入“java -version”查看当前安装的Java版本,然后对比目标应用所需的Java版本,确保其满足要求。
812 1
|
安全 关系型数据库 数据库
FastAPI数据库操作秘籍:如何通过高效且安全的数据库访问策略,使你的Web应用飞速运转并保持数据完整性?
【8月更文挑战第31天】在构建现代Web应用时,数据库操作至关重要。FastAPI不仅简化了API创建,还提供了高效数据库交互的方法。本文探讨如何在FastAPI中实现快速、安全的数据处理。FastAPI支持多种数据库,如SQLite、PostgreSQL和MySQL;选择合适的数据库可显著提升性能。通过安装相应驱动并配置连接参数,结合ORM库(如Tortoise-ORM或SQLAlchemy),可以简化数据库操作。使用索引、批量操作及异步处理等最佳实践可进一步提高效率。同时,确保使用参数化查询防止SQL注入,并从环境变量中读取敏感信息以增强安全性。
735 1
|
机器学习/深度学习 算法 决策智能
Python高级算法——遗传算法(Genetic Algorithm)
Python高级算法——遗传算法(Genetic Algorithm)
1252 0
|
缓存 资源调度 JavaScript
yarn安装和使用及与npm的区别
yarn安装和使用及与npm的区别
363 0
|
存储 开发框架 Java
SpringBoot开发符合S3协议的文件存储服务
公司最近的业务大量涉及安可项目,要求避免使用第三方组件,原有开发框架支持本地文件存储/Minio/各类云存储,现在要求文件独立存储且文件服务需要自研,经调研评估后决定基于SpringBoot开发文件存储服务,使用s3协议标准,这样可以直接使用aws-sdk接入无需再开发客户端,且安全安全性方面可以得到足够的保证
506 0
SpringBoot开发符合S3协议的文件存储服务
|
分布式计算 监控 Java
spark开发环境详细教程1:IntelliJ IDEA使用详细说明
spark开发环境详细教程1:IntelliJ IDEA使用详细说明
543 0
spark开发环境详细教程1:IntelliJ IDEA使用详细说明