自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

InnoDB 行超長(zhǎng)時(shí)怎么選擇溢出字段?

數(shù)據(jù)庫(kù) MySQL
InnoDB 索引頁(yè)的大小默認(rèn)為 16K,然而,varchar、text、blob 類型的單個(gè)字段內(nèi)容長(zhǎng)度就有可能超過(guò) 16K,這種情況下,整個(gè)索引頁(yè)都存不下一個(gè)字段的內(nèi)容了。接下來(lái),我們來(lái)聊聊 InnoDB 選擇溢出字段的邏輯。

InnoDB 索引頁(yè)的大小默認(rèn)為 16K,然而,varchar、text、blob 類型的單個(gè)字段內(nèi)容長(zhǎng)度就有可能超過(guò) 16K,這種情況下,整個(gè)索引頁(yè)都存不下一個(gè)字段的內(nèi)容了。

解決這個(gè)問(wèn)題的辦法,是找到那些內(nèi)容比較長(zhǎng)的字段作為??溢出字段??,把它們的內(nèi)容存放到溢出頁(yè)中,減少留在索引頁(yè)記錄中的內(nèi)容。

接下來(lái),我們來(lái)聊聊 InnoDB 選擇溢出字段的邏輯。

本文內(nèi)容基于 MySQL 8.0.29 源碼。

正文

進(jìn)入正題之前,大家可以思考一個(gè)問(wèn)題:一個(gè)表中每條記錄的溢出字段都是一樣的嗎?

1、建表時(shí)的限制

單從字段數(shù)量看,MySQL 的 server 層限制一個(gè)表最多只能創(chuàng)建 1024 個(gè)字段。

InnoDB 則限制最多只能創(chuàng)建 1023 個(gè)字段,但是,如果我們創(chuàng)建表時(shí),真要?jiǎng)?chuàng)建 1023 個(gè)字段,會(huì)很榮幸的收到這個(gè)錯(cuò)誤:1117 - Too many columns。

因?yàn)?InnoDB 會(huì)往表中增加 2 ~ 3 個(gè)隱藏字段:DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR。

只有表中沒(méi)有主鍵,并且在建表時(shí)也沒(méi)有創(chuàng)建所有字段都不允許為 NULL? 的唯一索引時(shí),才會(huì)增加 DB_ROW_ID 字段。

創(chuàng)建表時(shí),能定義 1023 - 3 = 1020 個(gè)字段嗎?

依然不能,因?yàn)楸罎⒒謴?fù)過(guò)程中,解析 Redo 日志時(shí),REDUNDANT? 記錄還會(huì)往表的內(nèi)存對(duì)象(dict_table_t)中加入 3 個(gè)字段。

從以上介紹可知,InnoDB 需要保留 6 個(gè)字段自用,所以,我們建表時(shí),最多能創(chuàng)建的字段數(shù)量是:1023 - 3 * 2 = 1017。

表中實(shí)際能創(chuàng)建多少個(gè)字段,除了受限于 server 層和存儲(chǔ)引擎的字段數(shù)量限制,還會(huì)受到字段長(zhǎng)度的限制。

創(chuàng)建表時(shí),InnoDB 會(huì)問(wèn)自己一個(gè)問(wèn)題:

如果我放過(guò)這條 DDL 語(yǔ)句,讓它建表成功,以后對(duì)這個(gè)表進(jìn)行插入、更新操作時(shí),有沒(méi)有可能因?yàn)橛涗洺L(zhǎng)導(dǎo)致操作失?。?/p>

要回答這個(gè)問(wèn)題,總不能憑空想象,隨心而動(dòng)吧?

所以,得有一個(gè)規(guī)則,要按規(guī)則辦事,規(guī)則是這樣的:

假定有資格被選擇成為溢出字段的那些字段,都已經(jīng)被當(dāng)作溢出字段了,它們的字段內(nèi)容都部分或全部存放到溢出頁(yè)中了。

溢出字段內(nèi)容是部分還是全部存放到溢出頁(yè),這取決于記錄的格式。

REDUNDANT、COMPACT 記錄只會(huì)把溢出字段的部分內(nèi)容存放到溢出頁(yè)。

DYNAMIC、COMPRESSED 記錄會(huì)把溢出字段的全部?jī)?nèi)容存放到溢出頁(yè)。

在這個(gè)規(guī)則之下,再來(lái)計(jì)算留在索引頁(yè)中的記錄內(nèi)容長(zhǎng)度,看看是不是還會(huì)超長(zhǎng)?

如果還會(huì)超長(zhǎng),InnoDB 是不會(huì)放過(guò)這條 DDL 語(yǔ)句的,這時(shí),建表就會(huì)失敗,并且報(bào)如下錯(cuò)誤:

1118 - Row size too large (> 8126).
Changing some columns to TEXT or BLOB may help.
In current row format, BLOB prefix of 0 bytes is stored inline.

為啥判斷超長(zhǎng)的條件是大于 8126 字節(jié)呢?先別急,后面會(huì)有介紹。

如果不會(huì)超長(zhǎng),自然就會(huì)建表成功了。

2、索引頁(yè)長(zhǎng)什么樣?

InnoDB 的索引頁(yè),不管是 B+ 樹(shù)的非葉子結(jié)點(diǎn),還是葉子結(jié)點(diǎn),初始化完成之后,未插入記錄之前,都包含以下幾個(gè)部分:

  • 38 字節(jié)的 File Header
  • 56 字節(jié)的索引頁(yè)頭信息
  • 13 字節(jié)的 Infimum 記錄
  • 13 字節(jié)的 Supremum 記錄
  • 2 字節(jié)的 Supremum Slot
  • 2 字節(jié)的 Infimum Slot
  • 8 字節(jié)的 File Trailer

總共占用 132 字節(jié),如下圖所示:

圖片

3、怎么判斷行超長(zhǎng)了?

通過(guò)上一小節(jié),我們知道一個(gè)索引頁(yè)初始化完成之后,會(huì)占用 132 字節(jié)的空間。

索引頁(yè)默認(rèn)大小為 16K,初始化之后,索引頁(yè)中剩余空間為:16 * 1024 - 132 = 16252 字節(jié)。

InnoDB 規(guī)定?:一個(gè)索引頁(yè)中最少要存放 2? 條記錄。所以,索引頁(yè)中一條記錄的最大長(zhǎng)度就是:16252 / 2 = 8126 字節(jié)。

插入或者更新記錄時(shí),如果插入記錄的長(zhǎng)度,或者更新之后記錄的長(zhǎng)度大于 8126 字節(jié),就會(huì)選擇記錄中的部分字段作為溢出字段。

一條記錄的長(zhǎng)度為下幾個(gè)部分的長(zhǎng)度之和:

  • 字段 NULL 標(biāo)記區(qū)域?,標(biāo)記每個(gè)字段內(nèi)容是否為 NULL,如果表中所有字段都定義為NOT NULL?,記錄中沒(méi)有此區(qū)域。
  • 字段內(nèi)容長(zhǎng)度區(qū)域?,存儲(chǔ)每個(gè)變長(zhǎng)?字段的內(nèi)容長(zhǎng)度,如果表中所有字段都不是?變長(zhǎng)字段,記錄中沒(méi)有此區(qū)域。
  • 記錄的頭信息,REDUNDANT 格式:6 字節(jié);COMPACT、DYNAMIC、COMPRESSED 格式:5 字節(jié)。
  • 用戶字段內(nèi)容。
  • DB_ROW_ID?,6 字節(jié),創(chuàng)建表時(shí),表中既沒(méi)有主鍵,也沒(méi)有創(chuàng)建所有字段都定義為 NOT NULL 的唯一索引時(shí),InnoDB 才會(huì)添加這個(gè)列,作為表的主鍵。
  • DB_TRX_ID,8 字節(jié),最后修改記錄的事務(wù) ID。
  • DB_ROLL_PTR,7 字節(jié),指向上一個(gè)事務(wù)產(chǎn)生的 undo 日志。

4、選擇溢出字段的邏輯

選擇溢出字段環(huán)節(jié)可能會(huì)進(jìn)行一輪或多輪循環(huán),每輪循環(huán)從表中選擇一個(gè)?字段作為溢出字段,直到留在索引頁(yè)中的記錄長(zhǎng)度小于等于 8126 字節(jié),選擇溢出字段環(huán)節(jié)也就結(jié)束了。

選擇溢出字段時(shí),有一些字段是會(huì)被排除在外的,命中?以下規(guī)則的字段都不會(huì)被選為溢出字段:

  • 主鍵字段。
  • 固定長(zhǎng)度字段(char、binary 字段除外)。
  • 內(nèi)容為 NULL 的字段。
  • REDUNDANT、COMPACT 記錄,字段內(nèi)容長(zhǎng)度<= 788 字節(jié)。
  • DYNAMIC、COMPRESSED 記錄,字段內(nèi)容長(zhǎng)度<= 40 字節(jié)?,且字段類型是 BLOB、GEOMETRY、VAR_POINT。
  • DYNAMIC、COMPRESSED 記錄,字段內(nèi)容長(zhǎng)度<= 255 字節(jié)?,且字段類型不是 BLOB、GEOMETRY、VAR_POINT。

沒(méi)有命中以上規(guī)則的字段,都有資格被選為溢出字段。

每輪循環(huán)都會(huì)遍歷表中的所有字段,并根據(jù)以上規(guī)則,從有資格被選為溢出字段的那些字段中,找到??內(nèi)容最長(zhǎng)??的字段,就是溢出字段了。

5、頁(yè)地址

字段被選為溢出字段之后,該字段的部分或全部?jī)?nèi)容會(huì)存放到溢出頁(yè),然后,索引頁(yè)記錄中,該字段的末尾?,會(huì)有一個(gè) 20 字節(jié)?的區(qū)域,保存著溢出頁(yè)地址。

20 字節(jié)的溢出頁(yè)地址由以下 4 個(gè)部分構(gòu)成:

  • 表空間 ID,4 字節(jié),溢出頁(yè)所在表空間 ID。
  • 頁(yè)號(hào),4 字節(jié),第 1 個(gè)溢出頁(yè)的頁(yè)號(hào)。一個(gè)溢出頁(yè)存不下字段的溢出內(nèi)容時(shí),會(huì)有多個(gè)溢出頁(yè),組成溢出頁(yè)鏈表。
  • 字段內(nèi)容 Offset?,4 字節(jié),第 1 個(gè)溢出頁(yè)中,字段內(nèi)容在頁(yè)中的 Offset。根據(jù)是否啟用了壓縮頁(yè),字段內(nèi)容在溢出頁(yè)中的 Offset 會(huì)不一樣,所以需要記下來(lái)。
  • 溢出頁(yè)內(nèi)容長(zhǎng)度?,當(dāng)前字段存放到溢出頁(yè)中的內(nèi)容長(zhǎng)度,8 字節(jié),實(shí)際只使用了最后4 字節(jié)來(lái)存儲(chǔ)溢出頁(yè)的內(nèi)容長(zhǎng)度之和,如下圖所示:

圖片

溢出字段留在索引頁(yè)記錄中的內(nèi)容根據(jù)記錄格式的不同而不同:

REDUNDANT、COMPACT 記錄?,溢出字段在索引頁(yè)記錄中的長(zhǎng)度為 788 字節(jié),由以下兩部分組成:

  • 768 字節(jié)的字段內(nèi)容。
  • 20 字節(jié)的溢出頁(yè)地址。

溢出字段中 768 字節(jié)之后的內(nèi)容,會(huì)存放到溢出頁(yè)中。

DYNAMIC、COMPRESSED 記錄,溢出字段的全部?jī)?nèi)容都會(huì)存放到溢出頁(yè)中,索引頁(yè)記錄中只保存 20 字節(jié)的溢出頁(yè)地址。

6、回答文章開(kāi)頭的問(wèn)題

經(jīng)過(guò)前面的介紹,相信大家對(duì)于本文開(kāi)頭的那個(gè)問(wèn)題已經(jīng)有了答案,回到問(wèn)題:

問(wèn):一個(gè)表中每條記錄的溢出字段都是一樣的嗎?

答?:每條記錄的溢出字段,可能一樣,也可能不一樣,記錄中哪些字段會(huì)成為溢出字段,取決于每條記錄中,所有有資格被選為溢出字段的內(nèi)容長(zhǎng)度。

7、總結(jié)

一條記錄中,所有字段內(nèi)容長(zhǎng)度之和超過(guò) 8126 字節(jié)時(shí),就會(huì)有部分字段被選擇成為溢出字段。

選擇溢出字段可能會(huì)進(jìn)行多輪循環(huán)?,每輪循環(huán)都會(huì)從有資格被選為溢出字段的那些字段中,選擇內(nèi)容最長(zhǎng)?的字段作為溢出字段,直到留在索引頁(yè)中的記錄長(zhǎng)度小于等于 8126 字節(jié)。

REDUNDANT、COMPACT 記錄,溢出字段內(nèi)容的前 768 字節(jié)存放在索引頁(yè)記錄中,剩余內(nèi)容存放到溢出頁(yè)。

DYNAMIC、COMPRESSED 記錄,溢出字段的全部?jī)?nèi)容都存放到溢出頁(yè)。

本文轉(zhuǎn)載自微信公眾號(hào)「一樹(shù)一溪」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系一樹(shù)一溪公眾號(hào)。

責(zé)任編輯:姜華 來(lái)源: 一樹(shù)一溪
相關(guān)推薦

2021-12-15 19:37:49

索引字符串字段

2009-05-05 10:19:37

存儲(chǔ)引擎InnoDBMyISAM

2011-09-20 17:29:56

筆記本技巧

2023-12-07 07:22:52

MySQLIGNORE

2023-09-04 09:42:43

2011-09-14 17:18:04

方正筆記本

2009-05-19 09:58:41

MyISAMInnoDB存儲(chǔ)引擎

2020-05-09 13:49:00

內(nèi)存空間垃圾

2024-04-15 10:30:22

MySQL存儲(chǔ)引擎

2010-12-16 09:34:48

差異備份

2022-11-17 09:14:58

MySQL加行級(jí)鎖幻讀

2022-07-13 07:31:43

數(shù)據(jù)分片SQL

2018-08-26 06:24:58

MySQLInnoDB行鎖

2022-08-04 10:18:32

棧遷移?寄存器內(nèi)存

2009-07-27 16:22:54

GridView選擇行

2012-04-01 09:43:24

摩托羅拉

2023-10-10 08:36:57

數(shù)據(jù)庫(kù)MyISMInnoDB

2022-06-30 08:01:53

mysqlmyisamcount

2021-07-28 10:35:14

云計(jì)算云計(jì)算環(huán)境云區(qū)域

2013-01-17 09:49:51

BI企業(yè)云數(shù)據(jù)分析
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)