ElasticSearch
关于es的几个概念:
集群:多个运行es节点可以组成一个集群,它们拥有相同的cluster.name。
节点:运行es的实例
索引:相当于数据库database,一个集群可以有多个索引(数据库)。 索引实际上是指向一个或者多个物理分片的逻辑命名空间
分片:索引的子集,一个索引可以被切成多个分片。分片又分为主分片和副分片,副分片是主分片的副本。一个分片是一个底层的工作单元 ,它仅保存了全部数据中的一部分。Elasticsearch 是利用分片将数据分发到集群内各处的。分片是数据的容器,文档保存在分片内,分片又被分配到集群内的各个节点里。 当你的集群规模扩大或者缩小时, Elasticsearch 会自动的在各节点中迁移分片,使得数据仍然均匀分布在集群里。
副本分片:一个副本分片只是一个主分片的拷贝。 副本分片作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。副本分片数量可以随时更改。
类型:相当于数据库中的(表)table,一个索引(数据库)包含多个类型(表)。
文档:相当于表中的行(row)。
字段:相当于表中的列(cloum)。
分配:将分片分配给某个节点。
在es中,每个索引的主分片默认为5个,每个主分片的副本默认为1个。
一、安装运行elasticsearch:
官网下载安装包:https://www.elastic.co/cn/downloads。其中5.5版本的需要jdk1.8的支持。
配置文件的更改,参考:http://www.cnblogs.com/ahaii/p/7279930.html
运行es:
elasticsearch/bin/elasticsearch -d
-d 参数表示以守候进程方式在后台运行。
查看是否运行成功:
curl http://localhost:9200/?pretty { "name" : "node-164", "cluster_name" : "elk-cluster", "cluster_uuid" : "QLhDVsXUR8yHrD8ts3JZBA", "version" : { "number" : "5.5.1", "build_hash" : "19c13d0", "build_date" : "2017-07-18T20:44:24.823Z", "build_snapshot" : false, "lucene_version" : "6.6.0" }, "tagline" : "You Know, for Search" }
?pretty参数使得结果以json方式显示。
二、利用es的RESTful API进行交互:
ElasticSearch提供RESTful API,常用格式为:
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
其中:
-X<VBER>:可以指定curl发起是的请求方式,如GET、PUT、POST、DELETE、HEAD
<PATH>:API路径,如_cluster/stats、_nodes/stats/jvm
<QUERY_STRING>:查询字符串参数,如?pretty参数将以json格式显示查询结果
<BODY>:json格式的请求体
例如以下统计文档总数的请求格式:
curl -XGET 'http://localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } } '
1、创建文档:
curl -XPUT http://localhost:9200/索引(数据库)/类型(表)/id -d '{信息体}'
curl -XPUT http://localhost:9200/<index>/<type>/ID -d '{message}'
如果不指定id,es也支持随机生成一个id,但是,请求方法需要用POST:
curl -XPOST http://localhost:9200/<index>/<type>
自动生成的 ID 是 URL-safe、 基于 Base64 编码且长度为20个字符的 GUID 字符串。 这些 GUID 字符串由可修改的 FlakeID 模式生成,这种模式允许多个节点并行生成唯一 ID ,且互相之间的冲突概率几乎为零。
{ "_index": "people", "_type": "man", "_id": "AVFgSgVHUP18jI2wRx0w", "_version": 1, "created": true }
2、查询(检索)文档:
根据某ID检索:
curl -XGET http://localhost:9200/<index>/<type>/ID
该请求默认获取多有文档数据,且获得的数据放存在"_source"字段内。
只请求文档中一部分:
如,只需要id为5的文档中的name字段和job字段的值,则需要使用"?_source"字段:
curl -XGET http://localhost:9200/<index>/<type>/5?_source=name,job
因此,如果只需要文档中的数据,而不需要其他元数据(如索引,id,和类型),则可以使用"_source":
curl -XGET http://localhost:9200/<index>/<type>/id/_source
检查一个文档是否存在:
curl -XHEAD http://loclhost:9200/<index>/<type>/id
这里使用的是HEAD请求方法,返回http状态码。若文档存在,则返回200。若不存在, 则返回404。
检索所有文档:
curl -XGET http://localhost:9200/<index>/<type>/_search
根据字符串检索:
curl -XGET http://localhost:9200/<index>/<type>/_search?q=name:ahaii
使用查询表达式检索:
curl -XGET http://localhost:9200/<index>/<type>/_search? -d ' { "query" : { "match" : { "name" : "ahaii" } } }'
使用过滤器检索:
curl -XGET http://localhost:9200/<index>/type/_search -d' { "query" : { "bool": { "must": { "match" : { "name" : "smith" } }, "filter": { "range" : { "age" : { "gt" : 20 } } } } } }'
注意,这里使用了filter过滤器,除了查询name是ahaii的外,还要匹配年龄大于20。
全文检索:
curl -XGET http://localhost:9200/<index>/<type>/_search -d ' { "query" : { "match" : { "about" : "like coding" } } }'
检索所有包含"like coding"字段的文档。
如果有多个文档包含该字段,则都会被返回。但是,es在返回结果中会添加一个“_score”字段。该字段是搜索结果的相关性得分。
相关性得分:
Elasticsearch 默认按照相关性得分对搜索结果进行排序,即每个文档跟查询的匹配程度。完全匹配查询条件的其相关性得分最高,也就排在搜索结果的第一个。其他包含查询条件某个字段的相关性得分就会较低,排在搜索结果的后面。
短语检索:
curl -XGET http://localhost:9200/<index>/<type>/_search { "query" : { "match_phrase" : { "about" : "like coding" } } }
这里使用的是"match_phrase",匹配结果里必须包含"like coding",且两个单词连在一起。
高亮检索:
curl -XGET http://locahost:9200/<index>/<type>/_search -d ' { "query" : { "match_phrase" : { "about" : "like coding" } }, "highlight": { "fields" : { "about" : {} } } }'
这里多了一个"highlight"部分,该部分包括了"about"属性匹配到的字段,并且以 HTML 标签 <em></em> 封装,以变化颜色。
3、更新文档:
在es中文档是不可改变的,即不能修改它们。当需要更新数据时,可以进行替换。
如现在id为5的文档数据如下:
curl -XGET http://localhost:9200/people/man/5 { "_index": "people", "_type": "man", "_id": "5", "_version": 1, "found": true, "_source": { "name": "ming", "age": 23, "job": "coding" } }
现在,需要更新"age"字段的值,操作及结果如下:
curl -XPUT http://localhost:9200/people/man/5 -d ' { "name":"ming", "age":29, "job":"coding" } ' #更新后结果如下: { "_index": "people", "_type": "man", "_id": "5", "_version": 2, "result": "updated", "_shards": { "total": 2, "successful": 2, "failed": 0 }, "created": false }
返回的结果中,"_version"字段由原来的1变成了2,"result"为"updated",且"created" 字段为"false"。
在内部,Elasticsearch 已将旧文档标记为已删除,并增加一个全新的文档。 尽管你不能再对旧版本的文档进行访问,但它并不会立即消失。当继续索引更多的数据,Elasticsearch 会在后台清理这些已删除文档。
从以上执行更新文档的操作中得出一个问题,在创建文档时,如果该文档已经存在,则会被覆盖。这结果可能不是我们想要的,那怎么办?
前面介绍过,在创建文档时,可以使用POST方法在创建文档时生成一个随机的ID,这样可确保文档不会重复(被覆盖)。但是如果我想自己指定ID呢?
由于es通过"_index"、"_type" 和"_id"三个字段来确定一个唯一文档,因此我们在创建文档时,必须要告诉es,只有这三个字段都不相同时才能创建新文档。这里有两种方法:
3.1、使用"?op_type=create"参数:
创建一个已经的文档:
curl -XPUT /people/man/5?op_type=create -d' { "name":"daka", "age":25, "job":"teacher" }' #返回结果: { "error": { "root_cause": [ { "type": "version_conflict_engine_exception", "reason": "[man][5]: version conflict, document already exists (current version [2])", "index_uuid": "_Xe9jJTdS6yKbGRBvet2qg", "shard": "1", "index": "people" } ], "type": "version_conflict_engine_exception", "reason": "[man][5]: version conflict, document already exists (current version [2])", "index_uuid": "_Xe9jJTdS6yKbGRBvet2qg", "shard": "1", "index": "people" }, "status": 409 }
由于"/people/man/5"文档已存在,再创建时,会提示409。
3.2、在url中使用"create"参数:
同样创建一个"/people/man/5"文档,同样返回409:
{ "error": { "root_cause": [ { "type": "version_conflict_engine_exception", "reason": "[man][5]: version conflict, document already exists (current version [2])", "index_uuid": "_Xe9jJTdS6yKbGRBvet2qg", "shard": "1", "index": "people" } ], "type": "version_conflict_engine_exception", "reason": "[man][5]: version conflict, document already exists (current version [2])", "index_uuid": "_Xe9jJTdS6yKbGRBvet2qg", "shard": "1", "index": "people" }, "status": 409 }
这样,就避免了旧文档被覆盖的情况。
4、删除文档:
curl -XDELETE http://localhost:9200/<index>/<type>/id
这里使用的是DELETE方法,成功删除后,返回以下结果:
{ "found": true, "_index": "people", "_type": "man", "_id": "5", "_version": 2, "result": "deleted", "_shards": { "total": 2, "successful": 2, "failed": 0 } }
注意,返回结果中的"_version"字段的值会增加,无论是否删除成功,该字段的值都会增加,这是 Elasticsearch 内部记录本的一部分,用来确保这些改变在跨多节点时以正确的顺序执行。
删除文档不会立即将文档从磁盘中删除,只是将文档标记为已删除状态。随着你不断的索引更多的数据,Elasticsearch 将会在后台清理标记为已删除的文档。
5、更新文档:
我们知道,es中文档不能被直接修改,只能被替换。可以使用update参数对文档进行更新的操作。 update API 简单使用与之前描述相同的 检索-修改-重建索引 的处理过程。
update 请求最简单的一种形式是接收文档的一部分作为 doc 的参数, 它只是与现有的文档进行合并。对象被合并到一起,覆盖现有的字段,增加新的字段。
curl -XPOST http://localhost:9200/people/man/5/_update { "doc": { "tel":"122334", "like":"football" } }
更新id为5的文档,添加"tel"和"like"两个字段。更新后的文档为:
{ "_index": "people", "_type": "man", "_id": "5", "_version": 4, "found": true, "_source": { "name": "ming", "age": 33, "job": "coding", "like": "football", "tel": "122334" } }
6、检索多个文档:
需要从 Elasticsearch 检索很多文档,那么使用 multi-get 或者 mget API 来将这些检索请求放在一个请求中,将比逐个文档请求更快地检索到全部文档。
mget API 要求有一个 docs 数组作为参数,每个元素包含需要检索文档的元数据, 包括 _index 、 _type 和 _id 。如果你想检索一个或者多个特定的字段,那么你可以通过 _source 参数来指定这些字段的名字。同样,查询到的结果也包含在_source字段中:
curl -XGET http://localhost:9200/_mget -d ' { "docs" : [ { "_index" : "people", "_type" : "man", "_id" : 1 }, { "_index" : "people", "_type" : "man", "_id" : 2, "_source": "name" } ] }'
docs参数中包含两个查询条件,第一个查询符合这三个条件的整个文档的数据,第二个查询查询符合条件的文档的"name"字段的值。查询结果如下:
{ "docs": [ { "_index": "people", "_type": "man", "_id": "1", "_version": 1, "found": true, "_source": { "name": "ahaii", "age": 27, "job": "coding" } }, { "_index": "people", "_type": "man", "_id": "2", "_version": 1, "found": true, "_source": { "name": "fukk" } } ] }
如果要检索的文档在同一个index和type下,只需要传一个 ids 数组,而不是整个 docs 数组:
curl -XGET http://localhost:9200/people/man/_mget -d' { "ids":[3,5] }'
查询到的结果为:
{ "docs": [ { "_index": "people", "_type": "man", "_id": "3", "_version": 1, "found": true, "_source": { "name": "tim", "age": 32, "job": "stu" } }, { "_index": "people", "_type": "man", "_id": "5", "_version": 4, "found": true, "_source": { "name": "ming", "age": 33, "job": "coding", "like": "football", "tel": "122334" } } ] }
在检索多个文档时,如果其中某些文档不存在,则不会影响其他文档的检索。不存在的文档会返回false。
聚合检索:
该部分在以后详细介绍。
三、ElasticSearch中的避免冲突:
1、通过内部version参数避免冲突
Elasticsearch 是分布式的。当文档创建、更新或删除时, 新版本的文档必须复制到集群中的其他节点。Elasticsearch 也是异步和并发的,这意味着这些复制请求被并行发送,并且到达目的地时也许 顺序是乱的 。 Elasticsearch 需要一种方法确保文档的旧版本不会覆盖新的版本。
当我们之前讨论 index , GET 和 delete 请求时,我们指出每个文档都有一个 _version (版本)号,当文档被修改时版本号递增。 Elasticsearch 使用这个 _version 号来确保变更以正确顺序得到执行。如果旧版本的文档在新版本之后到达,它可以被简单的忽略。
我们可以利用 _version 号来确保 应用中相互冲突的变更不会导致数据丢失。我们通过指定想要修改文档的 version 号来达到这个目的。 如果该版本不是当前版本号,我们的请求将会失败。
按照_version的值对文档进行修改:
curl -XPUT http://localhost:9200/<index>/<type>/5?version=2 -d' { "name":"ming", "age":33, "job":"coding" }'
即,只有当前_version的值为2时,才会执行该操作。如果该不存在,则返回409。
ElasticSearch中,每次更新和删除,文档的_version字段都会加1。
2、通过version_type=external字段添加外部版本控制
外部版本号的处理方式和我们之前讨论的内部版本号的处理方式有些不同, Elasticsearch 不是检查当前 _version
和请求中指定的版本号是否相同, 而是检查当前 _version
是否 小于 指定的版本号。 如果请求成功,外部的版本号作为文档的新 _version
进行存储。
创建文档时设置外部版本号:
curl -XPUT http://localhost:9200/people/man/30?version=3&version_type=external -d '
{
"name":"tomi",
"age":42,
"job":"master"
}'
#返回结果: { "_index": "people", "_type": "man", "_id": "30", "_version": 3, "result": "created", "_shards": { "total": 2, "successful": 2, "failed": 0 }, "created": true }
可以看到,当前版本是3。现在更新这个文档,指定外部版本为5:
curl -XPUT http://localhost:9200/people/man/30?version=5&version_type=external -d ' { "name":"tomi", "age":19, "job":"master" } ' #返回结果: { "_index": "people", "_type": "man", "_id": "30", "_version": 5, "result": "updated", "_shards": { "total": 2, "successful": 2, "failed": 0 }, "created": false }
从返回结果看到,version被设置成了10。若再次请求,会返回409。