12.30 生成唯一ID的四種辦法 程序員必備

生成唯一ID的四種辦法 程序員必備

我們在實際編程過程中會經常遇到需要用唯一ID的場合,這些唯一ID還會存到數據庫中以便於我們將來進行查詢。

例如用戶編號、訂單編號、客戶編號等等,幾乎凡是需要用來嚴格劃分用戶數據歸屬性的地方就需要用到唯一ID,否則A的數據到了B那,數據亂了整個系統也就算是毀了。

那麼唯一ID該如何有效的生成就變成了一門學問了。

今天我們來講講在Python裡生成唯一ID的幾種方式,包括但不限於以下4種。

1.UUID

UUID應該是大家耳熟能詳的一個東西了,它的全稱叫 通用唯一識別碼(英語:Universally Unique Identifier,縮寫:UUID

<code>import uuid

uid = uuid.uuid1()
print(uid)
print(uid.hex)/<code>


生成唯一ID的四種辦法 程序員必備

通過以上代碼我們用Python生成來一個UUID字符串,用的是uuid1方法生成,默認會生成一個帶減號(-)的字符串,我們可以通過hex數據拿到不帶減號的版本,可以根據實際情況使用。

那麼什麼是uuid1呢?我們來看看Python關於UUID的源碼。


生成唯一ID的四種辦法 程序員必備

篇幅有限我就不貼所有源碼了,不過我們可以從源碼註釋裡得知,uuid1這個方法主要是根據當前機器ID,一個隨機序列號和時間戳生成的一個隨機字符串。

如果是簡單的應用,數據量也不大的情況,是可以用UUID來作為唯一編號的,不過在大型系統裡,UUID其實不太適合作為主要數據的唯一ID,特別是用戶ID,訂單ID等等。

原因是我們在數據庫裡通常會對這些數據字段做了索引以便於系統進行頻繁的查詢,而在很大的數據體量裡,一串32位長度的隨機字符串作為索引就是一個災難,我們一般不建議在非線性數據的字段上做索引,這樣效率會非常低。

至於為什麼字符串不適合做數據庫索引我們以後再講,在這裡只需要知道UUID不適合做需要頻繁查詢的數據唯一ID就是了。


2:數據庫主鍵自增ID

前面說到UUID因為索引的問題不適合在海量數據裡作為主要數據的唯一ID,那麼數據庫自帶的主鍵ID是否能作為唯一ID使用呢?

什麼是自增ID呢?它長成下面這樣。

生成唯一ID的四種辦法 程序員必備

默認情況下在數據庫每一個表裡都有一個名為id的字段,它也被稱之為主鍵ID,它的特點是從1開始,當每新增一條數據,id裡的值就會自動加1,因為它的內容是int類型的且數據具有連續性,也適合用來作為索引,看起來我們找到了唯一ID的辦法。

但是自增ID的問題在於以下兩點:

  • 當數據量太大,比如客戶信息一張表的數據超過上千萬條時,我們就面臨著要分表存儲的問題,在多張表的情況下,如何劃分自增ID是一個很麻煩的問題。
  • 安全問題,客戶端可以根據自增ID很輕易猜出我們的業務數據,按照順序遍歷就是了。

綜上所述,自增ID雖然能用,但也不是一個特別好的辦法。

3: mongodb的ObjectId


相信使用過mongodb的朋友們很清楚,它的文檔默認的key其實也是一個uuid,所以我們也可以利用mongodb的ObjectId來產生一個UUID。

在python裡直接使用一個叫bson的第三方包即可,BSON是一種計算機數據交換格式,主要被用作MongoDB數據庫中的數據存儲和網絡傳輸格式。


主要代碼如下:

<code>import bson
demoid = bson.ObjectId()
print(demoid)/<code>


生成唯一ID的四種辦法 程序員必備

不過mongodb的ObjectId和uuid一樣,還是存在相同的問題,這裡僅作參考。


4: 雪花算法 Snowflake。

以上三種辦法我們都試過了,或多或少都有一些缺陷,那麼有沒有更好的辦法呢?既可以兼顧查詢效率,也可以兼顧數據的唯一性。

讓我們來把目光瞄向很多大廠,因為它們的服務每天都會產生海量的數據,那麼我們看看它們是如何做的。

twitter(推特)前些年把自己的唯一ID生成算法開源了,也叫做雪花算法,取自(世界上沒有一片相同的雪花)。

先看看雪花算法的基本原理。


生成唯一ID的四種辦法 程序員必備

snowflake算法核心思想上是用64位的二進制來表示數據。

其中41位的時間戳表示:當前時間戳減去某個設定的起始時間。

10位標識表示:不同的機器、數據庫的標識ID等等,序列號為每秒或每毫秒內自增的id。

因為雪花算法最後生成出來的其實是一串唯一的數字,而非字符串,適合做數據庫索引,看起來非常滿足我們的需求,下面就來看看雪花算法在Python中的實現吧。

核心代碼如下:

<code>def __init__(self, worker_id=0, data_center_id=0):
self.worker_id = worker_id
self.data_center_id = data_center_id

self.user_agent_parser = re.compile("^[a-zA-Z][a-zA-Z\\-0-9]*$")
self.logger = logging.getLogger("idworker")

# stats
self.ids_generated = 0


# 2019.08.08 08:08:08 timestamp
self.twepoch = 1565222888000

self.sequence = 0
self.worker_id_bits = 5
self.data_center_id_bits = 5
self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)
self.max_data_center_id = -1 ^ (-1 << self.data_center_id_bits)
self.sequence_bits = 12

self.worker_id_shift = self.sequence_bits
self.data_center_id_shift = self.sequence_bits + self.worker_id_bits
self.timestamp_left_shift = self.sequence_bits + self.worker_id_bits + self.data_center_id_bits

self.sequence_mask = -1 ^ (-1 << self.sequence_bits)

self.last_timestamp = -1
def _time_gen(self):
return int(time.time() * 1000)

def _till_next_millis(self, last_timestamp):
timestamp = self._time_gen()
while last_timestamp <= timestamp:
timestamp = self._time_gen()

return timestamp

def _next_id(self):
timestamp = self._time_gen()

if self.last_timestamp > timestamp:
self.logger.warning("clock is moving backwards. Rejecting request until %i" % self.last_timestamp)
raise InvalidSystemClock(
"Clock moved backwards. Refusing to generate id for %i milliseocnds" % self.last_timestamp)

if self.last_timestamp == timestamp:
self.sequence = (self.sequence + 1) & self.sequence_mask
if self.sequence == 0:
timestamp = self._till_next_millis(self.last_timestamp)
else:
self.sequence = 0

self.last_timestamp = timestamp

new_id = ((timestamp - self.twepoch) << self.timestamp_left_shift) | (
self.data_center_id << self.data_center_id_shift) | (self.worker_id << self.worker_id_shift) | self.sequence
self.ids_generated += 1
return new_id/<code>


最後我們來用雪花算法生成幾個唯一ID試試。


生成唯一ID的四種辦法 程序員必備

通過以上例子我們可以發現,雪花算法第一是生成的數字類型的數據,第二數字之間是具有一定的連續性的,這樣的優點在查詢上非常具有效率。


以上介紹的4種生成唯一ID的方式各有千秋,在不同的應用場景下會發揮不同的用處,事實上在分佈式系統裡,還有很多各種各樣的解決方案,在這裡就不一一提供了。

我們先來總結以上四種方式的優缺點吧。

總結

  • UUID/ObjectID
    • 優點: 本機生成,效率高,全局唯一性,通用標準。
    • 缺點:不利於存儲,在Mysql的InnoDB引擎下做索引很影響效率,不利於海量數據查詢。
  • 數據庫自增ID
    • 優點:簡單,生成時沒有編碼成本。
    • 缺點:極度依賴數據庫,分表分庫或者數據庫做主從結構時無法保證唯一性,ID在生成時會出現性能瓶頸。
  • 雪花算法(snowflake)
    • 優點: 不依賴第三方系統,ID全局唯一,數據具有遞增的連續性,便於查詢。
    • 缺點:依賴系統時鐘,如果系統時鐘有問題,會導致ID重複(該問題可以通過很多方式避免)

關於如何生成唯一ID我們就介紹到這,在設計系統時根據實際情況選擇最適合我們的方案,從個人的角度出發,有限推薦雪花算法的方案,理論上來說,它在一秒鐘可以生成400多萬個唯一ID,也就是說我們的業務數據併發量沒有達到每秒400萬次的情況下,這個方案都是安全的。

還有更多的唯一ID生成方式,歡迎大家回覆,便於交流。

因篇幅有限,關於Python的雪花算法實現代碼請私信我獲取。


分享到:


相關文章: