PushID改进算法和实现

149 阅读3分钟

本文移植和修改源自作者在blogger上的一篇博文,如有雷同,绝非巧合。

笔者最近研究了一下标准的PushID的原理和实现方式,觉得可以在其基础上进行一些改进,遂著此博文进行分析和记录,并分享给大家来使用和研究。

PushID

PushID是一种类似于nanoid和uuid的信息标识符。它是一个22个字符长度的字符串,可以由10个数字,52个大小写字符和下划线_以及减号-构成,其中前8位是一个时间戳的编码,后12位是随机数字符编码。这一设计的特点和优势包括:

  • 信息空间是和UUID一样的,但精简了表示方式
  • 其选择的字符集是Web安全的,不会在URL、PostBody和SQL中造成歧义
  • 其生成是严格有序(生成时间)的,对于其标识的信息处理可能有帮助(如排序和索引)
  • 在一定程度上,可以抵抗时间校准的影响

改进思路

参考标准的PushID算法实现,笔者觉得可以在以下方面进行改进:

  • 字符集采用纯数字和大小写字符,共62个,并可以灵活调整
  • 时间戳编码的部分动态实现,可以精简到6位(标准PushID使用8位,但其实第一位永远都是-,感觉有点浪费,这一部分可以自动计算,并忽略最高位)
  • 随机生成的部分可以选择设置为10位
  • 如果添加后缀作为子系统标识,则可以方便的实现分布式ID生成
  • 尽量使用buffer进行操作和资源复用

代码和分析

根据上述的改进构想,这个生成算法的参考实现代码如下:

关于这个实现的基本思路和过程如下:

1 定义一个id编码的字符集,这里是base62, 而且要注意严格遵循ASCII顺序,这是PushID有序的关键

2 由于需要使用字符集表达时间戳(使用毫秒的整数值),可以实现计算需要的字符长度,此次为6,就是可以使用6个字符表达毫秒数值,这里使用了对数计算

3 计算并记录当前时间(精确到毫秒),用于毫秒内的重复规避

4 对时间戳整数循环计算余数,并使用一个数组进行记录,即ID的时间戳部分,这里是6位

5 计算并填充ID的随机信息部分,策略如后

6 如果时间和当前时间记录不冲突,则依据时间的最后一位,填充随机数的一部分(这里是一半)

7 如果时间冲突,则说明是在同一毫秒内,则将当前随机信息+1,并考虑进位的问题

8 组合时间戳部分和随机部分,编码为字符串,并按需加入尾字符

9 返回最终结果

这个流程和操作的特点是:

  • 时间戳字符串位数可以预先计算,而且高位可以舍弃(其实是一样的)
  • 如果在同1毫秒中,需要用累加的方式复用产生的随机字符串
  • 在不同的毫秒中,随机字符串重新生成
  • 改进了字符串操作为数组操作,并减少转换过程,提高性能

小结

本文总结了PushID算法的基本构想、技术特点和实现算法,并提出了一些改进的想法和建议,编写了相关的实现和测试代码。