OSはCentOS 7.2
elasticsearch.yml
cluster.name: クラスタ名
node.name: "ノード名"
node.master: true
node.data: true
network.host: '_eth0:ipv4_'
discovery.zen.minimum_master_nodes: 1
discovery.zen.ping.timeout: 3s
discovery.zen.ping.unicast.hosts: ["ホスト名orIPアドレス"]
path.data: /data/elasticsearch
index.number_of_shards: 1
index.number_of_replicas: 0
bootstrap.memory_lock: true
script.groovy.sandbox.enabled: true
script.engine.groovy.inline.aggs: true
script.engine.groovy.indexed.aggs: true
script.engine.groovy.indexed.mapping: true
script.inline: true
script:indexed: true
1ノードでmaster兼用設定
多ノードの場合もshardsの数は1,6,12,24,36,60等データノードを随時増やしても割り切れる数を指定するようにしている。
scriptはfunction_scoreでカスタムの_scoreを計算するためのもの。もっと権限絞りこめるが、とりあえず動かすこと優先。
bootstrap.memory_lockは以前はbootstrap.mlockだったものが名称変更。内部的にはまだmlockでも互換性のために残っているが非推奨なのかな。
Multi-AZでZone毎にデータ完結させたい(Zoneを跨がないので通信遅延の削減にもなる)場合は下記を追加
node.zone: ap-northeast-1a *ノードのあるzone
cluster.routing.allocation.awareness.attributes: zone
cluster.routing.allocation.awareness.force.zone.values: ap-northeast-1a,ap-northeast-1c
mapping template
{
"template" : "適用インデックスの頭文字列*",
"order" : 0,
"settings" : {
"refresh_interval" : "1m",
"index.analysis.filter.romaji.type" : "kuromoji_neologd_readingform",
"index.analysis.filter.romaji.use_romaji" : "true",
"index.analysis.filter.synonym.type" : "synonym",
"index.analysis.filter.synonym.synonyms_path" : "user_synonym.txt",
"index.analysis.tokenizer.ja_tokenizer.type" : "kuromoji_neologd_tokenizer",
"index.analysis.tokenizer.ja_tokenizer.user_dictionary" : "userdict_ja.txt",
"index.analysis.tokenizer.ja_tokenizer.mode" : "search",
"index.analysis.analyzer.default.tokenizer" : "ja_tokenizer",
"index.analysis.analyzer.facet_analyzer.type" : "custom",
"index.analysis.analyzer.facet_analyzer.char_filter.0" : "html_strip",
"index.analysis.analyzer.facet_analyzer.tokenizer" : "keyword",
"index.analysis.analyzer.facet_analyzer.filter.0" : "cjk_width",
"index.analysis.analyzer.facet_analyzer.filter.1" : "lowercase",
"index.analysis.analyzer.ws_analyzer.type" : "custom",
"index.analysis.analyzer.ws_analyzer.char_filter.0" : "html_strip",
"index.analysis.analyzer.ws_analyzer.tokenizer" : "whitespace",
"index.analysis.analyzer.ws_analyzer.filter.0" : "cjk_width",
"index.analysis.analyzer.ws_analyzer.filter.1" : "lowercase",
"index.analysis.analyzer.ws_analyzer2.type" : "custom",
"index.analysis.analyzer.ws_analyzer2.char_filter.0" : "html_strip",
"index.analysis.analyzer.ws_analyzer2.tokenizer" : "whitespace",
"index.analysis.analyzer.ja_analyzer.type" : "custom",
"index.analysis.analyzer.ja_analyzer.char_filter.0" : "html_strip",
"index.analysis.analyzer.ja_analyzer.char_filter.1" : "kuromoji_neologd_iteration_mark",
"index.analysis.analyzer.ja_analyzer.tokenizer" : "ja_tokenizer",
"index.analysis.analyzer.ja_analyzer.filter.0" : "cjk_width",
"index.analysis.analyzer.ja_analyzer.filter.1" : "lowercase",
"index.analysis.analyzer.ja_analyzer.filter.2" : "kuromoji_neologd_stemmer",
"index.analysis.analyzer.ja_analyzer.filter.3" : "kuromoji_neologd_part_of_speech",
"index.analysis.analyzer.ruigi_analyzer.type" : "custom",
"index.analysis.analyzer.ruigi_analyzer.char_filter.0" : "html_strip",
"index.analysis.analyzer.ruigi_analyzer.char_filter.1" : "kuromoji_neologd_iteration_mark",
"index.analysis.analyzer.ruigi_analyzer.tokenizer" : "ja_tokenizer",
"index.analysis.analyzer.ruigi_analyzer.filter.0" : "cjk_width",
"index.analysis.analyzer.ruigi_analyzer.filter.1" : "lowercase",
"index.analysis.analyzer.ruigi_analyzer.filter.2" : "kuromoji_neologd_stemmer",
"index.analysis.analyzer.ruigi_analyzer.filter.3" : "kuromoji_neologd_part_of_speech",
"index.analysis.analyzer.ruigi_analyzer.filter.4" : "synonym"
},
"mappings" : {
"_default_" : {
"dynamic_templates" : [ {
"string_fields2" : {
"match_pattern" : "regex",
"mapping" : {
"type" : "string",
"fields" : {
"raw" : {
"index" : "not_analyzed",
"type" : "string"
},
"ws2" : {
"analyzer" : "ws_analyzer2",
"type" : "string"
}
}
},
"match_mapping_type" : "string",
"match" : "str_特殊なフィールド名.*"
}
}, {
"string_fields" : {
"match_pattern" : "regex",
"mapping" : {
"type" : "string",
"fields" : {
"raw" : {
"index" : "not_analyzed",
"type" : "string"
},
"facet" : {
"analyzer" : "facet_analyzer",
"type" : "string"
},
"ws" : {
"analyzer" : "ws_analyzer",
"type" : "string"
},
"ja" : {
"analyzer" : "ja_analyzer",
"type" : "string"
},
"ruigi" : {
"analyzer" : "ruigi_analyzer",
"type" : "string"
}
}
},
"match_mapping_type" : "string",
"match" : "str_.*"
}
}, {
"text_fields" : {
"mapping" : {
"type" : "string",
"fields" : {
"raw" : {
"index" : "not_analyzed",
"type" : "string"
}
}
},
"match_mapping_type" : "string",
"match" : "txt_.*"
}
}, {
"double_fields" : {
"match_mapping_type" : "double",
"mapping" : {
"type" : "double"
},
"match" :"dbl_.*"
}
}, {
"long_fields" : {
"match_mapping_type" : "long",
"mapping" : {
"type" : "long"
},
"match" :"l_.*"
}
}, {
"integer_fields" : {
"match_mapping_type" : "integer",
"mapping" : {
"type" : "integer"
},
"match" :"i_.*"
}
}, {
"boolean_fields" : {
"match_mapping_type" : "boolean",
"mapping" : {
"type" : "boolean"
},
"match" :"b_.*"
}
}, {
"date_fields" : {
"match_mapping_type" : "date",
"mapping" : {
"type" : "date",
"format": "yyyy-MM-dd hh:mm:ss"
},
"match" :"date_.*"
}
}, {
"timezone_fields" : {
"match_mapping_type" : "date",
"mapping" : {
"type" : "date",
"format": "hh:mm:ss"
},
"match" :"tz_.*"
}
} ],
"_all" : {"enabled" : false}
}
},
"aliases" : { }
}
投入するデータの型をカラム名で決める方式。途中でスキーマ変わるかもとか仕様が不確定な場合とか。
str_は文字列解析有り、txt_は文字列解析無し。
string_fields2はアルファベットの大文字小文字を区別したい場合用
NGRAMは検索精度が悪くなりすぎるので使いません。
refresh_interval設定はBulk転送時の負荷を下げるため。
elasticsearch.ymlでは設定できなくなったようなのでテンプレートに移動
反映待ち1分だと長過ぎる場合は30sとかに、単位付けないとms扱いらしい。デフォルト値は1s
*2.4以降から?5.0でindex系の設定がelasticsearch.ymlで設定できなくなった影響かも
情報元:https://github.com/elastic/elasticsearch/issues/20584
/etc/sysconfig/elasticsearch
MAX_LOCKED_MEMORY=unlimited
ES_HEAP_SIZE=8g
搭載メモリが16GBの例
ただし、8gだと丁度半分なので起動時にWARNINGが発生する。
*31gまでか搭載メモリの半分まで制限がある。
*ES_HEAP_NEWSIZEと両方指定するとCPU負荷が上がりっぱなしになる。
/etc/init.d/elasticsearch
で指定する例も見かけるが、現状それではヒープサイズはデフォルトのまま。
*「ps -aelf |grep elas」とかして-Xms,-Xmxとかの値で確認できる。
/etc/systemd/system/elasticsearch.service
CentOS系(6.8, 7.2で確認)ではbootstrap.memory_lock: trueのために追加でsystemdの設定が必要
cp /usr/lib/systemd/system/elasticsearch.service /etc/systemd/system/elasticsearch.service
雛形をコピーして編集(コメントアウトを外す)
LimitMEMLOCK=infinity
変更したら
systemctl daemon-reload
/etc/init.d/elasticsearch restart
検索のクエリ例(PHP)
$query = [
'query' => [
'function_score' => [
'query' => [
'filtered' => [
'query' => [
'bool' => [
'must' => [
[ 'query_string' => [
'query' => $検索文字列,
'default_field' => 'str_検索対象.ws'
]
]
]
]
],
'filter' => [
'bool' => [
'must' => [
[ 'term' => [ "$フラグ" => true ] ]
],
'must_not' => [
[ 'wildcard' => [ 'str_特殊なフィールド名.ws2' => $検索文字列 ] ]
]
]
]
]
],
'script_score' => [
"script" => "(float)_score * doc['dbl_score_coff'].value + doc['dbl_score_term'].value"
],
'boost_mode' => 'replace'
]
]
,'from' => 0
,'size' => 10
];
ポイントは
・function_scoreの中にfilteredクエリ。逆はエラー。
・script内の_scoreは前に(float)を付けないと結果が整数値になる
・wildcardは「検索対象と全く同じであること」という条件に使用している。
→matchでいい。matchだと0/1ではなく、query_stringで完全一致したときと同じようなスコアを得られる。
他
・/var/log/elasticsearchを追加ディスクのシンボリックリンクに変更する。
*ルートと同じディスクに置かない。
・query_stringの特殊文字対策(とりあえず削る)
$k = preg_replace("/\'/", ' ', $k);
$k = preg_replace("/\"/", ' ', $k);
$k = preg_replace("/\//", ' ', $k);
$k = preg_replace("/\{/", ' ', $k);
$k = preg_replace("/\}/", ' ', $k);
$k = preg_replace("/\(/", ' ', $k);
$k = preg_replace("/\)/", ' ', $k);
$k = preg_replace("/\[/", ' ', $k);
$k = preg_replace("/\]/", ' ', $k);
$k = preg_replace("/\~/", ' ', $k);
$k = preg_replace("/\^/", ' ', $k);
$k = preg_replace("/\$/", ' ', $k);
$k = preg_replace("/\+/", ' ', $k);
$k = preg_replace("/\./", ' ', $k);
$k = preg_replace("/\?/", ' ', $k);
$k = preg_replace("/\*/", ' ', $k);
$k = preg_replace("/:/", ' ', $k);
$k = preg_replace("/;/", ' ', $k);
$k = preg_replace("/,/", ' ', $k);
$k = preg_replace("/!/", ' ', $k);
$k = preg_replace("/ +/", ' ', $k);
$k = preg_replace("/\t+/", ' ', $k);
$k = preg_replace("/\s+/", ' ', $k);
$k = trim($k);
求む
・データを完全にメモリー上に置く設定
*path.dataをtmpfs上に置くと安定して早くなるが、データ量が少ない場合しか使えない。