数据库id生成方案

数据库主键(id)的生成方案在设计系统时非常关键,既影响性能,也影响分布式扩展能力。

下面给出 5 种主流方案,并结合 Spring Boot + MyBatis + MySQL 项目,推荐最佳实践及示例代码。


🧩 一、常见主键生成方案对比

方案类型 示例 优点 缺点 适用场景
1. 自增主键(AUTO_INCREMENT) id BIGINT AUTO_INCREMENT 简单、易用、顺序性好 分布式环境易冲突、插入热点 单节点系统、小型项目
2. UUID(字符串) id CHAR(36) 全局唯一 查询慢、存储冗余大 无需排序的全局唯一标识
3. 雪花算法(Snowflake ID) long id = Snowflake.nextId(); 高性能、趋势递增 实现复杂、时间依赖 分布式系统
4. Redis / 数据库序列号 从 Redis 或 DB 获取自增号 全局唯一、可控 有外部依赖 多系统共享ID
5. 自定义混合ID(时间戳 + 随机数) 例如 20251025142312345 可读性好 可能重复(需控制) 临时或日志型记录

✅ 二、推荐方案(Spring Boot + MyBatis + MySQL)

建议使用:

雪花算法(Snowflake ID) + MyBatis 自动生成主键

这样既:

  • 不依赖 MySQL 自增(可分库分表);
  • 不会发生 ID 冲突;
  • 效率极高;
  • ID 可作为时间序列排序。

✅ 三、实现方案(雪花算法版本)

1️⃣ 新建工具类 SnowflakeIdGenerator.java

java 复制代码
package com.example.qrcode.util;

/**
 * 雪花算法ID生成器
 * 64位ID结构如下:
 * 1bit 符号位 + 41bit 时间戳 + 10bit 机器ID + 12bit 序列号
 */
public class SnowflakeIdGenerator {
    // 起始时间戳(自定义纪元)
    private static final long START_STAMP = 1672531200000L; // 2023-01-01

    // 每部分占位
    private static final long SEQUENCE_BIT = 12; // 序列号占12位
    private static final long MACHINE_BIT = 10;  // 机器ID占10位

    // 最大值
    private static final long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);

    // 偏移量
    private static final long MACHINE_LEFT = SEQUENCE_BIT;
    private static final long TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT;

    private long machineId;  // 机器ID
    private long sequence = 0L;
    private long lastStamp = -1L;

    public SnowflakeIdGenerator(long machineId) {
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("机器ID超出范围");
        }
        this.machineId = machineId;
    }

    // 获取下一个ID
    public synchronized long nextId() {
        long currStamp = System.currentTimeMillis();

        if (currStamp < lastStamp) {
            throw new RuntimeException("时钟回拨,拒绝生成ID");
        }

        if (currStamp == lastStamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0L) {
                currStamp = waitNextMillis(currStamp);
            }
        } else {
            sequence = 0L;
        }

        lastStamp = currStamp;

        return (currStamp - START_STAMP) << TIMESTAMP_LEFT // 时间部分
                | (machineId << MACHINE_LEFT)              // 机器部分
                | sequence;                                // 序列部分
    }

    private long waitNextMillis(long currStamp) {
        while (currStamp <= lastStamp) {
            currStamp = System.currentTimeMillis();
        }
        return currStamp;
    }
}

2️⃣ 在 Service 中使用

ini 复制代码
package com.example.qrcode.service;

import com.example.qrcode.entity.QrCodeRecord;
import com.example.qrcode.mapper.QrCodeMapper;
import com.example.qrcode.util.QrCodeUtil;
import com.example.qrcode.util.SnowflakeIdGenerator;
import org.springframework.stereotype.Service;

@Service
public class QrCodeService {

    private final QrCodeMapper qrCodeMapper;
    private final SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1L);

    public QrCodeService(QrCodeMapper qrCodeMapper) {
        this.qrCodeMapper = qrCodeMapper;
    }

    public QrCodeRecord createAndSave(String content) throws Exception {
        byte[] imageBytes = QrCodeUtil.generateQrAsBytes(content, 300, 300);

        QrCodeRecord record = new QrCodeRecord();
        record.setId(idGenerator.nextId());
        record.setContent(content);
        record.setImage(imageBytes);

        qrCodeMapper.insert(record);
        return record;
    }
}

3️⃣ 数据库表中改为手动ID插入

sql 复制代码
CREATE TABLE qr_code_record (
    id BIGINT PRIMARY KEY COMMENT '雪花算法ID',
    content VARCHAR(500) NOT NULL,
    image LONGBLOB NOT NULL,
    created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

✅ 四、其他备选方案(简略)

🚀 1. MySQL 自增主键

sql 复制代码
id BIGINT PRIMARY KEY AUTO_INCREMENT

简单直接,适合单机。


🚀 2. UUID 方案

less 复制代码
record.setId(UUID.randomUUID().toString());

表结构:

scss 复制代码
id CHAR(36) PRIMARY KEY

但效率不如 Long 型。


🚀 3. Redis 全局序列号

ini 复制代码
Long id = redisTemplate.opsForValue().increment("global:qr:id");

✅ 五、推荐结论

场景 推荐方案
单体应用、小项目 AUTO_INCREMENT
分布式系统、微服务 雪花算法 (Snowflake)
跨系统全局唯一ID Redis 序列 或 UUID
临时或可读性强 ID 时间戳 + 随机数拼接
相关推荐
To_OC1 小时前
跑了3个Docker容器后,我终于搞懂镜像和容器到底啥关系
后端·docker·容器
东风破_2 小时前
Docker 基础:为什么需要容器?
前端·后端·nginx
东风破_2 小时前
Docker 实战:使用 Nginx 作为容器入口代理 Node 服务
前端·后端·nginx
Lyyaoo.2 小时前
Spring Boot Validation 声明式参数校验
spring boot·后端·mysql
剩下了什么3 小时前
float32 与 float64 精度陷阱:如何在 Go 中避免错误使用
开发语言·后端·golang
Rain的Java大神之路3 小时前
介绍一下分布式事务
java·分布式·后端·spring·spring cloud·架构·springcloud
南城以南溫暖如初1474 小时前
外卖CPS实战指南:从技术选型到运营落地全流程解析
java·spring boot·mysql·架构·vue
IT_陈寒7 小时前
Vue的响应式什么时候会失灵?这个坑我踩了
前端·人工智能·后端
Patrick_Wilson7 小时前
Web 认证方案技术指南
前端·后端·面试
与驴OO7 小时前
设计 Skill 系统,这 3 个坑我替你踩过了
后端·aigc