在固定大小的欄位上使用 CHAR 與 VARCHAR 對性能有何影響?
我有一個儲存 MD5 雜湊的索引列。因此,該列將始終儲存 32 個字元的值。無論出於何種原因,它被創建為 varchar 而不是 char。遷移數據庫以將其轉換為字元是否值得麻煩?這是在帶有 InnoDB 的 MySQL 5.0 中。
之前有人問過類似的問題
這是我的答案的摘錄
您必須意識到使用 CHAR 與 VARCHAR 的權衡
使用 CHAR 欄位,您分配的正是您得到的。例如,CHAR(15) 分配並儲存 15 個字節,無論您在欄位中放置何種字元。字元串操作簡單直接,因為數據欄位的大小是完全可預測的。
使用 VARCHAR 欄位,您會得到一個完全不同的故事。例如,VARCHAR(15) 實際上動態分配最多 16 個字節,最多 15 個用於數據,至少還有 1 個額外的字節來儲存數據的長度。如果您要儲存字元串“hello”,則需要 6 個字節,而不是 5 個字節。字元串操作在所有情況下都必須始終執行某種形式的長度檢查。
當您做兩件事時,這種權衡更加明顯: 1. 儲存數百萬或數十億行 2. 索引 CHAR 或 VARCHAR 列
TRADEOFF #1 顯然,VARCHAR 具有優勢,因為可變長度數據會產生更小的行,從而產生更小的物理文件。
TRADEOFF #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 問題只能通過適當的計劃來解決。強大的力量伴隨著巨大的責任(陳詞濫調但真實)。
更新
說到MD5,
strlen
在切換整行格式的時候,內部的計算應該去掉。無需更改欄位定義。如果 MD5 鍵是唯一存在的 VARCHAR,我會選擇它並將表行格式轉換為 fixed。如果存在大量其他 VARCHAR 欄位,它們也會受益。作為交換,桌子將擴大到大約兩倍的大小。但是查詢應該在沒有額外調整的情況下加速大約 20%。
看起來您將通過轉換為每個值節省 1 個字節
char
或大約 3% 。如果你以十六進制儲存MD5可能不值得- 你可以通過使用 abinary
來節省 50%。感謝 Ovais(見評論)指出,如果您使用多字節字元集,
char(32)
則可以使用超過 32 個字節。感謝 Rick James 指出您應該使用該
unhex
函式將十六進製字元串轉換為二進制:create table foo(bar varbinary(100)); insert into foo(bar) values(md5('a')); insert into foo(bar) values(unhex(md5('a')));
select length(bar) from foo;
| 長度(條) | | ----------: | | 32 | | 16 |
db<>在這裡擺弄