使用脚本
"script": {
"lang": "...",
"source" | "id": "...",
"params": { ... }
}
参数说明:
lang:脚本使用的语言,默认是painless。
source:脚本的核心部分,id应用于:stored script。
params:传递给脚本使用的变量参数。
其他脚本:
expression:每个文档的开销较低:表达式的作用更多,可以非常快速地执行,甚至比编写native脚本还要快,支持javascript语法的子集:单个表达式。缺点:只能访问数字,布尔值,日期和geo_point字段,存储的字段不可用
mustache:提供模板参数化查询
java:
Painless:
Painless是一种专门用于Elasticsearch的简单,用于内联和存储脚本,类似于Java,也有注释、关键字、类型、变量、函数等,安全的脚本语言。它是Elasticsearch的默认脚本语言,可以安全地用于内联和存储脚本。
使用教程
update:对一个文档进行更新
POST /product2/_update/2
{
"script": {
"source": "ctx._source.tags.add(params.tag)",
"lang": "painless",
"params": {
"tag": "phone"
}
}
}
reindex:将文档从一个索引复制到另一个索引
POST test/_bulk
{"index":{"_id":"1"}}
{"counter":1,"tags":["red"]}
{"index":{"_id":"2"}}
{"counter":2,"tags":["green"]}
{"index":{"_id":"3"}}
{"counter":3,"tags":["blue"]}
{"index":{"_id":"4"}}
{"counter":4,"tags":["white"]}
DELETE test_2
POST _reindex
{
"source": {
"index": "test"
},
"dest": {
"index": "test_2"
},
"script": {
"source": "if (ctx._source.tags.contains('red')) { ctx._source.tags.remove(ctx._source.tags.indexOf('red')) }",
"lang": "painless"
}
}
GET /test_2/_search
script query:基于脚本的过滤查询,多用于filter中,且source需返回boolean类型
GET test/_search
{
"query": {
"bool": {
"filter": {
"script": {
"script": {
"source": "doc['counter'].value > 3",
"lang": "painless"
}
}
}
}
}
}
script score query/function score query:允许用户在检索中灵活修改文档score,来实现自己干预结果排名的目的
POST test/_bulk
{"index":{"_id":"1"}}
{"counter":1,"msg":"hello"}
{"index":{"_id":"2"}}
{"counter":2,"msg":"hello,hello"}
{"index":{"_id":"3"}}
{"counter":3,"msg":"hello,welcome"}
{"index":{"_id":"4"}}
{"counter":4,"msg":"ello"}
GET test/_search
{
"query": {
"match": {
"msg": "hello"
}
}
}
#script score query
GET test/_search
{
"query": {
"script_score": {
"query": {
"match": {
"msg": "hello"
}
},
"script": {
"source": "doc['counter'].value + _score "
}
}
}
}
search template:搜索模板
POST test/_bulk
{"index":{"_id":"1"}}
{"counter":1,"msg":"hello"}
{"index":{"_id":"2"}}
{"counter":2,"msg":"hello,hello"}
{"index":{"_id":"3"}}
{"counter":20,"msg":"hello,welcome"}
{"index":{"_id":"4"}}
{"counter":4,"msg":"ello"}
#定义搜索模板
POST _scripts/my_search_template
{
"script": {
"lang": "mustache",
"source": {
"query": {
"match": {
"msg": "{{query_string}}"
}
}
}
}
}
#查询模板
GET _scripts/my_search_template
#使用模板
GET test/_search/template
{
"id": "my_search_template",
"params": {
"query_string": "hello"
}
}
#验证模板
GET _render/template
{
"source": """
{
"query":{
"match":{
"msg":"hello"
}
}
}
""",
"params": {
"query_string": "hello"
}
}
#删除模板
DELETE _scripts/my_search_template
Stored scripts:可以理解为script模板
POST _scripts/calculate-discount
{
"script": {
"lang": "painless",
"source": "doc['price'].value * params.discount"
}
}
GET _scripts/calculate-discount
DELETE _scripts/calculate-discount
使用:
GET product2/_search
{
"script_fields": {
"discount_price": {
"script": {
"id":"calculate-discount",
"params": {
"discount": 0.8
}
}
}
}
}
读写原理
写数据步骤 :
1、客户端选择一个node发送请求过去,这个node就是coordinating node(协调节点)
2、coordinating node 对document进行路由,将请求转发给对应的node(有primary shard)
3、实际的node上的primary shard 处理请求,然后将数据同步到replica node。
4、coordinating node如果发现 primary node和所有replica node都搞定之后,就返回响应结果给客户端。
1、先写入内存buffer,在buffer里的时候数据是搜索不到的;同时将数据写入translog日志文件。如果buffer快满了,或者到一定时间,就会将内存buffer数据refresh 到一个新的segment file中,但是此时数据不是直接进入segment file磁盘文件,而是先进入os cache。这个过程就是 refresh。
每隔1秒钟,es将buffer中的数据写入一个新的segment file,每秒钟会写入一个新的segment file,这个segment file中就存储最近1秒内 buffer中写入的数据。
2.buffer默认1秒钟执行一次refresh操作,刷入一个新的segment file中。
数据写入磁盘文件之前,会先进入os cache,先进入操作系统级别的一个内存缓存中去。只要buffer中的数据被refresh 操作刷入os cache中,这个数据就可以被搜索到了。
3、为什么叫es是准实时的?NRT,全称 near real-time。默认是每隔1秒refresh一次的,所以es是准实时的,因为写入的数据1s之后才能被看到。可以通过es的restful api或者 java api,手动执行一次 refresh操作,就是手动将buffer中的数据刷入os cache中,让数据立马就可以被搜索到。只要数据被输入os cache中,buffer 就会被清空了,因为不需要保留buffer了,数据在translog里面已经持久化到磁盘去一份了。
4、重复上面的步骤,新的数据不断进入buffer和translog,不断将buffer数据写入一个又一个新的segment file中去,每次refresh完buffer清空,translog保留。随着这个过程的推进,translog会变得越来越大。当translog达到一定长度的时候,就会触发commit操作。
5、commit的第一步,就是将buffer中现有的数据refresh到os cache中去,清空buffer。然后将一个commit point写入磁盘文件,里面标识者这个commit point 对应的所有segment file,同时强行将os cache中目前所有的数据都fsync到磁盘文件中去。最后清空现有 translog日志文件,重启一个translog,此时commit操作完成。
6、这个commit操作叫做flush。默认30分钟自动执行一次flush,但如果translog过大,也会触发flush。flush操作就对应着commit的全过程,我们可以通过es api,手动执行
flush操作,手动将os cache中数据fsync强刷到磁盘上去。
7、translog其实也是先写入os cache的,默认每隔5秒刷一次到磁盘中去,所以默认情况下,可能有5s的数据会仅仅停留在buffer或者translog文件的os cache中,如果此时机器挂了,会丢失5秒钟的数据。但是这样性能比较好,最多丢5秒的数据。也可以将translog设置成每次写操作必须是直接fsync到磁盘,但是性能会差很多。
总结:
数据先写入内存buffer,然后每隔1s,将数据refresh到 os cache
到了 os cache数据就能被搜索到(所以我们才说es是近实时)。
每隔5s,将数据写入到translog文件(这样如果机器宕机,内存数据全没,最多会有5s的数据丢失),translog达到一定程度,或者默认每隔30min,会触发commit操作,将缓冲区的
数据都flush到segment file磁盘文件中。
数据写入 segment file之后,同时就建立好了倒排索引。
读数据步骤 :
通过doc id 来查询,会根据doc id进行hash,判断出来当时把doc id分配到了哪个shard上面去,从那个shard去查询。
1、客户端发送请求到任意一个node,成为coordinate node
2、coordinate node 对doc id进行哈希路由,将请求转发到对应node,此时会使用round-robin随机轮询算法,在primary shard 以及其所有replica中随机选择一个,让读请求负载均衡。
3、接收请求的node返回document给coordinate node。
4、coordinate node返回document给客户端。
es最强大的是做全文检索
1、客户端发送请求到一个coordinate node。
2、协调节点将搜索请求转发到所有的shard对应的primary shard 或 replica shard ,都可以。
3、query phase:每个shard将自己的搜索结果(其实就是一些doc id)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果。
4、fetch phase:接着由协调节点根据doc id去各个节点上拉取实际的document数据,最终返回给客户端。
删除/更新数据底层原理
如果是删除操作,commit的时候会生成一个 .del文件,里面将某个doc标识为 deleted状态,那么搜索的时候根据 .del文件就知道这个doc是否被删除了。
如果是更新操作,就是将原来的doc标识为deleted状态,然后重新写入一条数据。
buffer 每refresh一次,就会产生一个segment file,所以默认情况下是1秒钟一个segment file,这样下来segment file会越来越多,此时会定期执行merge。
每次merge的时候,会将多个segment file合并成一个,同时这里会将标识为 deleted的doc给物理删除掉,然后将新的segment file写入磁盘,这里会写一个
commit point,标识所有新的 segment file,然后打开segment file供搜索使用,同时删除旧的segment file。
ElasticSearch完整目录
1. Elasticsearch是什么
2.Elasticsearch基础使用
3.Elasticsearch Mapping
4.Elasticsearch 集群原理
5.Elasticsearch Scripts和读写原理
6.Elasticsearch 分词器
7.Elasticsearch TF-IDF算法及高级查询
8.Elasticsearch 地理位置及搜索
9.Elasticsearch ELK