问:分库分表场景下ID的生成策略?

news2024/10/25 7:28:15

在数据库架构设计中,分库分表是一种常见的优化策略,用于解决单表数据量过大导致的查询性能下降问题。然而,分库分表后如何处理主键ID成为了一个关键问题。因为每个表如果都从1开始累加,会导致主键冲突,因此需要生成全局唯一的ID来支持。以下是几种常见的ID生成方案。

UUID(Universally Unique Identifier)

UUID是一种全局唯一标识符,基于算法生成,不依赖于数据库。UUID的长度为128位,通常以36位长度的十六进制数字字符串表示(包括四个连字号)。UUID的优点在于全局唯一性,无需担心主键冲突的问题,适用于分布式系统或需要跨多个数据库实例进行数据同步的场景。然而,UUID作为主键存在以下缺点:

  • 存储空间大:UUID长度为36位,占用存储空间较大。
  • 无序性:UUID是无序的,新增数据时如果采用btree索引,每次新增一条数据都需要重新排序,影响性能。
  • 可读性差:UUID的随机性导致其不易读懂。

使用示例

import java.util.UUID;

public class ExampleEntity {
    private String id;
    private String name;

    public ExampleEntity(String name) {
        this.id = UUID.randomUUID().toString();
        this.name = name;
    }

    // 省略 getter 和 setter 方法
}

数据库自增ID

数据库自增ID是一种简单且常用的主键生成方式。MySQL使用auto_increment实现,Oracle则使用序列。然而,分库分表后,如果每个表都从1开始累加,会导致主键冲突。为了避免这种情况,可以采用以下两种方式:

  1. 单独创建主键表维护唯一标识:通过执行SQL获取唯一标识,然后添加到某个分表中。
  2. 设置不同步长:在分布式环境中,设置不同数据库的自增ID步长,确保全局唯一性。例如,设置实例1步长为1,实例2步长为2。

使用示例

-- 创建主键表
CREATE TABLE id_generator (
    id BIGINT AUTO_INCREMENT PRIMARY KEY
);

-- 获取唯一ID
INSERT INTO id_generator () VALUES ();
SELECT LAST_INSERT_ID();

Redis生成ID

Redis是一个高性能的键值存储系统,支持原子性操作如INCRINCRBY,因此可以用来生成全局唯一的ID。Redis生成ID的优点在于不依赖于数据库,性能优越,且支持高并发场景。然而,引入新的组件会增加系统的复杂度,可用性也会受到影响。

使用示例

import redis.clients.jedis.Jedis;

public class RedisIdGenerator {
    private Jedis jedis;
    private String key;

    public RedisIdGenerator(String host, int port, String key) {
        this.jedis = new Jedis(host, port);
        this.key = key;
    }

    public long generateId() {
        return jedis.incr(key);
    }

    // 省略其他方法
}

Snowflake算法

Snowflake算法是由Twitter开源的一种分布式ID生成算法,它将64位的long型ID分为四个部分:

  • 符号位(1位):始终为0,表示正数。
  • 时间戳(41位):精确到毫秒级,支持69年的唯一性。
  • 工作机器ID(10位):分为数据中心ID和工作机器ID各5位,支持最多1024个节点。
  • 序列号(12位):同一毫秒内生成的不同ID,支持每个节点每毫秒产生4096个唯一ID。

Snowflake算法生成的ID有序且唯一,适用于分布式系统,但依赖于机器时钟,如果时钟回拨会导致ID重复。

使用示例

import java.util.concurrent.atomic.AtomicLong;

public class SnowflakeIdGenerator {
    private final long twepoch = 1288834974657L; // 起始时间戳

    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final long sequenceBits = 12L;

    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    private final long sequenceMask = -1L ^ (-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("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

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

        lastTimestamp = timestamp;

        return ((timestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    }

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

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

    // 省略其他方法
}

美团Leaf分布式ID生成系统

Leaf是美团点评开源的分布式ID生成系统,支持多种ID生成策略,包括UUID、Snowflake、Segment等。Leaf的设计理念是简单、高效、安全和可扩展,旨在解决分布式系统中的ID生成问题。Leaf生成的ID具有全局唯一性、趋势递增、单调递增、信息安全等特点,但需要依赖关系数据库、Zookeeper等中间件。

使用示例

import com.meituan.leaf.core.LeafServer;
import com.meituan.leaf.core.LeafServerApplication;

public class LeafServerExample {
    public static void main(String[] args) {
        LeafServerApplication leafServerApplication = new LeafServerApplication();
        leafServerApplication.setZookeeperAddress("127.0.0.1:2181");
        leafServerApplication.setDatabaseUrl("jdbc:mysql://localhost:3306/leaf");
        leafServerApplication.setDatabaseUser("root");
        leafServerApplication.setDatabasePassword("password");

        LeafServer leafServer = new LeafServer(leafServerApplication);
        leafServer.start();
    }
}

总结

方案描述优点缺点适用场景
UUID全局唯一标识符全局唯一,无需额外中间件性能较低,占用存储空间较大需要全局唯一标识符但不关心性能和存储空间的场景
数据库自增ID数据库自带的自增ID简单易用,ID有序递增分库分表后需要特殊处理,存在单点故障风险单库单表环境
Redis生成ID利用Redis的原子操作生成ID性能优越,ID有序递增增加了系统复杂度,需要依赖Redis中间件对性能要求较高,且可以接受增加系统复杂度的场景
Snowflake算法基于Twitter的分布式ID生成算法性能优越,ID唯一且有序,可自定义ID长度和前缀需要依赖额外的中间件,ID长度受限大规模分布式系统,需要全局唯一且有序的ID
美团Leaf美团开源的分布式ID生成系统,支持多种ID生成方式,如号段模式、Snowflake模式等性能优越,支持多种ID生成方式,灵活性高,ID唯一且有序需要依赖额外的中间件,配置和运维相对复杂大规模分布式系统,需要全局唯一且有序的ID,且对灵活性有要求的场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2223080.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于PID控制器和四象限DC-DC功率转换器的永磁直流电机速度控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于PID控制器和四象限DC-DC功率转换器的永磁直流电机速度控制系统simulink建模与仿真。系统包括电流PI控制器&#xff0c;速度PI控制器&#xff0c;四象限DC-DC功率转换器&am…

Leetcode3. 无重复字符的最长子串

问题描述&#xff1a; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。示例 2: 输入: s "bbbbb&q…

Ubuntu 24.04 系统上配置 Node.js 运行环境

本文我们重点介绍两种安装 Node.js 的方法。第一种方法使用 NVM (Node VersionManager)&#xff0c;这是安装和管理多个 Node.js 版本的最好和最快的方法。第二种方法使用官方包存储库在 Ubuntu 上安装 Node.js&#xff0c;一次只允许安装一个版本。 必备条件 A running Ubun…

Java学习Day52:金酬外护遭魔毒,圣显幽魂救本原(验证码补全,新增预约逻辑)

1.验证码补全 //发送验证码sendValidateCode() {/*** 1.手机号不能为空* 2.手机号符合规范* 3.发送一分钟后才可再次发送*///获取手机号let telephone this.orderInfo.telephone;//验证手机号不能空if (telephone undefined){this.$message.error("请输入手机号");…

CentOS 8在Linux虚拟机修改IP地址,出现:错误:“ens160“ 不是活动的连接。错误:未提供活动连接。

问题&#xff1a;错误&#xff1a;"ens160" 不是活动的连接。错误&#xff1a;未提供活动连接。 1.查看网络服务运行状态&#xff1a; 1)CentOS 7执行命令&#xff1a;systemctl status network 2)CentOS 8执行命令&#xff1a;systemctl status NetworkManager&a…

【深度学习基础】详解Pytorch搭建CNN卷积神经网络实现手写数字识别

MNIST 数据集,其包含70000 个2828 的手写数字的数据集,其中又分为60000 个训练样本与10000 个测试样本。 安装实验用到的包 anaconda promt 安装python包, 首先在开始界面打开prompt 进入到相应的虚拟环境中,下面的python38你自己创建的虚拟环境名称。 # 激活虚拟环境,v…

微信小程序中关闭默认的 `navigationBar`,并使用自定义的 `nav-bar` 组件

要在微信小程序中关闭默认的 navigationBar&#xff0c;并使用自定义的 nav-bar 组件&#xff0c;你可以按照以下步骤操作&#xff1a; 1. 关闭默认的 navigationBar 在你的页面的配置文件 *.json 中设置 navigationBar 为 false。你需要在页面的 JSON 配置文件中添加以下代码…

如何在Debian操作系统上安装Doker

本章教程&#xff0c;主要介绍如何在Debian 11 系统上安装Docker。主要使用一键安装Docker脚本和一键卸载脚本来完成。 一、安装Docker #!/bin/bashRED\033[0;31m GREEN\033[0;32m YELLOW\033[0;33m BLUE\033[0;34m NC\033[0mCURRENT_DIR$(cd "$(dirname "$0")…

微知-Linux内核自带的模块被压缩为ko.xz后如何恢复成不压缩版本?(xz -d xxx.ko.xz)

背景 在使用crash定位问题的时候需要使用ko使用对应的符号信息。直接用ko.xz无法正确加载。需要恢复成ko文件。本文介绍如何解压缩。 恢复步骤 在Linux系统中&#xff0c;.xz文件是一种使用LZMA2算法压缩的文件格式。要解压ko.xz文件&#xff0c;你可以使用xz命令行工具。 …

UML外卖系统报告(包含具体需求分析)

1 系统背景 随着互联网技术的快速发展&#xff0c;外卖订餐服务逐渐成为人们生活中的一部分。传统的电话订餐方式面临诸多不便和限制&#xff0c;而基于互联网的外卖订餐系统则提供了更加便捷、快速和高效的订餐服务。这种系统通过将餐厅、顾客和配送人员连接起来&#xff0c;…

职业规划:程序员的成长之路

引言 在快速发展的技术行业中&#xff0c;作为一名程序员&#xff0c;职业规划是至关重要的。从初出茅庐到成为技术专家&#xff0c;每一步都需要明确的目标和不懈的努力。 成长历程 初级阶段&#xff08;0-2年&#xff09; 学习与适应&#xff1a;刚入行时&#xff0c;主要任…

Redis 安装部署与常用命令

目录 一、关系数据库与非关系型数据库 1.1 关系型数据库 1.2 非关系型数据库 1.3关系型数据库和非关系型数据库区别 &#xff08;1&#xff09;数据存储方式不同 &#xff08;2&#xff09;扩展方式不同 &#xff08;3&#xff09;对事务性的支持不同 1.4 非关系型数据…

大数据Azkaban(二):Azkaban简单介绍

文章目录 Azkaban简单介绍 一、Azkaban特点 二、Azkaban组成结构 三、Azkaban部署模式 1、solo-server ode&#xff08;独立服务器模式&#xff09; 2、two server mode&#xff08;双服务器模式&#xff09; 3、distributed multiple-executor mode&#xff08;分布式多…

54页可编辑PPT | 大型集团企业数据治理解决方案

这份PPT是关于大型集团企业数据治理的全面解决方案&#xff0c;它详细介绍了数据治理的背景、需求、管理范围、框架、解决思路&#xff0c;以及数据治理在实际操作中的关键步骤。内容涵盖了数据架构、数据质量、数据应用等方面的问题&#xff0c;并提出了数据资产透视、智能搜索…

使用 NumPy 和 Matplotlib 实现交互式数据可视化

使用 NumPy 和 Matplotlib 实现交互式数据可视化 在数据分析中&#xff0c;交互式可视化可以更好地帮助我们探索和理解数据。虽然 Matplotlib 是静态绘图库&#xff0c;但结合一些技巧和 Matplotlib 的交互功能&#xff08;widgets、event handlers&#xff09;&#xff0c;我…

使用 Python 的 BeautifulSoup(bs4)解析复杂 HTML

使用 Python 的 BeautifulSoup&#xff08;bs4&#xff09;解析复杂 HTML&#xff1a;详解与示例 在 Web 开发和数据分析中&#xff0c;解析 HTML 是一个常见的任务&#xff0c;尤其是当你需要从网页中提取数据时。Python 提供了多个库来处理 HTML&#xff0c;其中最受欢迎的就…

C++ 整型大数运算(大整数运算)项目

C 整型大数运算项目 一、项目介绍二、项目变量成员三、项目实现构造函数加法减法乘法先计算再进位边计算边进位 除法与取模判断输入输出 四、项目源代码展示在 Big_integer.h 中&#xff1a;在 Big_integer.cpp 中&#xff1a; 五、测试准确性六、优化方向 一、项目介绍 整型大…

Character AI被起诉!14岁青少年自杀,AI陪伴何去何从

终于&#xff0c;AI在青少年心理问题方面&#xff0c;被推上了风口浪尖。 最近&#xff0c;美国佛罗里达州&#xff0c;一名14岁男孩Sewell Setzer的父母控告Character AI公司&#xff0c;声称孩子沉迷该公司的AI聊天机器人&#xff0c;最后走上了自杀的道路。 跟AI聊天还能致…

基于STM32F103的按键检测仿真

基于STM32F103的按键检测 仿真软件&#xff1a; Proteus 8.17 编程软件&#xff1a; Keil 5 仿真实现&#xff1a; 按键第一次按下LED灯亮&#xff0c;第二次按下LED灯灭&#xff0c;往复 按键检测原理&#xff1a; 按键在电路中一端接单片机的IO口&#xff0c;一端接GND&a…

高效库存管理:金蝶云星空与管易云的盘亏单对接方案

高效库存管理&#xff1a;金蝶云星空与管易云的盘亏单对接方案 金蝶云星空与管易云的盘亏单对接方案 在企业日常运营中&#xff0c;库存管理是至关重要的一环。为了实现高效、准确的库存盘点和数据同步&#xff0c;我们采用了轻易云数据集成平台&#xff0c;将金蝶云星空的数据…