MySQL VARCHAR 大小的性能影響
MySQL 的 varchar 大小之間是否存在性能差異?例如,
varchar(25)
和varchar(64000)
。如果不是,是否有理由不聲明所有具有最大大小的 varchars,以確保您不會用完空間?
您必須意識到使用 CHAR 與 VARCHAR 的權衡
使用 CHAR 欄位,您分配的正是您得到的。例如,CHAR(15) 分配並儲存 15 個字節,無論您在欄位中放置何種字元。字元串操作簡單直接,因為數據欄位的大小是完全可預測的。
使用 VARCHAR 欄位,您會得到一個完全不同的故事。例如,VARCHAR(15) 實際上動態分配最多 16 個字節,最多 15 個用於數據,至少還有 1 個額外的字節來儲存數據的長度。如果您要儲存字元串“hello”,則需要 6 個字節,而不是 5 個字節。字元串操作在所有情況下都必須始終執行某種形式的長度檢查。
當您做兩件事時,這種權衡更加明顯:
儲存數百萬或數十億行
索引 CHAR 或 VARCHAR 列
權衡#1
顯然,VARCHAR 具有優勢,因為可變長度數據會產生更小的行,從而產生更小的物理文件。
權衡#2
由於 CHAR 欄位由於欄位寬度固定而需要較少的字元串操作,因此針對 CHAR 欄位的索引查找平均比 VARCHAR 欄位快 20%。這不是我的任何猜測。MySQL Database Design and Tuning一書在 MyISAM 表上做了一些奇妙的事情來證明這一點。書中的例子做瞭如下的事情:
ALTER TABLE tblname ROW_FORMAT=FIXED;
該指令強制 VARCHAR 表現為 CHAR。早在 2007 年,我在上一份工作中就做到了這一點,並使用了一個 300GB 的表並將索引查找速度提高了 20%,而沒有更改任何其他內容。它按已發布的方式工作。然而,它確實產生了一個幾乎兩倍大小的表,但這又回到了權衡#1。
您可以分析儲存的數據以查看 MySQL 推薦的列定義。只需對任何表執行以下命令:
SELECT * FROM tblname PROCEDURE ANALYSE();
這將遍歷整個表,並根據其包含的數據、最小欄位值、最大欄位值等為每列推薦列定義。有時,您只需要使用常識來規劃 CHAR 與 VARCHAR。這是一個很好的例子:
如果要儲存 IP 地址,則此類列的遮罩最多為 15 個字元 (xxx.xxx.xxx.xxx)。我會立即跳到 CHAR(15) 處,因為 IP 地址的長度不會有太大變化,而且字元串操作的複雜性由額外的字節控制。您仍然可以對這樣的列執行 PROCEDURE ANALYSE()。它甚至可能會推薦 VARCHAR。在這種情況下,我的錢仍然會放在 CHAR 上而不是 VARCHAR 上。
CHAR 與 VARCHAR 問題只能通過適當的計劃來解決。權力越大,責任越大(陳詞濫調但真實)
該執行緒中的大多數答案已有五八年的歷史,在 InnoDB 和 utf8 成為預設值之前編寫。那麼,讓我重新開始……
當查詢需要內部臨時表時,它會嘗試使用
MEMORY
表。但是如果 MEMORY 不能使用
TEXT
/BLOB
列被提取,甚至TINYTEXT
.VARCHAR
大於某個數量,在目前版本中可能為 512。另外,請注意
VARCHARs
變成CHARs
. (8.0 對此進行了修改。)因此,無論列中的內容如何,都將VARCHAR(255)
a擴展為 765 個字節。CHARACTER SET utf8
然後,這可能會被觸發:
- 如果
MEMORY
table 變得大於max_heap_table_size
ortmp_table_size
,它將被轉換為 MyISAM 並可能溢出到磁碟。因此,
VARCHAR(25)
更有可能留下來MEMORY
,因此速度更快。(255)
不是那麼好,而且(64000)
是壞的。(將來,臨時表可能會是
InnoDB
,這個答案的一部分需要修改。)(更新)MySQL 8.0.2:“TempTable 儲存引擎將 MEMORY 儲存引擎替換為記憶體內部臨時表的預設引擎。TempTable 儲存引擎為 VARCHAR 和 VARBINARY 列提供高效儲存。” (從那以後,臨時表的處理有了進一步的變化;我懷疑塵埃還沒有落定。)