前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >那些惊艳的算法们(四)——唯一ID生成器snowflake

那些惊艳的算法们(四)——唯一ID生成器snowflake

作者头像
全栈程序员站长
发布2022-06-28 10:48:05
6780
发布2022-06-28 10:48:05
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

分布式全局唯一ID生成器

很多场景需要使用全局唯一ID,用来标识唯一一条消息,唯一一笔交易,唯一一个用户,唯一一张图片等等。 传统数据库表的自增主键是很简单的一种实现方式,前提是你没有分库,也没有分表,如果你分表了,id就会重复,失去唯一性:

imagepng
imagepng

当然,通过数据库的一些配置,使不同的分表以不同的起始值但是相同的步长自增,可以绕开这个限制:

imagepng
imagepng

但是,如果哪天发现数据量增大,原先的分表不够用了,需要扩容,这时,就很麻烦很难搞了。 所以,如果存在一种和业务数据无关的全局唯一ID生成器就好了。开动脑筋,我们能想到的有以下几种:

时间戳

用时间做唯一id,这个在并发比较高或者分布式环境中基本不可行,统一时间生成的id是重复的,不满足全局唯一。

利用数据库自增

依然利用数据库产生自增id,保证唯一性,和开头提到的不同之处是,单独使用一张(或固定几张)数据库表专门用来产生自增id,与业务无关,后续不再重新分表,数据量大时,可以删除早一些时候产生的数据。 这样做的好处是,实现简单,容易理解。 不好的地方是,严重依赖数据库,id产生速率受数据库性能以及连接数据库的网络影响。

利用Redis原子操作incrBy

好处:实现简单,容易理解。 坏处:依赖Redis,且Redis需要持久化。

UUID/GUID

好处:使用非常简单,不需要依赖其他设施。 坏处:太长,128bit,不适合做数据库主键。

snowflake

通常情况下,用时间来表示是最简单的,如果同一时间(毫秒)有很多请求进来怎么办? 时间戳后面拼接上一个数字,这个数字可以通过锁控制每次递增,每毫秒清零,重新开始递增。

即便这样,只是解决了单机的问题,如果是分布式环境,不同的机器,还是可能产生一样的id的,这怎么解决? 在上述时间戳和数字的基础上在拼接上机器的id,这样就不会重复了。

不同的数据中心,机器id是可能重复的,怎么搞? 再拼接上数据中心的id就行了。

不同的星球上。。。

思想朴实无华,但是大道至简。

最终产生的id是这个样子的,时间戳,工作机器id,序列号可以根据实际需要调整长度(通常情况下不需要调整,完全够用),总体64bit就行:

imagepng
imagepng

snowflake名字起得真好

雪花(snowflake)的形状和算法的思想十分吻合,沿着主干(时间戳),如果有重复,那么分叉分出机器id,如果仍有重复,再分叉,分出序列号

imagepng
imagepng

好处与不足

snowflake有以下几个特点:

算法简单,不需要依靠额外组件

id可以直接靠算法在内存中产生,靠锁控制并发,不需有诸如MySQL,Redis这样的外部依赖,无维护成本。

长度合适

snowflake产生的id长度为64bit,对应大多数语言的long类型,用于作为数据库唯一键建立索引时,也不会因为长度过大影响性能。

趋势递增

snowflake产生的id并不是严格递增的,而是趋势递增的。 这是因为,当id生成器分布式部署的时候,比如统一毫秒由不同机器产生的id,时间戳的部分肯定是一样的,后面机器id的部分并不一定是递增的。 举个例子,有两个机器,id分别是0和1,那么同一毫秒内产生的id可能是这样的顺序:

imagepng
imagepng

从图中可以看出,由于机器id的存在,在同1毫秒内产生的id并不一定是递增的,但是因为时间戳的存在,在毫秒间总体上id是递增的。 所以总体上说snowflake产生的id是趋势递增的。

为何追求递增

为何追求递增?因为递增最大的优势就是对磁盘IO是友好的。 熟悉磁盘结构的同学们都知道,随机写的效率是很慢的,因为磁头需要转动到指定的位置,这个磁头转动的过程比起cpu或者内存来,完全不是一个数量级的,太慢太慢了,所以如果能尽可能的使数据靠近在一一起(递增就能靠在一起),那么就不需要频繁的抬起磁头,转动磁盘,写数据了,一路写到底会快很多。 一些大型分布式数据库,比如HBase,ElasticSearch等,也都是利用顺序写这个特点提高数据的写入性能的。

隐患

snowflake并不完美,因为有一种情况,snowflake产生的id是有可能会出现重复的。 为什么会重复,我们再回头看看snowflake产生的id的组成:时间戳+机器id+序列号。 这三部分,机器id可以不重复,序列号也可以做到不重复,那唯一可能重复的就是时间戳了。 什么?时间怎么回重复?时间明明是一直向前的,除非时间倒退,退回到之前的某个时间点,再次产生的id才可能是重复的。 你说对了,人类感受的时间是不会倒退的,但是,机器上的时间都是时钟,时钟可能会因为种种原因变慢了或者变快了,比如有一天你(或者机器上的时间同步器)发现有一台机器的时钟变快了,于是往回拨1秒,然后。。。 你懂的

时钟的问题,一直都是老大难,某些对时间及其敏感的程序,甚至会考虑使用GPS上的原子钟来做时钟同步,或者,干脆有土豪(某歌)直接在数据中心自己搞原子钟,然并卵,时间同步时的网络传输延迟、抖动,依然无解。 永远都是只能减小,无法消灭。

##最后 忍不住再夸一下算法的名字,snowflake,真是美妙。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/151073.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 分布式全局唯一ID生成器
    • 时间戳
      • 利用数据库自增
        • 利用Redis原子操作incrBy
          • UUID/GUID
            • snowflake
              • snowflake名字起得真好
                • 好处与不足
                  • 算法简单,不需要依靠额外组件
                  • 长度合适
                  • 趋势递增
                  • 隐患
              相关产品与服务
              云数据库 Redis
              腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档