一切的合理性都可以通過數學來解釋(自己瞎編的),今天就用數學給大家變個戲法,實現一個唯一ID生成器;而用到的數據知識包括排列組合+質數特性應用:
先給出一個數學定義:在一個質數集合中隨意取出2n個質數,讓他們兩兩相乘然後加和得m,放入後重新再取出2n個質數,經過相同步驟計算得出z,除非前後取出的質數相同且兩兩相乘的組合也相同,否則m != z(我拿着筆經過一陣噼裏啪啦,驗證得來的)。
所以當質數集合很大時,可以得到上千萬種的排列組合,即上千萬個唯一id,雖然在看起來還是很雞肋的,但是在分佈式應用,可以作爲snowflake算法生成的唯一id中的seq部分,這樣就不需要依賴任何服務去維護這個seq了,不需要依賴機器配置+中間件,就能實現一個分佈式唯一id生成器(哪天去試着實現試試),通過上千萬種的排列組合將一秒內生成兩個相同id的可能性降到無限接近0。
甩出代碼:
//生成唯一key
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"time"
)
const MAX_KEYS_CNT = 100000
const PL_BITS = 1024
const PL_BITS_MASK = 990
const PL_BITS2 = 4096
const PL_BITS2_MASK = 4095
var randoms []int64
var xuL []int64
var keys map[int64]bool
var cover int64
var maxKey int64
var randCt *rand.Rand
func keyRemove(cpRandoms []int64, index int) (int64, []int64) {
random := cpRandoms[index]
cpRandoms[len(cpRandoms)-1], cpRandoms[index] = cpRandoms[index], cpRandoms[len(cpRandoms)-1]
return random, cpRandoms[:len(cpRandoms)-1]
}
func init() {
randCt = rand.New(rand.NewSource(time.Now().UnixNano()))
randoms = make([]int64, 0, PL_BITS2)
xuL = make([]int64, 0, PL_BITS)
//測試
keys = make(map[int64]bool, MAX_KEYS_CNT)
fileA, err := os.OpenFile("zs_before.dat", os.O_RDONLY, 0755)
if err != nil {
fmt.Printf("file:`zs_before.dat`, err:`%v`\n", err)
}
defer fileA.Close()
fileB, err := os.OpenFile("zs_after.dat", os.O_RDONLY, 0755)
if err != nil {
fmt.Printf("file:`zs_after.dat`, err:`%v`\n", err)
}
defer fileB.Close()
scan := bufio.NewScanner(fileA)
countKey := 0
for scan.Scan() {
if countKey >= PL_BITS {
break;
}
intR, err := strconv.ParseInt(scan.Text(), 10, 64)
if err != nil {
fmt.Printf("err:`%v`\n", err)
break;
}
countKey++
xuL = append(xuL, intR)
}
scan = bufio.NewScanner(fileB)
countKey = 0
for scan.Scan() {
if countKey >= PL_BITS2 {
break;
}
intR, err := strconv.ParseInt(scan.Text(), 10, 64)
if err != nil {
fmt.Printf("err:`%v`\n", err)
break;
}
countKey++
randoms = append(randoms, intR)
}
}
func keyCreate() int64 {
var keyNumber, k int64 = 0, 0
initCnt := PL_BITS2
cpRandoms := randoms[:]
keysN := []int64{}
xlOffset := randCt.Intn(PL_BITS_MASK)
cpXuL := xuL[xlOffset:xlOffset+28]
for _, key := range cpXuL {
random := randCt.Intn(initCnt)&PL_BITS2_MASK
k, cpRandoms = keyRemove(cpRandoms, random)
keyNumber += key*k
keysN = append(keysN, k)
initCnt--
}
b, ok := keys[keyNumber]
if ok && b {
cover++
}
keys[keyNumber] = true;
if maxKey < keyNumber {
maxKey = keyNumber
}
return keyNumber
}
func main() {
t := time.Now()
for i := 0; i < MAX_KEYS_CNT; i++ {
keyCreate()
}
subXuL := xuL[:]
subRands := randoms[len(randoms)-len(xuL):]
var sum int64
for i, num := range subXuL {
sum += num*subRands[i]
}
fmt.Printf("cost:`%v`\n", time.Since(t))
fmt.Printf("max-key:`%v`, cover-`%d`\n", sum, cover)
}
實測:
看來這個`公式`還是很靠譜的!
在生成10w個唯一seq測試中,平均出現重複seq的次數爲0.5,平均時間爲174ms;
在生成100w個唯一seq測試中,平均出現重複seq的次數爲17.6,平均用時僅1.5s (重複次數出現的較多,可能是產生排列組合處的算法可以再優化“這裏依賴產生的隨機數”,因爲集合的大小是1024,排列的長度爲28,有1024*1023*....*997種可能,產生重複seq的概率極低)
總結:
snowflake算法生成的唯一ID的各個組成部分之間互不依賴,它們共同組成一個固定bit位數的ID,以上算法可構成ID的seq部分;如果ID重複,可重試生成新的ID,這時如再次重複的話,可將請求pass(服務繁忙,請稍後重試--`限流`)。