データ解析用にMySQLのMEMORYストレージエンジンを使っています。基本的にはinnodbを使っているのですが、インデックス付けに莫大な時間(日単位)がかかることがあり、取り回しが面倒なので、常時メモリに入れておき、定期的にinnodbにセーブするという運用にしています。
さて、このMEMORYストレージエンジンですが、いくつか落とし穴?があります。これについてメモしておきます。
キーの種類の違いの注意する
MySQLでは、キーを作成する際、USING HASH / USING BTREEという二つのオプションが選べます。MEMORYストレージエンジン以外では、BTREEしか使えませんが、MEMORYストレージエンジンではUSING HASHがデフォルトです。これが結構くせものです。
AUTO_INCREMENTでつけられたような任意の番号は、大小関係が問題にならないことが多いと思います。こういった場合、HASHの方が高速です。しかし、日付を使った集計では、不等号やMAXが頻出します。この場合、HASHでは全検索になりきわめて遅くなります!!・・・と内部のアルゴリズムを考えれば当たり前なのですが、知らないとはまります。
とは言え、常にというわけではないですが、PRIMARY KEYの場合はあまり問題にならない場合もあります。なぜなら、PRIMARY KEYはデータの物理的な並び順にも反映されていて、MySQLはこれをうまく使ってくれるようだからです。しかし、セカンダリインデックスのDATETIMEカラムにHASHを使ってしまった日には大変なことになります。
なので、MEMORYストレージエンジンでキーを使うときには、デフォルト / USING BTREEを意識するようにします。
ちなみに自分はUSING HASHは省略してデフォルトを使うするようにしています。なぜなら、CREATE TABLE文を他のストレージエンジンに転用するときにいちいち書き換えるのが面倒だからです。USING BTREEならこの心配はありません。
インデックスの後付けはしない。必要なら別テーブルを作ってデータを移す。
メモリに十分余裕があれば、大容量のデータでもsort_buffer_sizeを上げてインデックスをつけたりとったりしても良いのかもしれません。MEMORYストレージエンジンで大きなのデータを扱うとどうしてもメモリに余裕がない状態で使うことになり、sort_buffer_sizeが足りないために、インデックスを後からつけると、filesortが発生してしまいます。これはデータ自体が大きい場合には致命的です。特に、MEMORYストレージエンジンで分単位の作業をしている最中に、突然時間単位、日単位の作業が入ると面倒です。
ただ、回避策はあります。既存テーブルにインデックスを貼る代わりに、別テーブルを作ってそこにデータを移せばよいのです。MEMORYストレージエンジンのインデックス処理は高速なので、これでかなりの高速化を図れます。
メモリを適切に設定する
max_heap_table_sizeをきちんと設定しないとMEMORYストレージエンジンは使い物になりません。innnodbと併用するなら、innodb_buffer_pool_sizeと分配。innnodbのキャッシュは要らない(インデックスを貼ったりしない)のなら、ほとんどを割り当ててしまって大丈夫かと思います。ただし、用途によっては上述のsort_buffer_sizeも考慮する必要があります。
現在試行錯誤の真っ最中ですので、「いや、それはこうした方が良い」等、気づいたことがあればコメントいただけると幸いです。