不管我们用什么方法生成的id,都应该满足以下几个条件:
第一 , 必须是唯一,可以全局唯一,或者同一种业务内唯一也可以。
第二, 必须按时间递增(这个需要解释吗)。
第三,尽量使用数字类型的,其次才考虑字符串型的。原因很简单,数字不需要使用编码(utf-8,gbk等)翻译。直接可以保存到数据库,而字符串必须使用编码翻译后才能使用。
第四, 在id里面尽量能看出id是什么时候生成的。
关于唯一性id生成讨论几种方案:
第一种,首选方案,数据库id自动生成,理由是没有任何难度,大家都会用。但是这只能在前期使用,
(1) 当系统负载大了,数据库性能下降就会考虑更换别的方法了。
(2) 如果有分库分表的话,也是不能用了,需要想别的办法替代。
第二种, uuid。
这个方案只能说可以用,能保证唯一 ,用起来简单,但是用了之后处理有点麻烦,因为不能按时间递增生成,保存到数据库的时候对数据库的性能有影响,至少主键索引如果是btree的话,肯定会有影响,不过可以把数据库主键索引改成hash,具体没有测试过,按理来说应该是会影响比较小了。有谁测试过了可以留言说下效果。
第三种,redis。
Redis是单线程的,所以也可以用来生成全局唯一的ID,利用redis对数字的原子自增操作(Redis的 INCR和INCRBY)。
第四种, twitter的snowflake。
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看https://github.com/twitter/snowflake。
这种方法可以保证生成唯一,递增,数字型的id, 唯一的问题就是生成的id直接看不出是什么时候生成的。
第五种, snowflake 变种。
(1) 因为long型的十进制只有19位长度,如果我们要数字型的,又要递增,又要能从id里面看出时间,就只有下面这种方法了。
年2位 | 月2位 | 日2位 | 时分4位 | 数据中心id2位 | 机器id2位 | 取纳秒的最后5位 | |
18 | 01 | 01 | 1113 | 12 | 15 | 12345 |
这种方法有一定问题,会有重复的id,因为long型长度的限制,我们时间只能精确到分钟,一分钟有60秒,就是60000毫秒, 然后我们的最后5位最多只有10万个数。如果同一台机器有在同一毫秒内生成多个的话,就可能会出现重复的了。 不过这应该能满足多数需求了。请求量不多的话,这完全够用了。
( 2 ) 很多时候,19位长度不够使用,要超过19长度,我们就只有用字符串来实现了。字符串就比较方便了,可以任意生成满足我们需要的id或者订单号了。
年月日 | 时分秒 | 毫秒最后3位 | 纳秒最后6位 | 数据中心id2位 | 机器id2位 | 扩展id | 扩展id |
180101 | 121315 | 876 | 532524 | 10 | 22 |
这种方法除了不是数字型的id,几乎没有别的缺点了,甚至还可以再加几位表示具体的服务的id到里面。可以自由扩展,甚至想加具体的业务id都可以。
1801011213158765325241022 最后大概是像这样的。每毫秒钟可以生成十万个不同的id。