日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

uuid表示时间的部分_技术译文 | UUID 很火但性能不佳?今天我们细聊一聊

發(fā)布時間:2023/12/10 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 uuid表示时间的部分_技术译文 | UUID 很火但性能不佳?今天我们细聊一聊 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

作者:Yves Trudeau Yves 是 Percona 的首席架構師,專門研究分布式技術,例如 MySQL Cluster,Pacemaker 和 XtraDB cluster。他以前是 MySQL 和 Sun 的高級顧問。擁有實驗物理學博士學位。

原文鏈接:https://www.percona.com/blog/2019/11/22/uuids-are-popular-but-bad-for-performance-lets-discuss/

如果你在網(wǎng)上快速的做一個關于 UUID 和 MySQL 的搜索,你會得到相當多的結果。以下是一些例子:
  • 存儲 UUID 和 生成列

  • 在 MySQL 中存儲 UUID 的值

  • 說明 InnoDB 中的主鍵模型及其對磁盤使用的影響

  • 主鍵選型之戰(zhàn) UUID vs. INT

  • GUID / UUID 的性能突破

  • 到底需不需要 UUID?

另:以上文章鏈接請在文章結尾處查看。那么,像這樣一個眾所周知的話題還需要更多關注嗎?顯然是的。盡管大多數(shù)帖子都警告人們不要使用 UUID,但它們?nèi)匀环浅J軞g迎。這種受歡迎的原因是,這些值可以很容易地由遠程設備生成,并且沖突的概率非常低。這篇文章,目標是總結其他人已經(jīng)寫過的東西,并希望能帶來一些新的想法。UUID 是什么?UUID 代表通用唯一標識符,在 RFC 4122 中定義。它是一個 128 位數(shù)字,通常以十六進制表示,并用破折號分成五組。典型的 UUID 值如下所示:

RFC 4122:https://tools.ietf.org/html/rfc4122

yves@laptop:~$ uuidgen

83fda883-86d9-4913-9729-91f20973fa52

一共有 5 種正式的 UUID 值類型(版本 1 - 5),但最常見的是:基于時間的(版本 1 / 2)和純隨機的(版本 3)。自 1970 年 1 月 1 日起,對 10ns 內(nèi)基于時間類型的 7.5 個字節(jié)(60位)形式的 UUID 數(shù)目進行編碼,并以 "time-low"-"time-mid"-"time-hi" 的格式進行劃分。缺少的 4 位是用作 time-hi 字段前綴的版本號。前三組的 64 位值就這么產(chǎn)生了。最后兩組是時鐘序列,每次修改時鐘都會增加一個值以及一個主機唯一標識符。大多數(shù)情況下,主機主網(wǎng)絡接口的 MAC 地址用作唯一標識符。使用基于時間的 UUID 值時,需要注意以下幾點:
  • 可以從前三個字段確定生成值的大概時間

  • 連續(xù)的 UUID 值之間有許多重復字段

  • 第一個字段 "time-low" 每 429 秒滾動一次

  • MySQL UUID 函數(shù)產(chǎn)生 1 版本的值

這是一個使用 "uuidgen" (Unix 工具)生成基于時間的值的示例:

yves@laptop:~$ for i in $(seq 1 500); do echo "$(date +%s): $(uuidgen -t)"; sleep 1; done

1573656803: 572e4122-0625-11ea-9f44-8c16456798f1

1573656804: 57c8019a-0625-11ea-9f44-8c16456798f1

1573656805: 586202b8-0625-11ea-9f44-8c16456798f1

...

1573657085: ff86e090-0625-11ea-9f44-8c16456798f1

1573657086: 0020a216-0626-11ea-9f44-8c16456798f1

...

1573657232: 56b943b2-0626-11ea-9f44-8c16456798f1

1573657233: 57534782-0626-11ea-9f44-8c16456798f1

1573657234: 57ed593a-0626-11ea-9f44-8c16456798f1

...

第一個字段翻轉(t=1573657086),第二個字段遞增。第一個字段再次看到相似的值大約需要 429s。第三個字段每年大約更改一次。最后一個字段在給定主機上是靜態(tài)的,MAC 地址在筆記本電腦上使用:

yves@laptop:~$ ifconfig | grep ether | grep 8c

ether 8c:16:45:67:98:f1 txqueuelen 1000 (Ethernet)

另一個經(jīng)常看到的 UUID 是版本 4,即純隨機版本。默認情況下 "uuidgen" 工具會生成 UUID 版本4 的值:

yves@laptop:~$ for i in $(seq 1 3); do uuidgen; done

6102ef39-c3f4-4977-80d4-742d15eefe66

14d6e343-028d-48a3-9ec6-77f1b703dc8f

ac9c7139-34a1-48cf-86cf-a2c823689a91

唯一的 “重復”值是第三個字段開頭的版本 "4"。其他 124 位都是隨機的。UUID 的值到底有什么問題?為了了解使用 UUID 值作為主鍵的影響,重要的是要檢查 InnoDB 如何組織數(shù)據(jù)。InnoDB 將表的行存儲在主鍵的 b-tree(聚簇索引)中。聚簇索引通過主鍵自動對行進行排序。當插入具有隨機主鍵值的新數(shù)據(jù)時,InnoDB 必須找到該行所屬的頁面,如果尚不存在該頁面,則將其加載到緩沖池中,插入該行,然后最終將頁面刷新回 磁盤。如果使用純隨機值和大表,則所有 b-tree 的葉子頁都易于接收新行,沒有熱頁。不按主鍵順序插入的行會導致頁面拆分,從而導致較低的填充系數(shù)。對于比緩沖池大得多的表,插入很可能需要從磁盤讀取表頁。緩沖池中已插入新行的頁面將變?yōu)榕K頁。在需要刷新到磁盤之前,該頁面接收第二行的幾率非常低。在大多數(shù)情況下,每次插入都會導致兩次 IOP(一讀一寫)。第一個主要是對 IOP 速率的影響,它是可伸縮性的主要限制因素。因此,獲得良好性能的唯一方法是使用具有低延遲和高耐久性的存儲。這是第二個主要的影響因素。對于聚集索引,輔助索引將主鍵值用作指針。主鍵 b-tree 的葉子來存儲行,而二級索引 b-tree 的葉子來存儲主鍵值。假設一張一百萬行的表格具有 UUID 主鍵和五個輔助索引。通過閱讀上一段,我們知道每行主鍵值存儲六次。這意味著總共有六百萬個char(36) 類型的值,意味著數(shù)據(jù)總量 216 GB。這只是冰山一角,因為表通常具有指向其他表的外鍵(無論是否顯式)。當架構基于 UUID 值時,所有支持的列和索引均為 char(36) 類型。基于 UUID 的架構,大約 70% 的存儲用于這些值。如果這還不夠,那么使用 UUID 值會產(chǎn)生第三個重要影響。CPU 一次最多可比較 8 個字節(jié)的整數(shù)值,但 UUID 值每個字符之間都要比較。數(shù)據(jù)庫很少受到 CPU 的限制,但這仍然增加了查詢的延遲。如果還不確定,請看一下整數(shù)與字符串之間的性能比較:

mysql> select benchmark(100000000,2=3);

+--------------------------+

| benchmark(100000000,2=3) |

+--------------------------+

| 0 |

+--------------------------+

1 row in set (0.96 sec)

mysql> select benchmark(100000000,'df878007-80da-11e9-93dd-00163e000002'='df878007-80da-11e9-93dd-00163e000003');

+----------------------------------------------------------------------------------------------------+

| benchmark(100000000,'df878007-80da-11e9-93dd-00163e000002'='df878007-80da-11e9-93dd-00163e000003') |

+----------------------------------------------------------------------------------------------------+

| 0 |

+----------------------------------------------------------------------------------------------------+

1 row in set (27.67 sec)

當然,以上示例是最壞的情況,但至少可以說明問題的范圍。整數(shù)的比較大約快 28 倍。即使差值在 char 值中迅速出現(xiàn),也仍然比 UUID 慢了約 2.5 倍:

mysql> select benchmark(100000000,'df878007-80da-11e9-93dd-00163e000002'='ef878007-80da-11e9-93dd-00163e000003');

+----------------------------------------------------------------------------------------------------+

| benchmark(100000000,'df878007-80da-11e9-93dd-00163e000002'='ef878007-80da-11e9-93dd-00163e000003') |

+----------------------------------------------------------------------------------------------------+

| 0 |

+----------------------------------------------------------------------------------------------------+

1 row in set (2.45 sec)

讓我們探索一些解決這些問題的解決方案。值的尺寸UUID,hash 和 token 的默認表示形式通常是十六進制表示法。對于基數(shù),可能的值數(shù)(每個字節(jié)只有 16 個)遠沒有效率。使用其他表示形式(如 base64 或直接二進制)怎么辦?我們可以節(jié)省多少?性能如何受到影響?讓我們以 base64 表示法開始。每個字節(jié)的基數(shù)為 64(六十四進制),因此在 3 個字節(jié)在 base64 中需要 來表示 2 個字節(jié)的實際值。一個 UUID 的值由 16 個字節(jié)的數(shù)據(jù)組成,如果我們除以 3,則余數(shù)為 1。為處理該問題,base64 編碼在末尾添加了 '==' :

mysql> select to_base64(unhex(replace(uuid(),'-','')));

+------------------------------------------+

| to_base64(unhex(replace(uuid(),'-',''))) |

+------------------------------------------+

| clJ4xvczEeml1FJUAJ7+Fg== |

+------------------------------------------+

1 row in set (0.00 sec)

如果知道編碼實體的長度(例如 UUID 的長度),我們就可以刪除 "==",因為它只是一種長度配重。因此,以 base64 編碼的 UUID 的長度為 22。

下一步的邏輯步驟是直接以二進制格式存儲值。這是最理想的格式,但是在 MySQL 客戶端中顯示值不太方便。

那么,尺寸對性能有何影響?為了說明影響,我在具有以下定義的表中插入了隨機的 UUID 值。

CREATE TABLE `data_uuid` (

`id` char(36) NOT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

為默認的十六進制表示形式。對于 base64,"id" 列定義為 char(22),而 binary(16) 用于二進制示例。數(shù)據(jù)庫服務器的緩沖池大小為 128M,其 IOP 限制為 500。插入是在單個線程上完成的。

使用 UUID 值的不同表示形式的表的插入率在所有情況下,插入速率最初都是受 CPU 限制的,但是一旦表大于緩沖池,則插入將很快成為 IO 限制。對于 UUID 值使用較小的表示形式只會使更多的行進入緩沖池,但從長遠來看,這對性能沒有真正的幫助,因為隨機插入順序占主導地位。如果使用隨機 UUID 值作為主鍵,則性能會受到您可以承受的內(nèi)存量的限制。方案 1:使用偽隨機順序保存如我們所見,最重要的問題是值的隨機性。新的行可能會在任何表的子頁中結束。因此,除非整個表都已加載到緩沖池中,否則它意味著讀 IOP,最后是寫 IOP。我的同事 David Ducos 為這個問題提供了一個很好的解決方案,但是一些客戶不想 UUID 值中提取信息,例如生成時間戳。如果我們只是稍微減少值的隨機性,以使幾個字節(jié)的前綴在一個時間間隔內(nèi)不變,該怎么辦?在該時間間隔內(nèi),只需要將整個表的一小部分(對應于前綴的基數(shù))存儲在內(nèi)存中,以保存讀取的 IOP。這也將增加頁面在刷新到磁盤之前接收第二次寫入的可能性,從而減少了寫入負載。讓我們考慮以下 UUID 生成函數(shù):

drop function if exists f_new_uuid;

delimiter ;;

CREATE DEFINER=`root`@`%` FUNCTION `f_new_uuid`() RETURNS char(36)

NOT DETERMINISTIC

BEGIN

DECLARE cNewUUID char(36);

DECLARE cMd5Val char(32);

set cMd5Val = md5(concat(rand(),now(6)));

set cNewUUID = concat(left(md5(concat(year(now()),week(now()))),4),left(cMd5Val,4),'-',

mid(cMd5Val,5,4),'-4',mid(cMd5Val,9,3),'-',mid(cMd5Val,13,4),'-',mid(cMd5Val,17,12));

RETURN cNewUUID;

END;;

delimiter ;

函數(shù)說明

UUID 值的前四個字符來自當前年份和星期編號的串聯(lián) MD5 哈希值。當然,該值在一個星期內(nèi)是靜態(tài)的。UUID 值的其余部分來自隨機值的 MD5 和當前時間,精度為 1us。第三個字段以 "4" 為前綴,表示它是版本 4 的 UUID 類型。有 65536 個可能的前綴,因此在一周內(nèi),內(nèi)存中僅需要表行的 1/65536,以避免在插入時讀取 IOP。這更容易管理,一個 1TB 的表在緩沖池中只需要大約 16MB 的空間即可支持插入。方案 2:將 UUID 映射成整數(shù)即使您使用 binary(16) 存儲的偽有序的 UUID 值,它仍然是非常大的數(shù)據(jù)類型,這會增大數(shù)據(jù)集的大小。請記住,InnoDB 將主鍵值用作輔助索引中的指針。如果我們將所有的 UUID 值存儲在映射表中怎么辦?映射表將定義為:

CREATE TABLE `uuid_to_id` (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,

`uuid` char(36) NOT NULL,

`uuid_hash` int(10) unsigned GENERATED ALWAYS AS (crc32(`uuid`)) STORED NOT NULL,

PRIMARY KEY (`id`),

KEY `idx_hash` (`uuid_hash`)

) ENGINE=InnoDB AUTO_INCREMENT=2590857 DEFAULT CHARSET=latin1;

重要的是要注意 uuid_to_id 表不會強制 UUID 的唯一性。idx_hash 索引的作用有點像布隆過濾器。如果沒有匹配的哈希值,我們肯定會知道表格中沒有 UUID 值,但是如果有匹配的哈希值,我們就必須使用存儲的 UUID 值進行驗證。為幫助我們,請創(chuàng)建一個 SQL 函數(shù):

DELIMITER ;;

CREATE DEFINER=`root`@`%` FUNCTION `f_uuid_to_id`(pUUID char(36)) RETURNS int(10) unsigned

DETERMINISTIC

BEGIN

DECLARE iID int unsigned;

DECLARE iOUT int unsigned;

select get_lock('uuid_lock',10) INTO iOUT;

SELECT id INTO iID

FROM uuid_to_id WHERE uuid_hash = crc32(pUUID) and uuid = pUUID;

IF iID IS NOT NULL THEN

select release_lock('uuid_lock') INTO iOUT;

SIGNAL SQLSTATE '23000'

SET MESSAGE_TEXT = 'Duplicate entry', MYSQL_ERRNO = 1062;

ELSE

insert into uuid_to_id (uuid) values (pUUID);

select release_lock('uuid_lock') INTO iOUT;

set iID = last_insert_id();

END IF;

RETURN iID;

END ;;

DELIMITER ;

該函數(shù)檢查 uuid_to_id 表中是否存在通過驗證的 UUID 值,如果確實存在,則返回匹配的 id 值,否則將插入 UUID 值并返回 lastinsertid。為了防止同時提交相同的 UUID 值,我添加了一個數(shù)據(jù)庫鎖。數(shù)據(jù)庫鎖限制了解決方案的可伸縮性。如果您的應用程序無法在很短的時間內(nèi)提交兩次請求,則可以刪除該鎖。替代方案結論現(xiàn)在,讓我們看一下使用這些替代方案的插入率。

使用 UUID 值作為主鍵插入表的方法偽順序結果很好。在這里,我修改了算法,以使 UUID 前綴保持一分鐘而不是一星期不變,以便更好地適應測試環(huán)境。即使偽順序解決方案表現(xiàn)良好,也請記住,它仍然會使架構膨脹,總體而言,性能提升可能不會那么大。盡管由于所需的附加 DML 導致插入率較小,但映射到整數(shù)值會使架構與 UUID 值分離。這些表現(xiàn)在使用整數(shù)作為主鍵。此映射幾乎消除了使用 UUID 值的所有可伸縮性問題。盡管如此,即使在 CPU 和 IOP 受限的小型虛擬機上,UUID 映射技術也可以每秒產(chǎn)生近 4000次插入。在上下文中,這意味著每小時有 1400 萬行,每天 3.45 億行和每年 1260 億行。這樣的速度可能符合大多數(shù)要求。唯一的增長限制因素是哈希索引的大小。當哈希索引太大而無法容納在緩沖池中時,性能將開始下降。UUID 之外的選擇當然,還有其他生成唯一 ID 的可能性。MySQL 函數(shù) UUID_SHORT() 使用的方法很有趣。諸如智能手機之類的遠程設備可以使用 UTC 時間而不是服務器正常運行時間。這是一個建議:

(Seconds since January 1st 1970) << 32

+ (lower 2 bytes of the wifi MAC address) << 16

+ 16_bits_unsigned_int++;

16 位計數(shù)器應初始化為隨機值,并允許翻轉。兩個產(chǎn)生相同 ID 的設備的幾率很小。它必須大約同時發(fā)生,兩個設備的 MAC 必須具有相同的低字節(jié),并且它們的 16 位計數(shù)器必須以相同的增量遞增。

作者的 GitHub:https://github.com/y-trudeau/blog_data/tree/master/YetAnotherPostAboutUUIDs文章開頭提到的搜索列表鏈接:https://www.percona.com/blog/2017/05/03/uuid-generated-columns/https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/https://www.percona.com/blog/2015/04/03/illustrating-primary-key-models-in-innodb-and-their-impact-on-disk-usage/http://www.mysqltutorial.org/mysql-uuid/http://mysql.rjweb.org/doc.php/uuidhttps://www.percona.com/blog/2007/03/13/to-uuid-or-not-to-uuid/

社區(qū)近期動態(tài)

No.1

Mycat 問題免費診斷

診斷范圍支持:

Mycat 的故障診斷、源碼分析、性能優(yōu)化

服務支持渠道:

技術交流群,進群后可提問

QQ群(669663113)

社區(qū)通道,郵件&電話

osc@actionsky.com

現(xiàn)場拜訪,線下實地,1天免費拜訪

關注“愛可生開源社區(qū)”公眾號,回復關鍵字“Mycat”,獲取活動詳情。

No.2

社區(qū)技術內(nèi)容征稿

征稿內(nèi)容:

格式:.md/.doc/.txt

主題:MySQL、分布式中間件DBLE、數(shù)據(jù)傳輸組件DTLE相關技術內(nèi)容

要求:原創(chuàng)且未發(fā)布過

獎勵:作者署名;200元京東E卡+社區(qū)周邊

投稿方式:

郵箱:osc@actionsky.com

格式:[投稿]姓名+文章標題

以附件形式發(fā)送,正文需注明姓名、手機號、微信號,以便小編及時聯(lián)系

喜歡點“分享”,不行就“在看”

總結

以上是生活随笔為你收集整理的uuid表示时间的部分_技术译文 | UUID 很火但性能不佳?今天我们细聊一聊的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。