专栏名称: 唤之
目录
相关文章推荐
程序员的那些事  ·  马斯克狂吹的 Grok 3 ... ·  21 小时前  
程序员小灰  ·  如何用DeepSeek来变现?90%的人都不知道 ·  2 天前  
程序员小灰  ·  深夜王炸,微信搜索:接入 DeepSeek ... ·  3 天前  
码农翻身  ·  DeepSeek彻底爆了! ·  昨天  
OSC开源社区  ·  微信搜索接入DeepSeek,正在灰度测试中 ·  3 天前  
51好读  ›  专栏  ›  唤之

浅谈ES技术与实践

唤之  · 掘金  · 程序员  · 2018-06-14 02:42

正文

ES是基于Lucene的分布式存储.Lucene提供了全文检索的功能,ES在此之上加入索引分布式的机制,提供了数据分片,数据副本,数据同步等功能,保证数据的安全性

ES存储的基本单位是Document,相当于数据库中的一条记录,一个Doc包括多个Field(相当于字段),Doc可以随时添加或者删除字段

另外,ES对于需要检索的字段可以生命为需要索引,这样就可以很方便的对Document进行检索

API接口:

目前支持:

1.原生ES客户端访问(速度较快)ES 1.7版本 要求JDK 1.7或以上版本,从2016-01-20起,再发邮件申请的ES集群为2.1.0版本。

2.HTTP方式访问(速度较慢);

索引文档

一个文档的_index,_type和_id唯一标识一个文档,我们可以提供自定义的id,或者让index自动生成

指定ID:

PUT /website/blog/123

自动生成ID:

POST /{index}/{type}/{id}

在ElasticSearch中每一个文档都有一个版本号,每次对文档进行修改时(包括删除),_version的值会递增,

搜索一个文档

GET/website/blog/123?pretty

搜索文档的一部分

GET/website/blog/123?_source=title,text

只获取_source字段,不需要任何元数据

GET /website/blog/123/_source

如果只想检查一个文档是否存在 --根本不想关心内容--那么用 HEAD 方法来代替 GET 方法。 HEAD 请求没有返回体,只返回一个 HTTP 请求报头:

curl -i -XHEAD http://localhost:9200/website/blog/123

如果文档存在, Elasticsearch 将返回一个 200 ok 的状态码:

若文档不存在, Elasticsearch 将返回一个 404 Not Found 的状态码:

更新整个文档:

在内部,Elasticsearch 已将旧文档标记为已删除,并增加一个全新的文档。 尽管你不能再对旧版本的文档进行访问,但它并不会立即消失.实际上ES按前述完全相同的方式执行了以下过程:

  1. 从旧文档构建了JSON

  2. 更改了JSON

  3. 删除了旧文档

  4. 索引一个新的文档

唯一的区别在于, update API 仅仅通过一个客户端请求来实现这些步骤,而不需要单独的 get 和 index 请求。

创建新的文档

确认创建的是一个新的文档(不能覆盖现有的文档)

确保创建一个新文档的最简单办法是,使用索引请求的 POST 形式让 Elasticsearch 自动生成唯一 _id

如果已经有了ID,那么久必须告诉ES只有在相同的_index和_type和_id不存在的时候才能接受我们的索引请求.两种方式:

  1. 使用 op_type 查询 -字符串参数:

  1. 是在 URL 末端使用 /_create :

如果创建新文档的请求执行成功,ES会返回元数据的一个201 Created的HTTP响应吗.另一方面,如果具有相同的文档,ES将会返回409 Conflict响应码

删除文档

DELETE /website/blog/123

如果找到该文档,Elasticsearch 将要返回一个 200 ok 的 HTTP 响应码,和一个类似以下结构的响应体。注意,字段 _version 值会增加

如果文档没有 找到,我们将得到 404 Not Found 的响应码和类似这样的响应体:

_version仍然会增加

处理冲突

无论最后哪一个文档被索引,都将被唯一存储在 Elasticsearch 中。如果其他人同时更改这个文档,他们的更改将丢失。虽然两人同时更改相同的文档几率很小.或者对于我们的业务来说偶尔丢失并不是很严重的问题.但是有时丢失一个变更时很严重的(限时抢购,促销之类的)

悲观并发控制:

这种方法在关系型数据库广泛使用,假定变更冲突可能发生,因此阻塞访问防止冲突.典型的例子就是读取一行数据之前现将其锁住,确保只有放置锁的线程能够对这行数据进行修改

乐观并发控制:

ES中使用这种方法假定变更冲突不可能发生,并且不会阻塞正在尝试的操作.然而,如果源数据在读写当中被修改,更新将会失败.应用程序接下来将决定该如何解决冲突.可以重试更新,使用新数据或者将情况报告给用户

应用:

利用 _version 号来确保 应用中相互冲突的变更不会导致数据丢失。我们通过指定想要修改文档的 version 号来达到这个目的。 如果该版本不是当前版本号,我们的请求将会失败。

文档的部分更新

1.他们不能被修改,只能被替换。 update API 必须遵循同样的规则。 从外部来看,我们在一个文档的某个位置进行部分更新。然而在内部, update API 简单使用与之前描述相同的 检索-修改-重建索引 的处理过程。 区别在于这个过程发生在分片内部,这样就避免了多次请求的网络开销。通过减少检索和重建索引步骤之间的时间,我们也减少了其他进程的变更带来冲突的可能性。

POST /website/blog/1/_update

{

"doc" : {

"tags" : [ "testing" ],

"views": 0

}

}

2,用Groovy脚本编程

分布式的文档存储

文档存储算法

shard=hash(routing) % number_of_primary_shards

routing 是一个可变值,默认是文档的 _id ,也可以设置成一个自定义的值。 routing 通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到 余数 。这个分布在 0 到 number_of_primary_shards-1 之间的余数,就是我们所寻求的文档所在分片的位置。

主分片和副分片如何交互

相同分片的副本不会放在同一节点,

我们可以发送请求到集群中的任一节点。 每个节点都有能力处理任意请求。 每个节点都知道集群中任一文档位置,所以可以直接将请求转发到需要的节点上。 在下面的例子中,将所有的请求发送到 Node 1 ,我们将其称为 协调节点(coordinating node) 。

搜索

空搜索

搜索API的最基础的形式是没有指定任何查询的空搜索 ,它简单地返回集群中所有索引下的所有文档:

GET /_search

返回的结果中最重要的是hits,他包含了total字段来表示匹配的文档总数,并且一个hits数组锁查询结果的前十个文档

在hits数组中每个结果包含文档的_index,_type,_id加上_source字段.这意味着我们可以直接从返回的搜索结果中使用整个文档.不像其他的搜索引擎,仅仅返回文档的ID,需要你去获取文档

每个结果还有一个_source,他衡量了文档与查询的匹配程度.默认情况下,首先返回最相关的文档结果,就是说,返回文档是按照_score降序排列的.

took 值告诉我们执行整个搜索请求耗费了多少毫秒。

timed_out 值告诉我们查询是否超时。默认情况下,搜索请求不会超时。 如果低响应时间比完成结果更重要,你可以指定 timeout 为10或者 10ms(10毫秒),或者 1s(1秒)

GET /_search?timeout=10ms

应当注意的是 timeout 不是停止执行查询,它仅仅是告知正在协调的节点返回到目前为止收集的结果并且关闭连接。在后台,其他的分片可能仍在执行查询即使是结果已经被发送了。使用超时是因为 SLA(服务等级协议)对你是很重要的,而不是因为想去中止长时间运行的查询。

多索引搜索

我们可以通过在URL中指定特殊的索引和类型达到这种效果,如下所示:

/_search在所有的索引中搜索所有的类型

/gb/_search在 gb 索引中搜索所有的类型

/gb,us/_search在 gb 和 us 索引中搜索所有的文档

/g*,u*/_search在任何以 g 或者 u 开头的索引中搜索所有的类型

/gb/user/_search在 gb 索引中搜索 user 类型

/gb,us/user,tweet/_search在 gb 和 us 索引中搜索 user 和 tweet 类型

/_all/user,tweet/_search在所有的索引中搜索 user 和 tweet 类型

分页:

和SQL使用 LIMIT 关键字返回单个page结果的方法相同,Elasticsearch 接受 from 和 size 参数:

size

显示应该返回的结果数量,默认是 10

from

显示应该跳过的初始结果数量,默认是 0

如果每页展示 5 条结果,可以用下面方式请求得到 1 到 3 页的结果:

GET /_search?size=5

GET /_search?size=5&from=5

GET /_search?size=5&from=10

轻量搜索:

查询字符串搜索非常适用于通过命令行做即席查询。例如,查询在 tweet 类型中 tweet 字段包含 elasticsearch 单词的所有文档:

GET/_all/tweet/_search?q=tweet:elasticsearch

_all这个简单搜索返回包含 mary 的所有文档:

GET/_search?q=mary

在刚开始开发一个应用时,_all 字段是一个很实用的特性。之后,你会发现如果搜索时用指定字段来代替 _all 字段,将会更好控制搜索结果。当 _all 字段不再有用的时候,可以将它置为失效

精确值

Elasticsearch 中的数据可以概括的分为两类:精确值和全文。

对于精确值来讲,Foo 和 foo 是不同的,2014 和 2014-09-15 也是不同的。

精确值很容易查询。结果是二进制的:要么匹配查询,要么不匹配。

我们很少对全文类型的域做精确匹配。相反,我们希望在文本类型的域中搜索。不仅如此,我们还希望搜索能够理解我们的 意图 :

  • 搜索 UK ,会返回包含 United Kindom 的文档。

  • 搜索 jump ,会匹配 jumped , jumps , jumping ,甚至是 leap 。

  • 搜索 johnny walker 会匹配 Johnnie Walker , johnnie depp 应该匹配 Johnny Depp 。

  • fox news hunting 应该返回福克斯新闻(Foxs News )中关于狩猎的故事,同时, fox hunting news 应该返回关于猎狐的故事。

为了促进这类在全文域中的查询,Elasticsearch 首先 分析 文档,之后根据结果创建 倒排

索引 。

什么时候使用分词器:

1.当你查询一个 全文 域时, 会对查询字符串应用相同的分析器,以产生正确的搜索词条列表。

2.当你查询一个 精确值 域时,不会分析查询字符串,而是搜索你指定的精确值。

3.date 域包含一个精确值:单独的词条 `2014-09-15`。

4._all 域是一个全文域,所以分词进程将日期转化为三个词条: `2014`, `09`,,和 `15`。

当我们在 _all 域查询 2014`,它匹配所有的12条推文,因为它们都含有 `2014 :

最重要的查询:match_all查询

match_all 查询简单的 匹配所有文档。在没有指定查询方式时,它是默认的查询

match查询

无论你在任何字段上进行的是全文搜索还是精确查询,match 查询是你可用的标准查询。

如果你在一个全文字段上使用 match 查询,在执行查询前,它将用正确的分析器去分析查询字符串:

如果你在一个全文字段上使用 match 查询,在执行查询前,它将用正确的分析器去分析查询字符串:

{ "match": { "tweet": "About Search" }}

如果在一个精确值的字段上使用它, 例如数字`日期,布尔或者一个 not_analyzed 字符串字段,那么它将会精确匹配给定的值:

{ "match": { "age":    26           }}

{ "match": { "date":   "2014-09-01" }}

{ "match": { "public": true         }}

{ "match": { "tag":    "full_text"  }}

Multi_match查询

multi_match 查询可以在多个字段上执行相同的 match 查询

{    "multi_match": {        "query": "full text search",        "fields": [            "title",            "body"        ]    }

}

Range查询

range 查询找出那些落在指定区间内的数字或者时间:

{

"range": {

"age": {

"gte":  20,

"lt":   30

}

}

}

被允许的操作符如下:

gt大于

gte大于等于

lt小于

lte小于等于

term查询

term 查询被用于精确值 匹配,这些精确值可能是数字、时间、布尔或者那些 not_analyzed 的字符串

{ "term": { "age":    26           }}

{ "term": { "date":   "2014-09-01" }}

{ "term": { "public": true         }}

{ "term": { "tag":    "full_text"  }}

Terms查询

terms 查询和 term 查询一样,但它允许你指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件:

{ "terms": { "tag": [ "search", "full_text", "nosql" ] }}







请到「今天看啥」查看全文