MySQL的varchar(255)歷史問題

文章轉載自 https://galaxyyao.github.io/2019/07/30/MySQL-%E6%B2%A1%E6%9C%89%E5%BF%85%E8%A6%81%E7%9A%84varchar-255-%E9%95%BF%E5%BA%A6%E5%8F%8A%E5%AD%98%E5%82%A8%E6%B1%89%E5%AD%97%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB/, 並有修正.

起因

最近在整理代碼規範,按照之前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)有性能上的差別麼?

對於INNODBvarchar(50)varchar(255)這兩者在存放方式上完全一樣:1-2 byte保存長度,實際的字符串存放在另外的位置,每個字符1 byte4 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 ServernvarcharOraclevarchar2時造成的固有印象,讓我一直覺得varchar保存中文字時長度需要打對摺或除以3。

但這個也是MySQL 5.0版本之前的事。現在varchar(n)是幾,就能存幾個中文字。

不過也需要注意統計字數使用CHARACTER_LENGTH而非LENGTH

SELECT LENGTH("輕鬆工作") AS a, CHARACTER_LENGTH("輕鬆工作") AS b;
-- 返回: 12	4
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章