<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
昨天在某個技術群中,有個老哥傳送了一個技術視訊:講的是一個畢業生面試被問,前後端的互動ID是使用自增的嗎?為什麼不使用UUID?最後的解釋是說效能問題,這個引起了我的興趣,查了一下資料總結一下。
在《阿里巴巴 Java 開發手冊》第五章 MySQL 規定第九條中,強制規定了單表的主鍵 id 必須為無符號的 bigint 型別,且是自增的。
MySQL開發規範中經常可以看到:
推薦使用int,bigint 無符號做自增鍵
禁止使用uuid做主鍵
關於主鍵的型別選擇上最常見的爭論是用整型還是字元型的問題,關於這個問題《高效能MySQL》一書中有明確論斷:
整數通常是標識列的最好選擇,因為它很快且可以使用AUTO_INCREAMENT,如果可能,應該避免使用字串型別作為標識列,因為很消耗空間,且通常比數位型別慢。
如果是使用MyISAM,則就更不能用字元型,因為MyISAM預設會對字元型採用壓縮引擎,從而導致查詢變得非常慢。
通常主鍵 id 的資料型別有兩種選擇:字串或者整數,主鍵通常要求是唯一的,如果使用字串型別,我們可以選擇 UUID 或者具有業務含義的字串來作為主鍵。
對於 UUID 而言,它由 32 個字元+4 個’-'組成,長度為 36,雖然 UUID 能保證唯一性,但是它有兩個致命的缺點:
1.不是遞增的。MySQL 中索引的資料結構是 B+Tree,這種資料結構的特點是索引樹上的節點的資料是有序的,而如果使用 UUID 作為主鍵,那麼每次插入資料時,因為無法保證每次產生的 UUID 有序,所以就會出現新的 UUID 需要插入到索引樹的中間去,這樣可能會頻繁地導致頁分裂,使效能下降。
2.太佔用記憶體。每個 UUID 由 36 個字元組成,在字串進行比較時,需要從前往後比較,字串越長,效能越差。另外字串越長,佔用的記憶體越大,由於頁的大小是固定的,這樣一個頁上能存放的關鍵字數量就會越少,這樣最終就會導致索引樹的高度越大,在索引搜尋的時候,發生的磁碟 IO 次數越多,效能越差。
對於整數的數位型別,MySQL 中主要有 int 和 bigint 型別。其中 int 佔用 4 個位元組,bigint 佔用 8 個位元組,這和 Java 中的 int 和 long 對應。如果使用無符號的 int 型別作為主鍵,那麼主鍵的最大值為 2^32-1,即 4294967295,這個值不到 43 億,似乎有點太小了。雖然一張表的資料,我們不可能讓其達到 43 億條(太大會影響效能),但是對於頻繁進行插入、刪除的表來說,43 億這個值是可以達到的。而如果使用無符號的 bigint 型別的話,主鍵的最大值可以達到 2^64-1,這個數足夠大了,如果以每秒插入 100 萬條資料計算的,58 萬年以後才能達到最大值。所以 bigint 作為主鍵的資料型別,完全不用擔心超過最大值的問題。
而強制要求主鍵 id 是自增的,則是為了在資料插入的過程中,儘可能的避免索引樹上頁分裂的問題。
關於主鍵是聚簇索引,如果沒有主鍵,InnoDB會選擇一個唯一鍵來作為聚簇索引,如果沒有唯一鍵,會生成一個隱式的主鍵。
隱式主鍵:
InnoDB會自動幫你建立一個不可見的、長度為6位元組的row_id,而且InnoDB維護了一個全域性的dictsys.row_id,所有未定義主鍵的表都會共用該row_id,每次插入一條資料都把全域性row_id當成主鍵id,然後全域性row_id加1。
該全域性row_id在程式碼實現上使用的是bigint unsigned型別,但實際上只給row_id保留了6位元組,所以這種設計就會存在一個問題:如果全域性row_id一直漲,直到2的48次冪-1時,這個時候再加1,row_id的低48位元都會變為0,如果再插入新一行資料時,拿到的row_id就為0,這樣的話就存在主鍵衝突的可能,所以為了避免這種隱患,每個表都需要一個主鍵。
詳解-重點:
InnoDB引擎使用聚集索引,資料記錄本身被存於主索引(一顆B+Tree)的葉子節點上。這就要求同一個葉子節點內(大小為一個記憶體頁或磁碟頁)的各條資料記錄按主鍵順序存放,因此每當有一條新的記錄插入時,MySQL 會根據其主鍵將其插入適當的節點和位置,如果頁面達到裝載因子(InnoDB預設為15/16),則開闢一個新的頁(節點)
所以在使用innoDB表時要避免隨機的(不連續且值的分佈範圍非常大)聚簇索引,特別是針對I/O密集型的應用。例如:從效能角度考慮,使用UUID的方案就會導致聚簇索引的插入變得完全隨機。
理論總結:
自增的主鍵的值是順序的,所以 Innodb 把每一條記錄都儲存在一條記錄的後面。
當達到頁面的最大填充因子時候 ( innodb預設的最大填充因子是頁大小的15/16,會留出1/16的空間留作以後的修改):
1)下一條記錄就會寫入新的頁中,一旦資料按照這種順序的方式載入,主鍵頁就會近乎於順序的記錄填滿,提升了頁面的最大填充率,不會有頁的浪費
2)新插入的行一定會在原有的最巨量資料行下一行,mysql定位和定址很快,不會為計算新行的位置而做出額外的消耗
3)減少了頁分裂和碎片的產生
選擇 主鍵id:
tinyint、smallint、mediumint,這三個不常用就不說了。無符號是設定了 unsigned 屬性,表示不允許負值,這大致可以使正數的上限提高一倍。
以無符號int型別為例,42億雖然看起來是個很大的數位,但是對於一些插入刪除很頻繁的業務來說,並非無法觸達這個上限。特別是有的業務表設定的步長比較大,會導致id自增的速度更快。如果你的業務預期會產生很多資料,那麼建議你在建立表時,直接使用bigint。
因為MySQL的主鍵策略:id自增值達到上限以後,再申請下一個 id 時,仍然是最大值。
如果bigint真的還不夠使用的話,我們可以使用雪花演演算法生成的id做主鍵,由於其也是大致遞增的,對效能也不會產生影響,只需要由bigint改成更大範圍的decimal就行。
UUID:
一:使用場景
UUID是指在一臺機器上生成的數位,它保證對在同一時空中的所有機器都是唯一的。在UUID的演演算法中,可能會用到諸如網路卡MAC地址,IP,主機名,程序ID等資訊以保證其獨立性
二:有的開發就是喜歡使用UUID怎麼辦?
所以MySQL8.0也是順應時代潮流,擔負時代的革命重任,MySQL8.0也對uuid的儲存做了進一步的提升。整體上看MySQL8.0現在的重點方向也是對開發的友好度支援上。
結論:
在MySQL8.0中還是推薦使用無符號的int, bigint做主鍵,如果要使用uuid可以建一個唯一索引
MySQL和Java兩者預設生成的uuid是version 1格式:datetime|mac地址,因為高低位順序亂了,造成順序亂掉,可以使用MySQL的函數uuid_to_bin(@uuid,1) , bin_to_uuid(@uuid,1)進行調整轉換,實現有序化
對於使用uuid_to_bin轉化後的uuid儲存,使用binary(16)或是varbinary(16)替代varchar(36),從而實現從36byte降到16byte。
這個技巧不是萬能的,如果你的資料庫CPU是瓶頸,使用轉化儲存,可能帶來CPU上更重的開銷,反之,如果你的IO是瓶頸,但CPU有較大的空閒,使用這個技巧就是一個不錯的優化方案。如果不好把握,就用你可以用得到的最好硬體就可以了,一般情況下如果用上SSD後IO都沒啥問題,但也可以使用這個技術去降低表的物理大小。
實戰:
環境準備
在MySQL 5.7中分別建立三張資料表:
test_varchar:以UUID作為主鍵。
test_long:以bigint作為主鍵。
test_int:以int作為主鍵。
三個表的欄位,除了主鍵ID 分別採用varchar,bigint 和自動增長int不同外,其他三個欄位都為 varchar 36位元
另外,建表時使用InnoDB儲存引擎,並且向資料庫中插入100W條資料,用以測試。
壓測資訊
表型別:InnoDB
資料量:100W條
資料庫:
主鍵採用uuid 32位元
執行查詢語句1: SELECT COUNT(id) FROM test_varchar; 執行查詢語句2: SELECT * FROM test_varchar WHERE vname='71e88bab-2f0f-6811-89ff-4cc935c075d8'; 執行查詢語句3: SELECT * FROM test_varchar WHERE id='00004599b05211e196aa002655b28d7b';
三條查詢語句的耗時分別如下所示:
語句1消耗時間平均為:2.81秒;
語句2消耗時間平均為:3.11秒;
語句3消耗時間平均為:0秒;(多方測試,條件裡只要有主鍵ID,查詢速度毫秒級都顯示000。測試的ID值,有前一百條的,也有後90多萬條的。查詢時間完全一樣,毫秒級都為000)
主鍵採用bigint
主鍵採用bigint,使用uuid_short()產生資料,資料為有序列的純數位(22461015967875697)。(其相當於自動增長,只是固定的基數值較大而已。)
執行查詢語句1: SELECT COUNT(id) FROM test_long; 執行查詢語句2: SELECT * FROM test_long WHERE vname='63b10f80-0e20-28cc-3078-d7331ba410b6'; 執行查詢語句3: SELECT * FROM test_long WHERE id='22461015967875702';
三條查詢語句的耗時分別如下所示:
語句1消耗時間平均為:1.31秒;
語句2消耗時間平均為:1.51秒;
語句3消耗時間平均為:0秒;(多方測試,條件裡只要有主鍵ID,查詢速度毫秒級都顯示000。測試的ID值,有前一百條的,也有後90多萬條的。查詢時間完全一樣,毫秒級都為000)
主鍵採用自增int
執行查詢語句1: SELECT COUNT(id) FROM test_int; 執行查詢語句2: SELECT * FROM test_int WHERE vname='908b57a5-cdef-32d1-0320-e14209b08894'; 執行查詢語句3: SELECT * FROM test_int WHERE id=900002;
其中,主鍵採用mysql自帶的自動增長,資料為純數位(1,2,3,4,5……)。
三條查詢語句的耗時分別如下所示:
查詢語句1消耗時間平均為:1.20秒;
查詢語句2消耗時間平均為:1.41秒;
查詢語句3消耗時間平均為:0秒;(多方測試,條件裡只要有主鍵ID,查詢速度毫秒級都顯示000。測試的ID值,有前一百條的,也有後90多萬條的。查詢時間完全一樣,毫秒級都為000)
新增:
UUID做主鍵,其他欄位相同,插入100萬條資料,用了2.5個小時
自增主鍵,其他欄位相同,插入相同的100萬條資料,用了26分鐘
總結:由此可見,MySQL InnoDB 主鍵採用自動增長效能較高,但是在技術工作中,能否直接使用自增int型別的數位作為MySQL的主鍵,大家需要根據具體需求確定。
如果你設計的系統,資料量還沒有超過100W,你用啥主鍵型別都無所謂。我測試電腦是桌上型電腦,如果是專業的伺服器,估計100W條,mysql MyISAM 的這些測試,根本都測不出來時間差吧。
到此這篇關於MySql主鍵id不推薦使用UUID的文章就介紹到這了,更多相關MySql主鍵id不推薦使用UUID內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45