ES

elasticsearch学习

1.介绍

Elasticsearch是一个基于Lucene的搜索服务器,提供了一个分布式多用户能力的全文搜索引擎,基于Restful web接口。仅支持json格式,自带有分布式协调管理功能。

2.lucene与elasticsearch的关系

lucene只是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的服务框架搭建起来的应用。

3.与其他数据存储进行比较

redismysqleshbasehadoop/hive
容量较大海量海量
查询时效性极高中等较高中等
查询灵活性较差k-v模式非常好,支持sql较好,关联查询弱,但是可以全文检索,DSL语言可以处理过滤,匹配,排序,聚合等各种操作较差,主要靠rowkey,scan的话性能不行,或者建立二级索引非常好,支持sql
写入速度较快中等较快较快
一致性/事务

4.特点

  • 天然集群,天然分片
    es把数据分成多个shard,多个shard可以组成一份完整的数据,这些shard可以分布在集群的各个节点上,随着数据的不断增加,集群可以增加多个分片,把多个分片放到多个机子上,已达到负载均衡,横向扩展。
    在实际运算过程中,每个查询任务提交到某一个节点,该节点必须负责将数据进行整理汇聚,在返回给客户端,也就是一个简单的节点上进行map计算,在一个固定的节点上进行reduces得到最终结果向客户端返回。
  • 天然索引
    所有数据默认进行倒排索引(全文搜索引擎主流方式)
    每个记录保存数据时,都不会直接存入数据库,系统先会对数据分词,然后按照倒排索引结构保存
    搜索时同样会先把搜索关键词进行分词,还会根据匹配程度进行打分,次数越多位置越靠前
    在这里插入图片描述lucenu为倒排索引(Term Dictionary)部分又增加了一层Term Index 结构,用于快速定位,而Term Index是缓存在内存中的,所以速度快但同时更消耗资源(内存,磁盘)。
  • 实时分析

5.修改es配置文件

  • /config/elasticsearch.yml
    ①集群名称必须相同
    在这里插入图片描述
    ②同一集群,单个节点名称必须不同;是否有资格选举为master;是否存储数据
    在这里插入图片描述
    ③网络访问(bind_host内网)和内部通信(publish_host外网)接口
    在这里插入图片描述
    ④自发现配置:新节点向集群报道的主机名(集群通信端口9300,外部访问端口9200)
    在这里插入图片描述
    ⑤把bootstrap自检程序关掉
    在这里插入图片描述
    ⑥集群中最少的master数,如果集群中最少master数少于指定的数,将无法启动,官方推荐master数设置为:集群数/2+1
    在这里插入图片描述

6.修改linux系统配置(改完后重启)

问题1:max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536] elasticsearch
原因:系统允许 Elasticsearch 打开的最大文件数需要修改成65536
解决:vi /etc/security/limits.conf
添加内容:
*soft nofile 65536
*hard nofile 131072
*soft nproc 2048
*hard nproc 65536

问题2:max number of threads [1024] for user [judy2] likely too low, increase to at least [2048] (CentOS7.x 不用改)
原因:允许最大进程数修该成4096
解决:vi /etc/security/limits.d/90-nproc.conf
修改如下内容:
*soft nproc 4096

问题3:max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144] (CentOS7.x 不用改)
原因:一个进程可以拥有的虚拟内存区域的数量。
解决:可零时提高vm.max_map_count的大小
命令:sysctl -w vm.max_map_count=262144
上述方法修改之后,如果重启虚拟机将失效,所以:
在 vim /etc/sysctl.conf文件最后添加一行
vm.max_map_count=262144
即可永久修改

7.集群启动脚本

在这里插入图片描述

8.集群验证

curl http://kqkj101:9200/_cat/nodes?v(查询各个节点状态)
在这里插入图片描述

9.head插件

  • 下载head插件
    Get https://github.com/mobz/elasticsearch-head/archive/elasticsearch-head-master.zip
    也可以用git下载,前提yum install git
    unzip elasticsearch-head-master.zip
  • 安装node.js
    wget https://npm.taobao.org/mirrors/node/latest-v4.x/node-v4.4.7-linux-x64.tar.gz
    tar -zxvf node-v9.9.0-linux-x64.tar.gz
  • 添加node.js到环境变量
    在这里插入图片描述
    source /etc/profile
  • 测试 node -v | npm -v
  • 安装grunt(grunt是一个很方便的构建工具,可以进行打包压缩、测试、执行等等的工作)
    进入到elasticsearch-head-master
    npm install -g grunt-cli
    npm install
    (npm install -g cnpm --registry=https://registry.npm.taobao.org)
  • 修改elasticearch.yml(head插件才能访问es)
    添加:http.cors.enabled: true
    http.cors.allow-origin: “*”
  • 修改Gruntfile.js(注意’,’)
    打开elasticsearch-head-master/Gruntfile.js,找到下面connect属性,新增hostname:’’:
    connect: {
    server: {
    options: {
    hostname: '
    ’,
    port: 9100,
    base: ‘.’,
    keepalive: true
    }
    }
    }
  • 启动elasticsearch-head
    进入elasticsearch-head目录,执行命令:grunt server
    后台启动 nohup grunt server &exit
  • 关闭head插件
    ps -aux|grep head
    kill 进程号

10.logstash

  • vim ./config/log4j_to_es.conf
//创建nginx_logstash.conf文件
input {//输入
        /*file {//从文件中读取数据
                path=>[""]//文件位置
                exclue=>"*.gz"//不读取哪些文件
                sincedb_path=>""//记录sincedb文件路径
                type=>""
                start_position=>"beginning"//end,是否重头读取文件
        }*/
        stdin{//输入插件,可以管道插入,也可以终端交互插入
			codec=>"plain"
			tags=>["test"]//类型为array,自定义事件的tag
			type=>"std"//类型为string,自定义该事件类型,用于后续判断
			add_field=>{"key":"value"}//类型为hash,为该事件添加字段
		}
}
filter {
  grok {//①基于正则表达式提供丰富可重用模式pattern;②基于此可以将非结构化数据做结构化处理
    match => {
      "message" => '%{IPORHOST:remote_ip} - %{DATA:user_name} \[%{HTTPDATE:time}\] "%{WORD:request_action} %{DATA:request} HTTP/%{NUMBER:http_version}" %{NUMBER:response} %{NUMBER:bytes} "%{DATA:referrer}" "%{DATA:agent}"'
    }
  }
  date {//将字符串类型的时间字段转换为时间戳类型,方便后续处理
    match => [ "time", "dd/MMM/YYYY:HH:mm:ss Z" ]
    locale => en
  }
  geoip {//添加地理位置信息
    source => "remote_ip"
    target => "geoip"
  }
  mutate{}进行增加修改删除替换等字段相关处理
output {//输出
        stdout {
                codec=>rubydebug
        }
}
logstash启动解析nginx文件
head -n 2 /home/elk1/nginx_logs|./logstash -f ../config/nginx_logstash.conf
  • ./bin/logstash -f config/log4j_to_es.conf

10.kibana(端口:5601)

  • /config/kibana.yml
    在这里插入图片描述

11.IK分词

  • ①在/plugins/目录下建立ik文件夹
  • ②把elasticsearch-analysis-ik-6.6.0.zip拷贝到该目录下,执行unzip进行解压缩
  • ③修改分词词库/ik/config/IKAnalyzer.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 扩展配置</comment>
        <!--用户可以在这里配置自己的扩展字典 -->
        <entry key="ext_dict"></entry>
         <!--用户可以在这里配置自己的扩展停止词字典-->
        <entry key="ext_stopwords"></entry>
        <!--用户可以在这里配置远程扩展字典 -->
         <entry key="remote_ext_dict">http://192.168.67.163/fenci/myword.txt </entry>
        <!--用户可以在这里配置远程扩展停止词字典-->
        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
远程扩展词典可以利用nginx发布静态资源nginx.conf
server{
	listen 80;
	server_name 127.0.0.1;
	location /fenci/{
		root es;
	}
	并在/usr/local/nginx/下建/es/fenci目录下加myword.txt,每一行代表一个词(重启es,nginx),历史数据不会进行分词,如想对历史数据重新分词,执行
      POST /dul/_doc/_update_by_query?conflicts=proceed
      当遇到冲突而导致整个更新过程失败时,更新过程是不会回滚的。如果不想因为冲突导致整个更新过程终止,可以在url中添加参数conflicts=proceed
  • ④重启es

12.ES增删改查

  • text支持分词,keyword只能全部内容匹配。
  • ik_smart粗粒度分词器(不重复),ik_max_word细粒度分词
  • es客户端选择
    ①TransportClient为代表的ES的原生客户端,不能执行原生dsl语句必须使用它的java api方法
    ②以Rest Api为主的missing client,最典型的jest。这种客户端可以直接使用dsl语句拼接成字符串,直接传给服务端,然后返回json字符串在解析
    在这里插入图片描述
1)创建索引语句
PUT /dul/
{
  "mappings": {
    "_doc":{
      "properties":{
         "user_id":{
           "type":"keyword"
         },
         "sku_name":{
           "type":"text",
           "analyzer": "ik_max_word"
         },
         "sku_category1_name":{
           "type":"text",
           "analyzer": "ik_max_word"
         },
         "dt":{
           "type":"keyword",
           "index":"false"
         } 
      }
    }
  }
}
2)查看mapping
GET  /dul/_mapping/
结果:
{
  "dul" : {
    "mappings" : {
      "_doc" : {
        "properties" : {
          "dt" : {
            "type" : "keyword",
            "index" : false
          },
          "sku_category1_name" : {
            "type" : "text",
            "analyzer" : "ik_max_word"
          },
          "sku_name" : {
            "type" : "text",
            "analyzer" : "ik_max_word"
          },
          "user_id" : {
            "type" : "keyword"
          }
        }
      }
    }
  }
}
3)新增文档(和修改-整体替换无区别)
PUT /dul/_doc/3  //POST自动生成22位uuid
{
  "user_id":"2",
  "sku_name":"我是美国人",
  "sku_category1_name":"红米 平板电脑",
  "dt":"K"
}
4)修改某一个字段
POST /dul/_doc/3/_update
{
  "doc":{
    "dt":"D"
  }
}
5)用id查找
GET /dul/_doc/3

在这里插入图片描述

6)删除一个文档
DELETE /dul/_doc/3
7)搜索全部数据
GET /dul/_doc/_search
结果:
{
  "took" : 11, //耗费时间(毫秒)
  "timed_out" : false,//是否超时
  "_shards" : {
    "total" : 5,//发送给全部5个分片
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,//命中3条数据
    "max_score" : 1.0,//最大评分
    "hits" : [//结果
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "user_id" : "2",
          "sku_name" : "我是美国人",
          "sku_category1_name" : "红米 平板电脑",
          "dt" : "K"
        }
      },
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "user_id" : "1",
          "sku_name" : "我是中国人",
          "sku_category1_name" : "小米手机 平板手机",
          "dt" : "K"
        }
      }
    ]
  }
}
8)分词查询
GET /dul/_doc/_search
{
  "query":{
    "match":{"sku_category1_name":"平板手机"}
  }
}
结果:
{
  "took" : 19,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 0.970927,
    "hits" : [
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.970927,
        "_source" : {
          "user_id" : "1",
          "sku_name" : "我是中国人",
          "sku_category1_name" : "小米手机 平板手机",
          "dt" : "K"
        }
      },
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.2876821,
        "_source" : {
          "user_id" : "2",
          "sku_name" : "我是美国人",
          "sku_category1_name" : "红米 平板电脑",
          "dt" : "K"
        }
      }
    ]
  }
}
9)短语查询:不使用分词,直接原始数据中匹配(指定顺序)
GET /dul/_doc/_search
{
  "query":{
    "match_phrase":{"sku_category1_name":"平板电脑"}
  }
}
结果:
{
  "took" : 6,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.5753642,
    "hits" : [
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.5753642,
        "_source" : {
          "user_id" : "2",
          "sku_name" : "我是美国人",
          "sku_category1_name" : "红米 平板电脑",
          "dt" : "K"
        }
      }
    ]
  }
}
10)fuzzy查询(对非常接近的词进行查询匹配,消耗性能)
GET /dul/_doc/_search
{
  "query":{
    "fuzzy":{"sku_category1_name":"rad"}
  }
}
11)查询前过滤

Bool Query

filter只过滤符合条件的文档,不计算相关性得分,es有缓存功能,提升效率
must文档必须符合must中的所有条件,会影响相关性得分
must_not文档中必须不符合must_not中的所有条件
should文档可以符合should中的条件(一个或多个),会影响相关性得分
GET /dul/_doc/_search
{
  "query":{
    "bool":{
      "filter":[{
        "term":{
          "user_id":2
        }
      }],
      "must":{
        "match":{
          "sku_category1_name":"平板"
        }
      }
    }
  }
}
结果:
{
  "took" : 24,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.2876821,
        "_source" : {
          "user_id" : "2",
          "sku_name" : "我是美国人",
          "sku_category1_name" : "红米 平板电脑",
          "dt" : "K"
        }
      }
    ]
  }
}
12)按范围过滤

操作符介绍:
gt--------------------------------------大于
lt---------------------------------------小于
gte------------------------------------大于等于
lte-------------------------------------小于等于

GET /dul/_doc/_search
{
  "query":{
    "bool":{
      "filter":{
        "range":{
          "user_id":{"gte":3}
        }
      }
    }
  }
}
结果:
{
  "took" : 17,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.0,
    "hits" : [
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 0.0,
        "_source" : {
          "user_id" : "3",
          "sku_name" : "我是法国人",
          "sku_category1_name" : "红米 电脑",
          "dt" : "K"
        }
      }
    ]
  }
}
13)排序
GET /dul/_doc/_search
{
  "query":{
      "match":{
        "sku_category1_name":"平板"
    }
  },
 /** "query":{
      "match":{
        "sku_category1_name":{
        "query":"平板电脑",
        "operator": "and"//合并(or或者)控制单词间匹配关系(顺序不指定)
        }
    }
  }, **/
  "sort":{
    "user_id":{
      "order":"asc"
    }
  }
}
结果:
{
  "took" : 16,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : null,
    "hits" : [
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : null,
        "_source" : {
          "user_id" : "1",
          "sku_name" : "我是中国人",
          "sku_category1_name" : "小米手机 平板手机",
          "dt" : "K"
        },
        "sort" : [
          "1"
        ]
      },
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : null,
        "_source" : {
          "user_id" : "2",
          "sku_name" : "我是美国人",
          "sku_category1_name" : "红米 平板电脑",
          "dt" : "K"
        },
        "sort" : [
          "2"
        ]
      }
    ]
  }
}
14)分页查询
GET /dul/_doc/_search
{
  "query":{
      "match_all":{
    }
  },
  "from":0,
  "size":2
}
结果:
{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "user_id" : "2",
          "sku_name" : "我是美国人",
          "sku_category1_name" : "红米 平板电脑",
          "dt" : "K"
        }
      },
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "user_id" : "1",
          "sku_name" : "我是中国人",
          "sku_category1_name" : "小米手机 平板手机",
          "dt" : "K"
        }
      }
    ]
  }
}
15)查询指定字段
GET /dul/_doc/_search
{
  "query":{
      "match_all":{
    }
  },
  "from":0,
  "size":3,
  "sort":{
    "user_id":{
      "order":"desc"
    }
  },
  "_source":["sku_name","dt"]
}
结果:
{
  "took" : 16,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : null,
    "hits" : [
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : null,
        "_source" : {
          "user_id" : "1",
          "sku_name" : "我是中国人",
          "sku_category1_name" : "小米手机 平板手机",
          "dt" : "K"
        },
        "sort" : [
          "1"
        ]
      },
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : null,
        "_source" : {
          "user_id" : "2",
          "sku_name" : "我是美国人",
          "sku_category1_name" : "红米 平板电脑",
          "dt" : "K"
        },
        "sort" : [
          "2"
        ]
      }
    ]
  }
}
16)聚合
GET /dul/_doc/_search
{
  "aggs":{
    "groupby_name":{
      "terms":{
        "field":"user_id"
      }
    }
  }
}
结果:
{
  "took" : 13,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "user_id" : "2",
          "sku_name" : "我是美国人",
          "sku_category1_name" : "红米 平板电脑",
          "dt" : "K"
        }
      },
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "user_id" : "1",
          "sku_name" : "我是中国人",
          "sku_category1_name" : "小米手机 平板手机",
          "dt" : "K"
        }
      },
      {
        "_index" : "dul",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 1.0,
        "_source" : {
          "user_id" : "3",
          "sku_name" : "我是法国人",
          "sku_category1_name" : "红米 电脑",
          "dt" : "K"
        }
      }
    ]
  },
  "aggregations" : {
    "groupby_name" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "1",
          "doc_count" : 1
        },
        {
          "key" : "2",
          "doc_count" : 1
        },
        {
          "key" : "3",
          "doc_count" : 1
        }
      ]
    }
  }
}
17)检索多个文档(检索确定的字段,也可以定义一个_source参数)
POST /_mget
{
   "docs" : [
      {
         "_index" : "website",
         "_type" :  "blog",
         "_id" :    2
      },
      {
         "_index" : "website",
         "_type" :  "pageviews",
         "_id" :    1,
         "_source": "views"
      }
   ]
}
结果:
{
   "docs" : [
      {
         "_index" :   "website",
         "_id" :      "2",
         "_type" :    "blog",
         "found" :    true,
         "_source" : {
            "text" :  "This is a piece of cake...",
            "title" : "My first external blog entry"
         },
         "_version" : 10
      },
      {
         "_index" :   "website",
         "_id" :      "1",
         "_type" :    "pageviews",
         "found" :    true,
         "_version" : 2,
         "_source" : {
            "views" : 2
         }
      }
   ]
}

13.es写数据过程(写入primary shard,然后同步给所有的replca shard)

客户端选择一个node发送请求过去,这个node就是协调节点,协调节点对document进行路由,将请求发送给对用的node(有primary shard),实际的node上的primary shard 处理请求,将数据同步到replica node,协调节点如果发现primary node和所有的replica node 都搞定之后,就返回结果给客户端。

底层原理:先写入内存buffer,在buffer里的时候数据是搜索不到的,同时将数据写入到translog日志文件,如果buffer快满了,或者到一定时间,就会将内存buffer数据refresh到一个新的segment file中,但是此时数据不是直接进入segment file磁盘文件,而是进入os cache,这个过程就是refresh。每隔一秒钟,es将buffer中的数据写入一个新的segment file,每秒钟会产生一个新的磁盘文件segment file,这个segment file 中就存储最近1秒内buffer中写入的数据。但是如果buffer里面此时没有数据,那当然就不会执行refresh操作,如果buffer里面有数据,默认1秒钟执行一次refresh操作,刷入一个新的segment file中。
操作系统里面,磁盘文件其实都有一个东西叫做os cache,即操作系统缓存,就是说数据写入磁盘空间之前会先进入os cache,先进入操作系统级别的一个内存缓存中去,只要buffer中的数据被refresh操作刷入os cache中,这个数据就可以被搜索到。
es准实时:默认是一秒种refresh一次的,所以es是准实时的,因为写入到数据1秒之后才能被看到。可以通过es的restful api或者java api,手动执行一次refresh操作,就是手动将buffer中的数据刷入到os cache中,让数据立马就可以被搜索到,只要数据被输入到os cache中,buffer就会被清空了,因为不需要保留buffer了,数据在translog里面已经持久化到磁盘去一份了。
上述步骤不断重复,新的数据不断进入buffer和translog,不断将buffer数据写入一个有一个新的segment file中去,每次refresh完buffer清空,translog保留。随着这个进程的不断推进,translog会变得越来越大。当translog达到一定长度的时候就会触发commit操作。
commit操作发生的第一步,就是将buffer中现有的数据refresh到os cache,清空buffer。然后,将一个commit point写入磁盘文件,里面标识着这个commit point 对应的所有的segment file,同时强行将os cache中目前所有的数据都fsync(同步内存中所有已修改文件数据到存储设备)到磁盘文件中去。最后清空现有的translog日志文件,重启一个translog,此时commit操作完成。
这个commit操作默认叫做flush。默认30分钟自动执行一次flush,但如果translog过大,也会触发flush。flush操作对应着commit的全过程,我们可以通过es api,手动执行flush操作,手动将os cache中的数据fsync强刷到磁盘上去。
translog日志作用:执行commit操作之前,数据要么停留在buffer中,要么停留在os cache中,无论是buffer还是os cache都是内存,一旦这机器死了,内存中的数据就全丢了。所以需要将数据对应的操作写入一个专门的日志文件translog中,一旦此时机器宕机,再次重启时,es会自动读取translog日志文件中的数据,恢复到内存buffer和os cache中去。
translog其实也是先写入os cache的,默认每隔5秒刷一次到磁盘中去,所以默认情况下可能有5秒的数据仅仅停留在buffer或者translog文件的os cache中,如果此时机器挂了,会丢失5秒的数据。
总结:数据先写入内存buffer中,然后每隔一秒,将数据refresh到os cache,到了os cache数据就能被搜索到(所以说es从写入到能搜索到中间会有一秒的延迟)。每隔5秒,将数据写入到translog文件(如果此时宕机,内存数据全没了,最多会有5秒的数据丢失),translog大到一定程度,或者默认每隔30mins,会触发commit操作,将缓冲区的数据flush到segment file磁盘文件中。
数据写入到segment file之后,同时就建立好了倒排索引。
删除底层原理:commit的时候会生成一个.del文件,里面将某个doc标识为deleted状态,那么搜索的时候根据.del文件就知道这个doc是否被删除了。
更新:就是将原来的doc标识为deleted状态,然后重新写入一条数据。
buffer每refresh一次,就会产生一个segment file,所以默认情况下是1秒钟一个segment file,这样下来segment file会越来越多,此时会定期执行merge。每次merge的时候,会将多个segment file合并成一个,同时这里会将标识为deleted的doc给物理删除掉,然后新的segment file写入磁盘,这里会写一个commit point,标识所有新的segment file,然后打开segment file供搜索使用,同时删除旧的segment file。

14.es读数据过程(从primary shard或replica shard读取,采用的是随机轮询算法)

客户端发送请求到任意一个node,成为协调节点。协调节点对doc id 进行哈希路由,将请求发送给对应的node,此时会使用round-robin随机轮询算法,在primary shard以及其所有replica中随机选择一个,让读请求负载均衡。接收请求的node返回document给协调节点。协调节点返回document给客户端

15.es搜索数据过程

客户端发送请求到一个协调节点,协调节点将搜索请求转发到所有的shard对应的primary shard 或 replica shard
query phase(查询阶段):每个shard将自己的搜索结果(doc id)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果;
fetch phase(获取阶段):接着由协调节点根据doc id 去各个节点上拉取实际的document数据,最终返回给客户端。

16.jest

util类

object EsUtil {
  private val ES_HOST ="http://kqkj101"
  private val ES_HTTP_PORT = 9200
  private var factory:JestClientFactory = null
  /**
    * 获取客户端
    */
  def getClient = {
    if(factory == null) build()
    factory.getObject
  }
  /**
    *关闭客户端
   */
  def close(client : JestClient): Unit ={
    if(!Objects.isNull(client))
      try{
        client.shutdownClient()
      } catch{
        case e:Exception => e.printStackTrace()
      }
  }
  /**
    * 建立连接
    */
  private def build(): Unit ={
    factory = new JestClientFactory
    factory.setHttpClientConfig(new HttpClientConfig.Builder(ES_HOST+":"+ES_HTTP_PORT).multiThreaded(true)
        .maxTotalConnection(20)//连接总数
        .connTimeout(10000).readTimeout(10000).build)
  }
  /**
    * 批量
    */
  def excuteIndexBulk(indexName:String,list: List[Any]): Unit ={
    val jest = getClient
    val bulkBuilder = new Bulk.Builder().defaultIndex(indexName).defaultType("_doc")
    for (doc <- list){
      val index = new Index.Builder(doc).build()
      bulkBuilder.addAction(index)
    }
    val items = jest.execute(bulkBuilder.build()).getItems
    println(s"保存=${items.size()}")
    close(jest)
  }
}

例子

1.@Override
    public Integer getDauTotal(String date) {
        //方式一
        /*String query = "{\n" +
                "  \"query\" : {\n" +
                "    \"bool\" : {\n" +
                "      \"filter\" : {\n" +
                "          \"term\" : {\n" +
                "            \"logDate\" : \"2020-05-02\"\n" +
                "          }\n" +
                "      }\n" +
                "    }\n" +
                "  }";*/
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.filter(new TermQueryBuilder("logDate",date));
        searchSourceBuilder.query(boolQueryBuilder);

        String query = searchSourceBuilder.toString();
        Search search = new Search.Builder(query).addIndex(GmallConstant.ES_INDEX_DAU).addType("_doc").build();
        Integer total = 0;
        try {
            SearchResult searchResult = jestClient.execute(search);
            total = searchResult.getTotal();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return total;
    }
    2.    @Override
    public Double getOrderAmout(String date) {
        String query = "{\n" +
                "  \"query\": {\n" +
                "    \"bool\": {\n" +
                "      \"filter\": {\n" +
                "        \"term\": {\n" +
                "          \"createDate\": \"2019-02-13\"\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  }, \n" +
                "  \"aggs\": {\n" +
                "         \"sum_totalAmount\": {\n" +
                "           \"sum\": {\n" +
                "             \"field\": \"totalAmount\"\n" +
                "           }\n" +
                "         }\n" +
                "}";
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.filter(new TermQueryBuilder("createDate",date));
        SumBuilder aggsBuilder = AggregationBuilders.sum("sum_totalAmount").field("totalAmount");
        searchSourceBuilder.query(boolQueryBuilder).aggregation(aggsBuilder);
        Search search = new Search.Builder(searchSourceBuilder.toString()).addIndex(GmallConstant.ES_INDEX_ORDER).addType("_doc").build();
        Double sum_totalAmount = 0D;
        try {
            SearchResult searchResult = jestClient.execute(search);
            sum_totalAmount = searchResult.getAggregations().getSumAggregation("sum_totalAmount").getSum();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sum_totalAmount;
    }
3.    @Override
    public Map getOrderAmoutHourMap(String date) {
        String query = "{\n" +
                "  \"query\": {\n" +
                "    \"bool\": {\n" +
                "      \"filter\": {\n" +
                "        \"term\": {\n" +
                "          \"createDate\": \"2019-02-13\"\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  }, \n" +
                "  \n" +
                "   \"aggs\": {\n" +
                "     \"tm\": {\n" +
                "       \"terms\": {\n" +
                "         \"field\": \"createHour\"\n" +
                "       }\n" +
                "       , \"aggs\": {\n" +
                "         \"sum_totalAmount\": {\n" +
                "           \"sum\": {\n" +
                "             \"field\": \"totalAmount\"\n" +
                "           }\n" +
                "         }\n" +
                "       }\n" +
                "     }\n" +
                "   }\n" +
                "}";
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //过滤
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.filter(new TermQueryBuilder("createDate",date));
        //聚合
        TermsBuilder termsBuilder = AggregationBuilders.terms("groupby_createHour").field("createHour").size(24);
        SumBuilder sumBuilder = AggregationBuilders.sum("sum_totalAmount").field("totalAmount");
        //自聚合
        termsBuilder.subAggregation(sumBuilder);
        searchSourceBuilder.query(boolQueryBuilder).aggregation(termsBuilder);
        Search search = new Search.Builder(searchSourceBuilder.toString()).addIndex(GmallConstant.ES_INDEX_ORDER).addType("_doc").build();
        Map<String,Double> hourMap = new HashMap<>();
        try {
            SearchResult searchResult = jestClient.execute(search);
            List<TermsAggregation.Entry> buckets = searchResult.getAggregations().getTermsAggregation("groupby_createHour").getBuckets();
            for (TermsAggregation.Entry bucket : buckets){
                Double hourAmount = bucket.getSumAggregation("sum_totalAmount").getSum();
                String hourkey = bucket.getKey();
                hourMap.put(hourkey, hourAmount);

            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return hourMap;
    }   
3.    @Override
    public Map getSaleDetailMap(String date, String keyword, int pageNo, int pageSize, String aggsFieldName, int aggsSize) {
        String query = "{\n" +
                "  \"query\": {\n" +
                "    \"bool\": {\n" +
                "      \"filter\": {\n" +
                "        \"term\": {\n" +
                "          \"dt\": \"2019-02-14\"\n" +
                "        }\n" +
                "      }, \n" +
                "      \"must\": [\n" +
                "        {\"match\":{\n" +
                "          \"sku_name\": {\n" +
                "            \"query\": \"小米手机\",\n" +
                "            \"operator\": \"and\"\n" +
                "          }\n" +
                "         } \n" +
                "          \n" +
                "        }\n" +
                "     ] \n" +
                "    }\n" +
                "  }\n" +
                "  , \"aggs\":  {\n" +
                "    \"groupby_age\": {\n" +
                "      \"terms\": {\n" +
                "        \"field\": \"user_age\" \n" +
                "      }\n" +
                "    }\n" +
                "  }\n" +
                "  ,\n" +
                "  \"size\": 2\n" +
                "  , \"from\": 0\n" +
                "}";
        Integer total = 0;
        List<Map> aggsList = new ArrayList<>();
        Map aggsMap = new HashMap();
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //过滤
        boolQueryBuilder.filter(new TermQueryBuilder("dt",date));
        //全文匹配
        boolQueryBuilder.must(new MatchQueryBuilder("sku_name",keyword).operator(MatchQueryBuilder.Operator.AND));
        searchSourceBuilder.query(boolQueryBuilder);
        //聚合
        TermsBuilder termsBuilder = AggregationBuilders.terms("groupby_" + aggsFieldName).field(aggsFieldName).size(aggsSize);
        searchSourceBuilder.aggregation(termsBuilder);
        //分页
        searchSourceBuilder.from(pageNo-1);
        searchSourceBuilder.size(pageSize);

        Search search = new Search.Builder(searchSourceBuilder.toString()).addIndex(GmallConstant.ES_INDEX_SALE).addType("_doc").build();
        try {

            SearchResult searchResult = jestClient.execute(search);
            total = searchResult.getTotal();
            List<SearchResult.Hit<Map, Void>> hits = searchResult.getHits(Map.class);
            for (SearchResult.Hit hit : hits){
                aggsList.add((Map) hit.source);
            }
            //去聚合结果
            List<TermsAggregation.Entry> buckets = searchResult.getAggregations().getTermsAggregation("groupby_" + aggsFieldName).getBuckets();
            for (TermsAggregation.Entry bucket : buckets) {
                aggsMap.put(bucket.getKey(),bucket.getCount());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Map saleMap = new HashMap();
        saleMap.put("total",total);
        saleMap.put("detail",aggsList);
        saleMap.put("aggsMap",aggsMap);
        return saleMap;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值