11、ES实战:优化索引创建之mapping设置

####优化索引创建

一、_all

all字段是把所有其它字段中的值,以空格为分隔符组成一个大字符串,然后被分析和索引,但是不存储,也就是说它能被查询,但不能被取回显示。
_all 字段默认是关闭的,如果要开启 _all 字段,索引增大是不言而喻的。_all 字段开启适用于不指定搜索某一个字段,根据关键词,搜索整个文档内容。

开启_all 字段的方法,mapping 中的配置如下:

# 开启_all
{
   
     
   "yourtype": {
   
     
      "_all": {
   
     
         "enabled": true
      },
      "properties": {
   
     
            ... 
      }
   }
}

# 通过在字段中指定某个字段是否包含在_all中
{
   
     
   "yourtype": {
   
     
      "properties": {
   
     
         "field1": {
   
     
             "type": "string",
             "include_in_all": false
          },
          "field2": {
   
     
             "type": "string",
             "include_in_all": true
          }
      }
   }
}

如果要把字段原始值保存,要设置store属性为true,这样索引会更大,需要根据需求使用。下面给出测试代码。

# 设置mapping,这里设置所有字段都保存在_all中并且存储原始值
PUT test/test/_mapping
{
   
     
   "test": {
   
     
      "_all": {
   
     
         "enabled": true,
         "store": true
      }
   }
}

# 插入文档
POST test/test/1
{
   
     
    "title":"我是中国人",
    "content":"热爱共产党"
}

# 对_all字段进行搜索并高亮
POST test/_search
{
   
     
   "fields": ["_all"], 
   "query": {
   
     
      "match": {
   
     
         "_all": "中国人"
      }
   },
   "highlight": {
   
     
      "fields": {
   
     
         "_all": {
   
     }
      }
   }
}

# 返回结果
{
   
     
   "took": 3,
   "timed_out": false,
   "_shards": {
   
     
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
   
     
      "total": 1,
      "max_score": 0.15342641,
      "hits": [
         {
   
     
            "_index": "test",
            "_type": "test",
            "_id": "1",
            "_score": 0.15342641,
            "_all": "我是中国人 热爱共产党 ",
            "highlight": {
   
     
               "_all": [
                  "我是<em>中国人</em> 热爱共产党 "
               ]
            }
         }
      ]
   }
}

# Elasticsearch中的query_string和simple_query_string默认就是查询_all字段
GET test/_search
{
   
     
    "query": {
   
     
        "query_string": {
   
     
           "query": "共产党"
        }
    }
}

与此同时,_all能让你在不知道要查找的内容是属于哪个具体字段的情况下进行搜索,例如:

PUT my_index/user/1 
{
   
     
  "first_name":    "John",
  "last_name":     "Smith",
  "date_of_birth": "1970-10-24"
}

GET my_index/_search
{
   
     
  "query": {
   
     
    "match": {
   
     
      "_all": "john smith 1970"
    }
  }
}

_all 字段的内容为:”john smith 1970 10 24”

_all 字段其实就是一个字符串类型的字段,与其它普通字符串字段有相同的参数,包括:analyzer, term_vectors, index_options, and store。

_all 字段在查询时占用更多的CPU和占用更多的磁盘空间,如果确实不需要它可以完全的关闭它或者基于每字段定制。

二、_source

_source字段默认是存储的, 什么情况下不用保_source字段?如果某个字段内容非常多,业务里面只需要能对该字段进行搜索,最后返回文档id,查看文档内容会再次到mysql或者hbase中取数据,把大字段的内容存在Elasticsearch中只会增大索引,这一点文档数量越大结果越明显,如果一条文档节省几KB,放大到亿万级的量结果也是非常可观的。

source的总结:

1、 ES默认检索只会返回ID,如果在{“enabled”:false}情况下,你需通过根据这个ID去去倒排索引中去取每个Field数据,效率不高而反之,在{“enabled”:true}的情况下可以根据ID直接检索对应sourceJSON的字段,不用去倒排索引去按Field取数据;
2、 尽管_source非常有用,但它确实会占用索引的存储空间,所以也可以禁用(enabledfalse)、启用状态的压缩策略(compress);
压缩的策略有两类:

  • compress:是否进行压缩,建议一般情况下将其设为true 。
  • “includes” : [“author”, “name”], “excludes” : [“sex”] 。

如果想要关闭_source字段,在mapping中的设置如下:

{
   
     
    "yourtype":{
   
     
        "_source":{
   
     
            "includes":["field1","field2"]
        },
        "properties": {
   
     
            ... 
        }
    }
}

如果只想存储某几个字段的原始值到Elasticsearch,可以通过incudes参数来设置,在mapping中的设置如下:

{
   
     
    "yourtype":{
   
     
        "_source":{
   
     
            "includes":["field1","field2"]
        },
        "properties": {
   
     
            ... 
        }
    }
}

同样,可以通过excludes参数排除某些字段:

{
   
     
    "yourtype":{
   
     
        "_source":{
   
     
            "excludes":["field1","field2"]
        },
        "properties": {
   
     
            ... 
        }
    }
}

当设置mapping,禁用_source,返回结果的查询结果中不会再返回文档原始内容、

# _source禁用
PUT test/test/_mapping
{
   
     
   "test": {
   
     
      "_source": {
   
     
         "enabled": false
      }
   }
}

# 写入一条文档
POST test/test/1
{
   
     
    "title":"我是中国人",
    "content":"热爱共产党"
}

# 查询文档——搜索关键词”中国人”
GET test/_search
{
   
     
    "query": {
   
     
        "match": {
   
     
           "title": "中国人"
        }
    }
}

# 搜索关键词”中国人”:
GET test/_search
{
   
     
    "query": {
   
     
        "match": {
   
     
           "title": "中国人"
        }
    }
}
{
   
     
   "took": 9,
   "timed_out": false,
   "_shards": {
   
     
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
   
     
      "total": 1,
      "max_score": 0.30685282,
      "hits": [
         {
   
     
            "_index": "test",
            "_type": "test",
            "_id": "1",
            "_score": 0.30685282
         }
      ]
   }
}

三、index和store

index 和 store 属性实在字段内进行设置的,下面给出一个例子,设置 test 索引不保存 _source,title 字段索引但不分析,字段原始值写入索引,content 字段为默认属性。

store的总结:

1、 在不手动设置store的情况下,默认值为no;
2、 如果在**{“store”:yes}的情况下,ES会对该字段单独存储倒排索引,每次根据ID检索的时候,会多走一次IO来从倒排索引取数据而如果_sourceenabled情况下,ES可以直接根据Client类来解析_sourceJSON,只需一次IO就将所有字段都检索出来了;
3、 既然
{“store”:yes}**这么费力不讨好,但是仍然有两个应用场景:;

  • 文档很长,检索所有文档或者存储所有文档、获取所有field的代价比较大。
  • 仅仅针对某几个字段进行re-index的时候

代码如下:

# 设置index和store
PUT test/test/_mapping
{
   
     
   "test": {
   
     
      "_source": {
   
     
         "enabled": false
      },
      "properties": {
   
     
         "title": {
   
     
            "type": "string",
            "index": "not_analyzed",
            "store": "true"
         },
         "content": {
   
     
            "type": "string"
         }
      }
   }
}

# 对title字段进行搜索并高亮
GET test/_search
{
   
     
    "query": {
   
     
        "match": {
   
     
           "title": "我是中国人"
        }
    },
   "highlight": {
   
     
      "fields": {
   
     
         "title": {
   
     }
      }
   }
}

# 返回结果
{
   
     
   "took": 6,
   "timed_out": false,
   "_shards": {
   
     
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
   
     
      "total": 1,
      "max_score": 0.30685282,
      "hits": [
         {
   
     
            "_index": "test",
            "_type": "test",
            "_id": "1",
            "_score": 0.30685282,
            "highlight": {
   
     
               "title": [
                  "<em>我是中国人</em>"
               ]
            }
         }
      ]
   }
}

从返回结果中可以看到,虽然没有保存title字段到_source, 但是依然可以实现搜索高亮,store和source一样有保存原始文档的功能。

source 和 store 的对比总结
_source\store yes no
enabled store为yes的字段从倒排索引里检索,浪费IO次数 所有字段根据Client类解析实现存储的JSON串,仅需一次IO
disabled store为yes的字段从倒排索引里检索,其他字段能检索不能展示 所有字段只能检索,不能展示
Java APi设置示例
public class CreateIndex {
   
     

    public static void main(String[] args) {
   
     
        try {
   
     
            /* 创建客户端 */
            // client startup
            // 设置集群名称
            Settings settings = Settings.builder()
                                .put("cluster.name","elsearch")
                                .put("client.transport.sniff", true)
                                .build();
            // 创建client
            TransportClient client = new 
                            PreBuiltTransportClient(settings)
                .addTransportAddress(new InetSocketTransportAddress
                (InetAddress.getByName("10.122.4.71"), 9300));

            //创建索引
            client.admin().indices()
                .prepareCreate("pointdata2").execute().actionGet();

            //创建索引结构
            XContentBuilder builder = null;
            builder = XContentFactory.jsonBuilder()
                .startObject()
                    .startObject("pointdata2")
                        .startObject("_all")
                            //关闭_all字段
                            .field("enabled", false)
                        .endObject()
                        .startObject("_source")
                            //关闭_source字段
                            .field("enabled", false)
                        .endObject()
                        //properties:列出了文档中可能包含的每个字段的映射
                        .startObject("properties")
                            .startObject("pointid")
                                .field("type", "string")
                                .field("index", "not_analyzed")
                                .field("store", true)
                            .endObject()
                            .startObject("pointvalue")
                                .field("type", "string")
                                .field("index", "not_analyzed")
                            .endObject()
                            .startObject("inputtime")
                                .field("type", "date")
                                .field("format", "yyyy-MM-dd HH:mm:ss")
                                .field("index", "not_analyzed")
                            .endObject()
                        .endObject()
                    .endObject()
                .endObject();

            PutMappingRequest mapping = Requests
                                .putMappingRequest("pointdata2")
                                .type("pointdata2")
                                .source(builder);
            client.admin().indices().putMapping(mapping).actionGet();
            System.out.println("创建成功!");
        } catch (Exception e) {
   
     
            e.printStackTrace();
        }
    }
}

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: