ElasticSearch定义

  1. index索引含义
    1. 一个索引index就像传统关系数据库的数据库,它是存储相关文档存储的地方
    2. 索引一个文档,表示把一个文档存储到索引中,以便他可以被检索或者查询
  2. shard分片
    1. 我们的文档存储在分片中,并且在分片中被索引

ElasticSearch安装和配置

ElasticSearch 安装

ElasticSearch基于JDK1.8,请先安装JDK
关于Linux如何安装JDK和配置
请参考Ubuntu下配置jdk

curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.3.tar.gz
tar -xvf elasticsearch-5.6.3.tar.gz
cd elasticsearch-5.6.3/bin
./elasticsearch #启动

ElasticSearch配置

由于虚拟机内存问题
需要将JVM虚拟机内存设置修改
./elasticsearch-5.6.3/config/jvm.options
修改
# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space
#从2g修改为1g
-Xms1g
-Xmx1g

常见问题

max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536]
max number of threads [1024] for user [lishang] likely too low, increase to at least [2048]
切换到root用户,编辑limits.conf 添加类似如下内容
vi /etc/security/limits.conf
添加如下内容:
* soft nofile 655360
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096
--------------------------------------
或者以一下方法
sysctl -w vm.max_map_count=655360
并用以下命令查看是否修改成功
sysctl -a | grep "vm.max_map_count"
如果能正常输出655360,则说明修改成功,然后再次启动elasticsearch

环境配置

远程访问主机
需要在config目录下的elasticsearch.yml修改如下

network.host: 0.0.0.0
http.port: 9200
transport.host: localhost
transport.tcp.port: 9300

插件cerebro安装

插件cerebro的GitHub地址为GitHub https://github.com/lmenezes/cerebro/

wget https://github.com/lmenezes/cerebro/releases/download/v0.6.5/cerebro-0.6.5.tgz
tar zxvf cerebro-0.6.5.tgz
cd cerebro-0.6.5/
bin/cerebro
#可以直接通过外网端口访问 http://yourip:9000/

ElasticSearch搜索curl

新建索引

新建并设置主分片和复制分片

PUT tj
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}

修改mapping

POST tj/mail/_mapping
{
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "integer"
}
}
}

添加数据

GET tj/mail
{
"name":"dubo",
"age":23
}

轻量搜索

GET tj/mail/_search
GET tj/mail/_search?q=name:dubo

更新数据

全部更新会是直接把之前的老数据,标记为删除状态,然后,再添加一条更新的。
是局部更新不会覆盖其他数据
POST _index/_type/_id/_update
{
"doc":{
}
}

查询表达式搜索

GET /tj/mail/_search
{
"query" : {
"match" : {
"name" : "dubo"
}
}
}

范围搜索

#过滤器搜索
GET tj/mail/_search
{
"query": {
"bool": {
"filter": {
"range": {
"FIELD": {
"gte": 10,#大于等于 great than
"lte": 20 #小于等于 less than
}
}
}
}
}
}

复杂的搜索

GET tj/mail/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "dubo"
}
}
],
"filter": {
"range": {
"age": {
"gte": 10,
"lte": 20
}
}
}
}
}
}
嵌套过滤器与bool
#搜索大于20岁,并且姓名为dubo或者爱好为book的
#bool查询中能继续嵌套bool
GET tj/mail/_search
{
"query": {
"bool": {
"must": [
{
"bool": {
"should": [
{
"match": {
"name": "dubo"
}
},
{
"match": {
"interests": "book"
}
}
]
}
}
],
"filter": {
"range": {
"age": {
"gt": 20
}
}
}
}
}
}

搜索相关性得分问题

Elasticsearch 默认按照相关性得分排序,即每个文档跟查询的匹配程度。
Elasticsearch中的 相关性 概念非常重要,也是完全区别于传统关系型数据库的一个概念,数据库中的一条记录要么匹配要么不匹配。

精确匹配

使用match_phrase精确匹配一系列单词或者短语.
切分是根据空格切分
如果没有切分,即使match也不会返回

GET tj/mail/_search
{
"query": {
"match_phrase": {
"name": "dubo"
}
}
}

高亮搜索

#em标签在html为红色斜体
GET tj/mail/_search
{
"query": {
"match": {
"name": "dubo"
}
},
"highlight": {
"fields": {
"name": {}
}
}
}

分析聚合

Elasticsearch 有一个功能叫聚合(aggregations),允许我们基于数据生成一些精细的分析结果。聚合与 SQL 中的 GROUP BY 类似但更强大。

在使用aggs会提示报错

Fielddata is disabled on text fields by default.
Set fielddata=true on [name] in order to load fielddata in memory by uninverting the inverted index.
Note that this can however use significant memory.
Alternatively use a keyword field instead.
默认情况下,Fielddata在文本字段上禁用。
在[name]中设置fielddata = true
以便通过反转反向索引来加载内存中的fielddata。
PUT my_index/_mapping/my_type
{
"properties": {
"my_field": {
"type": "text",
"fielddata": true
}
}
}
GET tj/mail/_search
{
"aggs": {
"NAME": {
"AGG_TYPE": {}
}
}
}
AGG_TYPE可以选择terms,avg,sum,value_count,range
#计算每种爱好的平均年龄
GET tj/mail/_search
{
"aggs": {
"all_interests": {
"terms": { "field": "interests" },
"aggs": {
"ages": {
"avg": {
"field": "age"
}
}
}
}
}
}

权重

过指定 boost 来控制任何查询语句的相对的权重

GET /_search
{
"query": {
"bool": {
"must": {
"match": {
"content": {
"query": "full text search",
"operator": "and"
}
}
},
"should": [
{ "match": {
"content": {
"query": "Elasticsearch",
"boost": 3
}
}},
{ "match": {
"content": {
"query": "Lucene",
"boost": 2
}
}}
]
}
}
}

dis_max

单个匹配最佳字段
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "Quick pets" }},
{ "match": { "body": "Quick pets" }}
]
}
}
}
{
"hits": [
{
"_id": "1",
"_score": 0.12713557, #两个评分一样
"_source": {
"title": "Quick brown rabbits",
"body": "Brown rabbits are commonly seen."
}
},
{
"_id": "2",
"_score": 0.12713557,
"_source": {
"title": "Keeping pets healthy",
"body": "My quick brown fox eats rabbits on a regular basis."
}
}
]
}
#我们可能期望同时匹配 title 和 body 字段的文档
#比只与一个字段匹配的文档的相关度更高,
#但事实并非如此,
#因为 dis_max 查询只会简单地使用 单个 最佳匹配语句
#的评分 _score 作为整体评分。
tie_breaker_参数
可以通过指定 tie_breaker 这个参数将其他匹配语句的评分也考虑其中:
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "Quick pets" }},
{ "match": { "body": "Quick pets" }}
],
"tie_breaker": 0.3
}
}
}

集群监控操作

集群健康

GET _cluster/health
{
"cluster_name": "PDVM01", #集群名称
"status": "yellow",
"timed_out": false,
"number_of_nodes": 1,
"number_of_data_nodes": 1,
"active_primary_shards": 4, # 指出你集群中的主分片数量。这是涵盖了所有索引的汇总值
"active_shards": 4, #是涵盖了所有索引的_所有_分片的汇总值,即包括副本分片
"relocating_shards": 0, #显示当前正在从一个节点迁往其他节点的分片的数量
"initializing_shards": 0,
"unassigned_shards": 4,
"delayed_unassigned_shards": 0, #推迟的未分配
"number_of_pending_tasks": 0,
"number_of_in_flight_fetch": 0,
"task_max_waiting_in_queue_millis": 0,
"active_shards_percent_as_number": 50
}
GET _cluster/health?level=indices
#具体至每一个索引
{
"cluster_name": "PDVM01",
"status": "yellow",
"timed_out": false,
"number_of_nodes": 1,
"number_of_data_nodes": 1,
"active_primary_shards": 4,
"active_shards": 4,
"relocating_shards": 0,
"initializing_shards": 0,
"unassigned_shards": 4,
"delayed_unassigned_shards": 0,
"number_of_pending_tasks": 0,
"number_of_in_flight_fetch": 0,
"task_max_waiting_in_queue_millis": 0,
"active_shards_percent_as_number": 50,
"indices": {
"tj": {
"status": "yellow",
"number_of_shards": 3,
"number_of_replicas": 1,
"active_primary_shards": 3,
"active_shards": 3,
"relocating_shards": 0,
"initializing_shards": 0,
"unassigned_shards": 3
}
}
}

集群的部署

重要配置修改

#指定集群的名字和节点名字
cluster.name:
node.name:
#指定路径
#默认情况下, Elasticsearch 会把插件、日志以及你最重要的数据放在安装目录下。
#这会带来不幸的事故, 如果你重新安装 Elasticsearch 的时候不小心把安装目录覆盖了。如果你不小心,你就可能把你的全部数据删掉了。
path.data: /path/to/data1,/path/to/data2
# Path to log files:
path.logs: /path/to/logs
# Path to where plugins are installed:
path.plugins: /path/to/plugins

Elasticsearch之元数据

什么是meta-fields

在Elasticsearch下,一个文档除了有数据之外,它还包含了元数据(Metadata)。每创建一条数据时,都会对元数据进行写入等操作,当然有些元数据是在创建mapping的时候就会设置,
它里面定义了每个添加的doc的处理方式。 类似于数据库的表结构数据。

有哪些meta-fields

ES中元数据大体分为五中类型:身份元数据、索引元数据、文档元数据、路由元数据以及其他类型的元数据

  1. 身份元数据
    _index:文档所属索引 , 自动被索引,可被查询,聚合,排序使用,或者脚本里访问

    _type:文档所属类型,自动被索引,可被查询,聚合,排序使用,或者脚本里访问

    _id:文档的唯一标识

  2. 索引元数据
    _all: 自动组合所有的字段值,以空格分割,可以指定分器词索引,但是整个值不被存储,所以此字段仅仅能被搜索,不能获取到具体的值

    _field_names:索引了每个字段的名字,可以包含null值,可以通过exists查询或missing查询方法来校验特定的字段

    _timestamp:可以手工指定时间戳值,也可以自动生成使用now()函数,除此之外还可以设置日期的格式化,忽略确实等功能

    _ttl:对于一些会话数据或者验证码失效时间,一般来说是有生命周期的,在es中可以很方便的通过这个ttl来设置存活时间,比如1小时,或者10分钟,在超时过后,这个doc会被自动删除,这种方式并不适合按周或按天删除历史数据,如果是这种需求,可考虑使用索引级别的管理方式

  3. 文档元数据

    _source : 一个doc的原生的json数据,不会被索引,用于获取提取字段值 ,启动此字段,索引体积会变大,如果既想使用此字段,又想兼顾索引体积,可以开启索引压缩 ._source是可以被禁用的,不过禁用之后部分功能不再支持更新、高亮、重建索引、索引自动修复

数据可以保存到多个不同的目录, 如果将每个目录分别挂载不同的硬盘,这可是一个简单且高效实现一个软磁盘阵列( RAID 0 )的办法。Elasticsearch 会自动把条带化(注:RAID 0 又称为 Stripe(条带化),在磁盘阵列中,数据是以条带的方式贯穿在磁盘阵列所有硬盘中的) 数据分隔到不同的目录,以便提高性能。

集群重启

再关闭一个节点时候,Elasticsearch会立即试图复制这个节点的数据到集群中的其他节点上,这导致大量的IO操作。设置参数避免。

PUT /_cluster/settings
{
"transient":{cluster.routing.allocation.enable":"none"}
}

等待所有的主分片分配完成后,启动部分分片分配

PUT /_cluster/settings
{
"transient":{"cluster.routing.allocation.enable":"all"}
}

lucene 高亮问题

高亮的逻辑
第一个高亮词前面的片段就是从0到这个词的起始偏移位置,人社部的起止位置为:[26,29],也就是0~26,然后下一次循环的时候srcIndex会变为29,即从29开始再找高亮片段,然后第二个词”杜波”的起止偏移为:[13,16],src.substring(29,13)显然会导致异常,第一个高亮词前面的片段就是从0到这个词的起始偏移位置,阿森纳的起止位置为:[26,29],也就是0~26,然后下一次循环的时候srcIndex会变为29,即从29开始再找高亮片段,然后第二个词”杜波”的起止偏移为:[13,16],src.substring(29,13)显然会导致异常,

bootstrap.memory_lock:true

关闭swap缓冲区

#elasticsearch性能调优

  1. 评估集群初始大小信息包括:
    • 索引吞吐量
    • 文档数
    • 搜索吞吐量
    • 查询类型
    • 热索引文档数
    • 保留策略
    • 需要的响应时间
    • SLA 级别
  2. 显式设置mapping
    • Elasticsearch可以动态创建mapping,但这并不适用于所有场景。例如,在Elasticsearch5.x中默认string字段同时是”keyword”和”text”类型。这在很多场景下是不需要的。
  3. 如果文档是以用户自定义ID或者路由方式索引的请避免不平衡的分片。
    • Elasticsearch使用随机生成DI和hash算法以确保文档均匀的分配到各个分配。当你使用用户自定义ID或者路由,ID或者可能可能不够随机,这样某些索引可能会比其他索引大很多。在这种场景下,在大分片上进行读/写操作可能会比较慢。我们可以使用index.routing_partition_size 优化ID/路由key(5.3及以上版本)

Elasticsearch索引文档的过程

  • 协调节点默认使用文档ID参与计算(也支持通过routing),以便为路由提供合适的分片。
shard = hash(document_id) % (num_of_primary_shards)
  • 当分片所在的节点接收到来自协调节点的请求后,会将请求写入到Memory Buffer,然后定时(默认是每隔1秒)写入到Filesystem Cache,这个从Momery Buffer到Filesystem Cache的过程就叫做refresh;
  • 当然在某些情况下,存在Momery Buffer和Filesystem Cache的数据可能会丢失,ES是通过translog的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到translog中,当Filesystem cache中的数据写入到磁盘中时,才会清除掉,这个过程叫做flush;
  • 在flush过程中,内存中的缓冲将被清除,内容被写入一个新段,段的fsync将创建一个新的提交点,并将内容刷新到磁盘,旧的translog将被删除并开始一个新的translog。
  • flush触发的时机是定时触发(默认30分钟)或者translog变得太大(默认为512M)时;

esindex

segment中的refresh 和flush

ES的一个特性就是提供实时搜索,新增加的文档可以在很短的时间内就被搜索到。在创建一个commit point时,为了确保所有的数据都已经成功写入磁盘,避免因为断电等原因导致缓存中的数据丢失,在创建segment时需要一个fsync的操作来确保磁盘写入成功。但是如果每次新增一个文档都要执行一次fsync就会产生很大的性能影响。在文档被写入segment之后,segment首先被写入了文件系统的缓存中,这个过程仅使用很少的资源。之后segment会从文件系统的缓存中逐渐flush到磁盘,这个过程时间消耗较大。但是实际上存放在文件缓存中的文件同样可以被打开读取。ES利用这个特性,在segment被commit到磁盘之前,就打开对应的segment,这样存放在这个segment中的文档就可以立即被搜索到了。


上图中灰色部分即存放在缓存中,还没有被commit到磁盘的segment。此时这个segment已经可以进行搜索。

在ES中,将缓存中的文档写入segment,并打开segment使之可以被搜索的过程叫做refresh。默认情况下,分片的refresh频率是每秒1次。这就解释了为什么es声称提供实时搜索功能,新增加的文档会在1s内就可以进行搜索了。

Refresh的频率通过index.refresh_interval:100s参数控制,一条新写入es的日志,在进行refresh之前,是在es中不能立即搜索不到的。

通过执行curl -XPOST127.0.0.1:9200/_refresh,可以手动触发refresh行为。

flush与translog

前面讲到,refresh行为会立即把缓存中的文档写入segment中,但是此时新创建的segment是写在文件系统的缓存中的。如果出现断电等异常,那么这部分数据就丢失了。所以es会定期执行flush操作,将缓存中的segment全部写入磁盘并确保写入成功,同时创建一个commit point,整个过程就是一个完整的commit过程。

但是如果断电的时候,缓存中的segment还没有来得及被commit到磁盘,那么数据依旧会产生丢失。为了防止这个问题,es中又引入了translog文件。

每当es接收一个文档时,在把文档放在buffer的同时,都会把文档记录在translog中。

执行refresh操作时,会将缓存中的文档写入segment中,但是此时segment是放在缓存中的,并没有落入磁盘,此时新创建的segment是可以进行搜索的。

按照如上的流程,新的segment继续被创建,同时这期间新增的文档会一直被写到translog中。

当达到一定的时间间隔,或者translog足够大时,就会执行commit行为,将所有缓存中的segment写入磁盘。确保写入成功后,translog就会被清空。

执行commit并清空translog的行为,在es中可以通过_flush api进行手动触发。

如:

curl -XPOST 127.0.0.1:9200/tcpflow-2015.06.17/_flush?v

通常这个flush行为不需要人工干预,交给es自动执行就好了。同时,在重启es或者关闭索引之间,建议先执行flush行为,确保所有数据都被写入磁盘,避免照成数据丢失。通过调用sh service.sh start/restart,会自动完成flush操作。

以上内容参考萌萌哥ElasticSearch集群部署文档
以上内容参考官方API www.elastic.co
未经授权请勿转载
Copyright © 2017 Mr.DuBo. All Rights Reserved