Mysql

在固定大小的欄位上使用 CHAR 與 VARCHAR 對性能有何影響?

  • August 16, 2018

我有一個儲存 MD5 雜湊的索引列。因此,該列將始終儲存 32 個字元的值。無論出於何種原因,它被創建為 varchar 而不是 char。遷移數據庫以將其轉換為字元是否值得麻煩?這是在帶有 InnoDB 的 MySQL 5.0 中。

之前有人問過類似的問題

MySQL VARCHAR 大小的性能影響

這是我的答案的摘錄

您必須意識到使用 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<>在這裡擺弄

引用自:https://dba.stackexchange.com/questions/2640