1. 程式人生 > >MySQL的一個表最多可以有多少個欄位

MySQL的一個表最多可以有多少個欄位

原文:http://mp.weixin.qq.com/s?__biz=MzAwMjkyMjEwNg==&mid=2247483785&idx=1&sn=1d90a44915d1028c6dc150367e1af033#rd

問題由來引用我們客戶的原話:

*建立如下表,提示我:*


*如果我將下面表中的varchar(200),修改成text(或blob):報錯變為另一個:*


*我們查閱了很多的資料,不確定The maximum row size到底是65535 還是8126?原理是什麼?*

先把問題原因的總結和建議給大家列出來,有興趣的朋友可以檢視後面的問題細節描述,或者按照附錄的建立表、插入表語句來手工驗證一下。總結●  MySQL Server最多隻允許4096個欄位
● InnoDB 最多隻能有1000個欄位● 欄位長度加起來如果超過65535,MySQL server層就會拒絕建立表 欄位長度加起來(根據溢位頁指標來計算欄位長度,大於40的,溢位,只算40個位元組)如果超過8126,InnoDB拒絕建立表 表結構中根據Innodb的ROW_FORMAT的儲存格式確定行內保留的位元組數(20 VS 768),最終確定一行資料是否小於8126,如果大於8126,報錯。優化建議1. 放棄使用Antelope這種古老的儲存格式吧,原因上面也說到了把大欄位的前768位元組放在資料頁中,這樣會導致索引的層級很高,會直接影響到查詢的效能。2. 對於大欄位型別建議單獨存放到一張表中,不要與經常訪問的表放在一起,會造成物理IO的增加。三種報錯的疑惑我們整理了一下,其實類似的錯誤有三種:

錯誤1 建立表報maximum row size > 65535


錯誤2 建立表報Row size too large (> 8126)


錯誤3 表建立成功但是插入報 Row size too large (> 8126)


到底要鬧哪樣這麼多錯誤,還都不一樣,MySQL到底要鬧那樣

別急,一個問題一個問題的看。

錯誤1這個報錯其實我們查詢MySQL官方手冊就可以查詢到, 對於一行記錄最大的限制是65535位元組。為什麼是65535,不要問我,手冊也沒說:)——一行資料裡面欄位長度定義有64k,我也是醉了。錯誤2

既生瑜何生亮?有了65535的限制以後還有一個8126的限制是為什麼呢?

MySQL是分兩層的,MySQL Server層 + 儲存引擎層。

第2個問題其實是MySQL除了在Server層做了一次限制還會在Innodb儲存引擎層在做一次限制。

innodb為了保證B+TREE是一個平衡樹結構,強制要求一條記錄的大小不能超過一個頁大小的一半。這也就是我們上面看到的第二個錯誤。

下面是innodb B+樹的結構,我們可以想象一下二分查詢時,一個頁的只有一條資料會是什麼樣子?


每個頁只有一條資料的查詢就變成了連結串列查找了。這樣就沒有二分查詢的意義了。

而MySQL中預設的頁大小是16K,16K的一半是8196位元組減去一些元資料資訊就得出了8126這個數字。**這就是8126的由來**錯誤3突破錯誤2

8126是不是不能突破的呢?

我們這裡就有個案例:按照附1的建表語句建立一個150個欄位,每個欄位是100個字元(特地使用了ASCII字符集,這樣一個字元就是一個位元組)的表。(建表語句和insert語句參見附錄)

150 * 100=15000 > 8126。按照上面的說法,應該要報錯的,但是各位可以在自己的資料庫上試一下,表能夠建立成功,這是為什麼呢?其實MySQL在計算欄位長度的時候並不是按照欄位的全部長度來記的。列欄位小於40個位元組的都會按實際位元組計算,如果大於20 * 2=40 位元組就只會按40位元組。

對應到MySQL程式碼中storage/innobase/dict/dict0dict.cc的dict_index_too_big_for_tree()中:


也就是說,如果欄位長度超過BTR_EXTERN_FIELD_REF_SIZE * 2,欄位就只算20 * 2=40(BTR_EXTERN_FIELD_REF_SIZE=20)

舉例如下:  建立一個300個欄位長度型別為varchar(30)的表,在建立時不會建立成功。因為varchar(30)沒有超過20*2,那麼總長度就是300*30=9000 > 8126就會建立失敗。  建立一個150個欄位長度型別為varchar(100)的表可以建立成功。因為varchar(100) 大於了20*2那麼就只會按40計算 總長度就是150*20*2=6000 < 8126 就會建立成功。這個20位元組是不是看著有點眼熟,可以聯絡到InnoDB的一個引數:innodb_file_format。該引數用於設定Innodb表內部儲存的檔案格式,該引數可設定為Antelope,Barracuda兩種格式。 Antelope是MySQL原始的記錄格式,是較古老的記錄格式。

在這種格式記錄下Innodb 對於大欄位的處理如下:


對於大欄位,innodb只會存放前DICT_ANTELOPE_MAX_INDEX_COL_LEN(768)位元組在資料頁中,超過768位元組都會放到溢位頁中。這種方式也是B+TREE結構,但是也並不是完美的,因為我們將大欄位存放到了資料頁中會造成葉子節點的個數會很多,同樣會造成非葉子節點的的個數增加。最終導致索引層級增高,訪問IO次數增加。

 Barracuda格式是InnoDB新的儲存格式

他的溢位儲存方式如下:


在Barracuda格式下,會用20位元組的指標指向溢位頁,這樣做的好處就是不會造成索引層級的增高。

回到錯誤3迴歸正題,第二個錯誤我們可以越過去,但是我們是不是能夠真的插入150個100字元的欄位列。用附2的插入語句試一下就知道,錯誤3也會報錯出來。也就是說表可以建立成功但是插入卻失敗,原因如下: Antelope格式下的COMPACT大欄位按照DICT_ANTELOPE_MAX_INDEX_COL_LEN(768)位元組溢位頁。varchar(100)沒有儲存為溢位頁。 Barracuda的DYNAMIC和COMPRESSED格式下只有長欄位才會用20位元組溢位頁的方式,varchar(100)也沒有儲存為溢位頁。

引用reference的原文如下:


參考網址●  MySQL reference: Limits on Table Column Count and Row Size●  MySQL reference: innodb row format dynamic●  mysqlserverteam:Externally Stored Fields in InnoDB●  MySQL · 引擎特性 · InnoDB 檔案系統之檔案物理結構附1.建表語句

附上測試的建表語句和insert語句,有興趣的朋友可以自己按照上面的幾種方式在Antelope和Barracuda的幾種不同ROW_FORMAT格式上試試。


附2.insert語句


相關推薦

MySQL一個可以多少

原文:http://mp.weixin.qq.com/s?__biz=MzAwMjkyMjEwNg==&mid=2247483785&idx=1&sn=1d90a44915d1028c6dc150367e1af033#rd 問題由來引用我們客戶的原話

sql 兩張對比出的

比如表dbo.DangAn表中有的而dbo.YongHu表沒有的值都顯示出來 SELECT * FROM dbo.DangAn a WHERE a.xCode NOT IN( SELECT b.yCode FROM dbo.YongHu b)   表dbo.YongHu有的欄位

一張中把兩相同的資料合併(listagg函式)

問題描述: 如下圖所示,需求是把省和產品相同的NTID合併到一起。 元資料: 想要的結果: 折騰了半天,結果經專案組大神指教,說有一個函式可以直接完成上訴操作。 listagg(); 程式碼如下: select yearmonth, listagg(ntid,

Linux下統計出現次數的指定

假設桌面上有一個data.txt文字,內容如下: {id='xxx' info='xxx' kk='xxx' target='111111' dd='xxx'} {id='xxx' info='xxx' kk='xxx' target='777' dd='xxx'} {i

oracle資料庫中某幾的重複資料去重

delete from szpj_biz_水文觀測資料 a where (a.觀測點_id, a.觀測時間, a.取樣位置) in       (select 觀測點_id, 觀測時間, 取樣位置          from szpj_biz_水文觀測資料         g

mysql建立的時候,新增註釋

mysql建立表的時候,新增欄位註釋 直接po程式碼和案例 #建立表的時候寫註釋 CREATE TABLE userinfo( id INT COMMENT '編號', uname VARCHAR(40) COMMENT '使用者名稱', address VARCHAR(120)

mysql SQL語句order by兩同時排序

ORDER BY  後可加2個欄位,用英文逗號隔開。 f1用升序, f2降序,sql該這樣寫 ORDER BY  f1, f2  DESC 也可以這樣寫,更清楚: ORDER BY  f1

一個的資料的某些更新到另一個表裡面的某些

update jwxt.jy_pyjhyq a set( a.PYJHZWMC ,a.ywpymbzw,a.ywpymbyw,a.ywpyyqzw,a.ywpyyqyw,a.zgxkzw,a.zgxkyw,a.jhxzzw,a.jhxzyw,a.pyjhyqzw,a.py

資料庫中查詢2張中某兩不同的資料

例:  表a    欄位  as  aid  at ao 表b    欄位  bs  bid  bf   bg   bh     其中表a中欄位as 的資料是    1    5    7   2   9   90   87    23 其中表b中欄位bs  的資料是  

一個單中相同的,以及springmvc接收相同物件問題

                                      &

MySQL根據某一個或者查詢重複資料,並且保留某大的記錄

問題場景 當系統沒有處理好併發操作的情況下,操作人員同時操作一張表的情況下,資料庫有可能被插入相同記錄,這些會帶來隱藏的bug。 解決思路一 解決併發操作的衝突。 解決思路二 對資料庫(MySQL)某張表去重,首先確定你的業務是否允許重複,不允許你

mysql一張關聯另一張查詢

如下:一張訂單表多個欄位關聯使用者表: 1.連結串列查詢 SELECT cu.id AS 'id',cu.version AS 'version',cu.cid AS 'cid',cu.uid AS 'uid',cu.shopName AS 'shopName',cu.address AS 'addre

mysql建立的唯一約束

mysql中有些表有時需要做一些欄位的唯一約束,當然你也可以在insert前判斷有無的方式來防止重複,如果不想額外增加程式碼來更靈活的實現一些欄位的唯一約束,mysql提供了兩種方式: 1.unique key   alter table xx add unique ke

給定一個二維平面,平面上 n 點,求多少點在同一條直線上。

需求:給定一個二維平面,平面上有 n 個點,求最多有多少個點在同一條直線上。 分析思路: 1、將所有點二維座標化,即定義出所有點的x,y座標值 2、遍歷出所有取出兩點的情況(不考慮先後順序),根據任意兩點都確定一條直線,直線引數為k斜率,b與y軸交點的縱座標(此時x=0),將他們放入一個

oracle 中 一張能夠建立多少?

oracle 中 一張表最多能夠建立多少個欄位? 收藏帖子 回覆 比比路克 結帖率 100% help! 問題點數:20分  0 2002-01-25 15:51:39 回覆數 4 只看樓主 引

mysql】sql刪除重複資料主鍵和沒主鍵解決方法

table user      name age nub      張三    12  23      張三    12  23      張三    12  23      李四    13  21      李四    13  21      王五    11  25

mysql 刪除單重複的資料

遇到個問題,一個表內兩個欄位應該加上unique約束,但沒加導致出現重複資料,網上找到一條sql可以刪掉重複資料,原sql是這樣的: DELETE from test WHERE (mid,uid) in (SELECT mid,uid FROM test GROUP

Hibernate中的annotation的寫法(中間可以

一般情況下,多對多的關聯關係是需要中間表的; 情況一:如果中間表僅僅是做關聯用的,它裡面僅有2個外來鍵做聯合主鍵,則使用ManyToMany(不用寫中間表的Model,只需要寫出兩張主表的model即可) 學生表 @Entity@Table(name = "T_STUDENT")@SequenceGener

oracle 用一個更新另一個對應的

update wx_weather a set (a.high_l,a.low_l)= (select high_l, low_l from wx_original_weather b where a

發現mysql一個可以唯一索引

mysql一個表可以有多個唯一索引。如果能確定某個資料列將只包含彼此各不相同的值,在為這個資料列建立索引的時候就應該用關鍵字UNIQUE把它定義為一個唯一索引。這麼做的好處:一是簡化了MySQL對這個索引的管理工作,這個索引也因此而變得更有效率。二是MySQL會在有新記錄插入