跟同事杠上了!用雪花算法生成的id做主鍵對MySQL性能有影響?
公司最近開發(fā)了一個新項目,設計表時由于有些字段需要對外展示,所以使用了雪花算法生成的id做主鍵。
不過有位同事對此提出了異議,認為雪花算法生成的id不是順序遞增的,會對MySQL的性能造成影響。
經(jīng)過交流,發(fā)現(xiàn)持有這種認知的還有好幾位同事,估摸著對此有疑問的朋友也不少,所以今天我們來分析一下,用雪花算法生成的id做主鍵,對MySQL性能到底有沒有影響?MySQL必須使用連續(xù)遞增的主鍵才能發(fā)揮最大性能?
既然要分析不同主鍵的性能,那么就得先了解一下MySQL的數(shù)據(jù)是如何存儲的。
相信只要稍微了解過MySQL的朋友估計都知道,MySQL的InnoDB引擎采用B+樹來存儲數(shù)據(jù),為了數(shù)據(jù)的安全性,這些數(shù)據(jù)最終會持久化到磁盤上。
那么我們在查詢或者修改數(shù)據(jù)時,如果每次都把數(shù)據(jù)全部從磁盤加載到內(nèi)存好像不太現(xiàn)實,每次只讀一條數(shù)據(jù)又太浪費IO,那怎么辦呢?
于是設計MySQL的這些大神們提出了頁的概念,即將數(shù)據(jù)保存到很多個頁上面,內(nèi)存和磁盤交互時以頁為單位。
默認情況下,一個頁的大小是16KB,也就是說,每次從磁盤會最少加載16KB的數(shù)據(jù)到內(nèi)存里。反過來,每次最少把16KB的數(shù)據(jù)從內(nèi)存中持久化到磁盤。
這樣,時間和空間都利用到了,最大化的保證了性能。
當然,頁的種類也有很多,比如保存表空間信息的頁,undo日志頁,存放數(shù)據(jù)的數(shù)據(jù)頁等,本文中我們只討論數(shù)據(jù)頁和目錄頁。
下圖就是一個InnoDB數(shù)據(jù)頁的結構,大家心里有一個印象即可。
User Records就是用來真正保存我們的數(shù)據(jù)的,我們看一下數(shù)據(jù)是如何在頁中保存的。
需要特別注意的是,為了性能,這些記錄是按照主鍵的大小按從小到大順序排放的,最終組成一個單向鏈表。另外每個數(shù)據(jù)頁都會生成一個頁目錄,通過主鍵查找某一條記錄時通過二分查找法即可快速找到需要的數(shù)據(jù)。
上面我們提過,一個頁默認只有16KB,也就是說存儲的數(shù)據(jù)是有限的,所以當要存儲很多數(shù)據(jù)時,就需要申請很多數(shù)據(jù)頁,如下所示:
從上圖中我們可以看到,每個數(shù)據(jù)頁都保存了很多條記錄,相鄰頁之間還通過雙向鏈表保存著聯(lián)系。
需要注意的是,這些數(shù)據(jù)頁在物理空間上不一定是連續(xù)的地址。
到這里我們知道了MySQL通過數(shù)據(jù)頁來存儲數(shù)據(jù),但是隨著表數(shù)據(jù)的增多,會帶來一個很明顯的問題:頁太多了不好管理。
所以InnoDB的大神們又設計了目錄頁(目錄頁+數(shù)據(jù)頁就組成了一顆索引樹)。
看名字也知道,目錄頁只是一個目錄,不會存儲具體的數(shù)據(jù)。
它保存的數(shù)據(jù)其實特別簡單:主鍵和頁號。
從上圖中我們可以看到,頁30是一個目錄頁(可以把他當做樹的根節(jié)點),頁10、頁28、頁9、頁20是真正存放數(shù)據(jù)的數(shù)據(jù)頁。
在目錄頁中,會存放每一個數(shù)據(jù)頁的最小主鍵id以及對應的頁號,并且按照主鍵id排序。
在數(shù)據(jù)頁中,數(shù)據(jù)也是按照主鍵從小到大排序的,并且后一個頁的最小記錄會比上一個頁的最大記錄大,總體來說,這些頁的數(shù)據(jù)是遞增的。
注意!是遞增,但是并沒有要求順序遞增。
因為對于二分查找法來說,只要數(shù)據(jù)是有序遞增的,就可以保證其快速查找到我們需要的數(shù)據(jù)了。
以查找id=8的記錄為例,首先在根節(jié)點通過二分查找法找到記錄5,對應的頁號是28,然后找到頁28,通過二分法找到主鍵為8的記錄。
現(xiàn)在回到我們的問題,雪花算法生成的id會對MySQL性能造成影響嗎?
雪花算法的一大特性是什么呢?
大致遞增。
換句話說,只要是遞增的,哪怕我們用JAVA的AtomicInteger或者通過redis的incrmentBy來生成主鍵id也沒問題。
雪花算法就不過多介紹了,有想了解的朋友可以看一下這篇文章。??雪花算法介紹??。
另外再多說一句:MySQL自增主鍵雖然申請時是表級全局遞增的,但是最后保存到表中就不一定了。
舉個簡單的例子,批量保存10條數(shù)據(jù),由于某些原因,這個事務操作回滾了。當你再插入一條數(shù)據(jù)時,你會發(fā)現(xiàn)上次申請的10個id已經(jīng)被浪費掉了,表中的id是從11開始的。
MySQL的數(shù)據(jù)結構和索引是一個龐大的系統(tǒng),很難通過一篇簡單的文章將其徹底講清楚,如果你對本文有不同見解,也歡迎在評論區(qū)交流。