読者です 読者をやめる 読者になる 読者になる

『Amebaなう』リアルタイム検索機能に Apache Solr と MessagePack を採用

ミニブログサービス「Amebaなう」に検索機能を追加 Apache Solrのカスタマイズにより検索パフォーマンスが大幅向上

検索機能は、当社の研究開発組織「インキュベーションラボラトリー」が開発し、Apache Solrをベースに、検索インデックス作成アルゴリズムの効率化や、データを高速かつ効率的に保存できる技術仕様「MessagePack」と各種圧縮アルゴリズムを組み合わせる等の対応を行いました。

あわせて読みたいLuceneのインデックスにStoreするデータをMessagePackで圧縮してみた - 社内NEET宣言


MessagePack と各種圧縮アルゴリズムを組み合わせることで、インデックスサイズを80%程度に圧縮することが可能になったようです。

MessagePack を使うと、配列やMapなどの構造を、非常にコンパクトに保存することができます。例えば、[1,2,3]という3つの整数からなる配列はたったの4バイト{"a":null}という連想配列はたったの4バイトで保存することが可能です。もしこれが JSON だと、それぞれ7バイトと10バイトも必要になります。

さらに Deflate などで圧縮すれば、非常に高い圧縮率が得られるという寸法です。先のjust_do_neetさんのブログによれば、作成速度も検索速度も、それほど速度劣化は起こっていないようです。


他に MessagePack を使ってデータサイズを圧縮している事例には、Data::Modelがあります。

KVSに保存するデータのスキーマをあらかじめ定義しておき、キー名を整数に変換するなどの変換を施すことで、空間効率を飛躍的に高めています:

例えば、上記のコードを使った場合に以下のようなデータ構造だった場合に


{
file_id => 'dankogai',
media_type => 1,
client_type => 5,
is_broken => undef,
},

Key を任意の値に変換してから直列化をかけます。
Data::Model 標準のシリアライザの MessagePack では、数値をとても効率よく直列化してくれるので、 media_type だとかいう長ったらしい key name を 2 とか言う数値に変換してしまいます。
2 とかという小さい値だと直列化後も1バイトしか容量食わなくて嬉しいんです!

実際、上のほうで書いてるデータ例だと以下の用になります。


{
2 => 1,
3 => 5,
},

これを MessagePack でシリアライズすると、元のデータは5バイトという驚異的なサイズとなって kumofs の value に格納されます。
memcachedにデータを保存する場合でも、このように空間効率を高めることで、キャッシュ可能なデータ量を増やすことができます:Data::Model::Driver::Memcachedで超効率データ保存


このように MessagePack は、速いだけでなく「小さい」という利点があります。MessagePack は JSON と型システムが同じ*1なので、JSON を使うところを単に MessagePack を使うようにするだけで、キャッシュのヒット率を向上させられたり、トラフィック量やCPU使用率を減らせるかもしれません。


それでは、MessagePack を使ったプログラミングをお楽しみください^^;

*1:整数、浮動小数点数、文字列、配列、連想配列、nil。JSON との互換性が高い点は MessagePack の大きな利点です。他のシリアライズ形式、例えば Protocol Buffers には配列配列型や連想配列がなく、Avro は値の型が異なる連想配列({a:["string",10], b:20, ...} のような)を扱えません。