处理冲突:

乐观并发控制:

Elasticsearch是分布式的。当文档被创建、更新或删除,文档的新版本会被复制到集群的其它节点。Elasticsearch即是同步
的又是异步的,意思是这些复制请求都是平行发送的,并无序(out of sequence)的到达目的地。这就需要一种方法确保老版
本的文档永远不会覆盖新的版本。
上文我们提到 index 、 get 、 delete 请求时,我们指出每个文档都有一个 _version 号码,这个号码在文档被改变时加一。
Elasticsearch使用这个 _version 保证所有修改都被正确排序。当一个旧版本出现在新版本之后,它会被简单的忽略。

分布式文档存储

路由文档到分片

Elasticsearch将文档存储的哪一个分片上,是根据算法:

shard = hash(routing) % number_of_primary_shards

routing 值是一个任意字符串,它默认是 _id 但也可以自定义。这个 routing

字符串通过哈希函数生成一个数字,然后除以主切片的数量得到一个余数

(remainder),余数的范围永远是 0 到 number_of_primary_shards - 1 ,这个

数字就是特定文档所在的分片

新建、索引和删除文档都是写操作,它们必须在主分片上成功完成才能复制到相
关的的复制分片上。

步骤:

1、客户端给Node1发送新建、索引或删除文档请求。

2、主节点使用文档的_id确定文档属于分片0,它转发请求到Node3,分片0位于
这个节点上。

3、Node3在主分片上执行请求,如果成功,它转发请求到相应的位于Node1和
Node2的复制节点上。当所有复制节点报告成功,Node3报告到请求的节点,请求
的节点在报告给客户端。

请求参数:replication、consistency、timeout会更改上述过程

replication:默认值为sync,主分片得到复制分片的成功响应后才返回。

async,请求在主分片上被执行后就会返回给客户端。它

依旧会转发请求给复制节点,但你将不知道复制节点是否成功。

默认的sync复制允许Elasticsearch强制反馈传输。async复制可能导致在不等待
其他分片就绪的情况下发送过多的请求二使Elasticsearch过载

consistency:默认主分片在在尝试写入时需要规定数量(quorum)或过半的分片
可用,防止数据被写入到错的网络分区。规定数量:

int ((primary + number_of_replicas) / 2) +1

值:one(只有一个主分片),all(所有主分片),或者默认的(quorum)或者
过半分片

timeout:当副本不足时会怎样?Elasticsearch会等待更多的分片出现,默认为
一分钟。timeout:1000*60;timeout:30s

新索引默认有 1 个复制分片,这意味着为了满足 quorum 的要求需要两个活动
的分片。当然,这个默认设置将阻止我们
在单一节点集群中进行操作。为了避开这个问题,规定数量只有在
number_of_replicas 大于一时才生效。

 

检索文档

下面我们罗列在主分片或复制分片上检索一个文档必要的顺序步骤:

1. 客户端给 Node 1 发送get请求。

2. 节点使用文档的 _id 确定文档属于分片 0 。分片 0 对应的复制分片在三个
节点上都有。此时,它转发请求到 Node 2 。

3. Node 2 返回endangered给 Node 1 然后返回给客户端。

原因:对于读请求,为了平衡负载,请求节点会为每个请求选择不同的分片——

它会循环所有分片副本。

局部更新文档:

下面我们罗列执行局部更新必要的顺序步骤:

1. 客户端给 Node 1 发送更新请求。

2. 它转发请求到主分片所在节点 Node 3 。

3. Node 3 从主分片检索出文档,修改 _source 字段的JSON,然后在主分片上

重建索引。如果有其他进程修改了文档,它以 retry_on_conflict 设置的次数

重复步骤3,都未成功则放弃。


4. 如果 Node 3 成功更新文档,它同时转发文档的新版本到 Node 1 和 Node 2

上的复制节点以重建索引。当所有复制节点报告成功, Node 3 返回成功给请求

节点,然后返回给客户端。

update API还接受routing``replication``consistency 和 timout 参数。

基于文档的复制:

当主分片转发更改给复制分片时,并不是转发更新请求,而是转发整个文档的新版本。这些
修改转发到复制节点是异步的,不保证到达的顺序不发送的顺序相同。如果Elasticsearch

转发的仅仅是修改请求,修改的顺序可能是错误的,哪得到的就是个损坏的文档。

 

批量中每个引用的文档属于不同的主分片,每个分片可能被分布于集群中的某个节点上。这意味着批量中的每个操作
(action)需要被转发到对应的分片和节点上。
如果每个单独的请求被包装到JSON数组中,那意味着我们需要:
解析JSON为数组(包括文档数据,可能非常大)
检查每个请求决定应该到哪个分片上
为每个分片创建一个请求的数组
序列化这些数组为内部传输格式
发送请求到每个分片
这可行,但需要大量的RAM来承载本质上相同的数据,还要创建更多的数据结构使得JVM花更多的时间执行垃圾回收。
取而代之的,Elasticsearch则是从网络缓冲区中一行一行的直接读取数据。它使用换行符识别和解析action/metadata行,以
决定哪些分片来处理这个请求。
这些行请求直接转发到对应的分片上。这些没有冗余复制,没有多余的数据结构。整个请求过程使用最小的内存在进行。

数据是如何被索引的

映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型( string , number , booleans ,
date 等)。
分析(analysis)机制用于进行全文文本(Full Text)的分词,以建立供搜索用的反向索引。

在索引中有12个tweets,只有一个包含日期 2014-09-15 ,但是我们看看下面查询中的 total hits。
GET /_search?q=2014 # 12 个结果
GET /_search?q=2014-09-15 # 还是 12 个结果 !
GET /_search?q=date:2014-09-15 # 1 一个结果
GET /_search?q=date:2014 # 0 个结果 !

 

date 类型的字段和 string 类型的字段的索引方式是不同的,因此导致查询结果的不同,这并不会让我们觉得惊讶。
你会期望每一种核心数据类型(strings, numbers, booleans及dates)以不同的方式进行索引,而这点也是现实:在
Elasticsearch中他们是被区别对待的。
但是更大的区别在于确切值(exact values)(比如 string 类型)及全文文本(full text)之间。(Elasticsearch中的数据可以大致分为两种类型:

确切值 及 全文文本。)

这两者的区别才真的很重要 - 这是区分搜索引擎和其他数据库的根本差异。

确切值(Exact values) vs. 全文文本(Full text)

确切值是确定的,正如它的名字一样。比如一个date或用户ID,也可以包含更多的字符串比如username或email地址。
确切值 "Foo" 和 "foo" 就并不相同。确切值 2014 和 2014-09-15 也不相同。
全文文本,从另一个角度来说是文本化的数据(常常以人类的语言书写),比如一片推文(Twitter的文章)或邮件正文。

确切值查询较为简单,和sql比较像

全文文本查询首先对文本分析,然后使用结果建立一个倒排索引

倒排索引

例如,我们有两个文档,每个文档 content 字段包含:
1. The quick brown fox jumped over the lazy dog
2. Quick brown foxes leap over lazy dogs in summer

将词为统一为标准格式,

1. "Quick" 可以转为小写成为 "quick" 。
2. "foxes" 可以被转为根形式 ""fox 。同理 "dogs" 可以被转为 "dog" 。
3. "jumped" 和 "leap" 同义就可以只索引为单个词 "jump"

异步清空es索引数据 elasticsearch异步_Elastic

搜索“+Quick +fox”(前缀 + 表示单词必须匹配到)标准化处理为"+quick +fox"

分析和分析器

分析器完成上述标准化操作。包括三个功能:字符过滤器、分词器、表征过滤

首先字符串经过字符过滤器(character filter),它们的工作是在表征化(译者注:这个词叫做断词更合适)前处理字符串。
字符过滤器能够去除HTML标记,或者转换 "&" 为 "and" 。
下一步,分词器(tokenizer)被表征化(断词)为独立的词。一个简单的分词器(tokenizer)可以根据空格或逗号将单词分开
(译者注:这个在中文中不适用)。
最后,每个词都通过所有表征过滤(token filters),它可以修改词(例如将 "Quick" 转为小写),去掉词(例如停用词
像 "a" 、 "and"``"the" 等等),或者增加词(例如同义词像 "jump" 和 "leap" )

内建的分析器:标准分析器、简单分析器、空格分析器、语言分析器

"Set the shape to semi-transparent by calling set_trans(5)"

标准分析器是Elasticsearch默认使用的分析器。对于文本分析,它对于任何语言都是最佳选择(译者注:就是没啥特殊需
求,对于任何一个国家的语言,这个分析器就够用了)。它根据Unicode Consortium的定义的单词边界(word boundaries)
来切分文本,然后去掉大部分标点符号。最后,把所有词转为小写。产生的结果为:
set, the, shape, to, semi, transparent, by, calling, set_trans, 5


简单分析器将非单个字母的文本切分,然后把每个词转为小写。产生的结果为:
set, the, shape, to, semi, transparent, by, calling, set, trans


空格分析器依据空格切分文本。它不转换小写。产生结果为:
Set, the, shape, to, semi-transparent, by, calling, set_trans(5)


特定语言分析器适用于很多语言。它们能够考虑到特定语言的特性。例如, english 分析器自带一套英语停用词库——
像 and 或 the 这些与语义无关的通用词。这些词被移除后,因为语法规则的存在,英语单词的主体含义依旧能被理解(译者
注: stem English words 这句不知道该如何翻译,查了字典,我理解的大概意思应该是将英语语句比作一株植物,去掉无用
的枝叶,主干依旧存在,停用词好比枝叶,存在与否并不影响对这句话的理解。)。
english 分析器将会产生以下结果:
set, shape, semi, transpar, call, set_tran, 5
注意 "transparent" 、 "calling" 和 "set_trans" 是如何转为词干的。

测试分析器

异步清空es索引数据 elasticsearch异步_Elastic_02

 

指定分析器:通过映射(mapping)人工设置这些字段。

映射

索引中每个文档都有一个类型(type)。 每个类型拥有自己的映射(mapping)或者模式定义
(schema definition)一个映射定义了字段类型,每个字段的数据类型,以及字段被Elasticsearch处理的方式。映射还用于

设置关联到类型上的元数据。

如果你索引一个带引号的数字—— "123" ,它将被映射为 "string" 类型,而不是 "long" 类型。然而,如果
字段已经被映射为 "long" 类型,Elasticsearch将尝试转换字符串为long,并在转换失败时会抛出异常。

我们可以使用 _mapping 后缀来查看Elasticsearch中的映射

GET /gb/_mapping/tweet

注意:

错误的映射,例如把 age 字段映射为 string 类型而不是 integer 类型,会造成查询结果混乱。
要检查映射类型,而不是假设它是正确的!

自定义字段映射

映射中最重要的字段参数是 type 。除了 string 类型的字段,你可能很少需要映射其他的 type

string 类型的字段,默认的,考虑到包含全文本,它们的值在索引前要经过分析器分析,并且在全文搜索此字段前要把查询
语句做分析处理。
对于 string 字段,两个最重要的映射参数是 index 和 analyer 。

异步清空es索引数据 elasticsearch异步_Elastic_03

string 类型字段默认值是 analyzed 。如果我们想映射字段为确切值,我们需要设置它为 not_analyzed :
{
"tag": {
"type": "string",
"index": "not_analyzed"
}
}
其他简单类型—— long 、 double 、 date 等等——也接受 index 参数,但相应的值只能是 no 和 not_analyzed ,它们
的值不能被分析。

analyer

异步清空es索引数据 elasticsearch异步_其他_04

你可以在第一次创建索引的时候指定映射的类型。此外,你也可以晚些时候为新类型添加映射(或者为已有的类型更新映
射)。
你可以向已有映射中增加字段,但你不能修改它。如果一个字段在映射中已经存在,这可能意味着那个字段的数据已
经被索引。如果你改变了字段映射,那已经被索引的数据将错误并且不能被正确的搜索到。
我们可以更新一个映射来增加一个新字段,但是不能把已有字段的类型那个从 analyzed 改到 not_analyzed 。