十位用户唯一ID生成策略

本文介绍了如何在分布式系统中实现类似新浪微博和Twitter的十位唯一用户ID生成策略。通过创建数据库表,利用`replace into`获取自增ID,再结合`base_convert`将16进制转换为10进制确保唯一性。测试用例验证了55万个ID无重复,同时提出了在主从服务器环境中保持ID一致性的方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

新浪微博和twitter 等系统都有一窜数字ID来标示一个唯一的用户,这篇文章就是记录如何实现这种唯一数字ID

原理:使用MYSQL 自增ID 拼接任意字符..然后使用进制转换打乱规则

一般来说实现唯一性可以使用uniqid 或者time之类的方式生成,但是考虑到多台系统,也就是分布式系统。这种方式就不准确了。

实现:

新建一个数据库:

CREATE TABLE `user_number_id` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `stub` char(1) NOT NULL default '',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `stub` (`stub`)
) ENGINE=MyISAM

使用replace into 来取得生成的ID

REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID() as number_id;

取得number_id后生成十位数字ID

function auID($autoID)
{
    $autoID = $autoID;
    $autoCharacter = array("1","2","3","
<think>好的,我现在需要帮助用户解决如何在Java生成10位和12位的全局唯一ID的问题。首先,我需要回顾之前用户的问题历史,了解他们之前询问过除时间戳外的全局唯一ID生成方法,现在他们可能希望更具体地生成特定长度的数字ID用户之前的问题是关于Java中全局唯一ID的其他方法,比如UUID、Snowflake、数据库序列等。现在他们需要的是生成10位和12位的数字ID,这可能意味着他们需要的是更短、更紧凑的数字形式,而不是像UUID那样的字符串或者更长的Snowflake ID。 首先,我需要确定生成纯数字的全局唯一ID的方法。常见的数字ID生成方式包括数据库自增、Snowflake算法、Redis的自增操作,以及一些分段批量生成的方案,比如美团Leaf。不过,这些方法生成ID位数可能不符合用户的要求。例如,标准的Snowflake生成的是64位的长整型,转换成十进制通常是18位左右,显然超过了用户需要的10或12位。 因此,我需要考虑如何调整这些方法,或者采用其他方法来缩短位数。可能的思路包括限制时间戳的范围、减少机器ID和序列号的位数,或者采用哈希截取并处理冲突。但需要注意,缩短位数可能会增加冲突的风险,因此需要权衡。 对于10位数字ID,范围是0到9999999999,即大约100亿个唯一值。而12位则是到999999999999,即约1万亿个。系统需要确保在ID的生命周期内不会耗尽,并且生成速度足够快。 首先,考虑数据库自增。如果使用数据库的自增主键,可以通过设置起始值和步长来控制位数。例如,从1亿开始自增,可以生成10位数字,但需要考虑单点故障和分库分表的情况。可能需要分片策略,比如每个实例生成不同范围的ID,但需要确保不重叠。 其次,Snowflake算法的变种。调整Snowflake的时间戳、机器ID和序列号的位数分配。例如,对于10位ID,可以分配时间戳为24位(约16天),机器ID5位(32台机器),序列号5位(每个节点每秒生成32个ID)。但这样的时间范围可能太短,容易导致重复。或者采用更短的时间戳,比如使用相对时间,从某个起点开始计算。 另外,可以考虑使用Redis的自增命令,通过设置合适的初始值和自增步长,比如起始值为1000000000,这样自增生成ID都是10位的。但需要确保Redis的高可用性,避免单点故障。 还有一种方法是使用哈希函数(如MD5或SHA-1)生成较长的哈希值,然后截取部分并转换为数字,再取模得到所需位数。但这样可能会有冲突,需要额外处理,比如重试或使用布隆过滤器检测冲突。 接下来,针对10位和12位的具体实现,我需要考虑不同方法的可行性和潜在问题。例如,对于10位ID,使用数据库分片,每个实例生成不同范围的ID,比如实例1生成1-1000000000,实例2生成1000000001-2000000000,依此类推。但需要协调分片策略,避免重复。 或者,使用时间戳的一部分,比如取当前时间的秒数或毫秒数的后几位,结合机器ID和序列号,组合成较短的ID。例如,取毫秒数的后5位,机器ID3位,序列号2位,组合成10位数字。但需要确保时间戳部分不会在短时间内重复,特别是在高并发场景下。 对于12位ID,同样的方法可以有更多的位数分配,时间戳部分可以更长,比如30位(约34年),机器ID6位(64台机器),序列号6位(每个节点每秒生成64个ID),总和为30+6+6=42位,这样转换为十进制可能超过12位,所以需要调整。可能需要更紧凑的分配,例如时间戳用更小的粒度,或者采用不同的编码方式,如Base62而不是十进制,但用户要求的是纯数字,所以只能使用十进制。 可能的另一种方法是使用美团的Leaf方案,通过预分配号段的方式生成ID。例如,服务启动时从数据库获取一个号段范围,如1000000000-1000009999,这样每个实例可以生成1000个10位的ID,之后重新获取下一个号段。这需要数据库来管理号段的分配,并处理实例重启或故障的情况。 还需要考虑ID生成速度和系统的吞吐量。例如,如果每秒需要生成大量的ID,那么像Snowflake这样的算法可能更合适,而Leaf方案则需要足够的号段缓存来应对高并发。 此外,必须处理时钟回拨的问题,尤其是在使用基于时间的算法时。可能需要使用NTP同步时钟,或者在检测到回拨时等待或抛出异常。 最后,要确保生成ID不仅是唯一的,还要尽可能避免猜测和规律性,尤其是在安全敏感的场合。不过,用户可能更关注的是唯一性和性能,而不是安全性。 总结可能的实现方案: 1. 改造Snowflake算法,调整各部分的位数分配以适应10位或12位。 2. 使用数据库分片或自增步长生成特定范围的ID。 3. 利用Redis的自增操作,设置合适的起始值和步长。 4. 预分配号段,如Leaf方案,管理号段的分配和缓存。 5. 结合时间戳和随机数,通过哈希和截取生成较短的数字ID,处理可能的冲突。 需要为每种方案提供代码示例或具体步骤,并指出优缺点,帮助用户根据实际场景选择最合适的方法。</think>在Java生成10位/12位数字全局唯一ID需要根据具体场景选择不同实现方案,以下分步骤说明可行方法及实现要点: --- ### **一、10位数字ID生成方案(范围:1-9,999,999,999)** #### **1. 分段数据库自增法** ```java // 数据库设计(MySQL示例) CREATE TABLE id_generator ( biz_tag VARCHAR(32) PRIMARY KEY, -- 业务标识 max_id BIGINT NOT NULL, -- 当前最大ID step INT NOT NULL -- 号段长度 ); // Java代码(使用MyBatis获取) public synchronized Long generateId(String bizTag) { IdRange range = dao.selectForUpdate(bizTag); // 加锁查询 long current = range.getCurrent(); if (current >= range.getMax()) { // 号段用完时申请新号段 dao.updateMaxId(bizTag, range.getMax() + range.getStep()); current = range.getMax(); } return ++current; } ``` **核心参数**: - 初始`max_id=1_000_000_000`(10位起始值) - `step=1000`(每次取1000个ID) **优点**:完全可控、可扩展 **缺点**:需要数据库维护 --- #### **2. 压缩版Snowflake算法** ```java public class CompactSnowflake { private final long workerId; // 5位(0-31) private final long datacenterId; // 5位(0-31) private long sequence = 0L; private long lastTimestamp = -1L; public synchronized long nextId() { long timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new RuntimeException("时钟回拨"); } if (timestamp == lastTimestamp) { sequence = (sequence + 1) & 0x1F; // 5位序列号(0-31) if (sequence == 0) timestamp = tilNextMillis(lastTimestamp); } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - 1609459200000L) << 20) // 从2021-01-01开始的毫秒数 | (datacenterId << 15) | (workerId << 10) | sequence; } } ``` **位分配**: - 时间差:30位(约34年有效期) - 数据中心+机器:5+5位 - 序列号:5位 **输出处理**:将生成的64位数值转换为十进制后截取最后10位 **注意**:需定期校准基准时间戳 --- ### **二、12位数字ID生成方案(范围:1-999,999,999,999)** #### **1. Redis原子递增法** ```java Jedis jedis = new Jedis("redis-server"); // 初始化:jedis.set("order_id", "100000000000"); public String generate12DigitId() { Long id = jedis.incr("order_id"); if (id > 999_999_999_999L) { throw new RuntimeException("ID溢出"); } return String.format("%012d", id); // 补零到12位 } ``` **关键配置**: - 初始值设置为`100_000_000_000`(保证12位起点) - 启用Redis持久化防止数据丢失 --- #### **2. 时间戳+随机数组合法** ```java public class TimestampRandomGenerator { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHH"); private static final AtomicInteger counter = new AtomicInteger(0); public static String generate() { String timePart = sdf.format(new Date()); // 8位(年月日小时) String randomPart = String.format("%04d", ThreadLocalRandom.current().nextInt(10000)); // 4位随机数 return timePart + randomPart; // 合计12位 } } ``` **冲突处理**: - 添加自增序列:`counter.getAndIncrement() % 10000` - 每小时重置计数器 --- ### **三、方案对比选型** | 方案 | 10位适用性 | 12位适用性 | 并发能力 | 实现复杂度 | |---------------------|-----------|-----------|---------|----------| | 分段数据库自增 | ★★★★☆ | ★★★★☆ | 中 | 中 | | 压缩Snowflake | ★★★☆☆ | ★★★★☆ | 高 | 高 | | Redis原子递增 | ★★☆☆☆ | ★★★★☆ | 高 | 低 | | 时间戳+随机数 | ★☆☆☆☆ | ★★★☆☆ | 低 | 低 | --- ### **四、必须注意的临界问题** 1. **ID耗尽风险**: - 10位ID:每秒生成1万ID时,约需31年耗尽 - 12位ID:同等速率下可持续3,171年 2. **时钟回拨处理**: ```java protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { Thread.sleep(10); timestamp = timeGen(); } return timestamp; } ``` 3. **分布式一致性**: - 使用ZooKeeper管理机器ID分配 - 数据库方案需用`SELECT ... FOR UPDATE`实现行级锁 --- **最终建议**: - 金融交易类系统:优先选择分段数据库方案 - 物联网高频场景:使用压缩版Snowflake - 简单临时需求:时间戳+随机数组合
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值