成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

我說MySQL每張表最好不超過2000萬條數據,面試官讓我回去等通知?

數據庫 MySQL
根據上面三種不同情況下的計算,可以看出,InnoDB三層B+樹情況下的數據存儲量范圍為 一百二十多萬條 到 將近5億條,這個跨度還是非常大的,同時我們也計算了一張博客信息表,可以存儲約 一千萬條 數據。

事情是這樣的

下面是我朋友的面試記錄:

面試官:講一下你實習做了什么。

朋友:我在實習期間做了一個存儲用戶操作記錄的功能,主要是從MQ獲取上游服務發送過來的用戶操作信息,然后把這些信息存到MySQL里面,提供給數倉的同事使用。由于數據量比較大,每天大概有四五千多萬條,所以我還給它做了分表的操作。每天定時生成3張表,然后將數據取模分別存到這三張表里,防止表內數據過多導致查詢速度降低。

這表述,好像沒什么問題是吧,別急,接著看:

面試官:那你為什么要分三張表呢,兩張表不行嗎?四張表不行嗎?

朋友:因為MySQL每張表最好不超過2000萬條數據,否則會導致查詢速度降低,影響性能。我們每天的數據大概是在五千萬條左右,所以分成三張表比較穩妥。

面試官:還有嗎?

朋友:沒有了……你干嘛,哎呦~

面試官:那你先回去等通知吧。

??????講完了,看出什么了嗎,你們覺得我這位朋友回答的有什么問題嗎?

前言

很多人說,MySQL每張表最好不要超過2000萬條數據,否則就會導致性能下降。阿里的Java開發手冊上也提出:單表行數超過 500 萬行或者單表容量超過 2GB,才推薦進行分庫分表。

但實際上,這個2000萬或者500萬都只是一個大概的數字,并不適用于所有場景,如果盲目的以為表數據只要不超過2000萬條就沒問題了,很可能會導致系統的性能大幅下降。

實際情況下,每張表由于自身的字段不同、字段所占用的空間不同等原因,它們在最佳性能下可以存放的數據量也就不同。

那么,該如何計算出每張表適合的數據量呢?別急,慢慢往下看。

本文適合的讀者

閱讀本文你需要有一定的MySQL基礎,最好對InnoDB和B+樹都有一定的了解,可能需要有一年以上的MySQL學習經驗(大概一年?),知道 “InnoDB中B+樹的高度一般保持在三層以內會比較好” 這條理論知識。

本文主要是針對 “InnoDB中高度為3的B+樹最多可以存多少數據” 這一話題進行講解的。且本文對數據的計算比較嚴格(至少比網上95%以上的相關博文都要嚴格),如果你比較在意這些細節并且目前不太清楚的話,請繼續往下閱讀。

閱讀本文你大概需要花費10-20分鐘的時間,如果你在閱讀的過程中對數據進行驗算的話,可能要花費30分鐘左右。

本文思維導圖

圖片圖片

InnoDB三層B+數的存儲計算-思維導圖

基礎知識快速回顧

眾所周知,MySQL中InnoDB的存儲結構是B+樹,B+樹大家都熟悉吧?特性大概有以下幾點,一起快速回顧一下吧!

*注:下面這這些內容都是精華,看不懂或者不理解的同學建議先收藏本文,之后有知識基礎了再回來看 。*????

  1. 一張數據表一般對應一顆或多顆樹的存儲,樹的數量與建索引的數量有關,每個索引都會有一顆單獨的樹。
  2. 聚簇索引和非聚簇索引:主鍵索引也是聚簇索引,非主鍵索引都是非聚簇索引,兩種索引的非葉子節點都是只存索引數據的,比如索引為id,那非葉子節點就只存id的數據。葉子節點的區別如下:

聚簇索引的葉子節點存的是這條數據的所有字段信息。所以我們 select * from table where id = 1 的時候,都是要去葉子節點拿數據的。

非聚簇索引的葉子節點存的是這條數據所對應的主鍵和索引列信息。比如這條非聚簇索引是username,然后表的主鍵是id,那該非聚簇索引的葉子節點存的就是 username 和 id,而不存其他字段。相當于是先從非聚簇索引查到主鍵的值,再根據主鍵索引去查數據內容,一般情況下要查兩次(除非索引覆蓋),這也稱之為*回表*,就有點類似于存了個指針,指向了數據存放的真實地址。

  1. B+樹的查詢是從上往下一層層查詢的,一般情況下我們認為B+樹的高度保持在3層是比較好的,也就是上兩層是索引,最后一層存數據,這樣查表的時候只需要進行3次磁盤IO就可以了(實際上會少一次,因為根節點會常駐內存)。如果數據量過大,導致B+數變成4層了,則每次查詢就需要進行4次磁盤IO了,從而使性能下降。所以我們才會去計算InnoDB的3層B+樹最多可以存多少條數據。
  2. MySQL每個節點大小默認為16KB,也就是每個節點最多存16KB的數據,可以修改,最大64KB,最小4KB。擴展:那如果某一行的數據特別大,超過了節點的大小怎么辦?

MySQL5.7文檔的解釋是:

圖片圖片

文檔地址:https://dev.mysql.com/doc/refman/5.7/en/innodb-file-space.html

  • 對于 4KB、8KB、16KB 和 32KB設置 ,最大行長度略小于數據庫頁面的一半 ,例如:對于默認的 16KB頁大小,最大行長度略小于 8KB 。

  • 而對于 64KB 頁面,最大行則長度略小于 16KB。

  • 如果行超過最大行長度, 則將可變長度列用外部頁存儲,直到該行符合最大行長度限制。就是說把varchar、text這種長度可變的存到外部頁中,來減小這一行的數據長度。

  1. MySQL查詢速度主要取決于磁盤的讀寫速度,因為MySQL查詢的時候每次只讀取一個節點到內存中,通過這個節點的數據找到下一個要讀取的節點位置,再讀取下一個節點的數據,直到查詢到需要的數據或者發現數據不存在。

    肯定有人要問了,每個節點內的數據難道不用查詢嗎?這里的耗時怎么不計算?

    這是因為讀取完整個節點的數據后,會存到內存當中,在內存中查詢節點數據的耗時其實是很短的,再配合MySQL的查詢方式,時間復雜度差不多為  ,相比磁盤IO來說,可以忽略不計。

MySQL B+樹每個節點都存里些什么?

在Innodb的B+樹中,我們常說的節點被稱之為 **頁(page)**,每個頁當中存儲了用戶數據,所有的頁合在一起組成了一顆B+樹(當然實際會復雜很多,但我們只是要計算可以存多少條數據,所以姑且可以這么理解??)。

頁 是InnoDB存儲引擎管理數據庫的最小磁盤單位,我們常說每個節點16KB,其實就是指每頁的大小為16KB。

這16KB的空間,里面需要存儲 頁格式 信息和 行格式 信息,其中行格式信息當中又包含一些元數據和用戶數據。所以我們在計算的時候,要把這些數據的都計算在內。

頁格式

每一頁的基本格式,也就是每一頁都會包含的一些信息,總結表格如下:

名稱

空間

含義和作用等

File Header

38字節

文件頭,用來記錄頁的一些頭信息。

包括校驗和、頁號、前后節點的兩個指針、頁的類型、表空間等。

Page Header

56字節

頁頭,用來記錄頁的狀態信息。包括頁目錄的槽數、

空閑空間的地址、本頁的記錄數、已刪除的記錄所占用的字節數等。

Infimum & supremum

26字節

用來限定當前頁記錄的邊界值,包含一個最小值和一個最大值。

User Records

不固定

用戶記錄,我們插入的數據就存儲在這里。

Free Space

不固定

空閑空間,用戶記錄增加的時候從這里取空間。

Page Directort

不固定

頁目錄,用來存儲頁當中用戶數據的位置信息。

每個槽會放4-8條用戶數據的位置,一個槽占用1-2個字節,

當一個槽位超過8條數據的時候會自動分成兩個槽。

File Trailer

8字節

文件結尾信息,主要是用來校驗頁面完整性的。

示意圖:

圖片圖片

頁格式這塊的內容,我在官網翻了好久,硬是沒找到??。。。。不知道是沒寫還是我眼瞎,有找到的朋友希望可以在評論區幫我掛出來??。

所以上面頁格式的表格內容主要是基于一些博客中學習總結的。

另外,當新記錄插入到 InnoDB 聚集索引中時,InnoDB 會嘗試留出 1/16 的頁面空閑以供將來插入和更新索引記錄。如果按順序(升序或降序)插入索引記錄,則生成的頁大約可用 15/16 的空間。如果以隨機順序插入記錄,則頁大約可用 1/2 到 15/16 的空間。參考文檔:https://dev.mysql.com/doc/refman/5.7/en/innodb-physical-structure.html

除了 User Records和Free Space  以外所占用的內存是  字節,每一頁留給用戶數據的空間就還剩  字節(保留了1/16)。

當然,這是最小值,因為我們沒有考慮頁目錄。頁目錄留在后面根據再去考慮,這個得根據表字段來計算。

行格式

首先,我覺得有必要提一嘴,MySQL5.6的默認行格式為COMPACT(緊湊),5.7及以后的默認行格式為DYNAMIC(動態),不同的行格式存儲的方式也是有區別的,還有其他的兩種行格式,本文后續的內容主要是基于DYNAMIC(動態)進行講解的。

官方文檔鏈接:https://dev.mysql.com/doc/refman/5.7/en/innodb-row-format.html#innodb-compact-row-format-characteristics(包括下面的行格式內容大都可以在里面找到)

圖片圖片

每行記錄都包含以下這些信息,其中大都是可以從官方文檔當中找到的。我這里寫的不是特別詳細,僅寫了一些能夠我們計算空間的知識,更詳細內容可以去網上搜索 “MySQL 行格式”。

名稱

空間

含義和作用等

行記錄頭信息

5字節

行記錄的標頭信息

包含了一些標志位、數據類型等信息

如:刪除標志、最小記錄標志、排序記錄、數據類型、

頁中下一條記錄的位置等

可變長度字段列表

不固定

來保存那些可變長度的字段占用的字節數,比如varchar、text、blob等。

若變長字段的長度小于 255字節,就用1字節表示;

若大于 255字節,用2字節表示。

表字段中有幾個可變長字段該列表中就有幾個值,如果沒有就不存。

null值列表

不固定

用來存儲可以為null的字段是否為null。

每個可為null的字段在這里占用一個bit,就是bitmap的思想。

該列表占用的空間是以字節為單位增長的,例如,如果有 9 到 16 個

可以為null的列,則使用兩個字節,沒有占用1.5字節這種情況。

事務ID和指針字段

6+7字節

了解MVCC的朋友應該都知道,數據行中包含了一個6字節的事務ID和

一個7字節的回滾指針。

如果沒有定義主鍵,則還會多一個6字節的行ID字段

當然我們都有主鍵,所以這個行ID我們不計算。

實際數據

不固定

這部分就是我們真實的數據了。

示意圖:

圖片圖片

另外還有幾點需要注意:

溢出頁(外部頁)的存儲

注意:這一點是DYNAMIC的特性。

當使用 DYNAMIC 創建表時,InnoDB 會將較長的可變長度列(比如 VARCHAR、VARBINARY、BLOB 和 TEXT 類型)的值剝離出來,存儲到一個溢出頁上,只在該列上保留一個 20 字節的指針指向溢出頁。

而 COMPACT 行格式(MySQL5.6默認格式)則是將前 768 個字節和 20 字節的指針存儲在 B+ 樹節點的記錄中,其余部分存儲在溢出頁上。

列是否存儲在頁外取決于頁大小和行的總大小。當一行太長時,選擇最長的列進行頁外存儲,直到聚集索引記錄適合 B+ 樹頁(文檔里沒說具體是多少??)。小于或等于 40 字節的 TEXT 和 BLOB 直接存儲在行內,不會分頁。

優點

DYNAMIC 行格式避免了用大量數據填充 B+ 樹節點從而導致長列的問題。

DYNAMIC 行格式的想法是,如果長數據值的一部分存儲在頁外,則通常將整個值存儲在頁外是最有效的。

使用 DYNAMIC 格式,較短的列會盡可能保留在 B+ 樹節點中,從而最大限度地減少給定行所需的溢出頁數。

字符編碼不同情況下的存儲

char 、varchar、text 等需要設置字符編碼的類型,在計算所占用空間時,需要考慮不同編碼所占用的空間。

varchar、text等類型會有長度字段列表來記錄他們所占用的長度,但char是固定長度的類型,情況比較特殊,假設字段 name 的類型為 char(10) ,則有以下情況:

  • 對于長度固定的字符編碼(比如ASCII碼),字段 name 將以固定長度格式存儲,ASCII碼每個字符占一個字節,那 name 就是占用 10 個字節。
  • 對于長度不固定的字符編碼(比如utf8mb4),至少將為 name 保留 10 個字節。如果可以,InnoDB會通過修剪尾部空格空間的方式來將其存到 10 個字節中。如果空格剪完了還存不下,則將尾隨空格修剪為 列值字節長度的最小值(一般是 1 字節)。列的最大長度為:字符編碼的最大字符長度,比如 name 字段的編碼為 utf8mb4,那就是 。
  • 大于或等于 768 字節的 char 列會被看成是可變長度字段(就像varchar一樣),可以跨頁存儲。例如,utf8mb4 字符集的最大字節長度為 4,則 char(255) 列將可能會超過 768 個字節,進行跨頁存儲。

說實話對char的這個設計我是不太理解的,盡管看了很久,包括官方文檔和一些博客??,希望懂的同學可以在評論區解惑:

對于長度不固定的字符編碼這塊,char是不是有點像是一個長度可變的類型了?我們常用的 utf8mb4,占用為 1 ~ 4 字節,那么 char(10) 所占用的空間就是 10 ~ 40 字節,這個變化還是挺大的啊,但是它并沒有留足夠的空間給它,也沒有使用可變長度字段列表去記錄char字段的空間占用情況,就很特殊?

開始計算

好了,我們已經知道每一頁當中具體存儲的東西了,現在我們已經具備計算能力了。

由于頁的剩余空間我已經在上面頁格式的地方計算過了,每頁會剩余 15232 字節可用,下面我們直接計算行。

非葉子節點計算

單個節點計算

索引頁就是存索引的節點,也就是非葉子節點。

每一條索引記錄當中都包含了當前索引的值 、 一個 6字節 的指針信息 、一個 5 字節的行標頭,用來指向下一層數據頁的指針。

索引記錄當中的指針占用空間我沒在官方文檔里找到,這個 6 字節是我參考其他博文的,他們說源碼里寫的是6字節,這點我并未求證。

假設我們的主鍵id為 bigint 型,也就是8個字節,那索引頁中每行數據占用的空間就等于  字節。每頁可以存  條索引數據。

那算上頁目錄的話,按每個槽平均6條數據計算的話,至少有  個槽,需要占用 268 字節的空間。

把存數據的空間分一點給槽的話,我算出來大約可以存 787 條索引數據。

如果是主鍵是 int 型的話,那可以存更多,大約有 993 條索引數據。

前兩層非葉子節點計算

在 B+ 樹當中,當一個節點索引記錄為  條時,它就會有  個子節點。由于我們 3 層B+樹的前兩層都是索引記錄,第一層根節點有  條索引記錄,那第二層就會有  個節點,每個節點數據類型與根節點一致,仍然可以再存  條記錄,第三層的節點個數就會等于 。

則有:

  • 主鍵為 bigint 的表可以存放  個葉子節點
  • 主鍵為 int 的表可以存放  個葉子節點

OK計算完畢。

數據條數計算

最少存放記錄數

前面我們提到,最大行長度略小于數據庫頁面的一半,之所以是略小于一半,是由于每個頁面還留了點空間給頁格式 的其他內容,所以我們可以認為每個頁面最少能放兩條數據,每條數據略小于8KB。如果某行的數據長度超過這個值,那InnoDB肯定會分一些數據到 溢出頁 當中去了,所以我們不考慮。

那每條數據8KB的話,每個葉子節點就只能存放 2 條數據,這樣的一張表,在主鍵為 bigint 的情況下,只能存放  條數據,也就是一百二十多萬條,這個數據量,沒想到吧????。

較多的存放記錄數

假設我們的表是這樣的:

-- 這是一張非常普通的課程安排表,除id外,僅包含了課程id和老師id兩個字段,且這兩個字段均為 int 型(當然實際生產中不會這么設計表,這里只是舉例)。

CREATE TABLE `course_schedule` (
  `id` int NOT NULL,
  `teacher_id` int NOT NULL,
  `course_id` int NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

先來分析一下這張表的行數據:無null值列表,無可變長字段列表,需要算上事務ID和指針字段,需要算上行記錄頭,那么每行數據所占用的空間就是  字節,每個葉子節點可以存放  條數據。

算上頁目錄的槽位所占空間,每個葉子節點可以存放 502 條數據,那么三層B+樹可以存放的最大數據量就是 ,將近5億條數據!沒想到吧????。

常規表的存放記錄數

大部分情況下我們的表字段都不是上面那樣的,所以我選擇了一場比較常規的表來進行分析,看看能存放多少數據。表情況如下:

CREATE TABLE `blog` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '博客id',
  `author_id` bigint unsigned NOT NULL COMMENT '作者id',
  `title` varchar(50) CHARACTER SET utf8mb4 NOT NULL COMMENT '標題',
  `description` varchar(250) CHARACTER SET utf8mb4 NOT NULL COMMENT '描述',
  `school_code` bigint unsigned DEFAULT NULL COMMENT '院校代碼',
  `cover_image` char(32) DEFAULT NULL COMMENT '封面圖',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
  `release_time` datetime DEFAULT NULL COMMENT '首次發表時間',
  `modified_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時間',
  `status` tinyint unsigned NOT NULL COMMENT '發表狀態',
  `is_delete` tinyint unsigned NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  KEY `author_id` (`author_id`),
  KEY `school_code` (`school_code`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_general_mysql500_ci ROW_FORMAT=DYNAMIC;

這是我的開源項目“校園博客”(GitHub地址:https://github.com/stick-i/scblogs) 中的博客表,用于存放博客的基本數據。

分析一下這張表的行格式:

  1. 行記錄頭信息:肯定得有,占用5字節。
  2. 可變長度字段列表:表中 title占用1字節,description占用2字節,共3字節。
  3. null值列表:表中僅school_code、cover_image、release_time3個字段可為null,故僅占用1字節。
  4. 事務ID和指針字段:兩個都得有,占用13字節。
  5. 字段內容信息:

id、author_id、school_code 均為bigint型,各占用8字節,共24字節。

create_time、release_time、modified_time 均為datetime類型,各占8字節,共24字節。

status、is_delete 為tinyint類型,各占用1字節,共2字節。

cover_image 為char(32),字符編碼為表默認值utf8,由于該字段實際存的內容僅為英文字母(存url的),結合前面講的字符編碼不同情況下的存儲 ,故僅占用32字節。

title、description 分別為varchar(50)、varchar(250),這兩個應該都不會產生溢出頁(不太確定),字符編碼均為utf8mb4,實際生產中70%以上都是存的中文(3字節),25%為英文(1字節),還有5%為4字節的表情??,則存滿的情況下將占用  字節。

統計上面的所有分析,共占用 869 字節,則每個葉子節點可以存放  條,算上頁目錄,仍然能放 17 條。

則三層B+樹可以存放的最大數據量就是 ,約一千萬條數據,再次沒想到吧??。

數據計算總結

根據上面三種不同情況下的計算,可以看出,InnoDB三層B+樹情況下的數據存儲量范圍為 一百二十多萬條 到 將近5億條,這個跨度還是非常大的,同時我們也計算了一張博客信息表,可以存儲約 一千萬條 數據。

所以啊,我們在做項目考慮分表的時候還是得多關注一下表的實際情況,而不是盲目的認為兩千萬數據都是那個臨界點。

面試時如果談到這塊的問題,我想面試官也并不是想知道這個數字到底是多少,而是想看你如何分析這個問題,如何得出這個數字的過程。

責任編輯:武曉燕 來源: 程序員阿桿
相關推薦

2022-11-16 17:10:25

MySQL數據事務

2024-08-05 01:26:54

2022-10-27 21:32:28

數據互聯網數據中心

2022-11-15 17:45:46

數據庫MySQL

2021-02-06 09:21:17

MySQL索引面試

2022-12-06 09:03:31

MySQL索引

2024-08-21 10:28:54

Redis數據結構內存

2020-12-16 08:05:54

Mybatis面試動態代理

2025-05-20 01:00:00

2021-12-02 08:19:06

MVCC面試數據庫

2020-05-22 08:11:48

線程池JVM面試

2022-07-13 17:47:54

布局Flex代碼

2025-02-13 00:00:00

TCP網絡通信

2020-09-17 17:53:12

面試ArrayList數組

2020-07-02 07:52:11

RedisHash映射

2025-03-12 00:52:00

Java樂觀鎖悲觀鎖

2022-02-11 19:06:29

MySQL索引面試官

2021-03-01 18:42:02

緩存LRU算法

2020-08-13 10:15:34

MySQL數據庫面試

2020-05-20 17:35:40

JavaString面試官
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 自拍偷拍第一页 | 伊人欧美视频 | 99久久99久久精品国产片果冰 | 一级毛片大全免费播放 | 国产精品色一区二区三区 | 亚洲免费人成在线视频观看 | 亚洲精品久久久久久久久久久 | 久久精品一区 | 日韩一区二区免费视频 | 亚洲国产福利视频 | 久久亚洲国产精品 | 免费观看一级毛片 | 91传媒在线观看 | 国产精品五区 | 日本成人片在线观看 | 欧美精品91 | 精久久 | 欧美自拍一区 | 日韩成人在线免费视频 | 成人区精品一区二区婷婷 | 欧美一级淫片007 | 亚洲一区二区三区四区视频 | 天天草天天操 | 国产精品中文字幕一区二区三区 | 久久久久久免费看 | 色吊丝2| 久久伊人精品 | 91视频在线看 | 久久亚洲精品久久国产一区二区 | 亚洲精品成人网 | 久久另类视频 | 久久久久亚洲精品 | 国产一区二区三区免费观看视频 | 欧美区日韩区 | 亚洲视频在线看 | 久草综合在线视频 | av在线免费观看网址 | 国产日韩欧美精品一区二区三区 | 91高清视频在线 | 天天草天天干 | 日韩欧美一区二区三区在线播放 |