高并发环境下生成订单唯一流水号方法:SnowFlake

关于订单号的生成,一些比较简单的方案:

1、数据库自增长ID

  • 优势:无需编码
  • 缺陷:
    • 大表不能做水平分表,否则插入删除时容易出现问题
    • 高并发下插入数据需要加入事务机制
    • 在业务操作父、子表(关联表)插入时,先要插入父表,再插入子表

2、时间戳+随机数

  • 优势:编码简单
  • 缺陷:随机数存在重复问题,即使在相同的时间戳下。每次插入数据库前需要校验下是否已经存在相同的数值。

3、时间戳+会员ID

  • 优势:同一时间,一个用户不会存在两张订单
  • 缺陷:会员ID也会透露运营数据,鸡生蛋,蛋生鸡的问题

4、GUID/UUID

  • 优势:简单
  • 劣势:用户不友好,索引关联效率较低。

今天要分享的方案:来自twitter的SnowFlake

Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同.Snowflake算法核心把时间戳,工作机器id,序列号(毫秒级时间41位+机器ID 10位+毫秒内序列12位)组合在一起。

snowflake-64bit

在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。

除了最高位bit标记为不可用以外,其余三组bit占位均可浮动,看具体的业务需求而定。默认情况下41bit的时间戳可以支持该算法使用到2082年,10bit的工作机器id可以支持1023台机器,序列号支持1毫秒产生4095个自增序列id。下文会具体分析。

Snowflake – 时间戳

这里时间戳的细度是毫秒级,具体代码如下,建议使用64位linux系统机器,因为有vdso,gettimeofday()在用户态就可以完成操作,减少了进入内核态的损耗。

默认情况下有41个bit可以供使用,那么一共有T(1llu << 41)毫秒供你使用分配,年份 = T / (3600 * 24 * 365 * 1000) = 69.7年。如果你只给时间戳分配39个bit使用,那么根据同样的算法最后年份 = 17.4年。

Snowflake – 工作机器id

严格意义上来说这个bit段的使用可以是进程级,机器级的话你可以使用MAC地址来唯一标示工作机器,工作进程级可以使用IP+Path来区分工作进程。如果工作机器比较少,可以使用配置文件来设置这个id是一个不错的选择,如果机器过多配置文件的维护是一个灾难性的事情。

这里的解决方案是需要一个工作id分配的进程,可以使用自己编写一个简单进程来记录分配id,或者利用Mysql auto_increment机制也可以达到效果。

workid

工作进程与工作id分配器只是在工作进程启动的时候交互一次,然后工作进程可以自行将分配的id数据落文件,下一次启动直接读取文件里的id使用。这个工作机器id的bit段也可以进一步拆分,比如用前5个bit标记进程id,后5个bit标记线程id之类:D

Snowflake – 序列号

序列号就是一系列的自增id(多线程建议使用atomic),为了处理在同一毫秒内需要给多条消息分配id,若同一毫秒把序列号用完了,则“等待至下一毫秒”。

总体来说,是一个很高效很方便的GUID产生算法,一个int64_t字段就可以胜任,不像现在主流128bit的GUID算法,即使无法保证严格的id序列性,但是对于特定的业务,比如用做游戏服务器端的GUID产生会很方便。另外,在多线程的环境下,序列号使用atomic可以在代码实现上有效减少锁的密度。

该项目地址为:https://github.com/twitter/snowflake 是用Scala实现的。核心代码:

由UC实现的JAVA版本代码(略有修改)

来源:https://github.com/sumory/uc/blob/master/src/com/sumory/uc/id/IdWorker.java

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
生成年月日加6位流水唯一 ID,可以考虑使用 Snowflake 算法。Snowflake 算法是 Twitter 开源的分布式 ID 生成算法,使用一个 64 位的 long 型数字作为全局唯一 ID。具体实现可以使用 Java 来完成。 在 Java 中,可以使用 Snowflake 算法实现一个分布式 ID 生成器,具体步骤如下: 1. 定义一个 Snowflake 类,包含以下属性: - 起始的时间戳(epoch) - 机器 ID - 序列号 2. 在 Snowflake 类中实现一个 nextId() 方法,该方法包含以下步骤: - 获取当前时间戳(毫秒级) - 如果当前时间戳小于上一次生成 ID 的时间戳,则说明系统时钟回退过,抛出异常 - 如果当前时间戳等于上一次生成 ID 的时间戳,则将序列号加 1 - 如果当前时间戳大于上一次生成 ID 的时间戳,则将序列号重置为 0,并更新上一次生成 ID 的时间戳为当前时间戳 - 生成一个 64 位的 long 型数字,其中高位为当前时间戳,中间位为机器 ID,低位为序列号 - 返回生成的 ID 3. 在应用程序中使用 Snowflake 类的 nextId() 方法生成唯一 ID。 由于 Snowflake 算法生成的 ID 中包含时间戳信息,因此可以保证生成的 ID 有序递增。同时,Snowflake 算法中使用了位运算和异或运算,可以保证在分布式环境生成的 ID 不重复。 如果要使用 Snowflake 算法实现分布式并发,需要注意以下几点: - 每个机器的机器 ID 必须唯一 - 同一机器上的线程调用 nextId() 方法时,需要考虑线程安全问题 - 不同机器上的应用程序调用 nextId() 方法时,需要考虑网络延迟和时钟不同步问题 以上是使用 Java 实现年月日加6位流水唯一 ID 的思路,具体实现可以参考 Snowflake 算法的开源实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值