1. 程式人生 > >分布式唯一id生成器的想法

分布式唯一id生成器的想法

alt 順序 class logs 版本號 id號 一半 其中 分析

0x01 起因

前端時間遇到一個問題,怎麽快速生成唯一的id,後來采用了hashid的方法。最近在網上讀到了美團關於分布式唯一id生成器的解決方案,
其中提到了三種生成法:(建議看一下這篇文章,寫得很詳細,分析到位)

  • UUID
  • 數據庫生成
  • 類snowflake方案

0x02 問題

文中提到了如下幾個問題

1.全局唯一性:不能出現重復的ID號,既然是唯一標識,這是最基本的要求。
2.趨勢遞增:在MySQL InnoDB引擎中使用的是聚集索引,由於多數RDBMS使用B-tree的數據結構來存儲索引數據,在主鍵的選擇上面我們應該盡量使用有序的主鍵保證寫入性能。
3.單調遞增:保證下一個ID一定大於上一個ID,例如事務版本號、IM增量消息、排序等特殊需求。

4.信息安全:如果ID是連續的,惡意用戶的扒取工作就非常容易做了,直接按照順序下載指定URL即可;如果是訂單號就更危險了,競對可以直接知道我們一天的單量。所以在一些應用場景下,會需要ID無規則、不規則。
上述123對應三類不同的場景,3和4需求還是互斥的,無法使用同一個方案滿足。

0x03 解決思路

美團針對上面的場景,作了兩種方案,Leaf-segment(數據庫方式)和Leaf-snowflake(snowflake方式)。
由於之前用過hashid生成唯一id,突然覺得,我們可以保證上面的問題3和4,同時解決。
即用hashid來加密連續的id,發往客戶端前先用hashid加密id,這樣競爭對手就不能簡單的相減訂單號了。

不知道是否可用,還望大家指正。

0x04 其它方案

采用xid,xid和其它方案的對比如下:
技術分享圖片

xid的由四部分組成

  • 4 byte時間戳
  • 3 byte機器特征
  • 2 byte 進程id
  • 3 byte 隨機計數器

上代碼測試一下

package main

import (
    "github.com/rs/xid"
)

func main() {
    for i := 1; i < 20; i++ {
        guid := xid.New()
        println(guid.String())
    }
}

生成20個id,如下:

b8t55lhafc333a3mibh0

b8t55lhafc333a3mibhg
b8t55lhafc333a3mibi0
b8t55lhafc333a3mibig
b8t55lhafc333a3mibj0
b8t55lhafc333a3mibjg
b8t55lhafc333a3mibk0
b8t55lhafc333a3mibkg
b8t55lhafc333a3mibl0
b8t55lhafc333a3miblg
b8t55lhafc333a3mibm0
b8t55lhafc333a3mibmg
b8t55lhafc333a3mibn0
b8t55lhafc333a3mibng
b8t55lhafc333a3mibo0
b8t55lhafc333a3mibog
b8t55lhafc333a3mibp0
b8t55lhafc333a3mibpg
b8t55lhafc333a3mibq0

可以看到這些id也是單調遞增的,那麽可以搭建一個xid的服務,調用一次返回一個id。
另外還可以生成一個id池,比如預先存儲一百萬個id,然後消費者批量消費,當庫存
剩下一半時,立馬繼續生產id放入id池。

分布式唯一id生成器的想法