关于我为何写这篇文章是由于今天在作订单模块的时候,看到以前的PRD上描述的年月日+用户id2位+企业id位
+四位自增加数。而后竟被我反驳的忽然改为了精确时间+4位自增加数,因而我更失望了。算法
咱们考虑一下,据我所常见的订单基本都14-20位。(年月日时分秒和随机数)基本上就有14位了。虽然通常项目作不到淘宝双11这种
支付峰值达到每秒10万笔订单.可是我以为至少事先能够考虑到,想必当初淘宝或许也没意识到之后发展
得这么好。sql
对于其定订单的生成。我以为要至少要符合如下这三种,全局惟一 ,数据库
在复杂的分布式系统中,不少场景须要的都是全局惟一ID的场景,通常为了防止冲突能够考虑的有36
位的UUID,twitter的snowflake等。服务器
可是能够思考这些问题?网络
查阅了相关资料,主要有如下这几种并发
1.UUID
组成:当前日期+时间+时钟序列+机器识别号(Mac地址或其余)没有mac网卡的话会有别的东西识别。
在分布式系统中,全部元素(WEB服务器)都不须要经过中央控制端来判断数据惟一性。几十年以内能够达到全球惟一性。
snowflake的结构以下(每部分用-分开):less
2.Mysql经过AUTO_INCREMENT实现、Oracle经过Sequence序列实现。
在数据库集群环境下,不一样数据库节点可设置不一样起步值、相同步长来实现集群下生产全局惟1、递增ID分布式
3.Snowflake算法 雪花算法
41位时间戳+10位机器ID+12位序列号(自增) 转化长度为18位的长整型。
Twitter为知足美秒上万条消息的建立,且ID须要趋势递增,方便客户端排序。
Snowflake虽然有同步锁,可是比uuid效率高。ui
4.Redis自增ID
实现了incr(key)用于将key的值递增1,并返回结果。若是key不存在,建立默认并赋值为0。 具备原子性,保证在并发的时候。this
可是我在这主要想说的是雪花算法生成id
关于序列
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
第一位为未使用,接下来的41位为毫秒级时间(41位的长度可使用69年),而后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每一个节点每毫秒产生4096个ID序号)
一共加起来恰好64位,为一个Long型。(转换成字符串长度为18)
snowflake生成的ID总体上按照时间自增排序,而且整个分布式系统内不会产生ID碰撞(由datacenter和workerId做区分),而且效率较高。听说:snowflake每秒可以产生26万个ID。
如下是代码
部分借鉴与网络
100万个ID 耗时2秒
/** * Created by youze on 18-7-5 */ public class IdWorker { /** * 起始的时间戳 */ private final static long START_STMP = 1530795377086L; /** * 每一部分占用的位数 */ /** * 序列号占用的位数 */ private final static long SEQUENCE_BIT = 12; /** * 机器标识占用的位数 */ private final static long MACHINE_BIT = 5; /** * 数据中心占用的位数 */ private final static long DATACENTER_BIT = 5; /** * 每一部分的最大值 */ private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT); private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); /** * 每一部分向左的位移 */ private final static long MACHINE_LEFT = SEQUENCE_BIT; private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; /** * 数据中心 */ private long datacenterId; /** * 机器标识 */ private long machineId; /** * 序列号 */ private long sequence = 0L; /** * 上一次时间戳 */ private long lastStmp = -1L; public IdWorker(long datacenterId, long machineId) { if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); } if (machineId > MAX_MACHINE_NUM || machineId < 0) { throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); } this.datacenterId = datacenterId; this.machineId = machineId; } /** * 产生下一个ID * @return */ public synchronized long nextId() { long currStmp = getNewstmp(); if (currStmp < lastStmp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (currStmp == lastStmp) { //相同毫秒内,序列号自增 sequence = (sequence + 1) & MAX_SEQUENCE; //同一毫秒的序列数已经达到最大 if (sequence == 0L) { currStmp = getNextMill(); } } else { //不一样毫秒内,序列号置为0 sequence = 0L; } lastStmp = currStmp; return ( //时间戳部分 currStmp - START_STMP) << TIMESTMP_LEFT //数据中心部分 | datacenterId << DATACENTER_LEFT //机器标识部分 | machineId << MACHINE_LEFT //序列号部分 | sequence; } private long getNextMill() { long mill = getNewstmp(); while (mill <= lastStmp) { mill = getNewstmp(); } return mill; } private long getNewstmp() { return System.currentTimeMillis(); } public static void main(String[] args) { IdWorker snowFlake = new IdWorker(2, 3); long start = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { System.out.println(snowFlake.nextId()); } System.out.println(System.currentTimeMillis() - start); } }