Linux环境Elasticsearch安装配置及使用

1,832 阅读25分钟

Linux环境Elasticsearch安装配置及使用

1. 关于搜索

(1) 什么是搜索

  • 说起搜索,脑海中首先想到的一定是百度、Google等搜索引擎,当我们想寻找外部信息的时候,总会习惯性的上百度去搜索一下,比如说找一部自己喜欢的电影,或者说找一本喜欢的书,或者找一条感兴趣的新闻。但是,百度 != 搜索
  • 搜索分类:
    • <1>. 互联网的搜索:电商网站,招聘网站,新闻网站,各种app
    • <2>. IT系统的搜索:OA软件(如内部员工搜索软件),办公自动化软件,会议管理,日程管理,项目管理
  • 搜索定义:
    • 在任何场景下,找寻你想要的信息,这个时候,会输入一段你要搜索的关键字,然后就期望找到这个关键字相关的有些信息。

(2) 用数据库做搜索

  • 用数据库来实现搜索不太靠谱,只能检索连续的字段,而且通常性能很差。

(3) 全文检索和Lucene

  • <1>. 全文检索

    • 全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。
  • <2>. 倒排索引

    • 倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。
  • <3>. Lucene

    • Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供。Lucene就是一个jar包,里面包含了封装好的各种建立倒排索引,以及进行搜索的代码,包括各种算法,提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。

2. 认识Elasticsearch

(1) Elasticsearch介绍

  • 官网:www.elastic.co/
  • Elasticsearch,基于Lucene,隐藏复杂性,提供简单易用的restful api接口、java api接口(还有其他语言的api接口)。
  • 关于Elasticsearch的一个传说,有一个程序员失业了,陪着自己妻子去英国伦敦学习厨师课程。程序员在失业期间想给老婆写一个菜谱搜索引擎,觉得Lucene实在太复杂了,就开发了一个封装了Lucene的开源项目——Compass。后来程序员找到了工作,是做分布式的高性能项目的,觉得compass不够,就写了Elasticsearch,让Lucene变成分布式的系统。
  • Elasticsearch是一个实时分布式搜索和分析引擎。它用于全文搜索、结构化搜索、数据分析。
    • **全文检索:**将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。
    • **结构化检索:**例如搜索商品分类为日化用品的商品都有哪些,select * from products where category_id='日化用品'
    • **数据分析:**电商网站,最近7天牙膏这种商品销量排名前10的商家有哪些;新闻网站,最近1个月访问量排名前3的新闻版块是哪些。

(2) Elasticsearch适用场景

  • <1>. 维基百科,类似百度百科,牙膏,牙膏的维基百科,全文检索,高亮,搜索推荐。
  • <2>. The Guardian(国外新闻网站),类似搜狐新闻,用户行为日志(点击,浏览,收藏,评论)+ 社交网络数据(对某某新闻的相关看法),数据分析,给到每篇新闻文章的作者,让他知道他的文章的公众反馈(好,坏,热门,垃圾,鄙视,崇拜)。
  • <3>. Stack Overflow(国外的程序异常讨论论坛),IT问题,程序的报错,提交上去,有人会跟你讨论和回答,全文检索,搜索相关问题和答案,程序报错了,就会将报错信息粘贴到里面去,搜索有没有对应的答案。
  • <4>. GitHub(开源代码管理),搜索上千亿行代码。
  • <5>. 国内:站内搜索(电商,招聘,门户,等等),IT系统搜索(OA,CRM,ERP,等等),数据分析(ES热门的一个使用场景)。

(3) Elasticsearch特点

  • <1>. 可以作为一个大型分布式集群(数百台服务器)技术,处理PB级数据,服务大公司;也可以运行在单机上,服务小公司;甚至几个树莓派都能搭建Elasticsearch集群环境。
  • <2>. Elasticsearch不是什么新技术,主要是将全文检索、数据分析以及分布式技术,合并在了一起,才形成了独一无二的ES;lucene(全文检索),商用的数据分析软件(也是有的),分布式数据库(mycat)。
  • <3>. 对用户而言,是开箱即用的,非常简单,作为中小型的应用,直接3分钟部署一下ES,就可以作为生产环境的系统来使用了,数据量不大,操作不是太复杂。
  • <4>. 数据库的功能面对很多领域是不够用的(事务,还有各种联机事务型的操作);特殊的功能,比如全文检索,同义词处理,相关度排名,复杂数据分析,海量数据的近实时处理;Elasticsearch作为传统数据库的一个补充,提供了数据库所不能提供的很多功能。

(4) Elasticsearch核心概念

  • <1>. 近实时

    • 近实时,两个意思,从写入数据到数据可以被搜索到有一个小延迟(大概1秒);基于es执行搜索和分析可以达到秒级。
  • <2>. Cluster(集群)

    • 集群包含多个节点,每个节点属于哪个集群是通过一个配置(集群名称,默认是elasticsearch)来决定的,对于中小型应用来说,刚开始一个集群就一个节点很正常。
  • <3>. Node(节点)

    • 集群中的一个节点,节点也有一个名称(默认是随机分配的),节点名称很重要(在执行运维管理操作的时候),默认节点会去加入一个名称为“elasticsearch”的集群,如果直接启动一堆节点,那么它们会自动组成一个elasticsearch集群,当然一个节点也可以组成一个elasticsearch集群。
  • <4>. Index(索引-数据库)

    • 索引包含一堆有相似结构的文档数据,比如可以有一个客户索引,商品分类索引,订单索引,索引有一个名称。一个index包含很多document,一个index就代表了一类类似的或者相同的document。比如说建立一个product index,商品索引,里面可能就存放了所有的商品数据,所有的商品document。
  • <5>. Type(类型-表)

    • 每个索引里都可以有一个或多个type,type是index中的一个逻辑数据分类,一个type下的document,都有相同的field,比如博客系统,有一个索引,可以定义用户数据type,博客数据type,评论数据type。
    • 商品index,里面存放了所有的商品数据,即商品document。但是商品分很多种类,每个种类的document的field可能不太一样,比如说电器商品,可能还包含一些诸如售后时间范围这样的特殊field;生鲜商品,还包含一些诸如生鲜保质期之类的特殊field
    • type,日化商品type,电器商品type,生鲜商品type
      • 日化商品type:product_id,product_name,product_desc,category_id,category_name
      • 电器商品type:product_id,product_name,product_desc,category_id,category_name,service_period
      • 生鲜商品type:product_id,product_name,product_desc,category_id,category_name,eat_period
    • 每一个type里面,都会包含一堆document
  • <6>. Document(文档-行)

    • 文档是es中的最小数据单元,一个document可以是一条客户数据,一条商品分类数据,一条订单数据,通常用JSON数据结构表示,每个index下的type中,都可以去存储多个document。
  • <7>. Field(字段-列)

    • Field是Elasticsearch的最小单位。一个document里面有多个field,每个field就是一个数据字段。
  • <8>. mapping(映射-约束)

    • 数据如何存放到索引对象上,需要有一个映射配置,包括:数据类型、是否存储、是否分词等。
    • 这样就创建了一个名为blog的Index。Type不用单独创建,在创建Mapping 时指定就可以。Mapping用来定义Document中每个字段的类型,即所使用的 analyzer、是否索引等属性,非常关键等。创建Mapping 的代码示例如下:
  • <9>. elasticsearch与数据库的类比

  • <10>. ES存入数据和搜索数据机制

  • <1>. 索引对象(index):存储数据的表结构 ,任何搜索数据,存放在索引对象上 。

  • <2>. 映射(mapping):数据如何存放到索引对象上,需要有一个映射配置, 包括:数据类型、是否存储、是否分词等。

  • <3>. 文档(document):一条数据记录,存在索引对象上 。

  • <4>. 文档类型(type):一个索引对象,存放多种类型数据,数据用文档类型进行标识。

3. Elasticsearch-5.6.2安装配置流程

(1) Elasticsearch准备

  • Java运行环境

(2) 安装包下载

(3) 解压Elasticsearch-5.6.2相关安装包到目标目录下:

  • tar -zxvf .tar.gz -C 目标目录

(4) 在elasticsearch路径下创建data和logs文件夹:

  • mkdir data
  • mkdir logs

(5) 修改配置文件:

  • 进入elasticsearch/config路径:

    • vi elasticsearch.yml
  • <1>. cluster.name如果要配置集群需要两个节点上的elasticsearch配置的cluster.name相同,都启动可以自动组成集群,这里如果不改cluster.name则默认是cluster.name=my-application。

  • <2>. nodename随意取但是集群内的各节点不能相同。

  • <3>. 修改后的每行前面不能有空格,修改后的:后面必须有一个空格

  • <4>. 关于端口:

    • 9200作为Http协议,主要用于外部通讯
    • 9300作为Tcp协议,jar之间就是通过tcp协议通讯
    • ES集群之间是通过9300进行通讯

(6) 拷贝配置好的elasticsearch到其他机器上

  • scp -r elasticsearch-5.6.2/ bigdata02:$PWD
  • scp -r elasticsearch-5.6.2/ bigdata03:$PWD

(7) 集群配置:

  • <1>. bigdata01:
    • vi elasticsearch.yml
    • node.master: true
      node.data: true
      discovery.zen.ping.unicast.hosts: ["bigdata01", "bigdata02", "bigdata03"]
      
  • <2>. bigdata02:
    • vi elasticsearch.yml
    • node.name: node-02
      node.master: false
      node.data: true
      network.host: 172.16.194.XXX
      discovery.zen.ping.unicast.hosts: ["bigdata01", "bigdata02", "bigdata03"]
      
  • <3>. bigdata03:
    • vi elasticsearch.yml
    • node.name: node-03
      node.master: false
      node.data: true
      network.host: 172.16.194.XXX
      discovery.zen.ping.unicast.hosts: ["bigdata01", "bigdata02", "bigdata03"]
      

(8) 配置linux系统环境

  • <1>. 编辑limits.conf添加如下内容:
    • sudo vi /etc/security/limits.conf
    •   * soft nofile 65536
        * hard nofile 131072
        * soft nproc 4096
        * hard nproc 4096
      
  • <2>. 进入limits.d目录下修改配置文件:
    • sudo vi /etc/security/limits.d/XX-nproc.conf (XX自行查看)
    • * soft nproc 4096
  • <3>. 修改配置sysctl.conf:
    • sudo vi /etc/sysctl.conf
    • vm.max_map_count=655360
    • 执行命令:
      • sudo sysctl -p

(9) 配置环境变量:

  • 修改配置文件:
    • vi /etc/profile
  • 增加以下内容:
    • export ELASTICSEARCH_HOME=/opt/module/elasticsearch-5.6.2
    • export PATH=$PATH:$ELASTICSEARCH_HOME/bin
  • 声明环境变量:
    • source /etc/profile

(10) 启动elasticsearch

  • elasticsearch
  • 可能报错:
    • <1>. An error report file with more information is saved as: /XXXXX/hs_err_pid82267.log

    • 原因:由于虚拟机设置,导致没有足够的内存供Java运行时环境继续运行

    • 解决方案:

      • vi /XXXX/elasticsearch/config/jvm.options
    • <2>. can not run elasticsearch as root

      • 原因:elasticsearch需要在非root用户下运行
      • 解决方案:
        • 在root用户下执行如下命令:
          • 创建用户:useradd es
          • 为用户设置密码:passwd es
          • 为用户赋予sudo权限:vi /etc/sudoers
          • 添加:es ALL=(ALL) ALL
          • 切换用户:su es
          • 修改elasticsearch权限:chown -R es /XXXX/elasticsearch/
    • <3>. 若集群开启前测试过单节点,首次启动保证data/目录为空

(11) 测试elasticsearch

  • 方法1——Linux中输入:curl http://XXXXXXX:9200
  • 方法1——浏览器输入:http://XXXXXXX:9200
  • 显示相关信息说明成功

(12) 安装elasticsearch-head.crx插件:

  • <1>. 给出免翻墙的下载地址:www.crx4chrome.com/crx/57382/

  • <2>. 打开谷歌浏览器,地址栏输入chrome://extensions/ 或者进入“更多工具”——>“扩展程序”,打开扩展程序,拖入以安装即可。然后在右上角可以看到一个小图标,如下图:

  • <3>. 点击出现如下界面:

    • 看到status是 green,证明启动成功:
      • Green - 一切运行正常(集群功能齐全)
      • Yellow - 所有数据是可以获取的,但是一些复制品还没有被分配(集群功能齐全)
      • Red - 一些数据因为一些原因获取不到(集群部分功能不可用)

(13) 停止elasticsearch

  • kill -9 进程号

4. Elasticsearch Java-API操作

(1) 介绍

  • Elasticsearch的Java客户端非常强大,可以建立一个嵌入式实例并在必要时运行管理任务。
  • 运行一个Java应用程序和Elasticsearch时,有两种操作模式可供使用。该应用程序可在Elasticsearch集群中扮演更加主动或更加被动的角色。在更加主动的情况下(称为Node Client),应用程序实例将从集群接收请求,确定哪个节点应处理该请求,就像正常节点所做的一样(应用程序甚至可以托管索引和处理请求)。另一种模式称为Transport Client,它将所有请求都转发到另一个Elasticsearch节点,由后者来确定最终目标。

(2) API基本操作

  • <1>. 操作环境准备

    • a. 创建maven工程
    • b. 添加pom文件:
      <dependencies>
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>3.8.1</version>
              <scope>test</scope>
          </dependency>
          <!-- ES API操作(注意版本对应) -->
          <dependency>
              <groupId>org.elasticsearch</groupId>
              <artifactId>elasticsearch</artifactId>
              <version>5.6.2</version>
          </dependency>
          <!-- ES连接(注意版本对应) -->
          <dependency>
              <groupId>org.elasticsearch.client</groupId>
              <artifactId>transport</artifactId>
              <version>5.6.2</version>
          </dependency>
          <dependency>
              <groupId>org.apache.logging.log4j</groupId>
              <artifactId>log4j-core</artifactId>
              <version>2.9.0</version>
          </dependency>
       </dependencies>
      
      • 注意:当直接在ElasticSearch 建立文档对象时,如果索引不存在的,默认会自动创建,映射采用默认方式。
  • <2>. 获取Transport Client

    • a. ElasticSearch服务默认端口9300

    • b. Web管理平台端口9200

    •    private TransportClient client;
      
         @SuppressWarnings("unchecked")
         @Before
         public void getClient() throws Exception {
      
                // 1 设置连接的集群名称
                Settings settings = Settings.builder().put("cluster.name", "my-application").build();
      
                // 2 连接集群
                client = new PreBuiltTransportClient(settings);
                client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("hadoop102"), 9300));
      
                // 3 打印集群名称
                System.out.println(client.toString());
         }
      
    • **注意:**如果显示log4j2报错,在resource目录下创建一个文件命名为log4j2.xml并添加如下内容:

      <?xml version="1.0" encoding="UTF-8"?>
      <Configuration status="warn">
          <Appenders>
              <Console name="Console" target="SYSTEM_OUT">
                  <PatternLayout pattern="%m%n"/>
              </Console>
          </Appenders>
          <Loggers>
              <Root level="INFO">
                  <AppenderRef ref="Console"/>
              </Root>
          </Loggers>
      </Configuration>
      
    • <3>. 创建索引

    public void createIndex_blog(){
                // 1 创建索引
                  client.admin().indices().prepareCreate("blog2").get();
                   
                    // 2 关闭连接
                    client.close();
         }
    
  • <4>. 删除索引

         @Test
         public void deleteIndex(){
                // 1 删除索引
                client.admin().indices().prepareDelete("blog2").get();
               
                // 2 关闭连接
                client.close();
         }
    
  • <5>. 新建文档

    • a. 源数据json串

      • 当直接在ElasticSearch建立文档对象时,如果索引不存在的,默认会自动创建,映射采用默认方式。
    •    @Test
         public void createIndexByMap() {
      
                // 1 文档数据准备
                Map<String, Object> json = new HashMap<String, Object>();
                json.put("id", "2");
                json.put("title", "基于Lucene的搜索服务器");
                json.put("content", "它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口");
      
                // 2 创建文档
                IndexResponse indexResponse = client.prepareIndex("blog", "article", "2").setSource(json).execute().actionGet();
      
                // 3 打印返回的结果
                System.out.println("index:" + indexResponse.getIndex());
                System.out.println("type:" + indexResponse.getType());
                System.out.println("id:" + indexResponse.getId());
                System.out.println("version:" + indexResponse.getVersion());
                System.out.println("result:" + indexResponse.getResult());
      
                // 4 关闭连接
                client.close();
         }
      
    • b. 源数据map方式添加json

    •    @Test
         public void createIndexByMap() {
      
                // 1 文档数据准备
                Map<String, Object> json = new HashMap<String, Object>();
                json.put("id", "2");
                json.put("title", "基于Lucene的搜索服务器");
                json.put("content", "它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口");
      
                // 2 创建文档
                IndexResponse indexResponse = client.prepareIndex("blog", "article", "2").setSource(json).execute().actionGet();
      
                // 3 打印返回的结果
                System.out.println("index:" + indexResponse.getIndex());
                System.out.println("type:" + indexResponse.getType());
                System.out.println("id:" + indexResponse.getId());
                System.out.println("version:" + indexResponse.getVersion());
                System.out.println("result:" + indexResponse.getResult());
      
                // 4 关闭连接
                client.close();
         }
      
    • c. 源数据es构建器添加json

    •    @Test
         public void createIndex() throws Exception {
      
                // 1 通过es自带的帮助类,构建json数据
                XContentBuilder builder = XContentFactory.jsonBuilder().startObject().field("id", 3).field("title", "基于Lucene的搜索服务器").field("content", "它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。")
                              .endObject();
      
                // 2 创建文档
                IndexResponse indexResponse = client.prepareIndex("blog", "article", "3").setSource(builder).get();
      
                // 3 打印返回的结果
                System.out.println("index:" + indexResponse.getIndex());
                System.out.println("type:" + indexResponse.getType());
                System.out.println("id:" + indexResponse.getId());
                System.out.println("version:" + indexResponse.getVersion());
                System.out.println("result:" + indexResponse.getResult());
      
                // 4 关闭连接
                client.close();
         }
      
  • <6>. 搜索文档数据

    • a. 单个索引
    •    @Test
         public void getData() throws Exception {
               
                // 1 查询文档
                GetResponse response = client.prepareGet("blog", "article", "1").get();
               
                // 2 打印搜索的结果
                System.out.println(response.getSourceAsString());
               
                // 3 关闭连接
                client.close();
         }
      
    • b. 多个索引
    •    @Test
         public void getMultiData() {
               
                // 1 查询多个文档
                MultiGetResponse response = client.prepareMultiGet().add("blog", "article", "1").add("blog", "article", "2", "3").add("blog", "article", "2").get();
               
                // 2 遍历返回的结果
                for(MultiGetItemResponse itemResponse:response){
                       GetResponse getResponse = itemResponse.getResponse();
                      
                       // 如果获取到查询结果
                       if (getResponse.isExists()) {
                              String sourceAsString = getResponse.getSourceAsString();
                              System.out.println(sourceAsString);
                       }
                }
               
                // 3 关闭资源
                client.close();
         }
      
  • <7>. 更新文档数据

    • a. update
    •    @Test
         public void updateData() throws Throwable {
      
                // 1 创建更新数据的请求对象
                UpdateRequest updateRequest = new UpdateRequest();
                updateRequest.index("blog");
                updateRequest.type("article");
                updateRequest.id("3");
      
                updateRequest.doc(XContentFactory.jsonBuilder().startObject()
                              // 对没有的字段添加, 对已有的字段替换
                              .field("title", "基于Lucene的搜索服务器")
                              .field("content","它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。大数据前景无限")
                              .field("createDate", "2017-8-22").endObject());
      
                // 2 获取更新后的值
                UpdateResponse indexResponse = client.update(updateRequest).get();
               
                // 3 打印返回的结果
                System.out.println("index:" + indexResponse.getIndex());
                System.out.println("type:" + indexResponse.getType());
                System.out.println("id:" + indexResponse.getId());
                System.out.println("version:" + indexResponse.getVersion());
                System.out.println("create:" + indexResponse.getResult());
      
                // 4 关闭连接
                client.close();
         }
      
    • b. upsert
      • 设置查询条件, 查找不到则添加IndexRequest内容,查找到则按照UpdateRequest更新。
    •    @Test
         public void testUpsert() throws Exception {
      
                // 设置查询条件, 查找不到则添加
                IndexRequest indexRequest = new IndexRequest("blog", "article", "5")
                              .source(XContentFactory.jsonBuilder().startObject().field("title", "搜索服务器").field("content","它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。").endObject());
               
                // 设置更新, 查找到更新下面的设置
                UpdateRequest upsert = new UpdateRequest("blog", "article", "5")
                              .doc(XContentFactory.jsonBuilder().startObject().field("user", "李四").endObject()).upsert(indexRequest);
      
                client.update(upsert).get();
                client.close();
         }
      
  • <8>. 删除文档数据(prepareDelete)

    •    @Test
         public void deleteData() {
               
                // 1 删除文档数据
                DeleteResponse indexResponse = client.prepareDelete("blog", "article", "5").get();
      
                // 2 打印返回的结果
                System.out.println("index:" + indexResponse.getIndex());
                System.out.println("type:" + indexResponse.getType());
                System.out.println("id:" + indexResponse.getId());
                System.out.println("version:" + indexResponse.getVersion());
                System.out.println("found:" + indexResponse.getResult());
      
                // 3 关闭连接
                client.close();
         }
      
  • <9>. 条件查询(QueryBuilder)

    • a. 查询所有(matchAllQuery)
      •  @Test
         public void matchAllQuery() {
               
                // 1 执行查询
                SearchResponse searchResponse = client.prepareSearch("blog").setTypes("article")
                              .setQuery(QueryBuilders.matchAllQuery()).get();
        
                // 2 打印查询结果
                SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象
                System.out.println("查询结果有:" + hits.getTotalHits() + "条");
        
                for (SearchHit hit : hits) {
             System.out.println(hit.getSourceAsString());//打印出每条结果
          }
        
                // 3 关闭连接
                client.close();
         }
        
    • b. 对所有字段分词查询(queryStringQuery)
      •  @Test
         public void query() {
                // 1 条件查询
                SearchResponse searchResponse = client.prepareSearch("blog").setTypes("article")
                              .setQuery(QueryBuilders.queryStringQuery("全文")).get();
        
                // 2 打印查询结果
                SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象
                System.out.println("查询结果有:" + hits.getTotalHits() + "条");
               
                for (SearchHit hit : hits) {
             System.out.println(hit.getSourceAsString());//打印出每条结果
          }
               
                // 3 关闭连接
                client.close();
         }
        
    • c. 通配符查询(wildcardQuery)
      • *:表示多个字符(0个或多个字符)
      • :表示单个字符
      •  @Test
         public void wildcardQuery() {
        
                // 1 通配符查询
                SearchResponse searchResponse = client.prepareSearch("blog").setTypes("article")
                              .setQuery(QueryBuilders.wildcardQuery("content", "*全*")).get();
        
                // 2 打印查询结果
                SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象
                System.out.println("查询结果有:" + hits.getTotalHits() + "条");
        
                for (SearchHit hit : hits) {
             System.out.println(hit.getSourceAsString());//打印出每条结果
          }
        
                // 3 关闭连接
                client.close();
         }
        
    • d. 词条查询(TermQuery)
      •  @Test
         public void termQuery() {
               
                // 1 第一field查询
                SearchResponse searchResponse = client.prepareSearch("blog").setTypes("article")
                              .setQuery(QueryBuilders.termQuery("content", "全文")).get();
               
                // 2 打印查询结果
                SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象
                System.out.println("查询结果有:" + hits.getTotalHits() + "条");
        
                for (SearchHit hit : hits) {
             System.out.println(hit.getSourceAsString());//打印出每条结果
          }
        
                // 3 关闭连接
                client.close();
         }
        
    • e. 模糊查询(fuzzy)
      •  @Test
         public void fuzzy() {
               
                // 1 模糊查询
                SearchResponse searchResponse = client.prepareSearch("blog").setTypes("article")
                              .setQuery(QueryBuilders.fuzzyQuery("title", "lucene")).get();
               
                // 2 打印查询结果
                SearchHits hits = searchResponse.getHits(); // 获取命中次数,查询结果有多少对象
                System.out.println("查询结果有:" + hits.getTotalHits() + "条");
        
                Iterator<SearchHit> iterator = hits.iterator();
        
                while (iterator.hasNext()) {
                       SearchHit searchHit = iterator.next(); // 每个查询对象
        
                       System.out.println(searchHit.getSourceAsString()); // 获取字符串格式打印
                }
        
                // 3 关闭连接
                client.close();
         }
        
  • <10>. 映射相关操作

    •    @Test
         public void createMapping() throws Exception {
               
                // 1设置mapping
                XContentBuilder builder = XContentFactory.jsonBuilder()
                              .startObject()
                                     .startObject("article")
                                            .startObject("properties")
                                                   .startObject("id1")
                                                          .field("type", "string")
                                                          .field("store", "yes")
                                                   .endObject()
                                                   .startObject("title2")
                                                          .field("type", "string")
                                                          .field("store", "no")
                                                   .endObject()
                                                   .startObject("content")
                                                          .field("type", "string")
                                                          .field("store", "yes")
                                                   .endObject()
                                            .endObject()
                                     .endObject()
                              .endObject();
      
                // 2 添加mapping
                PutMappingRequest mapping = Requests.putMappingRequest("blog4").type("article").source(builder);
               
                client.admin().indices().putMapping(mapping).get();
               
                // 3 关闭资源
                client.close();
         }
      

5. IK分词器

(1) IK分词器安装

  • 方法1:在线下载

    • 进入elasticsearch/plugins目录
    • elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.6.2/elasticsearch-analysis-ik-5.6.2.zip
    • 解压:unzip elasticsearch-analysis-ik-5.6.2.zip -d ik
  • 方法2:离线上传

    • 进入elasticsearch/plugins目录
    • 上传文件elasticsearch-analysis-ik-5.6.2.zip
    • 解压:unzip elasticsearch-analysis-ik-5.6.2.zip -d ik
  • 方法3:本地安装

    • elasticsearch-plugin install -f file:///XXXX/elasticsearch-analysis-ik-5.6.2.zip
  • 解压:unzip elasticsearch-analysis-ik-5.6.2.zip -d ik

  • 注意:

    • IK分词器版本需要与es版本相同
    • 若提示unzip未找到,执行命令yum install -y unzip zip安装
    • ik目录内即具体内容,不含中间文件夹

(2) IK分词器使用

  • 默认中文分词器效果:
    • 命令行查看:
    • curl -XGET 'http://bigdata01:9200/_analyze?pretty&analyzer=standard' -d '冰冻三尺非一日之寒'
    • {
        "tokens" : [
          {
            "token" : "冰",
            "start_offset" : 0,
            "end_offset" : 1,
            "type" : "<IDEOGRAPHIC>",
            "position" : 0
          },
          {
            "token" : "冻",
            "start_offset" : 1,
            "end_offset" : 2,
            "type" : "<IDEOGRAPHIC>",
            "position" : 1
          },
          {
            "token" : "三",
            "start_offset" : 2,
            "end_offset" : 3,
            "type" : "<IDEOGRAPHIC>",
            "position" : 2
          },
          {
            "token" : "尺",
            "start_offset" : 3,
            "end_offset" : 4,
            "type" : "<IDEOGRAPHIC>",
            "position" : 3
          },
          {
            "token" : "非",
            "start_offset" : 4,
            "end_offset" : 5,
            "type" : "<IDEOGRAPHIC>",
            "position" : 4
          },
          {
            "token" : "一",
            "start_offset" : 5,
            "end_offset" : 6,
            "type" : "<IDEOGRAPHIC>",
            "position" : 5
          },
          {
            "token" : "日",
            "start_offset" : 6,
            "end_offset" : 7,
            "type" : "<IDEOGRAPHIC>",
            "position" : 6
          },
          {
            "token" : "之",
            "start_offset" : 7,
            "end_offset" : 8,
            "type" : "<IDEOGRAPHIC>",
            "position" : 7
          },
          {
            "token" : "寒",
            "start_offset" : 8,
            "end_offset" : 9,
            "type" : "<IDEOGRAPHIC>",
            "position" : 8
          }
        ]
      }
      
  • IK分词器:
  • <1>. ik_smart模式:
    • curl -H "Content-Type:application/json" -XGET 'http://bigdata01:9200/_analyze?pretty' -d '{"analyzer":"ik_smart","text":"冰冻三尺非一日之寒"}'
    • {
        "tokens" : [
          {
            "token" : "冰冻三尺非一日之寒",
            "start_offset" : 0,
            "end_offset" : 9,
            "type" : "CN_WORD",
            "position" : 0
          }
        ]
      }
      
  • <2>. ik_max_word模式:
    • curl -H "Content-Type:application/json" -XGET 'http://bigdata01:9200/_analyze?pretty' -d '{"analyzer":"ik_max_word","text":"冰冻三尺非一日之寒"}'
    • {
        "tokens" : [
          {
            "token" : "冰冻三尺非一日之寒",
            "start_offset" : 0,
            "end_offset" : 9,
            "type" : "CN_WORD",
            "position" : 0
          },
          {
            "token" : "冰冻三尺",
            "start_offset" : 0,
            "end_offset" : 4,
            "type" : "CN_WORD",
            "position" : 1
          },
          {
            "token" : "冰冻",
            "start_offset" : 0,
            "end_offset" : 2,
            "type" : "CN_WORD",
            "position" : 2
          },
          {
            "token" : "三尺",
            "start_offset" : 2,
            "end_offset" : 4,
            "type" : "CN_WORD",
            "position" : 3
          },
          {
            "token" : "三",
            "start_offset" : 2,
            "end_offset" : 3,
            "type" : "TYPE_CNUM",
            "position" : 4
          },
          {
            "token" : "尺",
            "start_offset" : 3,
            "end_offset" : 4,
            "type" : "COUNT",
            "position" : 5
          },
          {
            "token" : "非一日之寒",
            "start_offset" : 4,
            "end_offset" : 9,
            "type" : "CN_WORD",
            "position" : 6
          },
          {
            "token" : "一日",
            "start_offset" : 5,
            "end_offset" : 7,
            "type" : "CN_WORD",
            "position" : 7
          },
          {
            "token" : "一",
            "start_offset" : 5,
            "end_offset" : 6,
            "type" : "TYPE_CNUM",
            "position" : 8
          },
          {
            "token" : "日",
            "start_offset" : 6,
            "end_offset" : 7,
            "type" : "COUNT",
            "position" : 9
          },
          {
            "token" : "之",
            "start_offset" : 7,
            "end_offset" : 8,
            "type" : "CN_CHAR",
            "position" : 10
          },
          {
            "token" : "寒",
            "start_offset" : 8,
            "end_offset" : 9,
            "type" : "CN_CHAR",
            "position" : 11
          }
        ]
      }
      

(3) Java-API操作

  •   /**
       * @ClassName: IKTest
       * @Description
       * @Author: YBCarry
       * @Date2019-05-06 10:27
       * @Version: V1.0
       **/
      public class IKTest {
      
          // 对es的所有操作都是通过client
          private TransportClient client;
      
          @SuppressWarnings("unchecked")
          @Before
          /**所有Test执行前都会执行此方法*/
          public void getClient() throws Exception {
      
              // 1 设置连接的集群名称
              Settings settings = Settings.builder().put("cluster.name", "YBCarry-application").build();
      
              // 2 连接集群
              client = new PreBuiltTransportClient(settings);
              //ES集群之间是通过9300进行通讯
              client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("bigdata01"), 9300));
          }
      
          @Test
          /**创建索引*/
          public void createIndex_blog() {
      
              // 1 创建索引
              client.admin().indices().prepareCreate("blog_ik").get();
      
              // 2 关闭连接
              client.close();
          }
      
          @Test
          /**创建使用ik分词器的mapping*/
          public void createMapping() throws Exception {
      
              // 1设置mapping
              XContentBuilder builder = XContentFactory.jsonBuilder()
                      .startObject()
                      .startObject("article")
                      .startObject("properties")
                      .startObject("id1")
                      .field("type", "string")
                      .field("store", "yes")
                      .field("analyzer","ik_smart")
                      .endObject()
                      .startObject("title2")
                      .field("type", "string")
                      .field("store", "no")
                      .field("analyzer","ik_smart")
                      .endObject()
                      .startObject("content")
                      .field("type", "string")
                      .field("store", "yes")
                      .field("analyzer","ik_smart")
                      .endObject()
                      .endObject()
                      .endObject()
                      .endObject();
      
              // 2 添加mapping
              PutMappingRequest mapping = Requests.putMappingRequest("blog_ik").type("article").source(builder);
              client.admin().indices().putMapping(mapping).get();
      
              // 3 关闭资源
              client.close();
          }
      
          @Test
          /**以map形式创建文档*/
          public void createDocumentByMap() {
      
              HashMap<String, String> map = new HashMap();
              map.put("id1", "2");
              map.put("title2", "Lucene");
              map.put("content", "它提供了一个分布式的web接口");
      
              IndexResponse response = client.prepareIndex("blog_ik", "article", "3").setSource(map).execute().actionGet();
      
              //打印返回的结果
              System.out.println("结果:" + response.getResult());
              System.out.println("id:" + response.getId());
              System.out.println("index:" + response.getIndex());
              System.out.println("type:" + response.getType());
              System.out.println("版本:" + response.getVersion());
      
              //关闭资源
              client.close();
          }
      
          @Test
          /**词条查询*/
          public void queryTerm() {
      
              SearchResponse response = client.prepareSearch("blog_ik").setTypes("article").setQuery(QueryBuilders.termQuery("content","提供")).get();
      
              //获取查询命中结果
              SearchHits hits = response.getHits();
      
              System.out.println("结果条数:" + hits.getTotalHits());
      
              for (SearchHit hit : hits) {
                  System.out.println(hit.getSourceAsString());
              }
          }
      }
    

6. Logstash

(1) Logstash简介

  • logstash是一个数据分析软件,主要目的是分析log日志。整一套软件可以当作一个MVC模型,logstash是controller层,Elasticsearch是一个model层,kibana是view层。
  • 首先将数据传给logstash,它将数据进行过滤和格式化(转成JSON格式),然后传给Elasticsearch进行存储、建搜索的索引,kibana提供前端的页面再进行搜索和图表可视化,它是调用Elasticsearch的接口返回的数据进行可视化。logstash和Elasticsearch是用Java写的,kibana使用node.js框架。

(2) Logstash安装

  • 访问 www.elastic.co/downloads/l… 页面找对应操作系统和版本,下载即可。
  • 配置环境变量:
    • 修改配置文件:
      • vi /etc/profile
    • 增加以下内容:
      • export LOGSTASH_HOME=/opt/module/logstash-5.6.16
      • export PATH=$PATH:$LOGSTASH_HOME/bin
    • 声明环境变量:
      • source /etc/profile

(3) Logstash启动

  • logstash -e 命令
    • e.g. logstash -e 'input{stdin{}}output{stdout{codec=>rubydebug}}'
  • logstash -f 脚本文件
  • **注意:**如果出现如下报错,请调高虚拟机内存容量。
    • Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000c5330000, 986513408, 0) failed; error='Cannot allocate memory' (errno=12)

(4) Logstash配置

  • Logstash文档查看:
    • 进入https://www.elastic.co/,选择Lean -> Documentation -> Get Started
  • <1>. input配置
    • 读取文件(File)
      • input {
            file {
                path => ["/var/log/*.log", "/var/log/message"]
                type => "system"
                start_position => "beginning"
        }
        
        }
        output{stdout{codec=>rubydebug}}
        
    • 标准输入(Stdin)
      • input {
            stdin {
                add_field => {"key" => "value"}
                codec => "plain"
                tags => ["add"]
                type => "std"
            }
        }
        output{stdout{codec=>rubydebug}}
        
  • <2>. codec配置
    • Logstash 不只是一个input | filter | output 的数据流,而是一个 input | decode | filter | encode | output 的数据流,codec 就是用来 decode、encode 事件的。
    • Json
    • input {
          stdin {
              add_field => {"key" => "value"}
              codec => "json"
              type => "std"
          }
      }
      output{stdout{codec=>rubydebug}}
      
  • <3>. filter配置
    • Grok插件
      • logstash拥有丰富的filter插件,它们扩展了进入过滤器的原始数据,进行复杂的逻辑处理,甚至可以无中生有的添加新的 logstash 事件到后续的流程中去!Grok 是 Logstash 最重要的插件之一。也是迄今为止使蹩脚的、无结构的日志结构化和可查询的最好方式。Grok在解析 syslog logs、apache and other webserver logs、mysql logs等任意格式的文件上表现完美。
      • 这个工具非常适用于系统日志,Apache和其他网络服务器日志,MySQL日志等。
      • 配置:
        • input {
              stdin {
                  type => "std"
              }
          }
          filter {
            grok {
              match=>{"message"=> "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}" }
            }
          }
          output{stdout{codec=>rubydebug}}
          
      • grok模式的语法如下:
        • %{SYNTAX:SEMANTIC}
          • SYNTAX:代表匹配值的类型,例如3.44可以用NUMBER类型所匹配,127.0.0.1可以使用IP类型匹配。
          • SEMANTIC:代表存储该值的一个变量名称,例如 3.44 可能是一个事件的持续时间,127.0.0.1可能是请求的client地址。所以这两个值可以用 %{NUMBER:duration} %{IP:client} 来匹配。
      • 也可以选择将数据类型转换添加到Grok模式。默认情况下,所有语义都保存为字符串。如果您希望转换语义的数据类型,例如将字符串更改为整数,则将其后缀为目标数据类型。例如%{NUMBER:num:int}将num语义从一个字符串转换为一个整数。目前唯一支持的转换是int和float。
      • Logstash附带约120个模式。在这里查找:github.com/logstash-pl…
    • GeoIP 地址查询归类
      • GeoIP 是最常见的免费 IP 地址归类查询库,同时也有收费版可以采购。GeoIP 库可以根据 IP 地址提供对应的地域信息,包括国别,省市,经纬度等,对于可视化地图和区域统计非常有用。
      • 配置:
        • input {
              stdin {
                  type => "std"
              }
          }
          filter {
              geoip {
                  source => "message"
              }
          }
          output{stdout{codec=>rubydebug}}
          
  • <4>. output配置
    • 保存成文件(File)

      • 通过日志收集系统将分散在数百台服务器上的数据集中存储在某中心服务器上,这是运维最原始的需求。Logstash 当然也能做到这点。

      • 与 LogStash::Inputs::File 不同, LogStash::Outputs::File 里可以使用 sprintf format 格式来自动定义输出到带日期命名的路径。

      • 配置:

      • input {
              stdin {
                  type => "std"
              }
          }
          output {
              file {
                  path => "../data_test/%{+yyyy}/%{+MM}/%{+dd}/%{host}.log"
                  codec => line { format => "custom format: %{message}"}
              }
          }
        
      • 启动后输入,可看到文件

    • 服务器间传输文件(File)

      • 配置:
        • 接收日志服务器配置:
        • input {
            tcp {
                 mode => "server"
                 port => 9600
                 ssl_enable => false
            }
          }
          filter {
              json {
                  source => "message"
              }
          }
          output {
              file {
                  path => "/home/hduser/app/logstash-6.6.2/data_test/%{+YYYY-MM-dd}/%{servip}-%{filename}"
                  codec => line { format => "%{message}"}
              }
          }
          
        • 发送日志服务器配置:
        • input{
              file {
                  path => ["/home/hduser/app/logstash-6.6.2/data_test/send.log"]
                  type => "ecolog"
                  start_position => "beginning"
              }
          }
          filter {
              if [type] =~ /^ecolog/ {
                  ruby {
                      code => "file_name = event.get('path').split('/')[-1]
                                              event.set('file_name',file_name)
                                              event.set('servip','接收方ip')"
                  }
                  mutate {
                      rename => {"file_name" => "filename"}
                  }
              }
          }
          output {
                 tcp {
                  host  => "接收方ip"
                  port  => 9600
                  codec => json_lines
              }
          }
          
        - 从发送方发送message,接收方可以看到写出文件。
        
        
    • 写入到ES

      • 配置:
      •   input {
              stdin {
                  type => "log2es"
              }
          }
          output {
              elasticsearch {
                  hosts => ["192.168.109.133:9200"]
                  index => "logstash-%{type}-%{+YYYY.MM.dd}"
                  document_type => "%{type}"
                  sniffing => true
                  template_overwrite => true
              }
          }
        

7. Kibana

(1) Kibana简介

  • Kibana是一个开源的分析和可视化平台,设计用于和Elasticsearch一起工作。可以用Kibana来搜索,查看,并和存储在Elasticsearch索引中的数据进行交互。执行高级数据分析,并且以各种图标、表格和地图的形式可视化数据。Kibana使得理解大量数据变得很容易。它简单的、基于浏览器的界面能够快速创建和共享动态仪表板,实时显示Elasticsearch查询的变化。

(2) Kibana安装

  • 解压安装包:
    • tar -zxvf .tar.gz -C 目标目录
  • 修改 kibana.yml 配置文件:
    • 进入目录:vi kibana/config
    • server.port: 5601
      server.host: "172.16.194.128"   ----部署kinana服务器的ip
      elasticsearch.hosts: ["http://172.16.194.128:9200"]
      kibana.index: ".kibana"
      
  • 配置环境变量:
    • 修改配置文件:
      • vi /etc/profile
    • 增加以下内容:
      • export KIBANA_HOME=/opt/module/kibana-5.6.15
      • export PATH=$PATH:$KIBANA_HOME/bin
    • 声明环境变量:
      • source /etc/profile

(3) Kibana启动

  • Kibana
  • 启动kibana,报错:
    • [error][status][plugin:remote_clusters@6.6.2] Status changed from red to red - X-Pack plugin is not installed on the [data] Elasticsearch cluster.
    • 解决——卸载x-pack插件:
      • elasticsearch-plugin remove x-pack
      • kibana-plugin remove x-pack