欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MySQL分庫(kù)分表后主鍵ID生成的八種方案

 更新時(shí)間:2025年08月08日 08:59:04   作者:墨夶  
當(dāng)你的MySQL數(shù)據(jù)庫(kù)因數(shù)據(jù)量或并發(fā)壓力進(jìn)行分庫(kù)分表后,主鍵ID重復(fù)會(huì)成為系統(tǒng)崩潰的導(dǎo)火索,本文將從底層原理出發(fā),結(jié)合真實(shí)業(yè)務(wù)代碼,深度解析8種主流主鍵ID生成方案,助你構(gòu)建穩(wěn)定可靠的分庫(kù)分表系統(tǒng),需要的朋友可以參考下

分庫(kù)分表后的主鍵ID沖突陷阱

當(dāng)你的MySQL數(shù)據(jù)庫(kù)因數(shù)據(jù)量或并發(fā)壓力進(jìn)行分庫(kù)分表后,主鍵ID重復(fù)會(huì)成為系統(tǒng)崩潰的導(dǎo)火索。假設(shè)你將訂單表拆分為10個(gè)分表,每個(gè)分表都從1開(kāi)始自增,最終會(huì)出現(xiàn)以下災(zāi)難性問(wèn)題:

  • ID沖突:不同分表中的訂單ID可能完全相同
  • 業(yè)務(wù)混亂:無(wú)法通過(guò)ID直接定位數(shù)據(jù)所在分表
  • 分布式事務(wù)失敗:主鍵沖突導(dǎo)致寫入操作異常

本文將從底層原理出發(fā),結(jié)合真實(shí)業(yè)務(wù)代碼,深度解析8種主流主鍵ID生成方案,助你構(gòu)建穩(wěn)定可靠的分庫(kù)分表系統(tǒng)。

一、方案一:數(shù)據(jù)庫(kù)自增ID + 分段設(shè)置

核心思想

通過(guò)為每個(gè)分表配置不同的起始值步長(zhǎng),確保ID全局唯一。

實(shí)現(xiàn)代碼

-- 分表1(t_order_0)配置
ALTER TABLE t_order_0 AUTO_INCREMENT = 1; -- 起始值1,步長(zhǎng)10

-- 分表2(t_order_1)配置
ALTER TABLE t_order_1 AUTO_INCREMENT = 11; -- 起始值11,步長(zhǎng)10

-- 分表3(t_order_2)配置
ALTER TABLE t_order_2 AUTO_INCREMENT = 21; -- 起始值21,步長(zhǎng)10

代碼注解

  • 步長(zhǎng)設(shè)置:若總分表數(shù)為N,則每個(gè)分表步長(zhǎng)設(shè)為N
  • 起始值計(jì)算:分表索引i的起始值為i * N + 1
  • 適用場(chǎng)景:分表數(shù)量固定的小規(guī)模系統(tǒng)

Java調(diào)用示例

// 根據(jù)訂單ID計(jì)算分表索引
int getTableIndex(Long orderId) {
    return (int)(orderId % 10); // 假設(shè)分10個(gè)表
}

// 插入訂單
void insertOrder(Order order) {
    int tableIndex = getTableIndex(order.getId());
    String tableName = "t_order_" + tableIndex;
    
    // 動(dòng)態(tài)拼接SQL
    String sql = "INSERT INTO " + tableName + "(id, order_no) VALUES (?, ?)";
    jdbcTemplate.update(sql, order.getId(), order.getOrderNo());
}

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)缺點(diǎn)
實(shí)現(xiàn)簡(jiǎn)單,無(wú)需額外組件分表數(shù)量固定,擴(kuò)容困難
性能優(yōu)秀,直接利用數(shù)據(jù)庫(kù)特性高并發(fā)下單表自增ID可能耗盡
存儲(chǔ)效率高,ID緊湊需手動(dòng)維護(hù)分表配置

二、方案二:UUID全局唯一標(biāo)識(shí)符

核心思想

利用UUID的128位隨機(jī)性保證全局唯一性。

實(shí)現(xiàn)代碼

-- 創(chuàng)建訂單表(主鍵字段為UUID)
CREATE TABLE t_order (
    id CHAR(36) PRIMARY KEY, -- UUID長(zhǎng)度36位
    order_no VARCHAR(50)
);

Java生成UUID

import java.util.UUID;

public class Order {
    private String id = UUID.randomUUID().toString(); // 生成UUID
    private String orderNo;

    // Getter & Setter
}

代碼注解

  • UUID格式xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
  • 存儲(chǔ)優(yōu)化:可壓縮為16字節(jié)二進(jìn)制存儲(chǔ)(Base128編碼)

性能優(yōu)化示例

// 壓縮UUID為16字節(jié)
public byte[] compressUuid(String uuid) {
    return UUID.fromString(uuid).toString().getBytes(StandardCharsets.UTF_8);
}

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)缺點(diǎn)
完全隨機(jī),無(wú)依賴數(shù)據(jù)庫(kù)存儲(chǔ)空間大(36字節(jié))
適用于分布式系統(tǒng)B+樹(shù)索引性能差(隨機(jī)寫入)
無(wú)需協(xié)調(diào)生成ID無(wú)法直接定位分表

三、方案三:Snowflake算法(Twitter開(kāi)源)

核心思想

通過(guò)時(shí)間戳+機(jī)器ID+序列號(hào)生成64位有序ID。

Java實(shí)現(xiàn)代碼

public class SnowflakeIdGenerator {
    private final long twepoch = 1288834974657L; // 起始時(shí)間戳(2010-11-04)
    private final long workerIdBits = 5L; // 機(jī)器ID位數(shù)
    private final long datacenterIdBits = 5L; // 數(shù)據(jù)中心ID位數(shù)
    private final long sequenceBits = 12L; // 序列號(hào)位數(shù)
    
    private final long maxWorkerId = ~(-1L << workerIdBits);
    private final long maxDatacenterId = ~(-1L << datacenterIdBits);
    private final long sequenceMask = ~(-1L << sequenceBits);
    
    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    public synchronized long nextId() {
        long timestamp = timeGen();
        
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("時(shí)鐘回?fù)? %d ms", lastTimestamp - timestamp));
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }

        lastTimestamp = timestamp;
        return (timestamp - twepoch) << (workerIdBits + datacenterIdBits + sequenceBits)
                | (datacenterId << (workerIdBits + sequenceBits))
                | (workerId << sequenceBits)
                | sequence;
    }

    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    protected long timeGen() {
        return System.currentTimeMillis();
    }
}

代碼注解

  • 位運(yùn)算邏輯
    • 時(shí)間戳(41位) → 代表從起始時(shí)間到現(xiàn)在的毫秒數(shù)
    • 數(shù)據(jù)中心ID(5位) → 支持32個(gè)數(shù)據(jù)中心
    • 機(jī)器ID(5位) → 支持32個(gè)節(jié)點(diǎn)
    • 序列號(hào)(12位) → 毫秒內(nèi)最多支持4096個(gè)ID

調(diào)用示例

SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
long orderId = idGenerator.nextId();
System.out.println("生成的ID: " + orderId);

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)缺點(diǎn)
全局唯一且有序依賴系統(tǒng)時(shí)間(時(shí)鐘回?fù)軉?wèn)題)
支持分布式場(chǎng)景需要預(yù)先分配機(jī)器ID
存儲(chǔ)效率高(8字節(jié))最大時(shí)間戳限制為69年

四、方案四:Redis原子自增

核心思想

利用Redis的INCR命令生成全局唯一ID。

Redis配置

# 啟動(dòng)Redis
redis-server

# 設(shè)置初始值
SET order_id 1000

Java調(diào)用示例

import redis.clients.jedis.Jedis;

public class RedisIdGenerator {
    private Jedis jedis = new Jedis("localhost");

    public long generateId() {
        return jedis.incr("order_id"); // 原子自增
    }
}

代碼注解

  • 高可用保障:建議使用Redis集群模式
  • 分片策略:可通過(guò)不同Key區(qū)分業(yè)務(wù)類型(如order_id、user_id

集群模式優(yōu)化

// Redis Cluster客戶端配置
JedisCluster jedisCluster = new JedisCluster(new HostAndPort("192.168.1.101", 6379),
                                             new HostAndPort("192.168.1.102", 6379));

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)缺點(diǎn)
高性能(每秒百萬(wàn)級(jí)QPS)依賴Redis服務(wù)穩(wěn)定性
支持分布式場(chǎng)景單點(diǎn)故障風(fēng)險(xiǎn)(需集群)
簡(jiǎn)單易用需處理網(wǎng)絡(luò)延遲

五、方案五:第三方ID生成服務(wù)

核心思想

使用獨(dú)立服務(wù)(如滴滴TinyID)生成ID。

TinyID集成示例

// 1. 添加Maven依賴
<dependency>
    <groupId>com.didi</groupId>
    <artifactId>tinyid-client</artifactId>
    <version>1.0.0</version>
</dependency>

// 2. 配置TinyID
TinyIdClient client = new TinyIdClient("http://tinyid-service.com");

// 3. 生成ID
String businessType = "order";
Long id = client.getId(businessType);

代碼注解

  • TinyID原理:基于數(shù)據(jù)庫(kù)的Sequence表生成ID
  • 多租戶支持:通過(guò)businessType區(qū)分不同業(yè)務(wù)

TinyID服務(wù)端配置

CREATE TABLE tinyid (
    id BIGINT PRIMARY KEY,
    business_type VARCHAR(50),
    max_id BIGINT,
    step INT,
    version INT
);

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)缺點(diǎn)
支持多業(yè)務(wù)類型需維護(hù)獨(dú)立服務(wù)
高可用性(支持集群)依賴網(wǎng)絡(luò)通信
靈活配置步長(zhǎng)學(xué)習(xí)成本較高

六、方案六:COMB ID(組合ID)

核心思想

時(shí)間戳隨機(jī)數(shù)組合,生成有序UUID。

Java實(shí)現(xiàn)代碼

import java.time.Instant;
import java.util.Random;

public class CombIdGenerator {
    private final Random random = new Random();

    public String generateId() {
        byte[] uuid = new byte[16];
        
        // 前6字節(jié)為時(shí)間戳
        long timestamp = Instant.now().toEpochMilli();
        for (int i = 0; i < 6; i++) {
            uuid[i] = (byte)(timestamp >> (8 * (5 - i)));
        }
        
        // 后10字節(jié)為隨機(jī)數(shù)
        random.nextBytes(uuid);
        
        return bytesToHex(uuid);
    }

    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
}

代碼注解

  • 時(shí)間戳部分:確保ID有序性
  • 隨機(jī)部分:避免重復(fù)

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)缺點(diǎn)
兼具有序性和隨機(jī)性實(shí)現(xiàn)復(fù)雜度高
降低B+樹(shù)索引碎片依賴時(shí)間同步

七、方案七:數(shù)據(jù)庫(kù)中間件內(nèi)置策略

ShardingSphere示例

// 1. 配置ShardingSphere
spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.t_order.key-generator.props.worker.id=123

代碼注解

  • 內(nèi)置算法:支持UUID和Snowflake
  • 自定義擴(kuò)展:可通過(guò)接口實(shí)現(xiàn)自定義生成器

自定義生成器示例

public class CustomKeyGenerator implements KeyGenerator {
    @Override
    public Comparable<?> generateKey() {
        return new SnowflakeIdGenerator(1, 1).nextId();
    }
}

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)缺點(diǎn)
無(wú)縫集成ShardingSphere依賴中間件版本
支持多種算法配置復(fù)雜度高

八、方案八:基因法 + Hash分片

核心思想

在ID中嵌入分片基因,直接定位分表。

Java實(shí)現(xiàn)代碼

public class ShardIdGenerator {
    private static final int SHARD_COUNT = 8; // 8個(gè)分表
    private static final int GEN_BITS = 3; // 3位基因(2^3=8)

    public long generateShardId(long baseId, int shardIndex) {
        // 基因掩碼:0b00000111
        long mask = (1 << GEN_BITS) - 1;
        
        // 清除低3位基因
        long idWithoutGene = baseId & ~mask;
        
        // 設(shè)置新的基因
        return idWithoutGene | (shardIndex & mask);
    }
}

代碼注解

  • 基因提取:通過(guò)位運(yùn)算定位分表索引
  • 適用場(chǎng)景:Hash分片的分庫(kù)分表系統(tǒng)

分表查詢示例

long shardId = 1234567890123456789L;
int shardIndex = (int)(shardId & ((1 << 3) - 1)); // 提取低3位
String tableName = "t_order_" + shardIndex;

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)缺點(diǎn)
直接定位分表ID修改后需重新計(jì)算
無(wú)需額外組件分片規(guī)則強(qiáng)依賴基因位

如何選擇最適合你的方案?

方案適用場(chǎng)景推薦指數(shù)
數(shù)據(jù)庫(kù)自增+步長(zhǎng)小規(guī)模分表???
UUID分布式系統(tǒng)????
Snowflake高并發(fā)系統(tǒng)?????
Redis高性能要求????
TinyID多業(yè)務(wù)場(chǎng)景????
COMB ID有序性優(yōu)先???
ShardingSphere中間件集成????
基因法Hash分片????

最后提醒:選擇主鍵生成方案時(shí),需綜合考慮業(yè)務(wù)規(guī)模、性能需求運(yùn)維成本。記住:沒(méi)有銀彈方案,只有最合適的解決方案!

代碼包結(jié)構(gòu)

MainKeyGenerator/
├── config/
│   └── shard.properties       # 分片配置
├── service/
│   ├── RedisIdService.java    # Redis生成器
│   ├── SnowflakeService.java  # Snowflake實(shí)現(xiàn)
│   └── TinyIdService.java     # TinyID客戶端
├── model/
│   └── Order.java             # 訂單模型
└── util/
    └── ShardUtil.java         # 分片工具類

部署建議

  • 生產(chǎn)環(huán)境建議采用SnowflakeTinyID
  • 高并發(fā)場(chǎng)景優(yōu)先使用Redis集群
  • 小規(guī)模系統(tǒng)可使用數(shù)據(jù)庫(kù)自增步長(zhǎng)

“主鍵ID是分庫(kù)分表系統(tǒng)的命脈,選對(duì)方案,你的系統(tǒng)才能穩(wěn)如泰山!”

以上就是MySQL分庫(kù)分表后主鍵ID生成的八種方案的詳細(xì)內(nèi)容,更多關(guān)于MySQL主鍵ID生成方案的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • MySQL備份和還原操作小結(jié)

    MySQL備份和還原操作小結(jié)

    備份數(shù)據(jù)庫(kù)兩個(gè)主要方法是用mysqldump程序或直接拷貝數(shù)據(jù)庫(kù)文件,這篇文章主要介紹了MySQL備份和還原操作小結(jié),需要的朋友可以參考下
    2024-08-08
  • mysql部分替換sql語(yǔ)句分享

    mysql部分替換sql語(yǔ)句分享

    有時(shí)候需要對(duì)mysql中的內(nèi)容進(jìn)行部分替換,那么可以參考下面的文章。
    2011-11-11
  • navicat連接mysql報(bào)錯(cuò)1251的解決方法

    navicat連接mysql報(bào)錯(cuò)1251的解決方法

    這篇文章主要為大家詳細(xì)介紹了navicat連接mysql報(bào)錯(cuò)1251的解決方法,文中解決方法介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • Mysql binlog的查看方法

    Mysql binlog的查看方法

    MySQL的二進(jìn)制日志可以說(shuō)是MySQL最重要的日志了,本文主要介紹了Mysql binlog的查看方法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-04-04
  • MySQL中Nested-Loop Join算法小結(jié)

    MySQL中Nested-Loop Join算法小結(jié)

    數(shù)據(jù)庫(kù)中JOIN操作的實(shí)現(xiàn)主要有三種:嵌套循環(huán)連接(Nested Loop Join),歸并連接(Merge Join)和散列連接或者哈稀連接(Hash Join)。其中嵌套循環(huán)連接又視情況又有兩種變形:塊嵌套循環(huán)連接和索引嵌套循環(huán)連接。
    2015-12-12
  • mysql 導(dǎo)入導(dǎo)出數(shù)據(jù)庫(kù)、數(shù)據(jù)表的方法

    mysql 導(dǎo)入導(dǎo)出數(shù)據(jù)庫(kù)、數(shù)據(jù)表的方法

    這篇文章主要介紹了mysql 導(dǎo)入導(dǎo)出數(shù)據(jù)庫(kù)、數(shù)據(jù)表的方法,有需要的朋友可以參考一下
    2013-11-11
  • 簡(jiǎn)單的MySQL備份與還原方法分享

    簡(jiǎn)單的MySQL備份與還原方法分享

    這篇文章主要介紹了簡(jiǎn)單的MySQL備份與還原方法,文中Linux與Windows的兩種情況都有介紹,需要的朋友可以參考下
    2015-08-08
  • MYSQL慢查詢和日志實(shí)例講解

    MYSQL慢查詢和日志實(shí)例講解

    這篇文章主要介紹了MYSQL慢查詢和日志實(shí)例講解,有對(duì)這方面感興趣的同學(xué)可以研究學(xué)習(xí)下
    2021-02-02
  • mysql如何通過(guò)my.ini更改默認(rèn)字符集

    mysql如何通過(guò)my.ini更改默認(rèn)字符集

    這篇文章主要介紹了mysql如何通過(guò)my.ini更改默認(rèn)字符集問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • MySQL OOM 系列一 Linux內(nèi)存分配

    MySQL OOM 系列一 Linux內(nèi)存分配

    今天想提到的是線上一個(gè)4G的RDS實(shí)例,發(fā)生了OOM(out of memory)的問(wèn)題,MySQL進(jìn)程被直接Kill掉了。在解釋這個(gè)問(wèn)題的時(shí)候,我們首先需要從Linux系統(tǒng)內(nèi)存分配策略講起
    2016-07-07

最新評(píng)論