在計算機裡,無論是記憶體還是磁碟,作業系統都是按頁的大小進行讀取的(頁大小通常為 4 kb),磁碟每次讀取都會預讀,會提前將連續的資料讀入記憶體中,這樣就避免了多次 io,這就是計算機中有名的區域性性原理,即我用到一塊資料,很大可能這塊資料附近的資料也會被用到,乾脆一起載入,省得多次 io 拖慢速度, 這個連續資料有多大呢,必須是作業系統頁大小的整數倍。
所以mysql 的頁,預設值為 16 kb,也就是說對於 b+ 樹的節點,最好設定成頁的大小(16 kb),這樣乙個 b+ 樹上的節點就只會有一次 io 讀。
那有人就會問了,這個頁大小是不是越大越好呢,設定大一點,節點可容納的資料就越多,樹高越小,io 不就越小了嗎,這裡要注意,頁大小並不是越大越好,innodb 是通過記憶體中的快取池(pool buffer)來管理從磁碟中讀取的頁資料的。頁太大的話,很快就把這個快取池撐滿了,可能會造成頁在記憶體與磁碟間頻繁換入換出,影響效能。
通過以上分析,相信我們不難猜測出 n 叉樹中的 n 該怎麼設定了,只要選的時候盡量保證每個節點的大小等於乙個頁(16kb)的大小即可。
現在我們來看看開頭的問題, 為啥推薦自增 id 作為主鍵,第乙個原因就是自增主鍵一般都會設定為int,占用4個位元組,所有可以這樣說,innodb的b+樹最多為4叉。
那第二原因就是下面舉例說到的。
有人可能會說使用者的身份證是唯一的,可以用它來做主鍵,假設以身份證作主鍵,會有什麼問題呢。
b+ 樹為了維護索引的有序性,每插入或更新一條記錄的時候,會對索引進行更新。假設原來基於身份證作索引的 b+ 樹如下(假設為二叉樹 ,圖中只列出了身份證的前四位)
現在有乙個開頭是 3604 的身份證對應的記錄插入 db ,此時要更新索引,按排序來更新的話,顯然這個 3604 的身份證號應該插到左邊節點 3504 後面(如下圖示,假設為二叉樹)
如果把 3604 這個身份證號插入到 3504 後面的話,這個節點的元素個數就有 3 個了,顯然不符合二叉樹的條件,此時就會造成頁**,就需要調整這個節點以讓它符合二叉樹的條件
這種由於頁**造成的調整必然導致效能的下降,尤其是以身份證作為主鍵的話,由於身份證的隨機性,必然造成大量的隨機結點中的插入,進而造成大量的頁**,進而造成效能的急劇下降,那如果是以自增 id 作為主鍵呢,由於新插入的表中生成的 id 比索引中所有的值都大,所以它要麼合到已存在的節點(元素個數未滿)中,要麼放入新建的節點中(如下圖示)所以如果是以自增 id 作為主鍵,就不存在頁**的問題了,推薦!
有頁**就必然有頁合併,什麼時候會發生頁合併呢,當刪除表記錄的時候,索引也要刪除,此時就有可能發生頁合併,如圖示
當我們刪除 id 為 7,9 對應行的時候,上圖中的索引就要更新,把 7,9 刪掉,此時 8,10 就應該合到乙個節點,不然 8,10 分散在兩個節點上,可能造成兩次 io 讀,勢必會影響查詢效率!
那什麼時候會發生頁合併呢,我們可以定個閾值,比如對於 n 叉樹來說,當節點的個數小於 n/2 的時候就應該和附近的節點合併,不過需要注意的是合併後節點裡的元素大小可能會超過 n,造成頁**,需要再對父節點等進行調整以讓它滿足 n 叉樹的條件。
實際中innodb的主鍵索引,非葉子節點只存了索引值,只在最後一行才存放了行記錄,這樣極大地減小了索引了大小,而且只要找到索引值就找到了行記錄,也提公升了效率,
這種在葉節點存放一整行記錄的索引被稱為聚簇索引,其他的就稱為非聚簇索引。
為什麼mysql事務回滾後, 自增ID依然自增
事務回滾後,自增id仍然增加,回滾後,自增id仍然增加。比如當前id是7,插入一條資料後,又回滾了。然後你再插入一條資料,此時插入成功,這時候你的id不是8,而是9。因為雖然你之前插入回滾,但是id還是自增了。如果你認為自增id不應該被事務化,那麼其他事務不得不等待著,檢查自增id是被使用還是被回滾...
為什麼mysql事務回滾後,自增ID依然自增
因為innodb的auto increament的計數器記錄的當前值是儲存在存內 存中的,並不是存在於磁碟上,當mysql server處於執行的時候,這個計數值只會隨著insert改增長,不會隨著delete而減少。而當mysql server啟動時,當我們需要去查詢auto increment計...
mysql 實現id自增序列 mysql自增id列
如果希望在每次插入新記錄時,自動地建立主鍵欄位的值。可以在表中建立乙個 auto increment 字段。mysql 使用 auto increment 關鍵字來執行 auto increment 任務。預設地auto increment 的開始值是 1,每條新記錄遞增 1。主鍵又稱主關鍵字,主關...