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