暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Elasticsearch学习笔记

小D学Java 2019-09-10
405


定义

Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎.

启动es

#下载
curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.3.tar.gz


# 2.解压
tar -xvf elasticsearch-5.6.3.tar.gz


# 3.启动
cd elasticsearch-5.6.3/bin
./elasticsearch


# 指定名字启动 :
./elasticsearch -Ecluster.name=my_cluster_name -Enode.name=my_node_name

#修改kibana/config下的配合文件kibana.yml
server.host: "192.168.198.128" #修改为外网可访问的地址
elasticsearch.url: "http://localhost:9200" #修改为es的地址


#进入bin文件夹启动 :
./kibana


#浏览器访问 : http://192.168.198.128:5601/app/kibana#/dev_tools/console?_g=()
#注意关闭防火墙 `service iptables stop`


lucence

lucene,最先进、功能最强大的搜索库;直接基于 lucene 开发,非常复杂,api 复杂,需要深入理解原理(各种索引结构).Elasticsearch 基于 lucene,隐藏复杂性,提供简单易用的 restful api 接口,是一个分布式的搜索引擎和分析引擎.

Lucene的抽象结构

Lucene 的不足

  • Lucene 是一个单机的搜索库,如何能以分布式形式支持海量数据?(路由)

  • Lucene 中没有更新,每次都是 Append 一个新文档,如何做部分字段的更新?

  • Lucene 中没有主键索引,如何处理同一个 Doc 的多次写入?(es 新增了一个 "_id" 字段)

  • 在稀疏列数据中,如何判断某些文档是否存在特定字段?("_source" 字段)

  • Lucene 中生成完整 Segment 后,该 Segment 就不能再被更改,此时该 Segment 才能被搜索,这种情况下,如何做实时搜索?

倒排索引

倒排索引由在文档中出现的唯一的单词列表,以及对于每个单词在文档中的位置组成。

基本元素

  • NRT(近实时,Near Realtime)

  • 集群

    • 分片(shard) - 每个 shard可创建多个 replica副本

    • 副本(replica) - replica可以在 shard 故障时提供备用服务,保证数据不丢失,多个replica还可以提升搜索操作的吞吐量和性能.默认的副本数为1

  • 数据结构

    • 索引

    • 类型

    • 文档

    • field

数据类型

  • text,keyword

  • byte,short,integer,long

  • float,double

  • boolean

  • date

Elasticsearch 5.0.0 版本之后 将string
拆分成两个新的类型: text和keyword.

Keyword类型:

  • 用于存储邮箱号码、手机号码、主机名、状态码、邮政编码、标签、年龄、性别等数据。

  • 用于筛选数据(例如: select * from x where status='open')、排序聚合(统计)

  • 直接将完整的文本保存到倒排索引中

Text类型:

  • 用于存储全文搜索数据, 例如: 邮箱内容、地址、代码块、博客文章内容等。

  • 默认结合standard analyzer(标准解析器)对文本进行分词、倒排索引。

  • 默认结合标准分析器进行词命中、词频相关度打分。

//如果想让一个keyword字段既可支持精确查询又可支持全文查询,可使用`fields`属性来声明一个类型的别名
{
"mappings": {
"my_type": {
"properties": {
"${field}": {
"type": "keyword",
"fields": {
"${field_searchable}": {
"type": "text"
}
}
}
}
}
}
}


//想查询全文搜索时
{
"query": {
"match": {
"${field}.${field_searchable}": "${val}"
}
}
}

乐观锁

es的记录中存在着一个_version
,用来声明当前记录的版本号.创建时初始值为0,之后每次更新成功后都会在操作后将值加一.所以如果要避免记录在更新时相互覆盖的场景,则可在更新记录时先到es获取对应记录得到版本号,然后更新时带上这个版本号来实现乐观锁更新


Bulk批量操作

Bulk 不是原子性的,不能用它来实现事务控制。每个请求是单独处理的,因此一个请求的成功或失败不会影响其他的请求。

Bulk Request 会加载到内存里,如果太大的话,性能反而会下降,因此需要反复尝试一个最佳的 bulk size。一般从 1000~5000 条数据开始,尝试逐渐增加。另外,如果看大小的话,最好是在 5~15MB 之间。

操作类型

  • index - 普通的put操作,可以是创建文档,也可以是全量替换文档

  • create - PUT /${index}/${type}/${id}/_create
    ,强制创建/存在则报错

  • update

  • delete

数据格式

采用这种一行定位信息,一行doc记录的行格式而不使用json格式的原因是es的数据是分片的,一行行读取后可将记录直接就转发到相应的分片上,而使用json需解析并整个数据需耗费更多的内存,更多的gc开销

{"action": {"meta"}}\n
{"data"}\n
{"action": {"meta"}}\n
{"data"}\n



路由算法

  • shard = hash(routing) % number_of_primary_shards

  • routing = _id
    or custom routing value

主分片默认为5个,一旦创建了就不能修改就是因为document记录是基于主分片数来hash取模的.

当然es支持手动指定路由键,比如说 put /${index}/${type}/${id}?routing=${user_id}|
,此时可将同一个用户的数据都路由到同一个分片中,能提升批量读取时的性能


分片之间的数据同步

在客户端请求时,主分片会将结果同步到副本分片后再返回客户端的响应

查询过程解析

  1. 客户端请求到es集群的某个节点A,此时节点A作为协调节点

  2. A根据路由键计算记录是分布在哪一个shard上,我们假设在分片one

  3. A将在分片one
    的主分片以及副本分片上随机选一个来执行这次查询

特殊情况,当查询的记录正在主分片上创建或更新时,记录还没同步到副本分片中,此时查询的结果可能是脏数据或者是查不到数据.解决方法是敏感数据只在主分片执行查询.

搜索请求的基本模块

  • query - 使用查询或者过滤器等

  • from/size

  • _source - 可明确指定只返回哪些字段

  • sort

filter与query对比

  • filter:仅仅只是按照搜索条件过滤出需要的数据而已,不计算任何相关度分数,对相关度没有任何影响.而且filter可以被缓存.所以一般来讲,filter比query快.

  • query:会去计算每个 document 相对于搜索条件的相关度,并按照相关度进行排序

一般来说,如果你是在进行搜索,需要将最匹配搜索条件的数据先返回,那么用 query;如果你只是要根据一些条件筛选出一部分数据,不关注其排序,那么用 filter;

GET /${index}/${type}/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"${field}": "${val}"
}
}
],
"filter": {
"range": {
"${filed2}": {
"gte": ${val2} //gte表示大于等于,也可使用如gt,lte等
}
}
}
}
}
}


写操作解析

es写入流程是写将数据写入lucene,数据载入内存后,再将数据写入translog刷新到磁盘,写磁盘成功后,请求返回给用户。Segment是不可变的,当我们更新一个文档时,会把老的数据打上已删除的标记,然后写一条新的文档。在执行flush操作的时候,才会把已删除的记录物理删除掉。



相关度得分算法

在开始计算得分之时,Elasticsearch使用了被搜索词条的频率以及它有多常见来影响得分。一个简短的解释是,一个词条出现在某个文档中的次数越多,它就越相关。但是,如果该词条出现在不同的文档的次数越多,它就越不相关。这一点被称为TF-IDF ( TF是词频,即term frequency), IDF是逆文档频率( inverse document frequency)

影响因素

  • Term frequency(不同词的频率) - 某个文档出现次数越多,就越相关

  • Inverse document frequency(相同词的频率) -  不同文档出现的次数越多,就越不相关

  • Field-length norm - field越长,相关度越弱

深度查询分页常用做法

  • from/size

  • search-after

  • srcoll

游标查询会取某个时间点的快照数据。查询初始化之后索引上的任何变化会被它忽略。它通过保存旧的数据文件来实现这个特性,结果就像保留初始化时的索引 '视图' 一样。

深度分页的代价根源是结果集全局排序,如果去掉全局排序的特性的话查询结果的成本就会很低。游标查询用字段 _doc
来排序。这个指令让 Elasticsearch 仅仅从还有结果的分片返回下一批结果。

启用游标查询可以通过在查询的时候设置参数 scroll
的值为我们期望的游标查询的过期时间。游标查询的过期时间会在每次做查询的时候刷新,所以这个时间只需要足够处理当前批的结果就可以了,而不是处理查询结果的所有文档的所需时间。这个过期时间的参数很重要,因为保持这个游标查询窗口需要消耗资源,所以我们期望如果不再需要维护这种资源就该早点儿释放掉。设置这个超时能够让 Elasticsearch 在稍后空闲的时候自动释放这部分资源。

GET /old_index/_search?scroll=1m  //保持游标查询窗口一分钟
{
"query": { "match_all": {}},
"sort" : ["_doc"], //关键字 _doc 是最有效的排序顺序
"size": 1000
}


//这个查询的返回结果包括一个字段 _scroll_id, 它是一个base64编码的长字符串 。
//现在我们能传递字段 _scroll_id 到 _search/scroll 查询接口获取下一批结果:
GET /_search/scroll
{
"scroll": "1m", //注意再次设置游标查询过期时间为一分钟。
"scroll_id" : "cXVlcnlUaGVuRmV0Y2g7...."
}


//注意游标查询每次返回一个新字段 _scroll_id。每次我们做下一次游标查询, 我们必须把前一次查询返回的字段 _scroll_id 传递进去。
//当没有更多的结果返回的时候,我们就处理完所有匹配的文档了。


分析

分析(analysis )是在文档被发送并加人倒排索引之前,Elasticsearch在其主体上进行的操作。在文档被加人索引之前,Elasticsearch让每个被分析字段经过一系列的处理步骤。

  • 字符过滤一使用字符过滤器转变字符。

  • 文本切分为分词一将文本切分为单个或多个分词。

  • 分词过滤一使用分词过滤器转变每个分词。

  • 分词索引 - 将这些分词存储到索引中

segment分段配置优化

  • 刷新(refresh)和冲刷( flush)的频率 - 刷新会让Elasticsearch重新打开索引,让新建的文档可用于搜索。冲刷是将索引的数据从内存写入磁盘。

  • 合并分段 - 过多的分段搜索是很慢的,因此在后台小分段会被合并为较大的分段,保持分段的数量可控。

  • 存储和存储限流 - 调节每秒写入的字节数,来限制合并对于I/O系统的影响

默认的行为是每秒自动地刷新每份索引,意味着你有一秒钟的数据查询延迟。你可以修改其设置,改变每份索引的刷新间隔,这个是可以在运行时完成的。例如,下面的命令将自动刷新的间隔设置为了5秒。

//设置index为5s刷新一次,
curl -XPUT localhost:9200/${index}/_ settings -d '{
"index. refresh interval": "5s"
}'


//查询设置的值
curl localhost:9200/${index}/_settings?pretty


//关闭自动刷新
curl -XPUT localhost:9200/${index}/_ settings -d '{
"index. refresh interval": "-1"
}'


//手动触发更新
curl localhost:9200/${index}/_refresh

冲刷即将内存中的分段提交到磁盘上的Lucene索引的过程.为了确保某个节点宕机或分片移动位置的时候,内存数据不会丢失, Elasticsearch将使用事物日志来跟踪尚未冲刷的索引操作。除了将内存分段提交到磁盘,冲刷还会清理事物日志

冲刷触发的时机,以下任一满足条件即可

  • 内存缓冲区已满。

  • 自上次冲刷后超过了一定的时间。

  • 事物日志达到了一-定的阈值。

//更新索引事务日志设置
curl -XPUT localhost: 9200/${index}/_ settings -d '{
"index. translog": {
"flush_ threshold size": "500mb", //达到500M才触发冲刷
" flush_ threshold period": "10m" ,//10m才触发
}
}'


合并分段

curl -XPUT localhost: 9200/${index}/_settings -d '{
"index.merge": {
"policy": {
"segments_per_tier": 5,#值越大,拥有的分段数越多
"max_merge_at_once": 5, #限制一次最多合并多少个分段
"max_merged_segment": "1gb" #控制最大的分段大小,
},
#控制合并的线程数量为1
”scheduler. max_ thread count": 1
}
}'


合并限流

因段合并会对IO产生压力,因此es提供了存储限流来限制合并时可使用的IO吞吐量.默认情况下有一个节点层级的设置,称为indices. store. throttle.max_ bytes_ per_ sec
, 在版本1.5中其默认值是20mb字节。



文章转载自小D学Java,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论