取號機制是專案中很常會使用到的項目。在我們的生活中小到飲料店的取餐單、銀行的號碼牌, 大到公文系統的公文編號、醫院的病歷號以及我們的身分證字號, 都存在取號機制, 取號機制是用來辨別項目是唯一
的方式。
將MYSQL 欄位設定成AUTO_INCREMENT
來建立唯一識別碼, 資料寫入DB之後就可以有不重複的編號, 有需要再把編號拿出來使用, 這在很久以前用在公文/表單系統時還可以, 當時的專案不太講究處理速度, 也不會發生高併發寫入的狀況,以當時的狀況來說, 用起來也算是相安無事
。 這個方式遇到大量同時寫入需求時, 由於資料庫寫入時會把整張表鎖住, 寫入速度會變慢, 也會發生Duplicate entry 'xxx' for key 'PRIMARY'
的錯誤, 。
MYSQL 有提供參數innodb_autoinc_lock_mode
可以設定AUTO_INCREMENT寫入狀況, 可參考官網:
效能最差
效能最好
第二種取號方式是透過Redis。Redis是single thread的架構,內部是採原子式操作,就算高併發的去打Redis, 它的INCR
指令在每次執行時能確保一次都只有一個請求被操作, 所以可以用來實現取號機制。概念就像是一個調酒師可以為多個客人提供服務, 但是他一次只能調出一杯酒交付給一位客人, 但最終每個客人都能拿到酒。
另外Redis是在記憶體上面操作, 他的效能會比在資料庫上面來的好, 除了用INCR
實現取號機制之外, Redis提供的鎖也可以依照不同的使用情境選擇不同的儲存型別來實現其他的取號方式。
最後是這幾年我們採用的產生唯一碼的方式:Snowflake。
snowflake 是Twitter用來產生有序的全域唯一ID的演算法, 為了在分散式系統中讓每台機器都能產生不重複ID紀錄每秒上萬條的訊息, Twiter內部提出了snowflake演算法, 它不需要依賴任何套件, 也不需要網路就可以運作。
我們使用的套件是bwmarrin/snowflake
用法:
// nodeID := GetSnowFlakeNodeIdKey(min, max)
// nodeID for 測試用 直接使用 1
nodeID := int64(1)
node, err = snowflake.NewNode(nodeID)
node.Generate().Int64()
帶入snowflake.NewNode的nodeID要把角色區分出來,所以會在GetSnowFlakeNodeIdKey
這個function依照使用情境來取得不重複的nodeID, 服務有多Pod時每個Pod都要能取得不重複的nodeID。
此外,如果是新舊系統整併時, 要注意是不是有資料整合的情況, 如果兩邊都使用snowflake取號就會有拿到同樣ID的機會, 所以還有一個sonyflake演算法可以使用喔
參考資料: