一、常用术语
索引(Index)、类型(Type)、文档(Document)
- 索引Index是含有相同属性的文档集合。索引在ES中是通过一个名字来识别的,且必须是英文字母小写,且不含中划线(-);可类比于 MySQL 中的 database ;在 7.0中,由于类型(Type)的移除,我们可以理解为,一个索引就是一张 table。
- 一个索引中可以定义一个或多个类型Type,文档必须属于一个类型;可类比于 MySQL 中的 table;
- 文档Document是可以被索引的基本数据单位。文档是Elasticsearch中最小的数据存储单位。可类比于 MySQL 中 一个table 中的一行记录
注意事项:
从ES6.0开始,官方便不建议一个索引中创建多个类型;在ES7.0中,更是移除了类型(Type)这个概念。为什么呢?
在Elasticsearch索引中,不同类型(Type)中具有相同名称的字段在内部由相同的Lucene字段支持。一个index中多个Type在Lucene中会有许多问题。具体的可以参考官方说明:Removal of mapping types
节点Node、集群Cluster
- 节点:一个ES运行实例,是集群的的构成单元
- 集群:由1个(只有1个节点也是1个集群)或多个节点组成,对外提供服务
分片Shard(集群—提高吞吐与计算性能)、副本Replica(主从—提高可用性)
- 在ES中,每个索引都有多个分片,每个分片都是一个Lucene索引。假设一个索引的数据量很大,就会造成硬盘压力很大,同时,搜索速度也会出现瓶颈。我们可以将一个索引分为多个分片,从而分摊压力;分片同时还允许用户进行水平地扩展和拆分,以及分布式的操作,可以提高搜索以及其他操作的效率。
- 拷贝一份分片,就完成了分片的备份。备份的好处是,当一个主分片出现问题时,备份的分片就能代替工作,从而提高了ES的可用性。同时,备份的分片还可以执行搜索操作,以分摊搜索的压力。ES禁止同一个分片的主分片和副本分片在同一个节点上。
RESTful API
Elasticsearch 集群对外提供 RESTful API
- REST - REpresentational State Transfer
- URI 指定资源,如Index、Document等
- Http Method 指明资源操作类型,如GET、POST、PUT、DELETE等
倒排索引
-
正排索引
倒排索引组成
倒排索引是搜索引擎的核心,主要包含两部分:
- 单词词典(Term Dictionary)
- 记录所有文档的单词,一般都比较大
- 记录单词到倒排列表的关联信息
- 单词字典的实现一般是用B+Tree,能兼顾内存与磁盘性能,保障增删改查高效
- 倒排列表(Posting List)
- 倒排列表(Posting List)记录了单词对应的文档集合,由倒排索引项(Posting)组成
- 倒排索引项(Posting)主要包含如下信息:
- 文档ld,用于获取原始信息
- 单词频率(TF,Term Frequency),记录该单词在该文档中的出现次数,用于后续相关性算分
- 位置(Position),记录单词在文档中的分词位置(多个),用于做词语搜索(Phrase Query)
- 偏移(Offset),记录单词在文档的开始和结束位置,用于做高亮显示
ES中的倒排索引
es存储的是一个json格式的文档,其中包含多个字段,每个字段会有自己的倒排索引。
相关性算分
相关性算分是指文档与查询语句间的相关度,英文为 relevance
- 通过倒排索引可以获取与查询语句相匹配的文档列表,那么如何将最符合用户查询需求的文档放到前列呢?
- 本质是一个排序问题,排序的依据是相关性算分
相关性算分的几个重要概念
- Term Frequency(TF)词频,即单词在该文档中出现的次数。词频越高,相关度越高
- Document Frequency(DF)文档频率,即单词出现的文档数
- Inverse Document Frequency(IDF)逆向文档频率,与文档频率相反,简单理解为1/DF。即单词出现的文档数越少,相关度越高
- Field-length Norm 文档越短,相关性越高
使用 explain 参数查看具体的计算方法
- es的算分是按照shard进行的,即shard的分数计算是相互独立的,所以在使用explain的时候注意分片数
-
可以通过设置索引的分片数为1来避免这个问题
ES中的相关性算分模型
- TF/IDF 模型
- BM25 模型5.x之后的默认模型
TF/IDF 模型
BM25 模型
二、Document API
1. 文档是一个Json Object,由字段(Field)组成,常见数据类型如下:
- 字符串:text,keyword(不分词)
- 数值型:long,integer,short byte,double,float,half float,scaled_float
- 布尔:boolean
- 日期:date
- 二进制:binary
- 范围类型:integer_range,float_range,long_range,double_range,date_range
2. 文档元数据,用于标注文档的相关信息
- _index:文档所在的索引名
- _type:文档所在的类型名(7.0后默认_doc)
- _id:文档唯一id
- _uid:组合id,由_type和_id组成(6.x_type不再起作用,因此同_id值一样),默认禁用
- _source:文档的原始Json数据,可以从这里获取每个字段的内容
- _all:整合所有字段内容到该字段,默认禁用
3. 每个文档有唯一的_Id标识
- 自行指定
- es自动生成
4. 文档API
- es有专门的Document API,创建文档,查询文档,更新文档,删除文档
创建文档(创建文档时,如果索引不存在,es会自动创建对应的index和type)
- 指定id创建文档
PUT /test_index/_doc/1
{
"username":"zhangsan",
"age":1
}
- 不指定id创建文档
POST /test_index/_doc
{
"username":"lisi",
"sex":2
}
查询文档
- 指定要查询的文档id
GET /test_index/_doc/1
- 搜索所有文档,用到_search
GET /test_index/_search # GET /test_index/_doc/_search在高版本提示类型已过期,因此不用指定type了
批量增删改查文档
- ES允许一次创建多个文档,从而减少网络传输开销,提升写入速率,endpoint为_bulk
- index 用于创建文档,文档已存在则更细文档
- create 同样可以创建文档,文档已存在则返回错误
- delete 用于删除文档
- update 用于更新文档,文档不存在则返回错误
- 在es6.0之后的版本可以省略_type,官方已舍弃_type这个概念
POST _bulk
{"index":{"_index":"test_index","_id":"3"}}
{"username":"alfred","age":10}
{"create":{"_index":"test_index","_id":"3"}}
{"username":"alfred2","age":110}
{"delete":{"_index":"test_index","_id":"1"}}
{"update":{"_id":"2","_index":"test_index"}}
{"doc":{"age":"20"}}
返回:
{
"took" : 18, "errors" : true, "items" : [ { "index" : { "_index" : "test_index", "_type" : "_doc", "_id" : "3", "_version" : 2, "result" : "updated", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 4, "_primary_term" : 1, "status" : 200 } }, { "create" : { "_index" : "test_index", "_type" : "_doc", "_id" : "3", "status" : 409, "error" : { "type" : "version_conflict_engine_exception", "reason" : "[3]: version conflict, document already exists (current version [2])", "index_uuid" : "jjJIqT7QSeaYcOeWxxY-og", "shard" : "0", "index" : "test_index" } } }, { "delete" : { "_index" : "test_index", "_type" : "_doc", "_id" : "1", "_version" : 3, "result" : "not_found", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 5, "_primary_term" : 1, "status" : 404 } }, { "update" : { "_index" : "test_index", "_type" : "_doc", "_id" : "2", "status" : 404, "error" : { "type" : "document_missing_exception", "reason" : "[_doc][2]: document missing", "index_uuid" : "jjJIqT7QSeaYcOeWxxY-og", "shard" : "0", "index" : "test_index" } } } ] }
批量查询文档[跨索引]
- es允许一次查询多个索引的文档,endpoint为_mget
GET /_mget
{
"docs": [
{
"_index": "test_index",
"_id": 1
},
{
"_index": "test_index2",
"_id": 1
}
]
}
返回
{
"docs" : [
{ "_index" : "test_index", "_type" : "_doc", "_id" : "1", "found" : false }, { "_index" : "test_index2", "_type" : null, "_id" : "1", "error" : { "root_cause" : [ { "type" : "index_not_found_exception", "reason" : "no such index [test_index2]", "resource.type" : "index_expression", "resource.id" : "test_index2", "index_uuid" : "_na_", "index" : "test_index2" } ], "type" : "index_not_found_exception", "reason" : "no such index [test_index2]", "resource.type" : "index_expression", "resource.id" : "test_index2", "index_uuid" : "_na_", "index" : "test_index2" } } ] }
三、Indices APIs
1. 索引中一般存储具有相同结构的文档(Document)
- 每个索引都有自己的mapping定义,用于定义字段名和类型
- 创建索引不定义mapping,es将自动根据插入的数据定义mapping,但是通常不建议这样做,mapping相当于数据库建表时的表结构定义
- 1个索引中可以存储不同结构的文档,但在6.0后type的舍弃,官方建议1个index存储1中结构的文档
2. 一个集群(只有1个节点也是1个集群)可以有多个索引,比如:nginx 日志存储的时候可以按照日期每天生成一个索引来存储
- nginx-log-2017-01-01
- nginx-log-2017-01-02
- nginx-log-2017-01-03
3. 索引API
- es有专门的IndexAPI,用于查询、创建、更新、删除索引配置等
创建索引
PUT /test_index
查看所有索引
GET /_cat/indices
删除索引
DELETE /test_index
4. 索引模板
索引模板,英文为Index Template,主要用于在新建索引时自动应用预先设定的配置,简化索引创建的操作步骤
- 可以设定索引的配置和mapping
- 可以有多个模板,当创建的索引匹配到多个模板时,根据order设置,order大的覆盖小的配置
- 查看所有索引模板
- GET /_template
- 查看指定名称的索引模板
- GET /_template/test_template
- 删除指定名称的索引模板
- DELETE /_template/test_template
四、Analysis
分词是指将文本转换成一系列单词(term or token)的过程,也可以叫做文本分析,在es 里面称为Analysis,如下图所示:
在es中,分词会在如下两个时机使用:
-
创建或更新文档时,也称索引时(Index Time),会对相应的文档进行分词处理
-
查询时(Search Time),会对查询语句进行分词
一般不需要特别指定查询时分词器,直接使用索引时分词器即可(此时查询也会默认使用索引时分词器)
分词的使用建议:
- 明确字段是否需要分词,不需要分词的字段就将type设置为keyword,可以节省空间和提高写性能
- 善用_analyze API,查看文档的具体分词结果
- 动手测试
1. 分词器组成
分词器是es中专门处理分词的组件,英文为Analyzer,它的组成如下:
- Character Filters
- 针对原始文本进行处理,比如去除html特殊标记符
- Tokenizer
- 将原始文本按照一定规则切分为单词
- Token Filters
- 针对tokenizer处理的单词就行再加工,比如转小写、删除或新增等处理
2. es内置的分词器
es 自带如下的分词器:
- Standard
- Simple
- Whitespace
- Stop
- Keyword
- Pattern
- Language
Standard Analyzer
POST _analyze
{
"analyzer":"standard",
"text":"The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}
分词结果:
{
"tokens" : [
{ "token" : "the", "start_offset" : 0, "end_offset" : 3, "type" : "", "position" : 0 }, { "token" : "2", "start_offset" : 4, "end_offset" : 5, "type" : "", "position" : 1 }, { "token" : "quick", "start_offset" : 6, "end_offset" : 11, "type" : "", "position" : 2 }, { "token" : "brown", "start_offset" : 12, "end_offset" : 17, "type" : "", "position" : 3 }, { "token" : "foxes", "start_offset" : 18, "end_offset" : 23, "type" : "", "position" : 4 }, { "token" : "jumped", "start_offset" : 24, "end_offset" : 30, "type" : "", "position" : 5 }, { "token" : "over", "start_offset" : 31, "end_offset" : 35, "type" : "", "position" : 6 }, { "token" : "the", "start_offset" : 36, "end_offset" : 39, "type" : "", "position" : 7 }, { "token" : "lazy", "start_offset" : 40, "end_offset" : 44, "type" : "", "position" : 8 }, { "token" : "dog's", "start_offset" : 45, "end_offset" : 50, "type" : "", "position" : 9 }, { "token" : "bone", "start_offset" : 51,