固定サイズのフィールドでCHARとVARCHARを使用すると、パフォーマンスにどのような影響がありますか?


58

MD5ハッシュを格納するインデックス列があります。したがって、列には常に32文字の値が格納されます。何らかの理由で、これはcharではなくvarcharとして作成されました。データベースを移行してcharに変換する手間をかける価値はありますか?これは、InnoDBを使用したMySQL 5.0にあります。


6
警告この質問とその回答は、InnoDBとutf8がデフォルトになる前に書かれました。
リックジェームズ

回答:


56

同様の質問が以前に尋ねられました

MySQL VARCHARサイズのパフォーマンスへの影響

これが私の答えの抜粋です

CHARとVARCHARを使用することのトレードオフを理解する必要があります

CHARフィールドを使用すると、割り当てるのはまさに取得したものです。たとえば、CHAR(15)は、フィールドにどのように文字を配置しても、15バイトを割り当てて保存します。データフィールドのサイズは完全に予測可能であるため、文字列操作は簡単で簡単です。

VARCHARフィールドを使用すると、まったく異なるストーリーが得られます。たとえば、VARCHAR(15)は実際に、最大16バイト、データ用に最大15バイト、データの長さを格納するために少なくとも1バイトを動的に割り当てます。文字列 'hello'を保存する場合、5ではなく6バイトを使用します。文字列操作では、常に何らかの形式の長さチェックを実行する必要があります。

次の2つのことを行うと、トレードオフがより明確になります。1.数百万または数十億の行を格納する2. CHARまたはVARCHARのいずれかの列にインデックスを付ける

トレードオフ#1可変長のデータはより小さな行を、したがってより小さな物理ファイルを生成するため、明らかにVARCHARには利点があります。

トレードオフ#2フィールド幅が固定されているため、CHARフィールドの文字列操作が少なくなるため、CHARフィールドに対するインデックス検索は、VARCHARフィールドよりも平均20%高速です。これは私の側の推測ではありません。MySQLデータベースの設計とチューニングの本は、これを証明するためにMyISAMテーブルですばらしい何かを実行しました。本の例は次のようなことをしました。

ALTER TABLE tblname ROW_FORMAT=FIXED;

このディレクティブは、すべてのVARCHARを強制的にCHARとして動作させます。2007年の以前の仕事でこれを行い、300 GBのテーブルを使用して、他の変更を加えることなく、インデックスルックアップを20%高速化しました。公開されたとおりに機能しました。ただし、テーブルのサイズはほぼ2倍でしたが、トレードオフ#1に戻ります。

格納されているデータを分析して、MySQLが列定義に推奨しているものを確認できます。任意のテーブルに対して次を実行するだけです:

SELECT * FROM tblname PROCEDURE ANALYSE();

これにより、テーブル全体を走査し、含まれるデータ、最小フィールド値、最大フィールド値などに基づいて、すべての列の列定義を推奨します。場合によっては、CHARとVARCHARの計画に常識を使用する必要があります。これが良い例です:

IPアドレスを保存している場合、そのような列のマスクは最大15文字(xxx.xxx.xxx.xxx)です。CHAR(15)IPアドレスの長さはそれほど変わらず、追加のバイトで制御される文字列操作の複雑さが増すので、私はすぐに思いつきます。PROCEDURE ANALYSE()そのような列に対してはまだできます。VARCHARを推奨することさえあります。この場合、私のお金はまだVARCHARよりCHARになります。

CHARとVARCHARの問題は、適切な計画を通してのみ解決できます。大きな力には大きな責任が伴います(決まり文句ですが真実です)。

更新

MD5に関してstrlenは、行フォーマット全体を切り替えるときに内部の計算を削除する必要があります。フィールド定義を変更する必要はありません。

MD5キーが存在する唯一のVARCHARである場合、そのキーを使用して、テーブルの行形式をfixedに変換します。他にもかなりの数のVARCHARフィールドが存在する場合は、同様にメリットがあります。その代わりに、テーブルはそのサイズの約2倍に拡大します。ただし、追加の調整を行わなくても、クエリは約20%加速するはずです。


1
私は(4)文字を使用したいと思うかのような符号なし整数 IPアドレスの
ジャック・ダグラス

@JackPDouglasあなたはその点について正しいです。
RolandoMySQLDBA

とにかく、インデックスは固定長で保存されていませんか?ストレージ形式を固定長に変更すると、インデックス検索がどのように改善されるかわかりません。テーブルスキャンが改善されたということですか?
マーカスアダムス

1
@JackDouglas、なぜないbitbinary
Pacerier 14

@Pacerierの方が良いでしょう、私は同意します:)
ジャックダグラス14

19

値ごとに1バイト、またはに変換することで約3%節約できるようですchar。とにかく16進数でMD5を保存している場合は、おそらく価値がありません- binary代わりにaを使用することで50%節約できます。

マルチバイト文字セットを使用している場合、32バイト以上を使用char(32)できることを指摘してくれたOvais(コメントを参照)に感謝します。

このunhex関数を使用して16進文字列をバイナリに変換する必要があることを指摘してくれたRick Jamesに感謝します。

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 <> fiddle here


バイナリに変更することをお勧めします。
-RThomas

これをバイナリに変換することを計画しています。考えてみると、エンコードはutf-8なので、バイトを使用するかcharを使用するかだけに基づいてサイズを変更する必要はありません。それとも私は間違っていますか?
ジェイソンベイカー

@Jason-エンコーディングは適用されませんbinary-または誤解しましたか?
ジャックダグラス

3
utf-8の文字セットを持つchar(32)列の場合、すべての値は32x3バイトのストレージを必要とします。MD5ハッシュ値をutf-8に設定する必要があるのはなぜですか。binary(32)に変換するには、値ごとに32バイトが必要です。
ovais.tariq

1
BINARY使用しても、を使用しない限りほとんど動作しませんUNHEX()。つまり、あなたが保存することができUNHEX(MD5(x))、16バイトにBINARY(16)保存する上で重要なスペースを節約するためMD5(x)CHAR(32) CHARACTER SET ascii
リックジェームズ

15

私の意見では変更する価値はありません。ここでドキュメントを見ると、この2つの違いがわかります。あなたの使用シナリオでは、行サイズに関連する余分なオーバーヘッドを本当に心配しない限り、一方は他方に対して実際に大きな利点を提供しません。

http://dev.mysql.com/doc/refman/5.0/en/char.html

上記のリンク先のドキュメントの最初のコメントにも注意してください。「CHARは、レコード全体が固定サイズの場合にのみアクセスを高速化します。つまり、可変サイズのオブジェクトを使用する場合は、可変サイズ。VARCHARも含まれているテーブルでCHARを使用しても速度が向上しない


この「高速化」は、InnoDBではなくMyISAMに適用されます。
リックジェームズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.