web-dev-qa-db-ja.com

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

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

60
Jason Baker

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

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

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

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

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

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

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

TRADEOFF#1明らかに、可変長データでは行が小さくなり、したがって物理ファイルも小さくなるため、VARCHARには利点があります。

TRADEOFF#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)です。 IPアドレスの長さはそれほど変化せず、追加のバイトによって制御される文字列操作の複雑さが増すので、ハートビートのCHAR(15)ですぐにジャンプします。そのような列に対してPROCEDURE ANALYSE()を実行することもできます。 VARCHARを推奨することもあります。この場合、私のお金はまだVARCHARよりもCHARです。

CHARとVARCHARの問題を解決するには、適切な計画を立てる必要があります。大きな力には大きな責任が伴います(陳腐だが真実)。

更新

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

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

57
RolandoMySQLDBA

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

マルチバイト文字セットを使用している場合、char(32)2バイトよりも多く使用 できることを指摘してくれた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 ここ

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

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

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

15
RThomas