elasticsearch学习笔记(三十五)——Elasticsearch 索引管理
索引的基本操作创建索引PUT /{index} { settings: {}, mappings: { properties: { } } }创建索引示例PUT /test_index { settings: { number_of_replicas: 1, number_of_shards: 5 }, mappings: { properties: { field1: { type: text, fields: { keyword: { type: keyword } } }, ctime: { type: date } } } }修改索引PUT /{index}/_settings { setttings: {} } PUT /test_index/_settings { settings: { number_of_replicas: 2 } }删除索引DELETE /{index}删除索引API也可以通过使用逗号分隔列表应用于多个索引或者通过使用_all或*作为索引应用于所有索引小心。要禁用允许通过通配符删除索引或者将配置中的_all设置action.destructive_requires_name设置为true。也可以通过群集更新设置api更改此设置。修改分词器以及定义自己的分词器Elasticsearch附带了各种内置分析器无需进一步配置即可在任何索引中使用standard analyzer: 所述standard分析器将文本分为在字边界条件由Unicode的文本分割算法所定义的。它删除了大多数标点符号小写术语并支持删除停用词。 Simple analyzer: 该simple分析仪将文本分为方面每当遇到一个字符是不是字母。然后全部变为小写 whitespace analyzer: whitespace只要遇到任何空格字符 分析器就会将文本划分为术语。它不会进行小写转换。 stop analyzer: 该stop分析器是像simple而且还支持去除停止词。 keyword analyzer: 所述keyword分析器是一个“空操作”分析器接受任何文本它被赋予并输出完全相同的文本作为一个单一的术语也就是不会分词进行精确匹配。 pattern analyzer: 所述pattern分析器使用一个正则表达式对文本进行拆分。它支持小写转换和停用字。 language analyzer: Elasticsearch提供了许多特定于语言的分析器如english或 french。 fingerprint analyzer: 所述fingerprint分析器是一种专业的指纹分析器它可以创建一个指纹用于重复检测。修改分词器的设置启动english停用词token filterPUT /my_index { settings: { analysis: { analyzer: { es_std: { type: standard, stopwords: _english_ } } } } }GET /my_index/_analyze { analyzer: standard, text: a dog is in the house } { tokens : [ { token : a, start_offset : 0, end_offset : 1, type : ALPHANUM, position : 0 }, { token : dog, start_offset : 2, end_offset : 5, type : ALPHANUM, position : 1 }, { token : is, start_offset : 6, end_offset : 8, type : ALPHANUM, position : 2 }, { token : in, start_offset : 9, end_offset : 11, type : ALPHANUM, position : 3 }, { token : the, start_offset : 12, end_offset : 15, type : ALPHANUM, position : 4 }, { token : house, start_offset : 16, end_offset : 21, type : ALPHANUM, position : 5 } ] }GET /my_index/_analyze { analyzer: es_std, text: a dog is in the house } { tokens : [ { token : dog, start_offset : 2, end_offset : 5, type : ALPHANUM, position : 1 }, { token : house, start_offset : 16, end_offset : 21, type : ALPHANUM, position : 5 } ] }定制自己的分词器PUT /test_index { settings: { analysis: { char_filter: { _to_and: { type: mapping, mappings: [and] } }, filter: { my_stopwords: { type: stop, stopwords: [the, a] } }, analyzer: { my_analyzer: { type: custom, char_filter: [html_strip, _to_and], tokenizer: standard, filter: [lowercase, my_stopwords] } } } } }GET /test_index/_analyze { text: tomjerry are a friend in the house, a, HAHA!!, analyzer: my_analyzer } { tokens : [ { token : tomandjerry, start_offset : 0, end_offset : 9, type : ALPHANUM, position : 0 }, { token : are, start_offset : 10, end_offset : 13, type : ALPHANUM, position : 1 }, { token : friend, start_offset : 16, end_offset : 22, type : ALPHANUM, position : 3 }, { token : in, start_offset : 23, end_offset : 25, type : ALPHANUM, position : 4 }, { token : house, start_offset : 30, end_offset : 35, type : ALPHANUM, position : 6 }, { token : haha, start_offset : 42, end_offset : 46, type : ALPHANUM, position : 7 } ] }定制化自己的dynamic mapping策略dynamic参数true: 遇到陌生字段就进行dynamic mappingfalse: 遇到陌生字段就忽略strict: 遇到陌生字段就报错举例PUT /test_index { mappings: { dynamic: strict, properties: { title: { type: text }, address: { type: object, dynamic: true } } } }PUT /test_index/_doc/1 { title: my article, content: this is my article, address: { province: guangdong, city: guangzhou } } { error: { root_cause: [ { type: strict_dynamic_mapping_exception, reason: mapping set to strict, dynamic introduction of [content] within [_doc] is not allowed } ], type: strict_dynamic_mapping_exception, reason: mapping set to strict, dynamic introduction of [content] within [_doc] is not allowed }, status: 400 }PUT /test_index/_doc/1 { title: my article, address: { province: guangdong, city: guangzhou } } { _index : test_index, _type : _doc, _id : 1, _version : 1, result : created, _shards : { total : 2, successful : 1, failed : 0 }, _seq_no : 0, _primary_term : 1 }date_detectionelasticsearch默认会按照一定格式识别date比如yyyy-MM-dd。但是如果某个field先过来一个2017-01-01的值就会被自动dynamic mapping成date后面如果再来一个hello world之类的值就会报错。此时的解决方案是可以手动关闭某个type的date_detention,如果有需要自己手动指定某个field为date类型。PUT /{index} { mappings: { date_detection: false } }dynamic templatedynamic_templates: [ { my_template_name: { ... match conditions ... mapping: {...} } } ]示例PUT /test_index { mappings: { dynamic_templates: [ { en: { match: *_en, match_mapping_type: string, mapping: { type: text, analyzer: english } } } ] } }PUT /test_index/_doc/1 { title: this is my first article } PUT /test_index/_doc/2 { title_en: this is my first article }GET /test_index/_mapping { test_index : { mappings : { dynamic_templates : [ { en : { match : *_en, match_mapping_type : string, mapping : { analyzer : english, type : text } } } ], properties : { title : { type : text, fields : { keyword : { type : keyword, ignore_above : 256 } } }, title_en : { type : text, analyzer : english } } } } }GET /test_index/_search?qis { took : 12, timed_out : false, _shards : { total : 1, successful : 1, skipped : 0, failed : 0 }, hits : { total : { value : 1, relation : eq }, max_score : 0.2876821, hits : [ { _index : test_index, _type : _doc, _id : 1, _score : 0.2876821, _source : { title : this is my first article } } ] } }此时title没有匹配到任何的dynamic模板默认就是standard分词器不会过滤停用词is会进入倒排索引用is来搜索就可以搜索到。而title_en匹配到了dynamic模板就是english分词器会过滤停用词is这种停用词就会被过滤掉用is来搜索就搜索不到了。基于scollbulk索引别名实现零停机重建索引1、重建索引一个field的设置是不能被修改的如果要修改一个field,那么应该重新按照新的mapping建立一个index,然后将数据批量查询出来重新用bulk api写入index中批量查询的时候建议采用scroll api并且采用多线程并发的方式来reindex数据每次scroll就查询指定日期的一段数据交给一个线程即可。1一开始依靠dynamic mapping插入数据但是不小心有些数据是2017-01-01这种日期格式的所以title的这种field被自动映射为了date类型实际上它应该是string类型。PUT /test_index/_doc/1 { title: 2017-01-01 } GET /test_index/_mapping { test_index : { mappings : { properties : { title : { type : date } } } } }2当后期向索引中加入string类型的title值的时候就会报错PUT /test_index/_doc/2 { title: my first article } { error: { root_cause: [ { type: mapper_parsing_exception, reason: failed to parse field [title] of type [date] in document with id 2 } ], type: mapper_parsing_exception, reason: failed to parse field [title] of type [date] in document with id 2, caused_by: { type: illegal_argument_exception, reason: failed to parse date field [my first article] with format [strict_date_optional_time||epoch_millis], caused_by: { type: date_time_parse_exception, reason: Failed to parse with all enclosed parsers } } }, status: 400 }3如果此时想修改title的类型是不可能的PUT /test_index { mappings: { properties: { title: { type: text } } } } { error: { root_cause: [ { type: resource_already_exists_exception, reason: index [test_index/mZALkQ8IQV67SjCVqkhq4g] already exists, index_uuid: mZALkQ8IQV67SjCVqkhq4g, index: test_index } ], type: resource_already_exists_exception, reason: index [test_index/mZALkQ8IQV67SjCVqkhq4g] already exists, index_uuid: mZALkQ8IQV67SjCVqkhq4g, index: test_index }, status: 400 }4此时唯一的办法就是reindex也就是说重新建立一个索引将旧索引的数据查询出来在导入新索引5如果说旧索引的名字是old_index新索引的名字是new_index终端应用已经在使用old_index进行操作了难到还要去终止应用修改使用的index为new_index在重新启动应用吗6所以说此时应该采用别名的方式给应用一个别名这个别名指向旧索引应用先用着。指向的还是旧索引格式POST /_aliases { actions: [ { add: { index: test, alias: alias1 } } ] }POST /_aliases { actions: [ { add: { index: test_index, alias: test_index_alias } } ] }7新建一个index,调整title为stringPUT /test_index_new { mappings: { properties: { title: { type: text } } } }8使用scroll api将数据批量查询出来GET /test_index/_search?scroll1m { query: { match_all: {} }, sort: [ _doc ], size: 1 } { _scroll_id : DXF1ZXJ5QW5kRmV0Y2gBAAAAAAACz3UWUC1iLVRFdnlRT3lsTXlFY01FaEFwUQ, took : 8, timed_out : false, _shards : { total : 1, successful : 1, skipped : 0, failed : 0 }, hits : { total : { value : 1, relation : eq }, max_score : null, hits : [ { _index : test_index, _type : _doc, _id : 1, _score : null, _source : { title : 2017-01-01 }, sort : [ 0 ] } ] } }POST /_search/scroll { scroll_id: DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAC0GYWUC1iLVRFdnlRT3lsTXlFY01FaEFwUQ, scroll: 1m } { _scroll_id : DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAC0GYWUC1iLVRFdnlRT3lsTXlFY01FaEFwUQ, took : 14, timed_out : false, _shards : { total : 1, successful : 1, skipped : 0, failed : 0 }, hits : { total : { value : 1, relation : eq }, max_score : null, hits : [ ] } }9采用bulk api将scroll查出来的一批数据批量写入新索引POST /_bulk {index: {_index: test_index_new, _id: 1}} {title: 2017-01-01} GET /test_index_new/_search { took : 1, timed_out : false, _shards : { total : 1, successful : 1, skipped : 0, failed : 0 }, hits : { total : { value : 1, relation : eq }, max_score : 1.0, hits : [ { _index : test_index_new, _type : _doc, _id : 1, _score : 1.0, _source : { title : 2017-01-01 } } ] } }10反复循环查询一批又一批的数据出来再批量写入新索引11将test_index_alias切换到test_index_new上面去应用会直接通过index别名使用新的索引中的数据应用不需要停机高可用POST /_aliases { actions : [ { remove : { index : test_index, alias : test_index_alias } }, { add : { index : test_index_new, alias : test_index_alias } } ] } GET /test_index_alias/_search { took : 0, timed_out : false, _shards : { total : 1, successful : 1, skipped : 0, failed : 0 }, hits : { total : { value : 1, relation : eq }, max_score : 1.0, hits : [ { _index : test_index_new, _type : _doc, _id : 1, _score : 1.0, _source : { title : 2017-01-01 } } ] } }2、基于alias对client客户端透明切换index格式POST /_aliases { actions : [ { remove : { index : test1, alias : alias1 } }, { add : { index : test2, alias : alias1 } } ] }注意actions里面的json一定不要换行否则无法解析会报错