ElasticSearch系列--索引与类型

简介

索引 Index

        单词对应:索引 index;分片 shard;类型 Type;索引集 indices index的复数方式

        索引存储在一组分片中,这些分片本身就是 Lucene 索引集。 由此可见始终使用新索引的限制:Lucene索引集在磁盘空间、内存使用情况和使用的文件描述符方面具有较小但固定的开销。 出于这个原因,一个单一的大型索引比几个小型索引更有效率:Lucene 索引集的固定成本在许多文件中被更好地分摊。

        另一个重要因素是您计划如何搜索数据。 当每个分片独立搜索时,Elasticsearch 最终需要合并来自所有搜索到的分片的结果。 例如,如果您搜索 10 个索引,每个索引有 5 个分片,协调搜索请求执行的节点将需要合并 5x10 = 50 个分片结果。 在这里,您还需要小心:如果有太多的分片结果要合并和/或如果您运行产生大分片响应的大量请求(当使用聚合的时候这可能很容易发生),合并所有这些分片结果的任务可能会变为 非常耗费资源,无论是CPU 还是内存。 这又会提倡减少索引集。

类型 Type

        类型是将多种类型的数据存储在相同索引中的一种便捷方式,为了保持上面讲述的原因而降低索引总数。 在实现方面,它通过在搜索特定类型过滤的文档中添加 “_type” 字段用于进行过滤选择。 类型的一个很好的属性是,与搜索单个类型相比,在相同索引下多个类型的中搜索并不会增加开销:它不会更改需要合并多少个分片结果。

类型存在一些限制

  1. 同一个索引下的类型中的字段必须保持同样的类型,这是因为同一个索引下的类型在底层保存是共享的。
  2. 在某个类型中的字段,在不存在该字段的其他类型中,仍然需要占据资源。这是因为 Lucene 的索引集不喜欢稀疏性的问题,主要是考虑反向索引在查询中的效率。也就说说在文档中即使相关的字段不存在,但是在进行底层存储的时候仍然需要占据对应的字节资源。
  3. Score 的统计是在索引级别的,在同一个类型的中的文档查询中的 Score 可能会被该索引下的其他的类型所影响。

因此说类型的作用可能仅仅会在索引中类型的 Mapping 比较相似的时候比较有作用。但是,事实是在文档中的不存在的字段仍然会占据资源的事实,在数据保存在单独的索引中可能会更加严重。

至于是选择每个索引一个类型,还是每个索引多个类型,取决于我们的使用场景和硬件资源。例如如果我们将五个类型放入了一个索引中,我们也可以创建一个5个索引每个创建一个主分片。

以下问题可以帮助我们选择:

  1. 如果使用了 parent/child 模型?如果是,那么只能通过相同索引下的两种类型来实现
  2. 如果文档具备相类似的 mapping?如果不是,则采用不同的索引
  3. 每种类型是否还有很多文档,如果是的话 Lucene 可以很容易处理这种问题,可以放心使用索引集,设置比默认值 5 更小的分区
  4. 否则你可以考虑将不同类型的文档放入到相同的索引中,甚至是同一种类型中

移除Mapping/type

其他网址

ElasticSearch 中的索引与类型的前生今世 - 程序印象
Elasticsearch 参考指南(删除映射类型)_风继续吹 - SegmentFault 思否

英文官网说明(6.0)

简介

问题复现

《从Lucene到Elasticsearch:全文检索实战》=> 5.3.4 静态映射

运行示例的程序时报错:Rejecting mapping update to [my_index] as the final mapping would have more than 1 type: [blogpost, user]"

类型映射

        类型映射类似于数据表的 Schema, 提供了在索引中数据如何被保存和索引。

        从Elasticsearch的第一个发行版开始,每一个文档都会被存储在一个单独的索引中,并且配以一个单独的映射类型。一个映射类型被用来表示被索引的文档或者实体的类型,比如一个twitter索引可能会有一个user类型和一个tweet类型。 每一个映射类型都可以有其自身的字段,所以user类型可能有一个full_name字段,一个user_name字段和一个email字段,而tweet类型可能会包含一个content字段,一个tweeted_at字段,以及与user类型中类似的user_name字段。

        _type字段的值会与文档的_id字段的值组合起来生成_uid字段,所以具有相同_id的不同类型的多个文档可以共存于一个索引中。每个文档都有一个_type元字段用来保存类型名,搜索可以通过在URL中指定类型名将搜索限定于一个或多个类型中:

GET twitter/user,tweet/_search
{
    "query": {
        "match": {
            "user_name": "kimchy"
        }
    }
}

移除计划

移除Mapping将会是一个比较大的变化,因此 Elasticsearch 也在尽量少影响用户的前提下,做了一些功能的规划:

Elasticsearch 5.6.0

  • 在索引上设置index.mapping.single_type: true将启用在6.0中强制执行的单类型/索引行为。
  • 5.6中创建的索引中可以使用父—子join字段替换。

Elasticsearch 6.x

  • 在5.x中创建索引将继续在6.x中运行就像5.x。
  • 索引在6.x中创建只允许每个索引使用单一类型,类型可以使用任何名称,但只能有一个,首选的类型名称是_doc,因此索引API具有与7.0中相同的路径:PUT {index}/_doc/{id} and POST {index}/_doc。
  • _type名称不能再与_id组合以形成_uid字段,_uid字段已成为_id字段的别名。 新的索引不再支持旧式的父/子索引,而是应该使用join字段。
  • _default_映射类型已弃用。
  • 6.8:索引创建、索引模板和映射API支持查询字符串参数include_type_name,该参数指示请求和响应是否应该包含类型名称。它默认为true,应该设置为一个显式值,以便准备升级到7.0,未设置include_type_name将导致一个弃用警告,没有显式类型的索引将使用虚拟类型名称_doc。

Elasticsearch 7.x

  • 删除_default_映射类型会中断对索引创建、put映射、get映射、put模板、get模板和get字段映射API的更改。
  • 在请求中指定类型已弃用。例如:索引文档不再需要文档type。对于显式id,新的索引API是PUT {index}/_doc/{id};对于自动生成的id则是POST {index}/_doc。在7.x中,_doc是路径的一个永久部分,它表示端点名称,而不是文档类型。
  • 索引创建、索引模板和映射API中的include_type_name参数默认为false,完全设置该参数将导致一个弃用警告。

Elasticsearch 8.x

  • 不再支持在请求中指定类型。
  • 删除include_type_name参数。

移除原因

        开始的时候,我们说”索引(index)”类似于SQL数据库中的”数据库”,将”类型(type)”等同于”表”。

        这是一个糟糕的类比,并且导致了一些错误的假设。在SQL数据库中,表之间是相互独立的。一个表中的各列并不会影响到其它表中的同名的列。而在映射类型(mappingtype)中却不是这样的。在同一个Elasticsearch索引中,其中不同映射类型中的同名字段在内部是由同一个Lucene字段来支持的。

        使用上面的例子,user类型中的user_name字段与tweet类型中的user_name字段是完全一样的,并且两个user_name字段在两个类型中必须具有相同的映射(定义)。这会在某些情况下导致一些混乱,如下:

  • 在同一个索引中,当你想在其中的一个类型中将deleted字段作为date类型,而在另一个类型中将其作为boolean字段。
  • 在此之上考虑一点,如果同一个索引中存储的各个实体如果只有很少或者根本没有同样的字段,这种情况会导致稀疏数据,并且会影响到Lucene的高效压缩数据的能力。

        基于这些原因,将映射类型的概念从Elasticsearch中移除。

5.x、6.x、7.x实例

其他网址

ElasticSearch 7.x 默认不在支持指定索引类型_周天祥的博客-CSDN博客

5.x

 以下代码在5.x是成功运行的。

put es_test
{
    "settings":{    
    "number_of_shards" : 3,   
    "number_of_replicas" : 0    
    },    
     "mappings":{    
      "books1":{     
        "properties":{        
            "title":{"type":"text"},
            "name":{"type":"text","index":false},
            "publish_date":{"type":"date","index":false},           
            "price":{"type":"double"},           
            "number":{
                "type":"object",
                "dynamic":true
            }
        }
      },
	  "books2":{     
        "properties":{        
            "title":{"type":"text"},
            "name":{"type":"text","index":false},
            "publish_date":{"type":"date","index":false},           
            "price":{"type":"double"},           
            "number":{
                "type":"object",
                "dynamic":true
            }
        }
      }
     }
}

6.x

上边5.x代码在6.x无法运行,因为6.x一个索引只能有一种类型。所以,要删掉一种:

put es_test
{
    "settings": {
        "number_of_shards": 3,
        "number_of_replicas": 0
    },
    "mappings": {
        "books": {
            "properties": {
                "title": {
                    "type": "text"
                },
                "name": {
                    "type": "text",
                    "index": false
                },
                "publish_date": {
                    "type": "date",
                    "index": false
                },
                "price": {
                    "type": "double"
                },
                "number": {
                    "type": "object",
                    "dynamic": true
                }
            }
        }
    }
}

7.x

上边6.x代码在7.x无法运行,因为6.x一个索引只能有一种类型。所以,要删掉类型的指定:

put es_test
{
    "settings": {
        "number_of_shards": 3,
        "number_of_replicas": 0
    },
    "mappings": {
        "properties": {
            "title": {
                "type": "text"
            },
            "name": {
                "type": "text",
                "index": false
            },
            "publish_date": {
                "type": "date",
                "index": false
            },
            "price": {
                "type": "double"
            },
            "number": {
                "type": "object",
                "dynamic": true
            }
        }
    }
}

默认索引类型是_doc,如果想改变,则配置include_type_name: true 即可(这个没有测试,官方文档说的,无论是否可行,建议不要这么做,因为elasticsearch8后就不在提供该字段)。官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/removal-of-types.html

映射类型的替代方案

1. 每种文档类型一个索引

        第一种选择就是每个文档类型对应一个索引。你可以不将tweets和users存储于同一个索引,而将它们分别存储于tweets索引和users索引中。索引之间是完全相互独立的,不同索引中的(同名的)字段类型也就不会产生冲突了。

这种方式有两个好处:

  1. 数据更倾向于密集(而不是稀疏),这样就能获益于Lucene的压缩技术;
  2. 因为同一个索引中的所有的文档代表同一种实体,用于为全文搜索打分的条件统计会更精确

每个索引可以依据其可能的文档存储量级来设置相关的配置:可以对users使用较少的主分片,同时对tweets使用较大数量的主分片。

2. 自定义类型字段

        当然,一个集群中可以创建的主分片的数量是有限制的,所以你可能不想为一个只有几千个文档的集合去浪费一整个分片。这种情况下你可以使用你自己定义的type字段,它看起来和原来的_type工作机制类似。 我们继续使用上面的user/tweet例子。原来的工作流程可能像下面这样:

PUT twitter
{
    "mappings": {
        "user": {
            "properties": {
                "name": {
                    "type": "text"
                },
                "user_name": {
                    "type": "keyword"
                },
                "email": {
                    "type": "keyword"
                }
            }
        },
        "tweet": {
            "properties": {
                "content": {
                    "type": "text"
                },
                "user_name": {
                    "type": "keyword"
                },
                "tweeted_at": {
                    "type": "date"
                }
            }
        }
    }
}
PUT twitter/user/kimchy
{
    "name": "Shay Banon",
    "user_name": "kimchy",
    "email": "[email protected]"
}
PUT twitter/tweet/1
{
    "user_name": "kimchy",
    "tweeted_at": "2017-10-24T09:00:00Z",
    "content": "Types are going away"
}
GET twitter/tweet/_search
{
    "query": {
        "match": {
            "user_name": "kimchy"
        }
    }
}

自定义type的写法:显式type字段替代隐式_type字段

PUT twitter
{
    "mappings": {
        "doc": {
            "properties": {
                "type": {
                    "type": "keyword"
                },
                "name": {
                    "type": "text"
                },
                "user_name": {
                    "type": "keyword"
                },
                "email": {
                    "type": "keyword"
                },
                "content": {
                    "type": "text"
                },
                "tweeted_at": {
                    "type": "date"
                }
            }
        }
    }
}
PUT twitter/doc/user-kimchy
{
    "type": "user",
    "name": "Shay Banon",
    "user_name": "kimchy",
    "email": "[email protected]"
}
PUT twitter/doc/tweet-1
{
    "type": "tweet",
    "user_name": "kimchy",
    "tweeted_at": "2017-10-24T09:00:00Z",
    "content": "Types are going away"
}
GET twitter/_search
{
    "query": {
        "bool": {
            "must": {
                "match": {
                    "user_name": "kimchy"
                }
            },
            "filter": {
                "match": {
                    "type": "tweet"
                }
            }
        }
    }
}

3. 在没有映射类型的情况下实现父子关系

        先前,我们通过将一个映射类型指定为父,另一个或多个映射类型为子来表示父子关系。在没有类型的情况下,我们就不能使用这种语法了。父子关系的特征会向之前那样工作,不同之处在于文档之间这种关系的表示方式变成了使用新的join字段。

多类型索引迁移到单类型索引

Reindex API可用于将多类型索引转换为单类型索引,下面的例子可以在Elasticsearch 5.6或Elasticsearch 6.x中使用。在6.x,不需要指定index.mapping.single_type,它默认值是true。

用以下两种方法处理之后,使用Logstash将处理后的索引数据迁移至高版本Elasticsearch 6.x实例中。

法1:拆分type

将Elasticsearch 5.x实例中的单索引多type数据,按照不同的type,通过reindex拆分成多个单索引单type数据的方式。

比如:将twitter索引拆分为tweets索引和users索引

PUT users {
    "settings": {
        "index.mapping.single_type": true
    },
    "mappings": {
        "_doc": {
            "properties": {
                "name": {
                    "type": "text"
                },
                "user_name": {
                    "type": "keyword"
                },
                "email": {
                    "type": "keyword"
                }
            }
        }
    }
}
PUT tweets {
    "settings": {
        "index.mapping.single_type": true
    },
    "mappings": {
        "_doc": {
            "properties": {
                "content": {
                    "type": "text"
                },
                "user_name": {
                    "type": "keyword"
                },
                "tweeted_at": {
                    "type": "date"
                }
            }
        }
    }
}
POST _reindex {
    "source": {
        "index": "twitter",
        "type": "user"
    },
    "dest": {
        "index": "users"
    }
}
POST _reindex {
    "source": {
        "index": "twitter",
        "type": "tweet"
    },
    "dest": {
        "index": "tweets"
    }
}

法2:合并type

将Elasticsearch 5.x实例中的单索引多type数据,通过reindex script方式合并成一个单索引单type数据。

PUT new_twitter
{
  "mappings": {
    "_doc": {
      "properties": {
        "type": {
          "type": "keyword"
        },
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        },
        "content": {
          "type": "text"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}


POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter"
  },
  "script": {
    "source": """
      ctx._source.type = ctx._type;
      ctx._id = ctx._type + '-' + ctx._id;
      ctx._type = '_doc';
    """
  }
}

7.0中的无类型API

在Elasticsearch 7.0中,每个API都支持无类型请求,指定类型将产生一个弃用警告。

即使目标索引包含自定义类型,无类型API也可以工作,例如,如果索引具有自定义类型名称my_type,则可以使用无类型index调用向其添加文档,并使用无类型get调用加载文档。

索引API

        索引创建、索引模板和映射API支持一个新的URL参数(include_type_name),该参数指定请求和响应中的映射定义是否应该包含类型名称,版本6.8中的参数默认为true,以匹配在映射中使用类型名称的7.0之前的行为,它在7.0版本中默认为false,将在8.0版本中删除。

        它应该在6.8中明确设置,以便准备升级到7.0,为了避免6.8中的弃用警告,可以将参数设置为true或false,在7.0中,设置include_type_name将导致一个弃用警告。

        查看一些与Elasticsearch交互的例子,这个选项设置为false:映射直接包含在mappings键下,没有类型名称。

PUT index?include_type_name=false
{
  "mappings": {
    "properties": { 
      "foo": {
        "type": "keyword"
      }
    }
  }
}
PUT index/_mappings?include_type_name=false
{
  "properties": { 
    "bar": {
      "type": "text"
    }
  }
}
GET index/_mappings?include_type_name=false

上面的调用返回:

{
  "index": {
    "mappings": {
      "properties": { 
        "foo": {
          "type": "keyword"
        },
        "bar": {
          "type": "text"
        }
      }
    }
  }
}

猜你喜欢

转载自blog.csdn.net/feiying0canglang/article/details/111796958
今日推荐