0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

MySQL單表數(shù)據(jù)最大不要超過多少行?為什么?

jf_ro2CN3Fa ? 來源:小白debug ? 2023-07-06 09:46 ? 次閱讀

故事從好多年前說起。

想必大家也聽說過數(shù)據(jù)庫(kù)單表建議最大2kw 條數(shù)據(jù)這個(gè)說法。如果超過了,性能就會(huì)下降得比較厲害。

巧了。

我也聽說過。

但我不接受它的建議,硬是單表裝了1億條數(shù)據(jù)。

這時(shí)候,我們組里新來的實(shí)習(xí)生看到了之后,天真無邪的問我:"單表不是建議最大兩千萬嗎?為什么這個(gè)表都放了1個(gè)億還不分庫(kù)分表 "?

我能說我是因?yàn)閼?/strong> 嗎?我當(dāng)初設(shè)計(jì)時(shí)哪里想到這表竟然能漲這么快。。。

我不能。

說了等于承認(rèn)自己是開發(fā)組里的毒瘤 ,雖然我確實(shí)是,但我不能承認(rèn) 。

我如坐針氈,如芒刺背,如鯁在喉。

開始了一波騷操作。

"我這么做是有道理的"

"雖然這個(gè)表很大,但你有沒有發(fā)現(xiàn)它查詢其實(shí)還是很快"

"這個(gè)2kw是個(gè)建議值,我們要來看下這個(gè)2kw是怎么來的"

數(shù)據(jù)庫(kù)單表行數(shù)最大多大?

我們先看下單表行數(shù)理論最大值是多少。

建表的SQL是這么寫的,

CREATETABLE`user`(
`id`int(10)unsignedNOTNULLAUTO_INCREMENTCOMMENT'主鍵',
`name`varchar(100)NOTNULLDEFAULT''COMMENT'名字',
`age`int(11)NOTNULLDEFAULT'0'COMMENT'年齡',
PRIMARYKEY(`id`),
KEY`idx_age`(`age`)
)ENGINE=InnoDBAUTO_INCREMENT=100037DEFAULTCHARSET=utf8;

其中id就是主鍵。主鍵本身唯一,也就是說主鍵的大小可以限制表的上限。

如果主鍵聲明為int大小,也就是32位,那么能支持2^32-1,也就是21個(gè)億 左右。

如果是bigint,那就是2^64-1,但這個(gè)數(shù)字太大 ,一般還沒到這個(gè)限制之前,磁盤先受不了 。

搞離譜點(diǎn)。

如果我把主鍵聲明為 tinyint,一個(gè)字節(jié),8位,最大2^8-1,也就是255。

CREATETABLE`user`(
`id`tinyint(2)unsignedNOTNULLAUTO_INCREMENTCOMMENT'主鍵',
`name`varchar(100)NOTNULLDEFAULT''COMMENT'名字',
`age`int(11)NOTNULLDEFAULT'0'COMMENT'年齡',
PRIMARYKEY(`id`),
KEY`idx_age`(`age`)
)ENGINE=InnoDBAUTO_INCREMENT=0DEFAULTCHARSET=utf8;

如果我想插入一個(gè)id=256的數(shù)據(jù),那就會(huì)報(bào)錯(cuò) 。

mysql>INSERTINTO`tmp`(`id`,`name`,`age`)VALUES(256,'',60);
ERROR1264(22003):Outofrangevalueforcolumn'id'atrow1

也就是說,tinyint主鍵限制表內(nèi)最多255條數(shù)據(jù)。

那除了主鍵,還有哪些因素會(huì)影響行數(shù)?

索引的結(jié)構(gòu)

索引內(nèi)部是用的B+樹,這個(gè)也是八股文老股了,大家估計(jì)也背得很熟了。

為了不讓大家有過于強(qiáng)烈的審丑疲勞,今天我嘗試從另外一個(gè)角度給大家講講這玩意。

頁的結(jié)構(gòu)

假設(shè)我們有這么一張user數(shù)據(jù)表。

aa87b092-1b9d-11ee-962d-dac502259ad0.pnguser表

其中id是唯一主鍵

這看起來的一行行數(shù)據(jù),為了方便,我們后面就叫它們record 吧。

這張表看起來就跟個(gè)excel表格一樣。excel的數(shù)據(jù)在硬盤上是一個(gè)xx.excel的文件。

而上面user表數(shù)據(jù),在硬盤上其實(shí)也是類似,放在了user.ibd 文件下。含義是user表的innodb data文件,專業(yè)點(diǎn),又叫表空間

雖然在數(shù)據(jù)表里,它們看起來是挨在一起的。但實(shí)際上在user.ibd里他們被分成很多小份的數(shù)據(jù)頁 ,每份大小16k。

類似于下面這樣。

aa9c8166-1b9d-11ee-962d-dac502259ad0.pngibd文件內(nèi)部有大量的頁

我們把視角聚焦一下,放到頁上面。

整個(gè)頁16k,不大,但record這么多,一頁肯定放不下,所以會(huì)分開放到很多頁里。并且這16k,也不可能全用來放record對(duì)吧。

因?yàn)閞ecord們被分成好多份,放到好多頁里了,為了唯一標(biāo)識(shí)具體是哪一頁,那就需要引入頁號(hào) (其實(shí)是一個(gè)表空間的地址偏移量)。同時(shí)為了把這些數(shù)據(jù)頁給關(guān)聯(lián)起來,于是引入了前后指針 ,用于指向前后的頁。這些都被加到了頁頭 里。

頁是需要讀寫的,16k說小也不小,寫一半電源線被拔了也是有可能發(fā)生的,所以為了保證數(shù)據(jù)頁的正確性,還引入了校驗(yàn)碼。這個(gè)被加到了頁尾

那剩下的空間,才是用來放我們的record的。而record如果行數(shù)特別多的話,進(jìn)入到頁內(nèi)時(shí)挨個(gè)遍歷,效率也不太行,所以為這些數(shù)據(jù)生成了一個(gè)頁目錄 ,具體實(shí)現(xiàn)細(xì)節(jié)不重要。只需要知道,它可以通過二分查找 的方式將查找效率從O(n) 變成O(lgn) 。

aab2a4fa-1b9d-11ee-962d-dac502259ad0.png頁結(jié)構(gòu)

從頁到索引

如果想查一條record,我們可以把表空間里每一頁都撈出來,再把里面的record撈出來挨個(gè)判斷是不是我們要找的。

行數(shù)量小的時(shí)候,這么操作也沒啥問題。

行數(shù)量大了,性能就慢了 ,于是為了加速搜索,我們可以在每個(gè)數(shù)據(jù)頁里選出主鍵id最小 的record,而且只需要它們的主鍵id和所在頁的頁號(hào) 。組成新的record ,放入到一個(gè)新生成的一個(gè)數(shù)據(jù)頁中,這個(gè)新數(shù)據(jù)頁跟之前的頁結(jié)構(gòu)沒啥區(qū)別,而且大小還是16k。

但為了跟之前的數(shù)據(jù)頁進(jìn)行區(qū)分。數(shù)據(jù)頁里加入了*頁層級(jí)(page level)** 的信息,從0開始往上算。于是頁與頁之間就有了*上下層級(jí) 的概念,就像下面這樣。

aad61a7a-1b9d-11ee-962d-dac502259ad0.png

兩層B+樹結(jié)構(gòu)

突然頁跟頁之間看起來就像是一棵倒過來的樹了。也就是我們常說的B+樹 索引。

最下面那一層,page level 為0 ,也就是所謂的葉子結(jié)點(diǎn) ,其余都叫非葉子結(jié)點(diǎn)

上面展示的是兩層 的樹,如果數(shù)據(jù)變多了,我們還可以再通過類似的方法,再往上構(gòu)建一層。就成了三層 的樹。

aaef6bf6-1b9d-11ee-962d-dac502259ad0.png

三層B+樹結(jié)構(gòu)

那現(xiàn)在我們就可以通過這樣一棵B+樹加速查詢。舉個(gè)例子。

比方說我們想要查找行數(shù)據(jù)5。會(huì)先從頂層頁的record們?nèi)胧帧?strong>record里包含了主鍵id和頁號(hào)(頁地址) ??聪聢D黃色的箭頭,向左最小id是1,向右最小id是7。那id=5的數(shù)據(jù)如果存在,那必定在左邊箭頭。于是順著的record的頁地址就到了6號(hào)數(shù)據(jù)頁里,再判斷id=5>4,所以肯定在右邊的數(shù)據(jù)頁里,于是加載105號(hào)數(shù)據(jù)頁。在數(shù)據(jù)頁里找到id=5的數(shù)據(jù)行,完成查詢。

ab1d0b92-1b9d-11ee-962d-dac502259ad0.png

B+樹查詢過程

另外需要注意的是,上面的頁的頁號(hào)并不是連續(xù)的,它們?cè)诖疟P里也不一定是挨在一起的。

這個(gè)過程中查詢了三個(gè)頁,如果這三個(gè)頁都在磁盤中(沒有被提前加載到內(nèi)存中),那么最多 需要經(jīng)歷三次磁盤IO查詢 ,它們才能被加載到內(nèi)存中。

B+樹承載的記錄數(shù)量

從上面的結(jié)構(gòu)里可以看出B+樹的最末級(jí)葉子結(jié)點(diǎn) 里放了record數(shù)據(jù)。而非葉子結(jié)點(diǎn) 里則放了用來加速查詢的索引數(shù)據(jù)。

也就是說

同樣一個(gè)16k的頁,非葉子節(jié)點(diǎn)里每一條數(shù)據(jù)都指向一個(gè)新的頁,而新的頁有兩種可能。

如果是末級(jí)葉子節(jié)點(diǎn)的話,那么里面放的就是一行行record數(shù)據(jù)。

如果是非葉子節(jié)點(diǎn),那么就會(huì)循環(huán)繼續(xù)指向新的數(shù)據(jù)頁。

假設(shè)

非葉子結(jié)點(diǎn)內(nèi)指向其他內(nèi)存頁的指針數(shù)量為x

葉子節(jié)點(diǎn)內(nèi)能容納的record數(shù)量為y

B+樹的層數(shù)為z

ab4a68e4-1b9d-11ee-962d-dac502259ad0.png

總行數(shù)的計(jì)算方法

那這棵B+樹放的行數(shù)據(jù)總量 等于 (x ^ (z-1)) * y。

x怎么算

我們回去看數(shù)據(jù)頁的結(jié)構(gòu)。

aab2a4fa-1b9d-11ee-962d-dac502259ad0.png

頁結(jié)構(gòu)

非葉子節(jié)點(diǎn)里主要放索引查詢相關(guān)的數(shù)據(jù),放的是主鍵和指向頁號(hào)。

主鍵假設(shè)是bigint(8Byte),而頁號(hào)在源碼里叫FIL_PAGE_OFFSET(4Byte),那么非葉子節(jié)點(diǎn)里的一條數(shù)據(jù)是12Byte左右。

整個(gè)數(shù)據(jù)頁16k, 頁頭頁尾那部分?jǐn)?shù)據(jù)全加起來大概128Byte,加上頁目錄毛估占1k吧。那剩下的15k 除以12Byte,等于1280,也就是可以指向x=1280頁 。

我們常說的二叉樹指的是一個(gè)結(jié)點(diǎn)可以發(fā)散出兩個(gè)新的結(jié)點(diǎn)。m叉樹一個(gè)節(jié)點(diǎn)能指向m個(gè)新的結(jié)點(diǎn)。這個(gè)指向新節(jié)點(diǎn)的操作就叫扇出(fanout) 。

而上面的B+樹,它能指向1280個(gè)新的節(jié)點(diǎn),恐怖如斯,可以說扇出非常高 了。

y的計(jì)算

葉子節(jié)點(diǎn)和非葉子節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)是一樣的,所以也假設(shè)剩下15kb可以發(fā)揮。

葉子節(jié)點(diǎn)里放的是真正的行數(shù)據(jù)。假設(shè)一條行數(shù)據(jù)1kb,所以一頁里能放y=15行 。

行總數(shù)計(jì)算

回到 (x ^ (z-1)) * y 這個(gè)公式。

已知x=1280,y=15。

假設(shè)B+樹是兩層 ,那z=2。則是(1280 ^ (2-1)) * 15 ≈ 2w

假設(shè)B+樹是三層 ,那z=3。則是(1280 ^ (3-1)) * 15 ≈ 2.5kw

這個(gè)2.5kw,就是我們常說的單表建議最大行數(shù)2kw的由來。 畢竟再加一層,數(shù)據(jù)就大得有點(diǎn)離譜了。三層數(shù)據(jù)頁對(duì)應(yīng)最多三次磁盤IO,也比較合理。

行數(shù)超一億就慢了嗎?

上面假設(shè)單行數(shù)據(jù)用了1kb,所以一個(gè)數(shù)據(jù)頁能放個(gè)15行數(shù)據(jù)。

如果我單行數(shù)據(jù)用不了這么多,比如只用了250byte。那么單個(gè)數(shù)據(jù)頁能放60行數(shù)據(jù)。

那同樣是三層B+樹,單表支持的行數(shù)就是 (1280 ^ (3-1)) * 60 ≈ 1個(gè)億。

你看我一個(gè)億的數(shù)據(jù),其實(shí)也就三層B+樹,在這個(gè)B+樹里要查到某行數(shù)據(jù),最多也是三次磁盤IO。所以并不慢。

這就很好的解釋了文章開頭,為什么我單表1個(gè)億,但查詢性能沒啥大毛病。

B樹承載的記錄數(shù)量

既然都聊到這里了,我們就順著這個(gè)話題多聊一些吧。

我們都知道,現(xiàn)在mysql的索引都是B+樹,而有一種樹,跟B+樹很像,叫B樹,也叫B-樹 。

它跟B+樹最大的區(qū)別在于,B+樹只在末級(jí)葉子結(jié)點(diǎn)處放數(shù)據(jù)表行數(shù)據(jù),而B樹則會(huì)在葉子和非葉子結(jié)點(diǎn)上都放。

于是,B樹的結(jié)構(gòu)就類似這樣

ab7ddbc0-1b9d-11ee-962d-dac502259ad0.png

B樹結(jié)構(gòu)

B樹將行數(shù)據(jù)都存在非葉子節(jié)點(diǎn)上,假設(shè)每個(gè)數(shù)據(jù)頁還是16kb,掐頭去尾每頁剩15kb,并且一條數(shù)據(jù)表行數(shù)據(jù)還是占1kb,就算不考慮各種頁指針的情況下,也只能放個(gè)15條數(shù)據(jù)。數(shù)據(jù)頁扇出明顯變少了。

計(jì)算可承載的總行數(shù)的公式也變成了一個(gè)等比數(shù)列 。

15+15^2+15^3+...+15^z

其中z還是層數(shù) 的意思。

為了能放2kw左右的數(shù)據(jù),需要z>=6。也就是樹需要有6層,查一次要訪問6個(gè)頁。假設(shè)這6個(gè)頁并不連續(xù),為了查詢其中一條數(shù)據(jù),最壞情況需要進(jìn)行6次磁盤IO 。

而B+樹同樣情況下放2kw數(shù)據(jù)左右,查一次最多是3次磁盤IO。

磁盤IO越多則越慢,這兩者在性能上差距略大。

為此,B+樹比B樹更適合成為mysql的索引。

總結(jié)

B+樹葉子和非葉子結(jié)點(diǎn)的數(shù)據(jù)頁都是16k,且數(shù)據(jù)結(jié)構(gòu)一致,區(qū)別在于葉子節(jié)點(diǎn)放的是真實(shí)的行數(shù)據(jù),而非葉子結(jié)點(diǎn)放的是主鍵和下一個(gè)頁的地址。

B+樹一般有兩到三層,由于其高扇出,三層就能支持2kw以上的數(shù)據(jù),且一次查詢最多1~3次磁盤IO,性能也還行。

存儲(chǔ)同樣量級(jí)的數(shù)據(jù),B樹比B+樹層級(jí)更高,因此磁盤IO也更多,所以B+樹更適合成為mysql索引。

索引結(jié)構(gòu)不會(huì)影響單表最大行數(shù),2kw也只是推薦值,超過了這個(gè)值可能會(huì)導(dǎo)致B+樹層級(jí)更高,影響查詢性能。

單表最大值還受主鍵大小和磁盤大小限制。







審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • SQL
    SQL
    +關(guān)注

    關(guān)注

    1

    文章

    764

    瀏覽量

    44134
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    811

    瀏覽量

    26580

原文標(biāo)題:阿里:MySQL 單表數(shù)據(jù)最大不要超過多少行?為什么?

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    誰說MySQL行數(shù)不要超過2000W?

    網(wǎng)上看了一篇文章《為什么說MySQL行數(shù)不要超過2000w》,親自實(shí)踐了一下,跟原作者有不同的結(jié)論。原文的結(jié)論是2000W左右性能會(huì)成指
    的頭像 發(fā)表于 12-15 10:02 ?1064次閱讀
    誰說<b class='flag-5'>MySQL</b><b class='flag-5'>單</b><b class='flag-5'>表</b>行數(shù)<b class='flag-5'>不要</b><b class='flag-5'>超過</b>2000W?

    TAS5717的MCLK如果是12.288MHZ,這個(gè)頻率的上下誤差最大不能超過多少呢?

    TAS5717的MCLK如果是12.288MHZ,這個(gè)頻率的上下誤差最大不能超過多少?
    發(fā)表于 11-05 08:23

    mysql中文參考手冊(cè)chm

    數(shù)據(jù)庫(kù)類型 10 從 MySQL 得到最大的性能 10.1 優(yōu)化概述 10.2 系統(tǒng)/編譯時(shí)和啟動(dòng)參數(shù)的調(diào)節(jié) 10.2.1 編譯和鏈接如何影響 M
    發(fā)表于 12-26 13:32

    變壓器的大小有效電流最大不會(huì)超過1A,這樣的話功率不是達(dá)不到嗎?

    輸入220v交流經(jīng)整壓濾波穩(wěn)流后輸出為24v5A,現(xiàn)在比較奇怪的是。在變壓器部分降壓之后的電壓最大不會(huì)超過40v,看變壓器的大小有效電流最大不會(huì)超過1A,這樣的話功率不是達(dá)不到嗎???
    發(fā)表于 11-10 20:38

    MySQL root密碼忘記怎么辦?

    MySQL實(shí)例1. 跳過授權(quán)登錄mysqld_safe --skip-grant-table --user=mysql &2. 更改密碼mysq
    發(fā)表于 06-22 17:54

    MySQL分區(qū)類型及介紹

    分區(qū)是將一個(gè)數(shù)據(jù)按照一定規(guī)則水平劃分成不同的邏輯塊,并分別進(jìn)行物理存儲(chǔ),這個(gè)規(guī)則就叫做分區(qū)函數(shù),可以有不同的分區(qū)規(guī)則。通過show plugins語句查看當(dāng)前MySQL是否支持
    發(fā)表于 06-29 16:31

    請(qǐng)問TAS5717的MCLK是12.288MHZ那頻率的上下誤差最大不能超過多少?

    TAS5717的MCLK如果是12.288MHZ,這個(gè)頻率的上下誤差最大不能超過多少?
    發(fā)表于 08-06 10:49

    如何利用labview獲取MySQL數(shù)據(jù)庫(kù)中某一列的最大

    如題,想獲取MySQL數(shù)據(jù)庫(kù)中的data7那一列的最大值,下面是程序框圖一直報(bào)語法錯(cuò)誤,但是該語句在mysql command line
    發(fā)表于 12-06 21:37

    mysql轉(zhuǎn)列如何操作

    mysql 轉(zhuǎn)列操作
    發(fā)表于 04-28 11:27

    mysql數(shù)據(jù)導(dǎo)出golang實(shí)現(xiàn)

    mysql數(shù)據(jù)導(dǎo)出為excel文件,golang實(shí)現(xiàn):首先下載依賴到的三方庫(kù):Simple install the package to your $GOPATH
    發(fā)表于 10-21 15:14

    關(guān)于MySQL的基礎(chǔ)知識(shí)簡(jiǎn)析

    隨機(jī)分配一個(gè)root密碼,記住密碼,安裝完畢用root登錄,但密碼是過期狀態(tài),mysql默認(rèn)情況下密碼有效期是360天,需要重新改下,基礎(chǔ)的概念數(shù)據(jù)庫(kù)(database): 保存有組織的數(shù)據(jù)庫(kù)。
    發(fā)表于 11-03 11:50

    B+樹索引如何對(duì)Mysql數(shù)據(jù)量造成影響

    我們說 Mysql 適合存儲(chǔ)的最大數(shù)據(jù)量,自然不是說能夠存儲(chǔ)的最大數(shù)據(jù)量,如果是說能夠存儲(chǔ)的最大
    的頭像 發(fā)表于 04-16 08:08 ?1606次閱讀
    B+樹索引如何對(duì)<b class='flag-5'>Mysql</b><b class='flag-5'>單</b><b class='flag-5'>表</b><b class='flag-5'>數(shù)據(jù)</b>量造成影響

    MySQL數(shù)據(jù)最大不要超過多

    最好不要超過 2000w”,“超過 200
    的頭像 發(fā)表于 06-02 15:30 ?623次閱讀
    <b class='flag-5'>MySQL</b><b class='flag-5'>單</b><b class='flag-5'>表</b><b class='flag-5'>數(shù)據(jù)</b><b class='flag-5'>最大不要</b><b class='flag-5'>超過多</b>少<b class='flag-5'>行</b>

    為什么 MySQL 不能超過 2000 萬?

    ,因?yàn)?b class='flag-5'>數(shù)據(jù)量超大(5000 萬條左右),需要每天定時(shí)生成 3 張,然后將數(shù)據(jù)取模分別存到這三張表里。 接下來是兩人的對(duì)話: 面試后續(xù)暫且不論,不過,互聯(lián)網(wǎng)江湖上的確流傳著一個(gè)說法:
    的頭像 發(fā)表于 06-29 16:48 ?711次閱讀
    為什么 <b class='flag-5'>MySQL</b> <b class='flag-5'>單</b><b class='flag-5'>表</b>不能<b class='flag-5'>超過</b> 2000 萬<b class='flag-5'>行</b>?

    MySQL數(shù)據(jù)量限制:為何2000萬成為瓶頸?

    很多人認(rèn)為:數(shù)據(jù)超過500萬或2000萬時(shí),引起B(yǎng)+tree的高度增加,延長(zhǎng)了索引的搜索路徑,進(jìn)而導(dǎo)致了性能下降。事實(shí)果真如此嗎?
    的頭像 發(fā)表于 02-27 10:38 ?6231次閱讀
    <b class='flag-5'>MySQL</b><b class='flag-5'>單</b><b class='flag-5'>表</b><b class='flag-5'>數(shù)據(jù)</b>量限制:為何2000萬<b class='flag-5'>行</b>成為瓶頸?