起因
最近在整理代碼規範,按照之前oracle的習慣,定了以下的字段長度設定規範:
- 名稱字段:
varchar(200)
- 較長的名稱字段/簡介字段:
varchar(500)
- 特別長的描述字段:
varchar(2000)
- 超過2000中文字的字段:
text
爲什麼是200長度,而不是100或300,也是拍腦袋想的,類似DND裏的房規。
但在被問起爲什麼不設置爲經常見到的varchar(255)
時,一時回答不上來。趁這個機會,把字段長度這塊的知識彙總梳理一下。
爲什麼會經常被設置爲varchar(255)
MySQL 4.1版本之前,varchar
的最大長度是255 byte字節(也有一說是5.0.3版本之前)。查了下這個版本發佈都是2004年的事情了。慣性真恐怖,我可不相信還有多少系統是從2004年升級過來的。
varchar(50)
和varchar(255)
有性能上的差別麼?
對於INNODB
,varchar(50)
和varchar(255)
這兩者在存放方式上完全一樣:1-2 byte
保存長度,實際的字符串存放在另外的位置,每個字符1 byte
到4 byte
不定(視編碼和實際存儲的字符而定)。所以將一個字段從varchar(50)
長度改成varchar(100)
長度不會導致表的重建。但如果把長度從varchar(50)
改成varchar(256)
就不一樣了,表示長度會需要用到2 byte
或更多。
既然255
長度以下對INNODB
都一樣,而且我們平時基本上也不太會使用到MYISAM
,那麼是不是爲了省心,我們就可以把255長度以下的字段的類型都設置成varchar(255)了呢?
非也!
因爲內存表介意。
雖然我們不會明文創建內存表,但所有的中間結果都會被數據庫引擎存放在內存表。我們可以通過EXPLAIN
或者SHOW STATUS
可以查看MYSQL是否使用了內存表用來幫助完成某個操作。
而內存表會按照固定長度來保存。以utf-8
編碼爲例,對於varchar(255)
,每一行所佔用的內存就是長度的2 byte + 3 * 255 byte
= 767 bytes。對於100條數據,光一個varchar字段就佔約 75Mb 內存。如果我們改用varchar(50)
,就可以節約80%的內存空間。
除此之外,255長度也可能會對索引造成坑。MySQL在5.6版本及之前的最大長度是767 byte。但MySQL 5.5版本後開始支持4個byte的字符集utf8mb4(沙雕表情用到的字符太多,長度不夠用)。255 * 4 > 767,所以索引就放不下varchar(255)長度的字段了。雖然MySQL在5.7版本後將限制改成了3072 byte,但如果是多字段的聯合索引還是有可能會超過這個限制。
所以我們的結論就是:在長度夠用的情況下,越短越好。
varchar的最大長度是多少
varchar的最大長度是65535 byte。所以
- 字符類型若爲gbk,每個字符最多佔2個字節,最大長度不能超過32766字符
- 字符類型若爲utf8 (同utf8mb3),每個字符最多佔3個字節,最大長度不能超過21845字符
- 字符類型若爲utf8mb4,每個字符最多佔4個字節,最大長度不能超過16383字符 但導致varchar長度限制的, 通常是一行定義的長度, 就是表裏所有字段定義的長度總和。這個限制也是65535 byte。如果超出長度,會報錯:
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs。
這也是爲什麼阿里開發規範中這麼要求:
【強制】varchar是可變長字符串,不預先分配存儲空間,長度不要超過5000,如果存儲長度大於此值,定義字段類型爲text,獨立出來一張表,用主鍵來對應,避免影響其它字段索引效率。
varchar(50)是能保存12個還是16個漢字,還是25個,抑或50個??
以前SQL Server
的nvarchar
轉 Oracle
的varchar2
時造成的固有印象,讓我一直覺得varchar
保存中文字時長度需要打對摺或除以3。
但這個也是MySQL 5.0版本之前的事。現在varchar(n)是幾,就能存幾個中文字。
不過也需要注意統計字數使用CHARACTER_LENGTH
而非LENGTH
SELECT LENGTH("輕鬆工作") AS a, CHARACTER_LENGTH("輕鬆工作") AS b;
-- 返回: 12 4