index 相当于传统数据库的 database,类似于上图中的 Goods 其中就包含了所有的商品。
type 相当于传统数据库的 table,商品中可能包含电子产品和食品。
document 是 es 存储数据的基本单位,相当于传统数据库的行数据。
field 是数据的域,相当于属性。不同 type 的 document ,他们的域类似,但不完全相同。比如食品比电子产品多了一个保质期的 field。
_id 便是 document 的唯一标识,在 index 和 type 下唯一,可以自定义也可以自动生成。
document 创建与替换
document 的创建和更新都是通过唯一标识 _id 来判断。
PUT /_index/_type/_id
如果 _id 不存在,那么就是创建;反之,就是更新。
document 是不可变的,如果要更新其中的内容,第一种方式就是全量替换,直接对 document 重新建立索引,替换其中所有的内容。被替换的 document 不会被立刻删除,而是被 es 标记成 deleted ,es 会在适当的时机自动删除被标记的 document。
因为创建和更新的语法都是相同的,那么如果此时我们仅需要创建一个 document ,在已经存在的情况下也不需要替换,那么我们可以选择使用 _create 指定不需要覆盖。
PUT /_index/_type/_id/_create
partial update
同理,如果只需要更新不需要创建,则可以使用第二种方式,使用 _update 进行 partial update 指定不需要创建。
PUT /_index/_type/_id/_update
partial update 原理与全量替换基本一致:
但是只需要传递部分的 field 到 es,es 会在内部获取 document,然后替换掉被更新的 field,最后将新的 document创建出来,并将被替换的 document 标记为 deleted。
partial update 将所有的操作都聚集在同一个 shard 内部,既减少了网络传输的开销,又减少了查询与修改的时间间隔,可以有效减少并发冲突。
document 删除
DELECT /_index/_type/_id
bulk 批量增删改 API
POST /_index/_bulk
{"action": {"meta"}}\n
{"data"}\n
{"action": {"meta"}}\n
{"data"}\n
为什么 bulk 要使用这样奇特的 json 格式?
bulk 中的每个操作可能要转发到不同的 node 的 shard 中执行。所以请求到达 es 后,如果采用标准的 json 格式,es 则需要花耗费更多的开销来将其解析成对象用于分离,还会增加 jvm gc 的开销。
使用这种格式的 json,无需将其转换成 json 对象,直接按照换行符切割,两行一组。然后读取 meta,进行 document 路由,直接将 json 发送到 node 上。
document 检索
GET /_index/_type/_id
分页
GET /_search?size=10&from=0
分布式分页需要使用协调节点来聚合并返回结果,请避免使用 deep paging,否则会带来严重的性能问题。
multi-index/type
GET /_index1,index2/type1,type2/_search
Query DSL
GET /_index/_type/_search
{
"query": {
"match_all": {}
}
}
mget 批量查询 API
不同 index:
GET /_mget
{
"docs": [
{
"_index": "index1",
"_type": "type",
"_id": 10
},
{
"_index": "index2",
"_type": "type",
"_id": 10
}
]
}
相同 index:
GET /_index/_type/_mget
{
"ids": [10, 11]
}
就是 index 的 type 元数据,每个 type 都有一个自己的 mapping,决定了数据类型,建立倒排索引的行为,还有进行搜索的行为。
对于不同的数据类型,可能是 exact value,也可能是 full text。
exact value,在建立倒排索引的时候,分词的时候,是将整个值一起作为一个关键词建立到倒排索引中的;
full text,会经历各种各样的处理,分词,normalization,之后才建立到倒排索引中。
normalization:时态转换、同义词转换、大小写转换等
对以上两种类型搜索的行为也是不一样的,exact value 会按照整个值进行匹配;full text 则会进行分词和 normalization 再去倒排索引中匹配。
filter:根据搜索条件进行过滤,不需要计算得分
query:会计算每个 document 相对于搜索条件的相关度,然后按照相关度排序
由于 es 需要对数据进行分词处理建立倒排索引,进行排序时结果可能不准确。
通常的解决方案是,建立两个索引,倒排索引用于查询,正排索引用于排序。
如果一次性要查出来数万条数据,那么性能会很差,此时一般会采用 scoll 滚动查询,分批查询,直到所有查询都处理完。
scroll 搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜索,如果这个期间数据变更,是不会让用户看到的。
每次发送 scroll 请求,我们还需要指定一个 scroll 参数,指定一个时间窗口,每次搜索请求只要在这个时间窗口内完成就可以了。
GET /_index/_type/_search?scroll=1m
{
"query": {
"match_all": {}
},
"sort": ["_doc"],
"size": 3
}
和分页不同的是,scroll 类似分页,但使用场景不一样。
分页主要是一页一页的搜索,但 scroll 是一批一批交给系统处理的。