소개
트위터의 눈꽃송이를 바탕으로 쓴 글입니다.
보시다시피 위 그림에서 64비트 ID는 가장 왼쪽 부호 비트(생성된 ID가 모두 양수인지 확인하기 위해 0으로 고정)를 제외하고 나머지 63비트를 사용할 수 있습니다.
아래 코드는 다음과 같습니다. 그림과 동일합니다. 가운데의 10비트 작업 기계 ID를 제외하고는 타임스탬프의 자릿수와 일련번호가 사용자의 필요에 따라 변경될 수 있습니다. 중앙에 있는 작업기 ID를 왼쪽으로 이동시키거나, 오른쪽으로 이동하시면 됩니다.
코드
/// <summary> /// 64位ID生成器,最高位为符号位,始终为0,可用位数63. /// 实例编号占10位,范围为0-1023 /// 时间戳和索引共占53位 /// </summary> public sealed class IdCreator { long timestamp = 0;//当前时间戳 long index = 0;//索引/计数器 long instanceID;//实例编号 int indexBitLength;//索引可用位数 long tsMax = 0;//时间戳最大值 long indexMax = 0; static IdCreator _default = new IdCreator(); /// <summary> /// /// </summary> /// <param name="instanceID">实例编号(0-1023)</param> /// <param name="indexBitLength">索引可用位数(1-32).每秒可生成ID数等于2的indexBitLength次方.大并发情况下,当前秒内ID数达到最大值时,将使用下一秒的时间戳,不影响获取ID.</param> /// <param name="initTimestamp">初始化时间戳,精确到秒.当之前同一实例生成ID的timestamp值大于当前时间的时间戳时, /// 有可能会产生重复ID(如持续一段时间的大并发请求).设置initTimestamp比最后的时间戳大一些,可避免这种问题</param> public IdCreator(int instanceID, int indexBitLength, long? initTimestamp = null) { if (instanceID < 0) { //这里给每个实例随机生成个实例编号 Random r = new Random(); this.instanceID = r.Next(0, 1024); } else { this.instanceID = instanceID % 1024; } if (indexBitLength < 1) { this.indexBitLength = 1; } else if (indexBitLength > 32) { this.indexBitLength = 32; } else { this.indexBitLength = indexBitLength; } tsMax = Convert.ToInt64(new string('1', 53 - indexBitLength), 2); indexMax = Convert.ToInt64(new string('1', indexBitLength), 2); if (initTimestamp != null) { this.timestamp = initTimestamp.Value; } } /// <summary> /// 默认每实例每秒生成65536个ID,从1970年1月1日起,累计可使用4358年 /// </summary> /// <param name="instanceID">实例编号(0-1023)</param> public IdCreator(int instanceID) : this(instanceID, 16) { } /// <summary> /// 默认每秒生成65536个ID,从1970年1月1日起,累计可使用4358年 /// </summary> public IdCreator() : this(-1) { } /// <summary> /// 生成64位ID /// </summary> /// <returns></returns> public long Create() { long id = 0; lock (this) { //增加时间戳部分 long ts = Harry.Common.Utils.GetTimeStamp() / 1000; ts = ts % tsMax; //如果超过时间戳允许的最大值,从0开始 id = ts << (10 + indexBitLength);//腾出后面部分,给实例编号和缩引编号使用 //增加实例部分 id = id | (instanceID << indexBitLength); //获取计数 if (timestamp < ts) { timestamp = ts; index = 0; } else { if (index > indexMax) { timestamp++; index = 0; } } id = id | index; index++; } return id; } /// <summary> /// 获取当前实例的时间戳 /// </summary> public long CurrentTimestamp { get { return this.timestamp; } } /// <summary> /// 默认每实例每秒生成65536个ID,从1970年1月1日起,累计可使用4358年 /// </summary> public static IdCreator Default { get { return _default; } } }
코드 설명
사용시 IdCreator의 새 인스턴스를 생성한 다음 Create() 메서드를 호출해야 합니다. ID 번호를 생성하려면 ID 번호의 고유성을 보장하기 위해 IdCreator의 인스턴스를 할당해야 합니다. 배포의 경우, IdCreator의 생성자에 인스턴스ID 매개변수를 전달해야 합니다. 각 배포에는 0-1023 범위의 다른 값이 있어야 합니다.
생성자의 indexBitLength 매개변수는 가장 오른쪽 '일련번호'의 길이를 나타냅니다. '는 더 이상 12비트로 고정되지 않으며 범위는 1-32입니다. 나머지는 사용 가능한 비트이므로 타임스탬프는 그대로 유지됩니다.
참고: IdCreator 클래스의 타임스탬프는 초 단위로 측정됩니다. 밀리초로 변경하려면 코드만 변경하면 됩니다. long ts = Harry.Common.Utils.GetTimeStamp() / 1000; long ts = Harry.Common.Utils.GetTimeStamp();.
샘플 코드
IdCreator c=new IdCreator(0,16); var id=c.Create();