The snowflake algorithm can specify an improved version of the number of domain bits

Source: Internet
Author: User
Tags diff parse error
This is a creation in Article, where the information may have evolved or changed. The snowflake algorithm is particularly efficient when generating IDs, and can be consulted: https://segmentfault.com/a/1190000011282426 it guarantees: * All generated IDs are incremented by time trend * Duplicate IDs are not generated throughout the distributed system (because there are datacenterid and Workerid) but in the shadow of a datacenterid, workerid do not need to occupy so many bits, or the machine is not so much. I wrote a bit of the individual fields can be customized settings. https://github.com/liuyongshuai/goutils/'/** * @author Liu yongshuai<liuyongshuai@hotmail.com> * @package Gosnowflake * @date 2018-01-25 19:19 */package gosnowflakeimport ("Sync" "FMT" "Time")/** See test cases: Go Test-test.run testnewidgenerator*///snowflake struct type snowflakeidgenerator struct {workerid Int64//Current Workeridworkeridaftershift Int64//Shift Workerid, can be directly followed by timestamp, ordinal take or Operation Lastmstimestamp Int64//Last timestamp cursequence Int64//Current serial number timebitsize uint8// The time stamp occupies the number of digits, the default is 41 bits, the maximum is not more than 60 bits workeridbitsize uint8//workerid occupies the number of digits, the default 10, the maximum not more than 60 bits sequencebitsize uint8//serial number occupies the number of digits, the default 12, Maximum no more than 60-bit lock *sync. Mutex//synchronization with ishaveinit BOOL//whether the maximum value of Maxworkerid Int64//workerid has been initialized, the maxsequence Int64//Last serial number maximum value calculated at initialization, The calculated Workeridleftshift Uint8//generated Workerid only takes the lowest number, which is to move to the left, to make a bit of the sequence numbers, and to calculate when initializingTo the Timestampleftshift uint8//generated timestamp left several, give WorkID, serial number to make a bit, initialize the calculated}//instantiate an ID generator func newidgenerator () * Snowflakeidgenerator {return &snowflakeidgenerator{workerid:0,lastmstimestamp:0,cursequence:0,timebitsize:41 ,//The default timestamp occupies the number of bits workeridbitsize:10,//default Workerid occupies the number of bits sequencebitsize:12,//default number of digits accounted for the number of bits maxworkerid:0,//Max Workerid, Maxsequence:0 computed at initialization,//maximum ordinal value, initialized when computed workeridleftshift:0,//worker ID left shift number timestampleftshift:0,lock:new (sync . Mutex), ishaveinit:false,}}//set worker Idfunc (SFG *snowflakeidgenerator) Setworkerid (w Int64) *snowflakeidgenerator { Sfg.lock.Lock () defer sfg.lock.Unlock () Sfg.ishaveinit = Falsesfg.workerid = Wreturn sfg}//Set the number of bits for the timestamp func (SFG * Snowflakeidgenerator) settimebitsize (n uint8) *snowflakeidgenerator {sfg.lock.Lock () defer sfg.lock.Unlock () Sfg.ishaveinit = Falsesfg.timebitsize = Nreturn sfg}//Set the number of digits of the worker ID func (SFG *snowflakeidgenerator) Setworkeridbitsize (n uint8) *snowflakeidgenerator {sfg.lock.Lock () defer sfg.lock.Unlock () Sfg.ishaveinit = Falsesfg.worKeridbitsize = Nreturn sfg}//Set number of digits in the number of bits func (SFG *snowflakeidgenerator) setsequencebitsize (n uint8) * Snowflakeidgenerator {sfg.lock.Lock () defer sfg.lock.Unlock () Sfg.ishaveinit = Falsesfg.sequencebitsize = Nreturn sfg}/ /initialization operation Func (SFG *snowflakeidgenerator) Init () (*snowflakeidgenerator, error) {Sfg.lock.Lock () defer sfg.lock.Unlock ()/ /if already initialized if Sfg.ishaveinit {return SFG, nil}if sfg.sequencebitsize < 1 | | Sfg.sequencebitsize > {return nil, fmt. Errorf ("Init failed:\tinvalid sequence bit size, should (1,60)")}if sfg.timebitsize < 1 | | Sfg.timebitsize > {return nil, fmt. Errorf ("Init failed:\tinvalid time bit size, should (1,60)")}if sfg.workeridbitsize < 1 | | Sfg.workeridbitsize > {return nil, fmt. Errorf ("Init failed:\tinvalid worker ID bit size, should (1,60)")}if sfg.workeridbitsize+sfg.sequencebitsize+ Sfg.timebitsize! = + + return nil, FMT. Errorf ("Init failed:\tinvalid sum of all bit size, should EQ 63")}//determines the number of shifts sfg.workeridleftshift = sfg.sequencebitsizesfg.t ImEstampleftshift = sfg.sequencebitsize + sfg.workeridbitsize//determine serial number and workerid maximum Sfg.maxworkerid =-1 ^ ( -1 << sfg.workeridbitsize) sfg.maxsequence =-1 ^ ( -1 << sfg.sequencebitsize)//After Shift Workerid, return results can be directly with the timestamp, Sfg.workeridaftershift = Sfg.workerid << sfg.workeridleftshift//Determine if the current Workerid is legal if Sfg.workerid > Sfg.maxworkerid {return nil, fmt. Errorf ("Init failed:\tinvalid worker ID, should not greater than%d", Sfg.maxworkerid)}//initialization complete sfg.ishaveinit = truesfg.la Stmstimestamp = 0sfg.cursequence = 0return SFG, nil}//generate a timestamp, according to the bit size set up several//that is, the resulting timestamp first shifted right several, and then left a few, the highest specified number of bits is retained func (SFG *snowflakeidgenerator) Gents () Int64 {rawts: = time. Now (). Unixnano () diff: = 64-sfg.timebitsizeret: = (rawts >> diff) << Diffreturn ret}//generates the next timestamp, if the number of bits in the timestamp is small and the sequence number is exhausted here The wait time will be longer func (SFG *snowflakeidgenerator) Gennextts (last Int64) int64 {for {cur: = sfg.gents () If cur > last {return cur} }}//generates the next Idfunc (SFG *snowflakeidgenerator) NextID () (Int64, error) {Sfg.lock.Lock () defer sFg.lock.Unlock ()//If you have not initialized the IF!sfg.ishaveinit {return 0, FMT. Errorf ("Gen nextid failed:\tplease Execute Init () First")}//determine the current timestamp, if it is smaller than the previous one, indicates a problem curts: = Sfg.gents () if Curts < Sfg.lastmstimestamp {return 0, FMT. Errorf ("Gen nextid failed:\tunknown error, the system clock occur some wrong")}//if the timestamp is the same as the last time stamp, increase the ordinal if curts = = Sfg.lastmsti Mestamp {sfg.cursequence = (sfg.cursequence + 1) & sfg.maxsequence//serial number 0 is run out, regenerate timestamp if sfg.cursequence = = 0 {curts = Sfg.gennextts (Sfg.lastmstimestamp)}} else {//If two timestamps are different, then 0 sequence number sfg.cursequence = 0}sfg.lastmstimestamp = curts// Assemble the processed bits into a int64 type Curts = Curts | Sfg.workeridaftershift | Sfg.cursequencereturn Curts, nil}//parse generated idfunc (SFG *snowflakeidgenerator) parse (ID Int64) (Int64, Int64, int64, error) { If not yet initialized if!sfg.ishaveinit {return 0, 0, 0, FMT. Errorf ("Parse failed:\tplease execute Init () First")}//extracts the timestamp section shift: = Sfg.sequencebitsize + Sfg.sequencebitsizetimestamp: = (ID & ( -1 << shift)) >> shift//extract workerid section shift = Sfg.sEquencebitsizeworkerid: = (ID & (Sfg.maxworkerid << shift)) >> shift//ordinal part sequence: = ID & sfg.maxsequ ence//Parse error if Workerid! = Sfg.workerid | | Workerid > Sfg.maxworkerid {fmt. Printf ("workerbitsize=%d\tmaxworkerid=%d\n", Sfg.workeridbitsize, Sfg.maxworkerid) return 0, 0, 0, FMT. Errorf ("Parse failed:invalid ID, originworkerid=%d\tparseworkerid=%d\n", Sfg.workerid, Workerid)}if sequence < 0 | | Sequence > Sfg.maxsequence {fmt. Printf ("sequesncebitsize=%d\tmaxsequence=%d\n", Sfg.sequencebitsize, Sfg.maxsequence) return 0, 0, 0, FMT. Errorf ("Parse failed:invalid ID, parsesequence=%d\n", sequence)}return timestamp, workerid, sequence, nil} " The test code generated about a total of 1亿三千多万个 IDs written to the file, and no duplicates were found for the time being. "Package Gosnowflakeimport (" Testing "" FMT "" Time "" OS ") func Testnewidgenerator (t *testing. T) {b: = "\t\t\t" B2: = "\t\t\t\t\t" D: = "====================================="//First generator Gentor1, err: = Newidgenerator (). Setworkerid (100). Init () if err! = Nil {fmt. PRINTLN (Err) t.error (ERR)}//Second generator Gentor2, ERR: = Newidgenerator (). Settimebitsize (48). Setsequencebitsize (10). Setworkeridbitsize (5). Setworkerid (30). Init () if err! = Nil {fmt. PRINTLN (Err) t.error (err)}fmt. Printf ("%s%s%s\n", D, B, D) fmt. Printf ("workerid=%d lasttimestamp=%d%s workerid=%d lasttimestamp=%d\n", Gentor1.workerid, Gentor1.lastmstimestamp, b , Gentor2.workerid, Gentor2.lastmstimestamp) fmt. Printf ("sequencebitsize=%d timebitsize=%d%s sequencebitsize=%d timebitsize=%d\n", Gentor1.sequencebitsize, Gentor1.timebitsize, B,gentor2.sequencebitsize, gentor2.timebitsize) fmt. Printf ("workerbitsize=%d sequencebitsize=%d%s workerbitsize=%d sequencebitsize=%d\n", Gentor1.workeridbitsize, Gentor1.sequencebitsize, B,gentor2.workeridbitsize, gentor2.sequencebitsize) fmt. Printf ("%s%s%s\n", D, B, D) var IDs []int64for I: = 0; I < 100; i++ {id1, err: = Gentor1. NextID () if err! = Nil {fmt. PRINTLN (Err) Return}id2, err: = Gentor2. NextID () if err! = Nil {fmt. PRINTLN (err) return}ids = append (IDs, Id2) fmt. Printf ("%d%s%d\n", Id1, B2, Id2)}//Parse idfor _, ID:= Range IDs {ts, workerid, seq, err: = Gentor2. Parse (ID) fmt. Printf ("id=%d\ttimestamp=%d\tworkerid=%d\tsequence=%d\terr=%v\n", ID, ts, workerid, seq, err)}}//multithreaded test func Testsnowflakeidgenerator_multithread (t *testing. T) {f: = "./snowflake.txt"//ready to write the file fp, err: = OS. OpenFile (f, OS. O_wronly|os. O_append|os. O_create, 0755) if err! = Nil {fmt. PRINTLN (Err) t.error (ERR)}//initializes the ID generator with default parameters Gentor, err: = Newidgenerator (). Setworkerid (100). Init () if err! = Nil {fmt. PRINTLN (Err) t.error (err)}//start 10 threads, the error is reported for I: = 0; I < 10; i++ {go func () {for {gid, err: = Gentor. NextID () if err! = Nil {panic (err)}n, err: = FP. WriteString (FMT. Sprintf ("%d\n", GID)) if err! = Nil | | N <= 0 {panic (err)}}} ()}time. Sleep (Ten * time. Second)//time. Sleep (* time. Second)} "383 reads  
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.