一切的合理性都可以通过数学来解释(自己瞎编的),今天就用数学给大家变个戏法,实现一个唯一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(服务繁忙,请稍后重试--`限流`)。