MySQLにMyISAMまたはInnoDBのハッシュインデックスがないのはなぜですか?


35

平等のみを選択するアプリケーションがあり、btreeインデックスよりもハッシュインデックスを使用する必要があると考えています。残念ながら、MyISAMまたはInnoDBではハッシュインデックスはサポートされていません。どうしたの?


2
mysqlは、関数ベースのインデックス、ビットマップインデックスなどもサポートしていません

1
ハッシュインデックスがとても基本的なものだと思ったのですが、特定の実装関連の理由があると思います。

1
@アレックス:その理由は「怠iness」と「官僚主義」だと


High Performance MySQL Bookの素敵なHASHアルゴリズムを回答の最後に追加しました。
-RolandoMySQLDBA

回答:


16

多くのデータベースはハッシュベースのインデックスを全くサポートしていません。

ハッシュテーブルを効率的にするには、存在する可能性のある行の数を知る必要があります。そうしないと、ベースハッシュテーブルが非常に大きくなります(多くの空のエントリ、無駄なスペース、潜在的にディスクIO)または多くの場合、インダイレクションが使用されます(おそらく複数レベルのインダイレクション、またはハッシュ実装が単一レベルの場合はかなりの数のレコードに対して線形検索を実行する可能性があります)。とにかくインデックス。

そのため、一般的に役立つ(つまり、通常よりも優れている)ために、データが大きくなる(および小さくなる)ときにインデックスを時々再構築する必要があり、これにより大きな断続的なオーバーヘッドが追加されます。これは通常、メモリベースのテーブルでは問題ありません。再構築はおそらく非常に高速になるためです(データは常にRAMに格納され、いずれの場合も大規模になる可能性は低いため)が、ディスク上の大きなインデックスの再構築は非常に重い操作(およびIIRC mySQLはライブインデックスの再構築をサポートしないため、操作中にテーブルロックを保持します)。

したがって、ハッシュインデックスは一般にパフォーマンスが向上するためメモリテーブルで使用されますが、ディスクベースのテーブルはボーナスではなくパフォーマンスを損なう可能性があるため、それらをサポートしません。もちろん、ディスクベースのテーブルでハッシュインデックスが使用可能になるのを止めるものはありません。一部のデータベースこの機能をサポートしていることは間違いありませんが、メンテナーは追加する価値のある機能を考慮していないため、おそらくISAM / InnoDBテーブルには実装されていません記述して保守する余分なコードは、重要な違いを生むこれらの少数の状況ではメリットがありません)。おそらくあなたが強く同意しないなら、彼らと話をして、機能の実装について良いケースを作ることができます。

大きな文字列のインデックスを作成する場合、独自の擬似ハッシュインデックスの実装(値と実際の値のハッシュを格納し、列を持つインデックスを作成することで)が機能する場合がありますが、これは大きな文字列の場合にのみより確実に効率的です(ここでハッシュ値の計算とこの値によるツリーインデックスの検索は、比較のために大きな値を使用してツリーインデックスを検索するよりも常に高速である可能性が高く、使用される追加のストレージは重要ではありません)これは生産中です。


テーブル全体をロックせずに並べ替えて再ハッシュ(再構築)できるようにする方法はありますか?
12

@Pacerier:私がMySQLで知っていることではありません(最後に使用してから機能を追加できたかもしれませんが、ドキュメントをチェックしてください)DBMSがオンラインインデックスの作成/再構築をサポートしている場合でも、デフォルトのオプションではありません。ロックされる対象はさまざまです。テーブルの書き込みロックを保持するものもあれば、読み取りのみの場合は遅延しない他のトランザクションもあり、一部のDMBSは完全なテーブルロックを取得します。オンラインで再構築する必要がある場合は、使用するものを選択する前に各DBMSのドキュメントを確認してください。
デビッドスピレット

通常、再構築が必要なのは、データ長が2倍になった場合のみです。データの長さが毎分2倍になることを本当に心配する必要がありますか?(通常、これが懸念されるほどデータベースが大きくなると、まれにしか発生しません)
SOFe

6

関連する注意事項として、PostgreSQLドキュメントのインデックスタイプに関する説明が興味深い場合があります。ドキュメントの最近のバージョンには存在しません(その後の最適化のため、私はそれを採用します)が、MySQLでも同様です(そして、ハッシュインデックスがヒープテーブルにのみ使用される理由):

http://www.postgresql.org/docs/8.1/static/indexes-types.html

注:テストの結果、PostgreSQLのハッシュインデックスはBツリーインデックスよりも性能が劣ることが示されており、ハッシュインデックスのインデックスサイズと構築時間ははるかに劣っています。さらに、ハッシュインデックス操作は現在WALログに記録されていないため、データベースのクラッシュ後にハッシュインデックスをREINDEXで再構築する必要があります。これらの理由により、ハッシュインデックスの使用は現在推奨されていません。同様に、Rツリーインデックスには、GiSTインデックスの同等の操作と比較してパフォーマンス上の利点はないようです。ハッシュインデックスと同様に、WALに記録されず、データベースのクラッシュ後にインデックスの再作成が必要になる場合があります。ハッシュインデックスの問題は最終的に修正される可能性がありますが、Rツリーインデックスタイプは将来のリリースで廃止される可能性があります。Rツリーインデックスを使用するアプリケーションをGiSTインデックスに移行することをお勧めします。

繰り返しますが、これはPostgreSQL固有の(旧バージョン)ですが、「自然な」インデックスタイプでは必ずしも最適なパフォーマンスが得られないことを示唆するはずです。


5

ここに興味深いものがあります:

本によるMySQLの5.0認定研究ガイド、ページ433、セクション29.5.1

MEMORYエンジンは、デフォルトのインデックス作成アルゴリズムによりHASHを使用します。

笑いのために、MySQL 5.5.12でHASHを使用して主キーを持つInnoDBテーブルとMyISAMテーブルを作成しようとしました

mysql> use test
Database changed
mysql> create table rolando (num int not null, primary key (num) using hash);
Query OK, 0 rows affected (0.11 sec)

mysql> show create table rolando\G
*************************** 1. row ***************************
       Table: rolando
Create Table: CREATE TABLE `rolando` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`) USING HASH
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> create table rolando2 (num int not null, primary key (num) using hash) engine=MyISAM;
Query OK, 0 rows affected (0.05 sec)

mysql> show create table rolando2\G
*************************** 1. row ***************************
       Table: rolando2
Create Table: CREATE TABLE `rolando2` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`) USING HASH
) ENGINE=MyISAM DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

MySQLは文句を言いませんでした。

更新

悪いニュース !!!SHOW INDEXES FROMを使用しました。インデックスがBTREEであると表示されます。

INDEX構文MySQLのページには、CREATE専用メモリとNDBストレージエンジンは、ハッシュインデックスに対応できると述べています。

mysql> show indexes from rolando;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando |          0 | PRIMARY  |            1 | num         | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

mysql> show indexes from rolando2;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando2 |          0 | PRIMARY  |            1 | num         | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

mysql> create table rolando3 (num int not null, primary key (num)) ENGINE=MEMORY;
Query OK, 0 rows affected (0.03 sec)

mysql> show create table rolando3\G
*************************** 1. row ***************************
       Table: rolando3
Create Table: CREATE TABLE `rolando3` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`)
) ENGINE=MEMORY DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> show indexes from rolando3;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando3 |          0 | PRIMARY  |            1 | num         | NULL      |           0 |     NULL | NULL   |      | HASH       |         |               |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

一部の人々は、「High Performance MySQL:Optimizations、Backups、Replication and More」という本のページ102-105のアイデアに従って、ハッシュアルゴリズムをエミュレートすることを提案しました

ページ105には、私が気に入っているこの手っ取り早いアルゴリズムがあります。

SELECT CONV(RIGHT(MD5('whatever value you want'),16),16,10) AS HASH64;

任意のテーブルにこの列を作成し、この値にインデックスを付けます。

試してみる !!!


5
本番環境で疑似ハッシュインデックス手法を使用する前に、パフォーマンス分析を実行します。大きな文字列の場合、大きな違いが生じる可能性がありますが、とにかく最後にツリーインデックスをナビゲートすることになり、ハッシュと一致するものから適切な行を見つけるために比較する必要があります。それらを保存するだけの価値はありません。これは実際にはハッシュインデックスではありません。ツリーを歩く作業を単純に減らしています(たとえば、x00バイトの文字列ではなく8バイトのINTを比較するなど、比較ごとにバイト数が少なくなるため)。
デヴィッドスピレット

@David Spillettこれで、私はあなたに完全に同意しなければなりません。他の索引付け戦略も、第11章「高性能のための索引付け戦略」の同じ本で提案されています。私の答えをさらに後押しするために、本では実際に、行とBTreeインデックスを同じ構造で保存するクラスター化インデックスの使用について言及しています。これは、あなたが言及した仕事の削減を加速するかもしれません。残念ながら、先ほど触れたように飛び越えなければならないフープは、やむを得ないものです。それでもあなたのコメントに対する私からの+1、先生!!! 実際、あなたの答えに対しても+1。
RolandoMySQLDBA

@RolandoMySQLDBAことができますが、「カスタムハッシュ」、最後の段落は、多くの手がかりを与えていないようだ...上の部分に、より手の込んだ
Pacerier

2

BTreeは、単一行のルックアップの場合、ハッシュほど遅くありません。BTreeは非常に効率的な範囲クエリを提供するので、なぜBTree以外のものに煩わされるのか。

MySQLはBTreeブロックをキャッシュする非常に良い仕事をしているので、BTreeベースのクエリがI / Oを行う必要はほとんどありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.