用雪花算法就不会产生重复的ID?
前言今天想和大家聊聊分布式系统中常用的雪花算法(Snowflake)——这个看似完美的ID生成方案,实际上暗藏玄机。
有些小伙伴在工作中一提到分布式ID,第一个想到的就是雪花算法。
确实,它简单、高效、趋势递增,但你知道吗?
雪花算法的隐蔽的坑不少。
今天这篇文章跟大家一起聊聊雪花算法的5大坑,希望对你会有所帮助。
一、雪花算法:美丽的陷阱
先简单回顾一下雪花算法的结构。
标准的雪花算法ID由64位组成:
// 典型的雪花算法结构
public class SnowflakeId {
// 64位ID结构
// 1位符号位(始终为0) +
// 41位时间戳(毫秒级) +
// 10位机器ID +
// 12位序列号
private long timestampBits = 41;// 时间戳占41位
private long workerIdBits = 10; // 机器ID占10位
private long sequenceBits = 12; // 序列号占12位
// 最大支持值
private long maxWorkerId = -1L ^ (-1L << workerIdBits);// 1023
private long maxSequence = -1L ^ (-1L << sequenceBits);// 4095
// 偏移量
private long timestampShift = sequenceBits + workerIdBits;// 22
private long workerIdShift = sequenceBits; // 12
}跨语言兼容性测试表
语言/环境最大安全整数处理方案示例JavaScript2^53 (9e15)字符串化"12345678901234567"Python无限制直接使用12345678901234567Java2^63-1直接使用12345678901234567LMySQL BIGINT2^63-1直接存储12345678901234567JSON传输2^53大数转字符串{"id": "12345678901234567"}总结
1. 时钟问题:必须处理的现实
最佳实践:
<ul>使用waitNextMillis处理小范围回拨( 0.8) { log.warn("序列号使用率过高: {}%", usageRate * 100); alertService.sendAlert("SNOWFLAKE_HIGH_USAGE", "序列号使用率: " + usageRate); // 自动扩容:调整时间戳粒度 if (usageRate > 0.9) { upgradeToMicrosecond(); } } }}4. 时间戳溢出:早做规划
最佳实践:
[*]选择合理的起始时间(如项目启动时间)
[*]定期检查剩余时间
[*]准备升级方案(如扩展位数)
// 有问题的雪花算法实现
public synchronized long nextId() {
long currentTimestamp = timeGen();
// 问题代码:如果发现时钟回拨,直接抛异常
if (currentTimestamp < lastTimestamp) {
throw new RuntimeException("时钟回拨异常");
}
// ... 生成ID的逻辑
}5. 跨系统兼容:设计时就考虑
最佳实践:
[*]ID对象包含多种表示形式
[*]API响应统一使用字符串ID
[*]提供ID转换工具类
public class SnowflakeIdWorker {
private long lastTimestamp = -1L;
private long sequence = 0L;
public synchronized long nextId() {
long timestamp = timeGen();
// 处理时钟回拨
if (timestamp < lastTimestamp) {
long offset = lastTimestamp - timestamp;
// 如果回拨时间较小(比如5毫秒内),等待
if (offset <= 5) {
try {
wait(offset << 1);// 等待两倍时间
timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException("时钟回拨过大");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("等待时钟同步被中断");
}
} else {
// 回拨过大,抛出异常
throw new RuntimeException("时钟回拨过大: " + offset + "ms");
}
}
// 正常生成ID的逻辑
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & maxSequence;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampShift) |
(workerId << workerIdShift) |
sequence;
}
// 等待下一个毫秒
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
}最后的建议
雪花算法虽然优雅,但它不是银弹。
在选择ID生成方案时,需要考虑:
[*]业务规模:小系统用UUID更简单,大系统才需要雪花算法
[*]团队能力:能处理好时钟回拨等复杂问题吗?
[*]未来规划:系统要运行多少年?需要迁移方案吗?
如果决定使用雪花算法,建议:
[*]使用成熟的开源实现(如Twitter的官方版)
[*]完善监控和告警
[*]准备降级和迁移方案
记住:技术选型不是寻找完美方案,而是管理复杂度的艺术。
雪花算法有坑,但只要我们知道坑在哪里,就能安全地跨过去。
如果你在雪花算法使用中遇到其他问题,欢迎留言讨论。
最后说一句(求关注,别白嫖我)
如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。
更多项目实战在我的技术网站:http://www.susan.net.cn/project
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! 感谢发布原创作品,程序园因你更精彩 喜欢鼓捣这些软件,现在用得少,谢谢分享! 过来提前占个楼 这个有用。 感谢分享 收藏一下 不知道什么时候能用到 分享、互助 让互联网精神温暖你我 热心回复! 感谢发布原创作品,程序园因你更精彩 前排留名,哈哈哈 感谢发布原创作品,程序园因你更精彩 很好很强大我过来先占个楼 待编辑 感谢分享,学习下。 感谢分享 这个好,看起来很实用 yyds。多谢分享 谢谢分享,试用一下 谢谢楼主提供! 收藏一下 不知道什么时候能用到
页:
[1]
2