ElasticSearch

来源:互联网 发布:mac更改管理员名称 编辑:程序博客网 时间:2024/06/06 06:52

前言

本文是在读LearnElasticSearch书后记下的笔记.由于书中的内容有些使用于老的版本需要格外注意.读者如遇到一些不一致的问题可以留言反馈.
elasticsearch提供了强大的API接口的支持
API接口具有RESTful特性

索引文档

index(相当于db)
type(相当于mysql的表)
id(唯一性,相当于一个主键的概念,可以自增也可以自定义值)
注意id默认为22位字母,称为UUID.

PUT /{index}/{type}/{id}{    "field": "value",    ...}

返回

{"_index": "log","_type": "mylog","_id": "123","_version": 1,"created": true}

文档

存储到唯一ID下的由最高级或者根对象 (root object )序列化而来的JSON。

元数据:

_index 文档存储的逻辑地方,实际存储的位置在每个分片当中.
命名规则:全部小写,不能以下划线开头
_type:它们定义了当前类型下的数据结构,类似于数据库表中的列.
_id:该文档的唯一标识.
_source:在创建原始文档传入的数据
found:true 在文档当中确实找到,HTTP CODE返回200,没有找到值为false,并且HTTP CODE返回404
_version:每一个文档都有一个版本号码。每当文档产生变化时(包括删除),_version 就会增大。
created:是否是新建文档,如果文档更新值为false,否则值为true(对应的也是HTTP 当中的PUT方法);

文档的增删改查

增加与替换:
如果创建失败的话返回一个409的状态码
替换是指完全重新将该_id的值进行替换,version值然后增加

PUT /log/mylog/123/_createPUT /log/mylog/123/op_type=createPUT /website/blog/123 

删除操作:当删除一个文档时,如果文档存在返回200
如果文档不存在返回404响应码

DELETE /log/mylog/123

查询操作:
如果文档不存在会返回404的响应码
成功返回200状态码

GET /log/mylog/123

查询多个:

有以下几种查询方式:

GET '/log/mylog/_mget'  {"ids":[1, 2,3 ,4]}GET /_mget{    "docs":[        {            "_index":"log",            "_type":"mylog",            "_id":2        },        {            "_index":"log",            "_type":"mylog",            "_id":1,            "_source":"views"        }    ]}

更新:
下述示例会将该文档添加字段

POST /log/mylog/123/_update{    "doc" : {        "test":1    }}

高并发如何解决数据冲突

ES所选用的锁:
乐观控制并发锁,假设这种情况并不会经常发生,也不会去阻止某一数据的访问.如果基础数据在我们读取和写入的间隔中发生了变化,更新就会失败.这就需要我们手动去处理

正如之前所说version在何时变化.

使用_version字段判断值,每次更新时先获取到_version的值,然后指定version参数的值,当服务端发现,version的值与传入的值不同时,服务端的值就不会改变.

version_type=external使用自定义的版本号更新.更新的时候只需要大于当前version的值.

PUT /website/blog/2?version=5&version_type=external{    "title": "My first external blog entry",    "text": "Starting to get the hang of this..."}

批量操作

我们可以进行批量操作,支持的动作有create,index,update,delete.
请求体如下,除delete操作外不需要进行request body其余都需要.

这样操作格式目的是为了提高性能

{ action: { metadata }}\n{ request body }\n
POST /_bulk{ "delete": { "_index": "log", "_type": "mylog", "_id": "123" }} { "create": { "_index": "log", "_type": "mylog", "_id": "123" }}{ "title": "123" }{ "index": { "_index": "website", "_type": "blog" }}{ "title": "456t" }

数据返回的顺序和执行的顺序一致,
如果能正确执行的status返回200,否则返回对应的状态码
返回的数据格式如下:

{    "took":3,    "errors":true,    "items":[        {            "create":{                "_index":"log",                "_type":"mylog",                "_id":"123",                "status":409,                "error":"DocumentAlreadyExistsException [[log][4] [mylog][123]:document already exists]"            }        },        {            "index":{                "_index":"log",                "_type":"mylog",                "_id":"456",                "_version":5,                "status":200            }        }    ]}

原理

从路由到分片

routing:默认是_id
分片是物理存储数据的地方,根据routing以及下述公式

分片 = hash(routing) % 主分片数量

我们就可以得到真正的shard

集群数据添加

假设集群存在三个节点,当我们需要添加数据时,请求到了节点1,但是我们根据上小节计算分片的规则我们可以获取到对应的物理分片.这时在集群节点3中有主分片2会处理这个创建或者删除的操作.并同时插入”其余全部”节点中的从分片,并对应的返回成功,这时才会对客户端返回成功.

replication 可设置为async sync
设置为async不关心操作结果的返回为异步操作.
sync操作过程如上所述

如何算写入成功

这里就是要解释上小节所讲的”其余全部”是指的哪些

consistency 其值可以为one all quorum(default),当主分片执行写入操作从分片时同时写入成功的个数

quorum定义:

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

显而易见,万变不离其宗,这个公式在RabbitMQ当中的HA高可用也是用类似的 公式,只不过换汤不换药,在RabbitMQ当中copy的是镜像,这里改了个名字叫做分片.

获取文档

获取文档一个路由规则,注意这里操作时,需要考虑负载均衡的问题:有可能每个节点都可以查到该文档但是查询任务可能会分发到其他的节点上.

更新部分文档

找出主分片所在节点,请求到该节点然后进行更新.期间如果在该节点更新失败如果参数当中有重试次数会进行相应的重试.

在该操作时,主分片同步到从分片时,会进行全文档同步.不会是这个更新的请求,因为分片中的文档是异步进行同步.当更新请求可能会无序到达,这会造成从分片数据混乱.

bulk文档更新与获取

批量操作,在请求的节点会并行请求到其他节点.当请求节点都返回数据后即请求成功.
当批量更新只是多了一个同步从分片的操作.

搜索

我们可以简单的搜索索引当中的数据,类型也可以一次搜索多个.单个搜索暂时不过多的介绍.多个搜索可以拿逗号进行分割:

GET /log,log2/mylog,herlog/_search 

分页搜索

from参数相当于offset
size参数相当于limit每次返回多少数据

GET /_search?size=5&from=10

精确搜索

前缀 “+” 表示必须要满足我们的查询匹配条件,而前缀 “-” 则表示绝对不能匹配条件。
也可以作比较,或,在_all字段当中搜索:

-name:test+sss:4d+time:>1234567890+(me 1)GET /_search?q=%2Bname%3Atest+%2Bsss%3A4d

一些特殊的符号放入到url里需要进行转义.

注意:搜索的过程当中需要搜索的数据必须存在于索引当中,不是存在于文档当中

映射

获取字段的映射

GET /mylog/_mapping

字段存储的类型:

Boolean: true or false :: "boolean"Whole number: 123 :: "long"Floating point: 123.45 :: "double"String, valid date: "2014-09-15" :: "date"String: "foo bar" :: "string"(新的版本当中去除了string 改为了text)

操作映射时可以注意:我们可以不能改变analyzed,not_analyzed已经被索引的字段,会造成数据影响造成对应的数据不适合搜索

Bitset

bitset本质就是一堆二进制,可用于存储一些索引的信息(用二进制存储)
在ES当中这种数据结构用于加速filter过滤.

优势如下:
1.比较简洁,用N个二进制片段表示w个词组(针对计算机而言).
2.寄存器操作可以直接访问,无需再访问内存
3.充分利用位并行(当处理器需要执行的指令,超过字组大小时,指令需要被拆成数次来存取;增加字组大小,减少了处理器需要执行的指令数量,从而增加了运算效率。).

劣势:
1.未经压缩的bitset数据结果,对存储空间的利用效率不高.(可以采取压缩算法)
2.对于查找单个元素当中,需要可以被寻址否则在词组当中的效率会很低.

ES中的使用

过滤器只是doc字段是否符合,因此符合Bitset符合这样的设计思想.
es将片段过滤器转换成简洁,单一的Bitset结构.
Roaring bitset 是一种bitset的升级版(对Bitset压缩,查找匹配速度增加)

Roaring Bitset可以被缓存,本质是一个filter 查询的结果集合.
Roaring Bitsets可以支持增量跟新,假如有新的的文档更新,对应的Roaring Bitsets会进行增量更新.

在执行过滤器时

在第一次根据反向索引取出文档,然会将lucene segment 解析为Roaring Bitset数据结构,决定哪些文档匹配.如果匹配记0否则记1.

然后根据Roaring bitset遍历找出匹配的文档.

增加引用计数机器,防止不常用的信息被缓存.会根据使用的频率决定是否会被缓存.
在第二次执行时,过滤器会利用这些被缓存的Roaring Bitset信息,加快过滤的速度.

原创粉丝点击