全文検索エンジン「Elasticsearch」を調べて使ってみた色々まとめ

    url

    こんにちは、AWSではcodedeployが好きな中村です。

    IT業界はドッグイヤーと言われて久しいですが、技術の進歩は目まぐるしく進んでいます。

    それに伴い、世の中が求めるWebサービス・スマホアプリのスピード感は日々増しています。

    ページを表示するのに2秒以上かけてはいけない、、0.1秒表示速度が遅くなるとxxx件のユーザーが離脱する。。など、いろいろな通説が出てきているほどです。

    今回はそんな世の中が求めるWebサービスの表示スピードを劇的に速くできるサービス「Elastichsearch」について調べてみました。

     

    このサービスはFacebookGithubでも採用されているサービスですので、知っておいて損はないです。

    ではまず、ElasicSearchとはどんなサービスでしょうか?

    ■Elasticsearchとは

    ・概要

    Elasticsearch(エラスティックサーチ)とは、Elastic社が提供する「Lucene」ベースのオープンソース全文検索エンジンです。
    なお全文検索エンジンとは、大量にあるドキュメントデータの中から、目的のワードを含むドキュメントデータを検索するための仕組みです。

    ・MySQLなどのRDBMSとの違いや特徴

    特徴として、MySQL(RDBMS) 、Redshift(データウェアハウス)、DynamoDB(NoSQL)などと比べると、複雑な検索、高速に実行などが実現可能です。
    キーワードとして以下があります。
    【スケーラブル】
    超大規模なシステムでも利用できます。理由としてクラスタ構成(複数のコンピュータを連結し、1台のコンピュータとして振る舞う)が前提で作られているからです。既存データの分散/レプリカなどが自動で実行できたりするため、Hadoopとの親和性も高いです。
    【スキーマレス】
    データ構造が非常に柔軟性に富んでいます。JSON 形式のデータを受けていおり、様々なデータ構造(ネスト形式など含む)でもインデックスができたり、スキーマ(データマッピング)定義もなしで、データ投入などもできます。
    【マルチテナント】
    様々なサービスで自由に横断して検索・分析が可能です。1つのクラスタに複数のIndex(データのまとまり) が作成でき、そこに対して簡単かつ高速に検索処理ができます。対象ワードの出現頻度(スコア)を使った検索ができることや、「Kibana」と連携すると簡単にデータ分析もできます。

    ■Elasticsearchで覚えておくべきキーワード

    MySQLと比較しながらElasticsearchの覚えておくべきキーワードをまとめます。

    ・Cluster(クラスタ)とNode(ノード)

    ClusterとはNodeの集合体です。
    Nodeとはサーバーと同じ概念のレベルのもので、1台のサーバーに1Node用意します。
    トラフィックが増加した場合は、Nodeを増やすことで、処理速度の分散ができるような仕組みになっています。

    ・Index(インデックス)

    RDBのDatabaseの概念レベルに該当します。
    1つのClusterに複数のIndexが作成できます。

    ・Type(タイプ)

    RDBのTableの概念レベルに該当します。
    1つのIndexに複数のTypeが作成できます。

    ・Document(ドキュメント)

    RDBのRowの概念レベルに該当します。
    idをキーにして個々のデータを管理します。

    ・Shard(シャード)

    RAIDで構成されたハードディスクと似た仕組みになります。
    1つのindexを作成すると、合わせてShadが複数作成されます。
    ShardはPrimary(master)とReplicaで構成されており、1Nodeには1shard配置されます。
    例えばPrimaryがすでに存在するShardには同じNodeにReplicaは存在できず、別のNodeに配置されます。

    ■Elasticsearchを触ってみる

    では実際にElasticsearchを触ってみます。
    なお、全てMacのローカル環境で実施しています。

    ・nodeも必要なためインストール

    使うにはNodeが必要です。nodebrewをインストール、ディレクトリがないエラーが発生したため、これも作成し、パスを通します。
    ※xxxはユーザー名
    brew install nodebrew
    mkdir /Users/xxxxx/.nodebrew/
    mkdir /Users/xxx/.nodebrew/src
    nodebrew install-binary latest
    export PATH=$PATH:/Users/tnakamura/.nodebrew/current/bin

    ・elastic searchのインストール

    ・インストールできるバージョンの確認し、バージョン2.4をインストール、実行
    brew search elasticsearch
    brew install elasticsearch@2.4
    cd /usr/local/Cellar/elasticsearch@2.4/2.4.4/bin/
    ./elasticsearch

    以下のipで起動されます
    127.0.0.1:9300
    これで準備完了

    ・データの投入や検索

    以下のようなコマンドで状態の確認ができます。

    curl 127.0.0.1:9200 #バージョンの概要
    curl 127.0.0.1:9200/_cat/health?v #クラスターの状態を確認
    curl 127.0.0.1:9200/_cat/indices?v #インデックスの状態を確認

    では実際にデータを投入してみます。

    #indexの作成
    curl 127.0.0.1:9200/customer -X PUT
    #sheardsのreplicaが不要なので削除する
    curl -H 'Content-Type: application/json' -X PUT -d '{"index":{"number_of_replicas": 0}}' 127.0.0.1:9200/customer/_settings
    #TypeとDocumentを作成
    curl -H 'Content-Type: application/json' -X PUT -d '{"name”:”test”}’ 127.0.0.1:9200/customer/external/1
    #投入結果を確認
    curl 127.0.0.1:9200/customer/external/1 | python -mjson.tool
    % Total % Received % Xferd Average Speed Time Time Time Current
    Dload Upload Total Spent Left Speed
    100 147 100 147 0 0 51006 0 --:--:-- --:--:-- --:--:-- 73500
    {
    "_id": "1",
    "_index": "customer",
    "_source": {
    "day": "2017-11-12",
    "name": "test",
    "timeFieldName": "day"
    },
    "_type": "external",
    "_version": 1,
    "found": true
    }

    データ投入ができました。

    他にも以下のようなコマンドで操作ができます。

    #paramsで指定の文字検索
    curl -H 'Content-Type: application/json' -X GET -d '{ "id": "template01", "params": { "firstname": "Tammy" }}' 127.0.0.1:9200/_search/template
    #_updatでのデータ更新
    curl -H 'Content-Type: application/json' -X POST -d '{"doc":{"day":"2017-11-12"}}' 127.0.0.1:9200/customer/external/1/_update

    ■ElasticsearchとMySQLのDBを連携させる

    ElasticsearchはMySQLのDBを連携させ、データ検索もできます。
    MySQLで検索速度を改善したい。そんな時は連動してElasticsearchを使うことでパフォーマンス向上ができます。
    連動させるサービスとして、以下を取得します。(JDBCを使っている連携ツールです)
    ・サイト
    https://github.com/jprante/elasticsearch-jdbc
    ここからelasticsearch-jdbcの取得をします。
    ※elasticsearchとのバージョンが連動していないといけなく、JDBCに合わせたelasticsearchをこの後入れ直しました。
    なお、ローカルでMySQLの環境は事前に用意していて、対象のテーブルは1万件程度のデータが入っています。
    ここからデータをMySQL→Elasticsearchへ投入するスクリプトを実行します。

    wget http://xbib.org/repository/org/xbib/elasticsearch/importer/elasticsearch-jdbc/2.3.4.1/elasticsearch-jdbc-2.3.4.1-dist.zip
    unzip elasticsearch-jdbc-1.7.1.0-dist.zip
    cd elasticsearch-jdbc-1.7.1.0/lib
    cp mysql-delete-document.sh mysql-oreore.sh
    #環境に合わせて取得情報を変更します
    vi mysql-oreore.sh
    -----
    "jdbc" : {
    "url" : "jdbc:mysql://localhost:3306/[DB名]”,
    "user" : "root",
    "password" : "",
    "sql" : "select id as _id, xxxx, xxxx,xxxx from xxxx”
    }
    -----
    ./mysql-oreore.sh

    ※注意として’as _id’の記載がないとデータが意図しないidで振られてしまいます。
    データ件数はかなりありましたが、1秒程度で処理が終わりました。
    この処理でMySQL→Elasticsearchへのデータ投入が完了です。

    実行結果を確認します。
    #'jdbc'indexデータを取得
    curl -XGET 'http://localhost:9200/jdbc/_search?pretty=true'
    #jdbcからindexのデータ件数を取得
    curl -H 'Content-Type: application/json' -X GET -d '{"query":{"match_all":{}},"size":0}' http://localhost:9200/jdbc/_search?pretty=true | python -mjson.tool
    #結果
    % Total % Received % Xferd Average Speed Time Time Time Current
    Dload Upload Total Spent Left Speed
    100 232 100 197 100 35 2605 462 --:--:-- --:--:-- --:--:-- 2626
    {
    "_shards": {
    "failed": 0,
    "successful": 5,
    "total": 5
    },
    "hits": {
    "hits": [],
    "max_score": 0.0,
    "total": 16295
    },
    "timed_out": false,
    "took": 31
    }

    “total”: 16295件のデータが投入されていること確認できました。

    ■ElasticsearchとMySQLの実行速度を比較する

    Apache benchを使ってMySQLへのデータ取得との速度を比較します。

    まずは適当にMySQLヘの接続、jsonでのレスポンスをするphpファイルを作り、AB実行します。
    # ABテスト
    ab -n 100 -c 1 'http://localhost:8000/testDBget.php'
    # 結果(抜粋)
    Requests per second: 1.62 [#/sec] (mean)
    Time per request: 618.267 [ms] (mean)
    Time per request: 618.267 [ms] (mean, across all concurrent requests)
    Transfer rate: 322.72 [Kbytes/sec] received

    次にElasticsearchを実行します。
    # ABテスト
    ab -n 100 -c 1 'http://localhost:9200/jdbc/_search?pretty=true'
    # 結果(抜粋)
    Requests per second: 1222.40 [#/sec] (mean)
    Time per request: 0.818 [ms] (mean)
    Time per request: 0.818 [ms] (mean, across all concurrent requests)
    Transfer rate: 45766.15 [Kbytes/sec] received

    Time per request(同時実行したリクエストの平均処理時間)が、
    ・MySQL:618.267ms
    ・Elasticsearch:0.818ms
    その差100倍以上、圧倒的に処理速度が速いです。
    当然環境に依存する部分があったりとかで正確な数字かは微妙ですが、間違いなくパフォーマンスは高いです。

    ■kibana(sense)を使いデータをビジュアライズ

    kibana(sense)を使ってデータをビジュアライズ化します。
    #kibana、senseのインストール
    wget https://download.elastic.co/kibana/kibana/kibana-4.3.1-darwin-x64.tar.gz
    bin/kibana plugin --install elastic/sense
    #kibanaの実行
    kibana-4.3.1-darwin-x64/bin/kibana

    ※バージョンが連動していないと動かないため、elasticsearchとのバージョン関係は注意が必要
    これは適当にいじっただけですが、それっぽいグラフが出せました。

    スクリーンショット 2017-02-05 18.38.07

    まとめ

    まだまだ奥が深く、調整もいろいろ必要そうですが、導入すると非常に破壊力のあるツールになると感じました。
    特に一番驚いたのは、その処理速度。
    大規模なシステムになった場合でもこの検索エンジンを使えば問題なくさばけそうです。
    今後もぜひ活用していきたいです。

    以下参考にさせていただきました。

    ‘http://dev.classmethod.jp/server-side/elasticsearch-getting-started-05/
    ‘http://dev.classmethod.jp/server-side/elasticsearch-getting-started-06/
    ‘http://dev.classmethod.jp/server-side/elasticsearch-getting-started-07/
    ‘http://dev.classmethod.jp/server-side/elasticsearch-getting-started-08/
    ‘https://www.ossnews.jp/oss_info/Elasticsearch
    ‘https://www.idcf.jp/words/schema.html