本文移植和修改源自作者在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算法的基本构想、技术特点和实现算法,并提出了一些改进的想法和建议,编写了相关的实现和测试代码。