1. UUID
UUID是可以生成時(shí)間、空間上都獨(dú)一無二的值,其本質(zhì)是隨機(jī)+規(guī)則組合而成的。即使在兩個(gè)獨(dú)立的服務(wù)器上生成UUID,其預(yù)期值也是不同的。以MySQL為例,說明下UUID。
格式
在MySQL中,UUID值是一個(gè)128位的數(shù)字,表示為以下格式的十六進(jìn)制數(shù)字的utf8字符串:aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee。其得到的隨機(jī)值由5個(gè)部分組成,且分隔符位為:中劃線。其各部分含義如下:
- 前三組值是時(shí)間戳換算過來的;?
- 第四組值是暫時(shí)性保持時(shí)間戳的唯一性。例如,使用夏令時(shí);
- 第五組值是一個(gè)IEE 802的節(jié)點(diǎn)標(biāo)識(shí)值,它是空間上唯一的。若后者不可用,則用一個(gè)隨機(jī)數(shù)字替換。假如主機(jī)沒有網(wǎng)卡,或者我們不知道如何在某系統(tǒng)下獲得機(jī)器地址,則空間唯一性就不能得到保證,即使這樣出現(xiàn)重復(fù)值的幾率還是非常小的。
在MySQL環(huán)境中多次調(diào)用或執(zhí)行得到的后兩組值相同,若把mysqld服務(wù)器關(guān)閉,重新啟動(dòng)之后,會(huì)發(fā)現(xiàn)第四組的組與未重啟前的值發(fā)生變化,然后一直不變化,只要重新啟動(dòng)mysqld服務(wù)就會(huì)發(fā)生變化。另外,對于同一臺(tái)機(jī)器,第五組值始終不會(huì)發(fā)生變化。
優(yōu)點(diǎn)
使用UUID作為主鍵具有以下優(yōu)點(diǎn):
- UUID值在表,數(shù)據(jù)庫甚至在服務(wù)器上都是唯一的,允許您從不同數(shù)據(jù)庫合并行或跨服務(wù)器分發(fā)數(shù)據(jù)庫。
- UUID值不會(huì)公開有關(guān)數(shù)據(jù)的信息,因此在URL中使用更安全。
- 可以在避免往返數(shù)據(jù)庫服務(wù)器的任何地方生成UUID值。它也簡化了應(yīng)用程序中的邏輯。
缺點(diǎn)
除了優(yōu)勢之外,UUID值也存在一些缺點(diǎn):
- 存儲(chǔ)UUID值(16字節(jié))比整數(shù)(4字節(jié))或甚至大整數(shù)(8字節(jié))占用更多的存儲(chǔ)空間。
- 調(diào)試似乎更加困難,想象一下WHERE id ='9d6212cf-72fc-11e7-bdf0-f0def1e6646c'和WHERE id = 10哪個(gè)舒服一點(diǎn)?
- 使用UUID值可能會(huì)導(dǎo)致性能問題,因?yàn)樗鼈兊拇笮『蜎]有被排序。
數(shù)據(jù)庫案例:MySQL
在MySQL中,就內(nèi)置了對UUID的支持。在使用上需注意若干問題。
- 作為主鍵問題
UUID()函數(shù)產(chǎn)生的值,并不適合作為InnoDB引擎表的主鍵。因?yàn)楦袷綗o序,作為索引組織表存儲(chǔ)會(huì)帶來管理上的不小開銷。
- 格式問題
在MySQL中,可以使用UUID()來生成主鍵,但是用MySQL的UUID()函數(shù) ,生成的UUID是36位的,其中包含32個(gè)字符以及4個(gè)分隔符(-),往往這個(gè)分隔符對我們來說是沒有用的,可以使用MySQL自帶的REPLACE函數(shù)去掉分隔符。
- 內(nèi)置函數(shù)
支持在MySQL中,可以以緊湊格式(BINARY)存儲(chǔ)UUID值,并通過以下功能顯示人機(jī)可讀格式(VARCHAR):UUID_TO_BIN、BIN_TO_UUID、IS_UUID。需要注意,UUID_TO_BIN(),BIN_TO_UUID()和IS_UUID()函數(shù)僅在MySQL 8.0或更高版本中可用。- UUID_TO_BIN()函數(shù)將UUID從人類可讀格式(VARCHAR)轉(zhuǎn)換成用于存儲(chǔ)的緊湊格式(BINARY)格式- BIN_TO_UUID()函數(shù)將UUID從緊湊格式(BINARY)轉(zhuǎn)換為人類可讀格式(VARCHAR)- IS_UUID()函數(shù)則可用來判斷參數(shù)是有效的字符串格式UUID。
2. NanoID
UUID 是軟件開發(fā)中最常用的通用標(biāo)識(shí)符之一。然而,在過去的幾年里,其他的競品挑戰(zhàn)了它的存在。其中,NanoID 是 UUID 的主要競爭對手之一。但是,這兩者之間的主要區(qū)別很簡單。它歸結(jié)為鍵所使用的字母表。由于 NanoID 使用比 UUID 更大的字母表,因此較短的 ID 可以用于與較長的 UUID 相同的目的。
優(yōu)點(diǎn)
- 更小
NanoID 只有 108 個(gè)字節(jié)那么大。與 UUID 不同,NanoID 的大小要小 4.5 倍,并且沒有任何依賴關(guān)系。此外,大小限制已用于將大小從另外 35% 減小。大小減少直接影響數(shù)據(jù)的大小。例如,使用 NanoID 的對象小而緊湊,能夠用于數(shù)據(jù)傳輸和存儲(chǔ)。隨著應(yīng)用程序的增長,這些數(shù)字變得明顯起來。
- 更安全
在大多數(shù)隨機(jī)生成器中,它們使用不安全的 Math.random()。但是,NanoID 使用 crypto module 和 Web Crypto API,意味著 NanoID 更安全。此外,NanoID 在 ID 生成器的實(shí)現(xiàn)過程中使用了自己的算法,稱為 統(tǒng)一算法,而不是使用“隨機(jī) % 字母表” random % alphabet。
- 更快
NanoID既快速又緊湊,NanoID 比 UUID 快 60%。與 UUID 字母表中的 36 個(gè)字符不同,NanoID 只有 21 個(gè)字符。
- 更多語言
NanoID 支持 14 種不同的編程語言,它們分別是:C#、C++、Clojure 和 ClojureScript、Crystal、Dart & Flutter、Deno、Go、Elixir、Haskell、Janet、Java、Nim、Perl、PHP、帶字典的 Python、Ruby、Rust、Swift。
- 更好兼容性
它還支持 PouchDB、CouchDB WebWorkers、Rollup 以及 React 和 Reach-Native 等庫。我們可以使用 npx nanoid 在終端中獲得唯一 ID。在 JavaScript 中使用 NanoID 唯一的要求是要先安裝 NodeJS。
- 自定義字母
NanoID 的另一個(gè)現(xiàn)有功能是它允許開發(fā)人員使用自定義字母表。我們可以更改文字或 id 的大小。在下面的示例中,我將自定義字母表定義為 ABCDEF1234567890,并將 Id 的大小定義為 12。
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('ABCDEF1234567890', 12);
model.id = nanoid();
- 沒有第三方依賴
由于 NanoID 不依賴任何第三方依賴,隨著時(shí)間的推移,它能夠變得更加穩(wěn)定自治。從長遠(yuǎn)來看,這有利于優(yōu)化包的大小,并使其不太容易出現(xiàn)依賴項(xiàng)帶來的問題。
數(shù)據(jù)庫案例-ShardingSphere
原生數(shù)據(jù)庫產(chǎn)品,大多沒有支持NanoID,但可通過外部方式引用進(jìn)來。例如在開源項(xiàng)目 Apache ShardingSphere 中可通過規(guī)則的配置,在其分片表中使用 NanoID作為主鍵生成器。參考如下配置:
CREATE SHARDING TABLE RULE t_order(
RESOURCES(ds_3307,ds_3308),
SHARDING_COLUMN=order_id,TYPE(NAME=hash_mod,PROPERTIES("sharding-count"=4)),
KEY_GENERATE_STRATEGY(COLUMN=order_id,TYPE(NAME=NanoID,PROPERTIES("worker-id"=123)))
);
CREATE TABLE t_order (
user_id int NOT NULL,
status varchar(50) DEFAULT NULL,
PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. SnowFlake
分布式系統(tǒng)中ID生成方案,比較簡單的是UUID(Universally Unique Identifier,通用唯一識(shí)別碼),但是其存在兩個(gè)明顯的弊端:一、UUID是128位的,長度過長;二、UUID是完全隨機(jī)的,無法生成遞增有序的UUID。而現(xiàn)在流行的基于 Snowflake 雪花算法的ID生成方案就可以很好地解決了UUID存在的這兩個(gè)問題。
原理
Snowflake 雪花算法,由Twitter提出并開源,可在分布式環(huán)境下用于生成唯一ID的算法。該算法生成的是一個(gè)64位的ID。在同一個(gè)進(jìn)程中,它首先是通過時(shí)間位保證不重復(fù),如果時(shí)間相同則是通過序列位保證。同時(shí)由于時(shí)間位是單調(diào)遞增的,且各個(gè)服務(wù)器如果大體做了時(shí)間同步,那么生成的主鍵在分布式環(huán)境可以認(rèn)為是總體有序的,這就保證了對索引字段的插入的高效性。例如 MySQL 的 Innodb 存儲(chǔ)引擎的主鍵。
格式
使用雪花算法生成的主鍵,二進(jìn)制表示形式包含 4 部分,從高位到低位分表為:1bit 符號(hào)位、41bit 時(shí)間戳位、10bit 工作進(jìn)程位以及 12bit 序列號(hào)位。
- 符號(hào)位(1bit)
預(yù)留的符號(hào)位,恒為零。
- 時(shí)間戳位(41bit)
41 位的時(shí)間戳可以容納的毫秒數(shù)是 2 的 41 次冪,一年所使用的毫秒數(shù)是:365 * 24 * 60 * 60 * 1000。通過計(jì)算可知:Math.pow(2, 41) / (365 * 24 * 60 * 60 * 1000L); 結(jié)果約等于 69.73 年。Apache ShardingSphere 的雪花算法的時(shí)間紀(jì)元從 2016 年 11 月 1 日零點(diǎn)開 始,可以使用到 2086 年,相信能滿足絕大部分系統(tǒng)的要求。
- 工作進(jìn)程位(10bit)
該標(biāo)志在 Java 進(jìn)程內(nèi)是唯一的,如果是分布式應(yīng)用部署應(yīng)保證每個(gè)工作進(jìn)程的 id 是不同的。該值默認(rèn)為0,可通過屬性設(shè)置。
- 序列號(hào)位(12bit)
該序列是用來在同一個(gè)毫秒內(nèi)生成不同的 ID。如果在這個(gè)毫秒內(nèi)生成的數(shù)量超過 4096 (2 的 12 次冪),那么生成器會(huì)等待到下個(gè)毫秒繼續(xù)生成。
優(yōu)點(diǎn)
使用SnowFlake的優(yōu)點(diǎn)是其空間占用更小,且具備一定有序性,這對于類似MySQL數(shù)據(jù)庫是比較友好的。
缺點(diǎn)
因?yàn)槠渖刹呗孕鑵⒖籍?dāng)前時(shí)間,當(dāng)服務(wù)器時(shí)鐘回?fù)軙?huì)導(dǎo)致產(chǎn)生重復(fù)序列,因此默認(rèn)分布式主鍵生成器提供了一個(gè)最大容忍的時(shí)鐘回?fù)芎撩霐?shù)。如果時(shí)鐘回?fù)艿臅r(shí)間超過最大容忍的毫秒數(shù)閾值,則程序報(bào)錯(cuò);如果在可容忍的范圍內(nèi),默認(rèn)分布式主鍵生成器會(huì)等待時(shí)鐘同步到最后一次主鍵生成的時(shí)間后再繼續(xù)工作。最大容忍的時(shí)鐘回?fù)芎撩霐?shù)的默認(rèn)值為 0,可通過屬性設(shè)置。
數(shù)據(jù)庫案例-ShardingSphere
原生數(shù)據(jù)庫產(chǎn)品,大多沒有支持SnowFlake,但可通過外部方式引用進(jìn)來。例如在開源項(xiàng)目 Apache ShardingSphere 中可通過規(guī)則的配置,在其分片表中使用 SnowFlake作為主鍵生成器。參考如下配置
CREATE SHARDING TABLE RULE t_order(
RESOURCES(ds_3307,ds_3308),
SHARDING_COLUMN=order_id,TYPE(NAME=hash_mod,PROPERTIES("sharding-count"=4)),
KEY_GENERATE_STRATEGY(COLUMN=order_id,TYPE(NAME= Snowflake,PROPERTIES("worker-id"=123)))
);
CREATE TABLE t_order (
order_id varchar(50) NOT NULL,
user_id int NOT NULL,
status varchar(50) DEFAULT NULL,
PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
作者介紹
韓鋒,51CTO社區(qū)編輯,CCIA(中國計(jì)算機(jī)協(xié)會(huì))常務(wù)理事,前Oracle?ACE,騰訊TVP,阿里云MVP,dbaplus等多家社群創(chuàng)始人或?qū)<覉F(tuán)成員。有著豐富的一線數(shù)據(jù)庫架構(gòu)、軟件研發(fā)、產(chǎn)品設(shè)計(jì)、團(tuán)隊(duì)管理經(jīng)驗(yàn)。曾擔(dān)任多家公司首席DBA、數(shù)據(jù)庫架構(gòu)師等職。在云、電商、金融、互聯(lián)網(wǎng)等行業(yè)均有涉獵,精通多種關(guān)系型數(shù)據(jù)庫,對NoSQL及大數(shù)據(jù)相關(guān)技術(shù)也有涉足,實(shí)踐經(jīng)驗(yàn)豐富。曾著有數(shù)據(jù)庫相關(guān)著作《SQL優(yōu)化最佳實(shí)踐》、《數(shù)據(jù)庫高效優(yōu)化》。