数据库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 时间戳 + 随机数拼接
相关推荐
Thepatterraining3 小时前
MySQL Java开发终极教程:三种技术栈对比,大厂资深开发经验分享
数据库·mysql
仪器工程师3 小时前
报错提示 “unclosed parenthesis”“mismatched quotes” 的解决办法
后端
yangwan3 小时前
Ubunut 22.04 安装 Docker 24.0.x
前端·后端
用户68545375977693 小时前
🌊 消息队列的削峰填谷作用和实际应用:洪水调节大师!
后端
bug攻城狮4 小时前
Spring Boot 2.6+ 整合 PageHelper 启动报错:循环依赖解决方案全解析
java·spring boot·后端
IT_陈寒5 小时前
Vue 3.4性能优化实战:5个鲜为人知的Composition API技巧让打包体积减少40%
前端·人工智能·后端
大厂码农老A5 小时前
我带的外包兄弟放弃大厂转正,薪资翻倍入职字节
java·后端·面试
武子康5 小时前
大数据-136 - ClickHouse 集群 表引擎详解 选型实战:TinyLog/Log/StripeLog/Memory/Merge
大数据·分布式·后端
Somehow0075 小时前
从Binlog到消息队列:构建可靠的本地数据同步管道(macOS本地部署Canal & RocketMQ并打通全流程)
后端·架构