ElasticSearch学习-p1

ES
一、概念

ElasticSearch:是一个分布式、可扩展、实时的搜索与数据分析引擎。它是全文检索、结构化搜索、分析以及这三个功能的组合。
二、elasticSearch与mysql的差异

1.mysql和elasticSearch都能实现对数据的分析与搜索,但是mysql偏向于持久化的存储与管理,如果使用mysql来进行海量数据的分析和搜索,mysql并不能像es那样提供秒级的搜索速度。
2.mysql单表存储的数据很大,用来做检索和查询会比较慢,会比较浪费性能,同时使用mysql对多个不同的关键字来进行查询,mysql承压能力不如es。
3.mysql在使用模糊查询时,使用like以及“%”,“_”来进行,但是这种方式是不通过索引来查找的,当数据库中表的数据量较大事就会导致查询时间较长的结果。
4.从Elasticsearch搜索到的数据可以根据评分过滤掉大部分的,只要返回评分高的给用户就好了(原生就支持排序)
三、es的查询机制

es使用倒排索引来实现模糊查询和相关性查询,它会将搜索的词段进行分词,再根据分词出现的记录来查找。
Term Index在内存中是以FST的形式保存,特点是非常节省内存;
额外:
FST有两个优点:
1)查询速度快,一般查询时间负责度为O(str(len))
2) 占用内存小:通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;
term PostingList存储的是分词出现的文档id,而查询时一般需要对这些文档id求交并集,term PostingList使用roaring Bitmaps是来实现该操作。
四、es核心点

1.index索引:

动词,相当于mysql中的insert
名词,相当于mysql的Database
2.Type类型(7.0版本以上废弃)

在index(索引)中,可以定义一个或多个类型,类似于mysql中的table,同一种类型的数据放在一起
3.Document(文档)

保存在某个索引(Index)下、某种类型(Type)的一个数据(Document),文档是json格式的数据,Document相当于mysql中某个table数据
4.集群

集群有一个或多个节点组成。
1.空集群:

 启动一个单独的节点,里面不包含任何的数据和索引

2.集群健康:

 curl -X GET "localhost:9200/myf/_cluster/health"

使用这个命令的返回的结果只有三种status:green、yellow 、red
green
所有的主分片和副本分片都正常运行。
yellow
所有的主分片都正常运行,但不是所有的副本分片都正常运行。
red
有主分片没能正常运行。

5.节点

节点负责保存集群数据,提供索引和搜索功能。在众多的节点中,其中会有一个Master Node,它主要负责维护索引元数据、负责切换主分片和副本分片身份等工作,如果主节点挂了,会选举出一个新的主节点。
6.接近实时

es的查询速度接近实时-可以达到秒级的查询速度
7.分片和复制

1)有些索引的大小超过了节点硬件限制,所以要将这些索引分片处理,即将索引化成多份。每个分片本身也是一个功能完善的并且独立的“索引”,分片可以提高性能。
2)主分片可以读写,副分片可以读;
3)es允许创建分片的一份或多份拷贝,这些拷贝即叫复制分片,即复制;
4)复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。
复制的好处就是可以保障当分片节点出现故障时可以保障分片继续使用。
5)搜索可以在所有的复制上并行运行;
ps:分片一旦被复制就有了主分片和复制分片的区别
6)数据写入的时候是写到主分片,副本分片会复制主分片的数据,读取的时候主分片和副本分片都可以读
8)技术上来说,一个主分片最大能够存储 Integer.MAX_VALUE - 128 个文档,但是实际最大值还需要参考你的使用场景:包括你使用的硬件, 文档的大小和复杂程度,索引和查询文档的方式以及你期望的响应时长。

索引、类型、文档间的关系:一个 Elasticsearch 集群可以 包含多个 索引 ,相应的每个索引可以包含多个 类型 。 这些不同的类型存储着多个 文档 ,每个文档又有 多个 属性 。

五、es写入的流程

集群上的每个节点都是coordinating node(协调节点),协调节点表明这个节点可以做路由。比如节点1接收到了请求,但发现这个请求的数据应该是由节点2处理(因为主分片在节点2上),所以会把请求转发到节点2上。
coodinate(协调)节点通过hash算法可以计算出是在哪个主分片上,然后路由到对应的节点
shard = hash(document_id) % (num_of_primary_shards)
1.将数据写到内存缓存区
2.然后将数据写到translog缓存区
3.每隔1s数据从buffer中refresh到FileSystemCache中,生成segment文件,一旦生成segment文件,就能通过索引查询到了
4.refresh完,memory buffer就清空了。
5.每隔5s中,translog 从buffer flush到磁盘中
6.定期/定量从FileSystemCache中,结合translog内容flush index到磁盘中。
说白了就是:写内存缓冲区(定时去生成segement,生成translog),能够让数据能被索引、被持久化。最后通过commit完成一次的持久化。等主分片写完了以后,会将数据并行发送到副本集节点上,等到所有的节点写入成功就返回ack给协调节点,协调节点返回ack给客户端,完成一次的写入。

六、es查询

Elasticsearch查询可以为三个阶段:
QUERY_AND_FETCH(查询完就返回整个Doc内容)
QUERY_THEN_FETCH(先查询出对应的Doc id ,然后再根据Doc id 匹配去对应的文档)
DFS_QUERY_THEN_FETCH(先算词出现的频率,再查询)

QUERY_THEN_FETCH总体的流程流程大概是:
1.客户端请求发送到集群的某个节点上,集群上的每个节点都是coordinate node(协调节点)
2.然后协调节点将搜索的请求转发到所有分片上(主分片和副本分片都行)
3.每个分片将自己搜索出的结果(doc id)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果。
4.接着由协调节点根据 doc id 去各个节点上拉取实际的 document 数据,最终返回给客户端。
说白了就是:由于Elasticsearch是分布式的,所以需要从各个节点都拉取对应的数据,然后最终统一合成给客户端
七、es删除和更新

Elasticsearch的更新和删除操作流程:
1.给对应的doc记录打上.del标识,如果是删除操作就打上delete状态,如果是更新操作就把原来的doc标志为delete,然后重新新写入一条数据
2.前面提到了,每隔1s会生成一个segement 文件,那segement文件会越来越多越来越多。 Elasticsearch会有一个merge任务,会将多个segement文件合并成一个segement文件。在合并的过程中,会把带有delete状态的doc给物理删除掉。
八、和es交互

Java代码中可以使用es的两个客户端:节点客户端和传输客户端
节点客户端会作为一个非数据节点加入到本地集群中,意思是它本身不保存数据,只是知道数据在集群中的那个节点中,并且可以把请求转发到正确的节点中。
传输客户端它本身不加入集群,但是它可以请求转发到正确的节点上去。

VERB
适当的 HTTP 方法 或 谓词 : GET、 POST、 PUT、 HEAD 或者 DELETE。
PROTOCOL
http 或者 https(如果你在 Elasticsearch 前面有一个 https 代理)
HOST
Elasticsearch 集群中任意节点的主机名,或者用 localhost 代表本地机器上的节点。
PORT
运行 Elasticsearch HTTP 服务的端口号,默认是 9200 。
PATH
API 的终端路径(例如 _count 将返回集群中文档数量)。Path 可能包含多个组件,例如:_cluster/stats 和 _nodes/stats/jvm 。
QUERY_STRING
任意可选的查询字符串参数 (例如 ?pretty 将格式化地输出 JSON 返回值,使其更容易阅读)
BODY
一个 JSON 格式的请求体 (如果请求需要的话)

例如:
计算集群中文档的数量,我们可以用这个:

curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
"query": {
"match_all": {}
 }
}'

Elasticsearch 返回一个 HTTP 状态码(例如:200 OK)和(除HEAD请求)一个 JSON 格式的返回值。前面的 curl 请求将返回一个像下面一样的 JSON 体:

{
"count" : 0,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
 }
}
 
curl <-Xaction> url -d 'body'

这里的action表示HTTP协议中的各种动作,包括GET、POST、PUT、DELETE等。

九、检索

1、检索文档

例如:创建一个索引myf,文档类型为testes,类型为json

curl -X PUT "localhost:9200/myf/testes/1?pretty" -H 'Content-Type: application/json' -d'
{
    "first_name" : "John",
    "last_name" :  "Smith",
    "age" :        25,
    "about" :      "I love to go rock climbing"
}'

在es中,HTTP GET请求并指定文档的地址——索引库、类型和ID。 使用这三个信息可以返回原始的 JSON 文档
例如:

curl  -X GET "localhost:9200/myf/testes/1"
curl -X GET "localhost:9200/myf/testes/_search"

可以看到,我们仍然使用索引库myf以及类型 testes,但与指定一个文档 ID 不同,这次使用 _search 。返回结果包括了所有三个文档,放在数组 hits 中。一个搜索默认返回十条结果。
2.轻量搜索

有两种形式的搜索API:一种是 “轻量的” 查询字符串 版本,要求在查询字符串中传递所有的参数,另一种是更完整的 请求体 版本,要求使用 JSON 格式和更丰富的查询表达式作为搜索语言。

查询字符串搜索非常适用于通过命令行做即席查询,例如查询testes1类型中word中包含eslearn的所有文档

curl -X GET "localhost:9200/myf/testes1/_search?q=word:eslearn"

下一个查询在 name字段中包含 jerry 并且在 tweet字段中包含 mary的文档。实际的查询就是这样

_search?q=name:john +tweet:mary

但是查询字符串参数所需要的 百分比编码 (译者注:URL编码)实际上更加难懂:

GET /_search?q=%2Bname%3Ajerry+%2Btweet%3Amary
  • 前缀表示必须与查询条件匹配。类似地, - 前缀表示一定不与查询条件匹配。没有 + 或者 - 的所有其他条件都是可选的——匹配的越多,文档就越相关。
    _all 字段
    这个简单搜索返回包含 mary 的所有文档:
curl -X GET "localhost:9200/_search?q=mary"  

我自己的理解:当索引一个文档的时候,Elasticsearch 取出所有字段的值拼接成一个大的字符串,作为 _all 字段进行索引。而之前的则是在制定的字段里进行搜索,只需要在字段里进行查找,而使用_all则是将es里的字段拼成一个长String,然后作为一个字段,在这个单独的字段里进行查找
3.复杂点的查询

下面的查询针对tweents类型,并使用以下的条件:

  • name 字段中包含 mary 或者 john
  • date 值大于 2014-09-10
  • _all 字段包含 aggregations 或者 geo
curl  -X GET "localhost:9200/myf/test1/_search?q=+name:(mary john) +date:>2014-09-10 +(aggregations geo)"
curl-X GET "localhost:9200/myf/test1/_search?q=%2Bname%3A(mary+john)+%2Bdate%3A%3E2014-09-10+%2B(aggregations+geo)"

轻量的查询的好处就是简洁明了,易于理解,但是轻量查询会出现符号不匹配的话会直接返回错误而不是返回结果。查询字符串搜索允许任何用户在索引的任意字段上执行可能较慢且重量级的查询,这可能会暴露隐私信息,甚至将集群拖垮。
4.全文搜索

curl -X GET "localhost:9200/megacorp/employee/_search?pretty" -H 'Content-Type: application/json' -d' { "query" : { "match" : { "about" : "rock climbing" } "}"" } '

这个会返回相关性从高到低的多个结果,即包含“rock climbing” 的相关性 ,包含“rock”|| “climbing”的相关性较低
5.短语搜索

curl -X GET "localhost:9200/myf/testes/_search?" -H 'Content-Type:application/json' -d '{"query:{"mathch_phrase:{"about":"rock climbing"}}}'

6.高亮查询

许多应用都倾向于在每个搜索结果中 高亮 部分文本片段,以便让用户知道为何该文档符合查询条件。在 Elasticsearch 中检索出高亮片段也很容易。再次执行前面的查询,并增加一个新的 highlight 参数:

curl -X GET "localhost:9200/megacorp/employee/_search?pretty" -H 'Content-Type: application/json' -d'
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    },
    "highlight": {
        "fields" : {
            "about" : {}
        }
    }
}
'

当执行该查询时,返回结果与之前一样,与此同时结果中还多了一个叫做 highlight 的部分。这个部分包含了 about 属性匹配的文本片段,并以 HTML 标签 封装:
7.分析

终于到了最后一个业务需求:支持管理者对员工目录做分析。 Elasticsearch 有一个功能叫聚合(aggregations),允许我们基于数据生成一些精细的分析结果。聚合与 SQL 中的 GROUP BY 类似但更强大。

curl -X GET "localhost:9200/myf/testes/_search?" -H 'Content-Type:applicatin/json' -d '"aggs":{"all_interests:{"terms":{"field:"interests"}}"}'
 
{
   ...
   "hits": { ... },
   "aggregations": {
      "all_interests": {
         "buckets": [
            {
               "key":       "music",
               "doc_count": 2
            },
            {
               "key":       "forestry",
               "doc_count": 1
            },
            {
               "key":       "sports",
               "doc_count": 1
            }
         ]
      }
   }
}
curl -X GET "localhost:9200/megacorp/employee/_search?pretty" -H 'Content-Type: application/json' -d'
{
    "aggs" : {
        "all_interests" : {
            "terms" : { "field" : "interests" },
            "aggs" : {
                "avg_age" : {
                    "avg" : { "field" : "age" }
                }
            }
        }
    }
}
'

这个是嵌套的,即查询兴趣爱好的情况,有查询不同爱好的平均年龄
十、文档元数据

文档不仅包含它的数据,还包括三个必须的数据元素:_index(文档在哪放)、_type(文档表示的对象类别)、_id(文档唯一的标识)
索引名必须为小写字母、开头不能以下划线,不能包含逗号。
数据可能在索引中只是松散的组合在一起,但是通常明确定义一些数据中的子分区是很有用的。 例如,所有的产品都放在一个索引中,但是你有许多不同的产品类别,比如 “electronics” 、 “kitchen” 和 “lawn-care”。
ID 是一个字符串,当它和 _index 以及 _type 组合就可以唯一确定 Elasticsearch 中的一个文档。 当你创建一个新的文档,要么提供自己的 _id ,要么让 Elasticsearch 帮你生成。
一、检查文档是否存在

curl -i -XHEAD http://localhost:9200/myf/testes/1

如果存在则会返回一个200 ok,如果不存在的话则会返回一个404 not found
你进行查找判断的时候只能看当下是不是存在,而不是代表它一直不存在

二、更新整个文档

一、在es中文档是不可改变的,不能修改它们。要是更新文档的话,只能重建索引或者进行替换
可以用的方式,先将文档标记为删除(DETELE),然后重新创建文档
二、或者说使用update
唯一的区别在于, update API 仅仅通过一个客户端请求来实现这些步骤,而不需要单独的 get 和 index 请求。
三、创建新文档

如果已经有自己的 _id ,那么我们必须告诉 Elasticsearch ,只有在相同的 _index 、 _type 和 _id 不存在时才接受我们的索引请求。这里有两种方式,他们做的实际是相同的事情。使用哪种,取决于哪种使用起来更方便。
第一种方法使用 op_type 查询-字符串参数:

PUT /website/blog/123?op_type=create
{ ... }

第二种方法是在 URL 末端使用 /_create :

PUT /website/blog/123/_create
{ ... }

如果创建新文档的请求成功执行,Elasticsearch 会返回元数据和一个 201 Created 的 HTTP 响应码。
另一方面,如果具有相同的 _index 、 _type 和 _id 的文档已经存在,Elasticsearch 将会返回 409 Conflict 响应码,以及如下的错误信息。
四、删除文档

删除文档的语法和我们所知道的规则相同,只是使用 DELETE 方法:

curl -X DELETE "localhost:9200/myf/testes/1?pretty"

ps:删除文档不会立即将文档从磁盘中删除,只是将文档标记为已删除状态。随着你不断的索引更多的数据,Elasticsearch 将会在后台清理标记为已删除的文档。

五、处理高并发

悲观并发控制
这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问资源以防止冲突。 一个典型的例子是读取一行数据之前先将其锁住,确保只有放置锁的线程能够对这行数据进行修改。
乐观并发控制
Elasticsearch 中使用的这种方法假定冲突是不可能发生的,并且不会阻塞正在尝试的操作。 然而,如果源数据在读写当中被修改,更新将会失败。应用程序接下来将决定该如何解决冲突。 例如,可以重试更新、使用新的数据、或者将相关情况报告给用户。
如何使用乐观锁控制

第一种方法、我们指定_version的具体值,如果第一次添加的version和第二次添加的version一样,第二次将会添加失败

curl -X PUT "localhost:9200/myf/testes/1?version=1"

第二种方法、我们为_version使用timestamp使用(如果你的主数据库已经有了版本号 — 或一个能作为版本号的字段值比如 timestamp — 那么你就可以在 Elasticsearch 中通过增加 version_type=external 到查询字符串的方式重用这些相同的版本号, 版本号必须是大于零的整数, 且小于 9.2E+18 — 一个 Java 中 long 类型的正值 )

PUT /website/blog/2?version=5&version_type=external
 
PUT /website/blog/2?version=10&version_type=external

六、更新文档

使用_update字段

curl -X POST "localhost:9200/website/blog/1/_update?pretty" -H 'Content-Type: application/json' -d' { "doc" : { "tags" : [ "testing" ], "views": 0 } } '

使用脚本部分更新文档

curl -X POST "localhost:9200/website/blog/1/_update?pretty" -H 'Content-Type: application/json' -d' { "script" : "ctx._source.tags+=new_tag", "params" : { "new_tag" : "search" } } '

更新后 tags已经追加了“search”

{
   "_index":    "website",
   "_type":     "blog",
   "_id":       "1",
   "_version":  5,
   "found":     true,
   "_source": {
      "title":  "My first blog entry",
      "text":   "Starting to get the hang of this...",
      "tags":  ["testing", "search"], 
      "views":  1 
   }
}

脚本删除

curl -X POST "localhost:9200/website/blog/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
   "script" : "ctx.op = ctx._source.views == count ? \u0027delete\u0027 : \u0027none\u0027",
    "params" : {
        "count": 1
    }
}
'

更新的文档可能尚不存在

假设我们需要在 Elasticsearch 中存储一个页面访问量计数器。 每当有用户浏览网页,我们对该页面的计数器进行累加。但是,如果它是一个新网页,我们不能确定计数器已经存在。 如果我们尝试更新一个不存在的文档,那么更新操作将会失败。
在这样的情况下,我们可以使用 upsert 参数,指定如果文档不存在就应该先创建它:

POST /website/pageviews/1/_update
{
   "script" : "ctx._source.views+=1",
   "upsert": {
       "views": 1
   }
}

我们第一次运行这个请求时, upsert 值作为新文档被索引,初始化 views 字段为 1 。 在后续的运行中,由于文档已经存在, script 更新操作将替代 upsert 进行应用,对 views 计数器进行累加。
更新和冲突

在本节的介绍中,我们说明 检索 和 重建索引 步骤的间隔越小,变更冲突的机会越小。 但是它并不能完全消除冲突的可能性。 还是有可能在 update 设法重新索引之前,来自另一进程的请求修改了文档。
为了避免数据丢失, update API 在 检索 步骤时检索得到文档当前的 _version 号,并传递版本号到 重建索引 步骤的 index 请求。 如果另一个进程修改了处于检索和重新索引步骤之间的文档,那么 _version 号将不匹配,更新请求将会失败。
对于部分更新的很多使用场景,文档已经被改变也没有关系。 例如,如果两个进程都对页面访问量计数器进行递增操作,它们发生的先后顺序其实不太重要; 如果冲突发生了,我们唯一需要做的就是尝试再次更新。
这可以通过设置参数 retry_on_conflict 来自动完成, 这个参数规定了失败之前 update 应该重试的次数,它的默认值为 0 。

POST /website/pageviews/1/_update?retry_on_conflict=5 
{
   "script" : "ctx._source.views+=1",
   "upsert": {
       "views": 0
   }
}
 

七、取回多个文档

1.使用mget来取回,这时要求要有一个docs数组,每个元素包含_index、_type、_id,这种方式返回的也是一个docs数组

   curl -X GET "localhost:9200/_mget?pretty" -H 'Content-Type: application/json' -d'
{
   "docs" : [
      {
         "_index" : "website",
         "_type" :  "blog",
         "_id" :    2
      },
      {
         "_index" : "website",
         "_type" :  "pageviews",
         "_id" :    1,
         "_source": "views"
      }
   ]
}
'

2.如果想检索的数据都在相同的 _index 中(甚至相同的 _type 中),则可以在 URL 中指定默认的 /_index 或者默认的 /_index/_type 。你仍然可以通过单独请求覆盖这些值:

curl -X GET "localhost:9200/website/blog/_mget?pretty" -H 'Content-Type: application/json' -d'
{
   "docs" : [
      { "_id" : 2 },
      { "_type" : "pageviews", "_id" :   1 }
   ]
}
'

即使有某个文档没有找到,上述请求的 HTTP 状态码仍然是 200 。事实上,即使请求 没有 找到任何文档,它的状态码依然是 200 --因为 mget 请求本身已经成功执行。 为了确定某个文档查找是成功或者失败,你需要检查 found 标记。
八、批量操作
bulk API 允许在单个步骤中进行多次 create 、 index 、 update 或 delete 请求。 action/metadata 行指定 哪一个文档 做 什么操作 。 metadata 应该指定被索引、创建、更新或者删除的文档的 _index 、 _type 和 _id 。

curl -X POST "localhost:9200/_bulk?pretty" -H 'Content-Type: application/json' -d'
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }} 
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "My first blog post" }
{ "index":  { "_index": "website", "_type": "blog" }}
{ "title":    "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} }
{ "doc" : {"title" : "My updated blog post"} }
'

八、分布式文档存储

路由一个文档到一个分片中

shard = hash(routing) % number_of_primary_shards
routing 是一个可变值,默认是文档的 _id ,也可以设置成一个自定义的值。 routing 通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到 余数 。这个分布在 0 到 number_of_primary_shards-1 之间的余数,就是我们所寻求的文档所在分片的位置。
这就解释了为什么我们要在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了。

九、多索引、多类型

/_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 类型

十、分页
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=1

0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值