首页 > 数据库 >数据库主键生成策略

数据库主键生成策略

时间:2023-08-27 15:23:38浏览次数:39  
标签:自增 UUID 数据库 private 主键 static 生成 id

首先明确的一点是,主键是为了区分不同的行记录,所以先抛开其他的因素,主键必须要保证:唯一性(单表或者分库分表的场景下)

单表

可选的方案有:

  1. 自增id
  2. UUID
  3. 业务字段,如:手机号、身份证号等等

自增id

自增主键是单表中很常用的使用方式。阿里Java开发中规定,表必备三字段: id,gmt_create,gmt_modified。说明:其中id必为主键,类型为unsigned bigint(8个字节)、单表时自增、步长为 1(这里后面还会提到)。【1】

优点:

  1. 递增,聚集索引的性能更好
  2. 节省空间(相比于UUID、业务字段)

缺点:

  1. 不利于迁移,如将数据表迁移到其他服务器中,这种场景下解决方案:【2】
    • 目标表的表结构与原表一致,只是主键不设置为递增
    • 将原表的数据迁移到目标表中
    • 最后将主键设置为递增即可
    • ( [1] 数据迁移的过程)
    • ( [2] 迁移的过程中有数据的修改该如何处理?)
  2. 不利于扩展,如:需要做分表时不能使用自增id

总结:单表的情况下没啥毛病


UUID

UUID (Universally Unique Identifier),UUID包含:【3】

  1. 当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同
  2. 时钟序列
  3. 全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得

java.util中也提供了生成UUID的方法,生成的格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)

UUID的特点:

  1. 无序
  2. 绝大部分情况下唯一,高并发的场景下可能也会出现重复的情况

优点:

  1. 相比于自增id更加安全

缺点:

  1. 无序,所以在进行insert操作时对于性能会有影响【5】
    • 相比于按顺序写入,随机写入时需要写的page可能已经不在buffer pool中了,这样InnoDB需要将目标page再读到内存中去,产生了大量的随机IO
    • 由于写入是乱序的,InnoDB可能需要进行频繁的页分裂操作,需要分配新页,移动数据,还有可能向上引发一系列的页分裂
    • 由于频繁的页分裂,可能会造成page中的碎片过大
  2. 相对浪费空间

业务字段

如手机号,不过尽可能避免使用业务字段作为主键

缺点:

  1. 无序
  2. 可能有变化,比如有这种需求:手机号需要加密存储,且如果该字段和其他表有依赖,那么需要改很多地方

分库分表

当我们单表的数据量很大时,可能需要分库分表,那么首先要解决的问题是如何生成主键,可选的方案有:

  1. UUID
  2. 单机数据库自增ID
  3. Redis自增ID
  4. 跳跃式自增ID
  5. 雪花算法

UUID

前面提到过,并不适合做主键


单机数据库自增ID

即所有的库和表都依赖于一个某个表的自增ID,这样生成的性能瓶颈在于数据库。( [3] AUTO_INCREMENT锁机制 )


Redis自增ID

依赖Redis INCR


跳跃式自增ID

比如分十个表:tb0,tb1,...,tb9,设置初始值为:1,2,...,10,自增步长设置为10即可。

缺点:再次扩展时不好处理,除非实现预估未来多少多少年数据量不会使得10个表成为瓶颈


雪花算法

雪花算法生成的ID一共64位(2进制),最高位为保留位,后41位为时间戳(最多约69年),后10位为机器id(共1024个),最后12位为序列号(即同一个时刻可以生成4096个不同的id)

简单的代码为:

/**
 * 格式:x-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxxxx
 */
public class SnowFlake {

    private static long lastTimestamp = -1L;

    /**
     * 初始时间戳,根据业务设置
     * <p>
     * 如果没有这个初始时间戳的话,可用的时间少于69年
     */
    private static long startTimestamp;

    /**
     * 数据中心id
     */
    private static long dataCenterId;

    /**
     * 机器id
     */
    private static long workerId;

    /**
     * 序列号
     */
    private static long sequence;

    /**
     * 工作id长度为5位
     */
    private static long workerIdBits = 5L;

    /**
     * 数据中心id长度为5位
     */
    private static long dataCenterIdBits = 5L;

    /**
     * 序列号长度
     */
    private static long sequenceBits = 12L;

    /**
     * 工作id最大值
     * <p>
     * 原理:
     *   -1L的二进制为:1111....111,共64位
     *   左移5位:1111....1100000
     *   再亦或:11111
     */
    private static long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
     * 数据中心id最大值
     */
    private static long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);

    /**
     * 序列号最大值
     */
    private static long maxSequence = -1L ^ (-1L << sequenceBits);

    /**
     * 时间戳需要左移位数 12+5+5=22位
     */
    private static long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;

    /**
     * 数据id需要左移位数 12+5=17位
     */
    private static long dataCenterIdShift = sequenceBits + workerIdBits;

    /**
     * 工作id需要左移的位数,12位
     */
    private static long workerIdShift = sequenceBits;


    public synchronized static Long get() {

        long timestamp = System.currentTimeMillis();

        if (timestamp == lastTimestamp) {
            // 这里需要提前预估好,每一毫秒生成的id会不会超过4096个
            sequence = (sequence + 1) & maxSequence;
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        return ((timestamp - startTimestamp) << timestampLeftShift) |
                (dataCenterId << dataCenterIdShift) |
                (workerId << workerIdShift) |
                sequence;

    }

    public static void main(String[] args) {
        System.out.println(SnowFlake.get());
    }

}

缺点:雪花算法强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态


美团Leaf
【7】

上面的问题

  1. 待整理...
  2. 待整理...








说明

===================================

仅作为校招时的《个人笔记》,详细内容请看【参考】部分

===================================

参考

  1. https://pdai.tech/md/dev-spec/code-style/code-style-alibaba.html
  2. https://blog.csdn.net/dinghua_xuexi/article/details/106075183
  3. https://www.cnblogs.com/java-class/p/4727698.html
  4. https://www.cnblogs.com/funnyzpc/p/13541713.html
  5. 《高性能MySQL》(第3版)
  6. https://www.cnblogs.com/jajian/p/11101213.html 最后发现一篇总结的很好的博文
  7. https://tech.meituan.com/2017/04/21/mt-leaf.html

标签:自增,UUID,数据库,private,主键,static,生成,id
From: https://www.cnblogs.com/optimjie/p/17659743.html

相关文章

  • Facechain使用教程:3张照片就能生成个人写真,还完全免费
    1.效果展示下面4张图片,小伙伴们有没有看出来哪些是原图,哪些是AI生成的呢?上面的图片第1张是原图,其他的都是AI生成的哦~今天来教大家怎么用facechain训练自己的人物写真模型,然后就可以尝试各种风格的照片了。2.Facechain说明准备工作:Facechain了解一下,地址:https://github.com/modelsc......
  • mysql数据库连接密码的修改
    使用MySQL创建新连接时,密码是一个非常重要的组成部分。默认情况下,MySQL会生成一个随机的密码,该密码由一串随机的字符组成,包括数字、字母和其它特殊字符。在安装MySQL时,您可以选择使用自己的密码,也可以使用默认密码。默认密码是MySQL在安装时为root用户设置的密码。在许多情况下,建......
  • 用普里姆算法求最小生成树
    /*用普里姆算法求最小生成树*/#include<iostream>usingnamespacestd;/*邻接矩阵的类型定义*/#defineMAX10000000#defineMAX_VERTEX_NUM20typedefstruct{ charvexs[MAX_VERTEX_NUM];//用一维数组存储顶点信息 intedges[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//用二维......
  • hibernate——和数据库关联的对象的三种状态
    和数据库关联的对象有三种状态:瞬时,持久,脱管。瞬时:在数据库中还没有与该对象关联的记录,仅仅是一个普通的对象而已,超过作用域,就会被回收。一般都是new出来的对象,并且还没有与session建立联系。持久:与session建立了联系,并且在数据库中已经有了和该对象关联的记......
  • oracle学习笔记(13)——数据库的启动与关闭
    1、常用的服务(1)OracleServiceSID     数据库服务,这个服务会自动地启动和停止数据库。如果安装了一个数据库,它的缺省启动类型为自动。服务进程为ORACLE.EXE,参数文件initSID.ora,日志文件SIDALRT.log,控制台SVRMGRL.EXE、SQLPLUS.EXE。     注:SID-数据库标识 ......
  • oracle学习笔记(12)——数据库服务器工作模式与数据字典
    1、 专用服务器工作模式    1)概念:       专用服务器模式是指Oracle为每个用户进程启动一个专门的服务器进程,该服务器进程仅为该用户进程提供服务,直到用户进程断开连接时,对应的服务器进程才终止。    2)特点:       服务器进程与客户进......
  • 使用 Java 生成二维码图片
    0x01准备(1)软件版本IntelliJIDEA2023.1.3JDK18Tomcat10.1.11Maven3.8.6(2)技术栈servletzxing谷歌项目生成黑白二维码并可以附上logoqrcodegithub开源项目基于并拓展zxing(3)创建项目创建空项目在菜单栏-文件-项目结构中设置JDK及语言级别......
  • Git查看本机 ssh 公钥或生成公钥
    第一步查看git的ssh公钥是否存在在安装目录中打开GitBash并打开,然后在GitBash输入以下命名查看是否有密钥:ls-al~/.ssh第二步查看git的ssh公钥内容,并复制git的ssh公钥存储在id_rsa.pub文件中,如果id_rsa.pub文件,直接输入命令查看git公钥:cat~/.ssh/id_rsa.pub......
  • 数据库基本语法四 数据库查询(DQL)
    数据查询语言(DQL)是SQL语言中的一部分,主要用于查询数据库中的数据。DQL允许用户根据特定的条件来检索表中的数据,并且可以包含一些基本的操作和条件。语法结构:SELECT列名称1,列名称2FROM表名称;其中,SELECT指定要检索的列,FROM指定要查询的表,WHERE指定筛选条件。以下是一些......
  • keytool生成证书
    配置jdk下载jdk  https://www.oracle.com/java/technologies/downloads/#java8-linux解压文件tar-xvfjdk-8u381-linux-x64.tar.gz 配置环境变量exportPATH=$PATH:/root/jdk1.8.0_381/bin注意:此处仅配置了建议环境变量,若需要配置jdk完整环境变量,可自行检索配置......