ElasticSearch全文搜索引擎-简单使用

1. 概述

ElasticSearch是基于lucence的全文搜索引擎,用java语言作为开发,可以直接集成到项目中,使用json/xml的方式通过http来索引数据!

特点:

①:基于lucene

②:使用简单

③:支持分布式

④:使用Restful API

⑤:支持多种客户端

⑥:可以处理PB级的数据

⑦:实时文件存储、速度快、性能高

⑧:支持集群

⑨:处理json格式数据

2. ES安装

2.1:安装服务

ES服务只依赖于JDK,推荐使用JDK1.7+

① 下载ES安装包

官方下载地址:https://www.elastic.co/downloads/elasticsearch

② 运行ES

bin/elasticsearch.bat

③ 验证

访问:http://localhost:9200/

2.2:ES交互方式

ES和所有客户端的交互都是使用JSON格式的数据!

其他所有程序语言都可以使用RESTful API,通过9200端口的与ES进行通信,java通过9300端口交互!

2.3: 辅助管理工具Kibana5

① Kibana5.2.2下载地址:https://www.elastic.co/downloads/kibana

② 解压并编辑config/kibana.yml,设置elasticsearch.url的值为已启动的ES

③ 启动Kibana5 : bin\kibana.bat

④ 默认访问地址:http://localhost:5601

Discover:可视化查询分析器

Visualize:统计分析图表

Dashboard:自定义主面板(添加图表)

Timelion:Timelion是一个kibana时间序列展示组件(暂时不用)

Dev Tools :Console(同CURL/POSTER,操作ES代码工具,代码提示,很方便)

Management:管理索引库(index)、已保存的搜索和可视化结果(save objects)、设置 kibana 服务器属性。

3. Restful风格

Restful是一种面向资源的架构风格,用名词描述资源,用动词描述操作!

例如:

获取id为1的用户:

GET /user/1

获取全部用户:

GET /users

删除id为1的用户:

DELETE /user/1

添加用户:

PUT /user

修改用户:

POST /user

使用Restful的优点:

①:透明性,暴露资源存在。

②:充分利用 HTTP 协议本身语义。

③:无状态,这点非常重要。在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了复杂度。

3. ES相关概念

(1)Near Realtime(NRT)近实时:ES从录入数据到可以被搜索可以达到秒级别,延迟很小!

(2)Index:索引库,包含一堆有相似结构的文档数据,就好比Mysql中的某一个数据库!

(3)**Type:类型 **每个索引库里都可以有一个或多个type,一个type下的document,都有相同的field,就好比Mysql中的某一张表!

(4)Document&field:文档,es 中的最小数据单元,一个document就是一条数据,好比Mysql中的某一张表中的某一行,一个Document可以有多个filed,好比Mysql中某一张表中的某一列!

(5)Cluster:集群,一个集群包含多个节点,可以通过配置指定哪些节点属于哪一个集群!

(6)Node:节点,集群中的一个节点,节点也有一个名称(默认是随机分配的),节点名称很重要(在执行运维管理操作的时候),默认节点会去加入一个名称为“elasticsearch”的集群,如果直接启动一堆节点,那么它们会自动组成一个elasticsearch集群,当然一个节点也可以组成一个elasticsearch集群

(7)shard(分片):单台机器无法存储大量数据,es可以将一个索引中的数据切分为多个shard,分布在多台服务器上存储。有了shard就可以横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务器上去执行,提升吞吐量和性能。每个shard都是一个lucene index。

(8)replica(复制品):任何一个服务器随时可能故障或宕机,此时shard可能就会丢失,因此可以为每个shard创建多个replica副本。replica可以在shard故障时提供备用服务,保证数据不丢失,多个replica还可以提升搜索操作的吞吐量和性能。primary shard(建立索引时一次设置,不能修改,默认5个),replica shard(随时修改数量,默认1个),默认每个索引10个shard,5个primary shard,5个replica shard,最小的高可用配置,是2台服务器。

4. ES集群

为什么需要集群

  • 解决高并发

  • 解决海量数据存储

  • 解决单点故障

ES节点类型

默认情况下,elasticsearch集群中每个节点都可以是主节点,即可存储数据也可提供查询服务,身兼数职,如果不配置它的角色,在高数据和高并发的场景下,如果该节点宕机,则数据就会分散不完整!

配置 解释
node.master true 是否是主节点
node.data true 是否存储数据
  • 主节点

node.master=true,代表该节点有成为主资格,主节点的主要职责是和集群操作相关的内容,如创建或删除索引,跟踪哪些节点是群集的一部分,并决定哪些分片分配给相关的节点。一般会把主节点和数据节点分开,node.master=true , node.data=false

  • 数据节点

node.data=true,数据节点主要是存储索引数据的节点,主要对文档进行增删改查操作,聚合操作等,数据节点对CPU,IO,内存要求较高,优化节点的时候需要做状态监控,资源不够时要做节点扩充。配置:node.master=false,node.data=true

  • 负载均衡节点

当主节点和数据节点配置都设置为false的时候,该节点只能处理路由请求,处理搜索,分发索引操作等,从本质上来说该客户节点表现为智能负载平衡器。配置:mode.master=false,mode.data=false

最佳实践

master节点:三台及以上,负责维护集群的状态

data节点:负责存储数据

client节点(node.master: false node.data: false):负责处理用户请求,转发请求,负载均衡!

5. ES集群理解

shard&replica机制

  • 一个index索引库包含多个shard(分片),一个shard是最小的存储单元
  • replica shard是primary shard的副本,负责容错,读写分离!
  • primary shard的数量在创建索引的时候就固定了,replica shard的数量可以随时修改
  • primary shard的默认数量是5,replica默认是1,默认有10个shard,5个primary shard,5个replica shard
  • primary shard不能和自己的replica shard放在同一个节点上,如果该节点宕机,primary shard和replica shard数据都会丢失,起不到容错的作用
  • 增减节点,shard会自动分配
  • 一个Document只可能存在一个primary shard及其对应的replica shard上,不可能存在多个primary shard上
图解Shard分配
a.单node环境下创建index

​ 单node环境下,创建一个index,有3个primary shard,3个replica shard

[图片上传失败...(image-df49c1-1582342886239)]

  • 这个时候,只会将3个primary shard分配到仅有的一个node上去,另外3个replica shard是无法分配的
  • 集群status是yellow
  • 集群可以正常工作,但是一旦出现节点宕机,数据全部丢失,而且集群不可用,无法承接任何请求
b.两个node环境下创建index

​ 2个node环境下,创建一个index, 3个primary shard,3个replica shard

[图片上传失败...(image-d6cb6f-1582342886240)]

c.扩容极限,提升容错

​ 如何让性能达到更优?

  • 每个Node更少的Shard,每个Shard资源跟充沛,性能更高
  • 扩容极限:6个shard(3 primary,3 replica),最多扩容到6台机器,每个shard可以占用单台服务器的所有资源,性能最好
  • 超出扩容极限,动态修改replica数量,9个shard(3primary,6 replica),扩容到9台机器,比3台机器时,拥有3倍的读吞吐量
容错机制-Master选举
  • master node宕机,自动进行master选举, - Red

    当某个PrimaryShard (主分片)宕机,这个PrimaryShard的某个ReplicShard(备分片)会通过选举成为PrimaryShard。

  • Replica容错:将replica提升为新的primary shard,- yellow

    新的主分片选举成功后,那么保证了主分片的完整性,但是少了一个备分片,所以状态变成了黄色

  • 重启宕机节点:会生成新的ReplicShard,如果宕机前有数据,会像恢复之前的数据,然后从PrimaryShard中拷贝新的数据,这样做的好处是:1.恢复性能好 , 2.可以避免数据同步延迟造成的数据丢失问题(在宕机的一瞬间,有些数据还没同步到ReplicShard,可能会导致数据丢失)

6. 集群搭建

环境准备

真实环境

NodeName Web端口,客户端端口
node-1 172.168.1.1:9200 172.168.1.1:9300
node-2 172.168.1.2:9200 172.168.1.2:9300
node-3 172.168.1.3:9200 172.168.1.3:9300

模拟环境

NodeName Web端口,客户端端口
node-1 127.0.0.1:9201 127.0.0.1:9301
node-2 127.0.0.1:9202 127.0.0.1:9302
node-3 127.0.0.1:9203 127.0.0.1:9303

注意:需要准备三个ES(拷贝),然后删除data目录 , 如果电脑内存不够,可以把jvm.properties中的内存设置改小

配置说明
- cluster.name

  集群名,自定义集群名,默认为elasticsearch,建议修改,因为低版本多播模式下同一网段下相同集群名会自动加入同一集群,如生产环境这样易造成数据运维紊乱。

- node.name

  节点名,同一集群下要求每个节点的节点名不一致,起到区分节点和辨认节点作用

- node.master

  是否为主节点,选项为true或false,当为true时在集群启动时该节点为主节点,在宕机或任务挂掉之后会选举新的主节点,恢复后该节点依然为主节点

- node.data

  是否处理数据,选项为true或false。负责数据的相关操作

- path.data

  默认数据路径,可用逗号分隔多个路径

- path.logs

  默认日志路径

- bootstrap.mlockall

  内存锁,选项为true或false,用来确保用户在es-jvm中设置的ES_HEAP_SIZE参数内存可以使用一半以上而又不溢出

- network.host

  对外暴露的host,0.0.0.0时暴露给外网

- http.port

  对外访问的端口号,默认为9200,所以外界访问该节点一般为http://ip:9200/

- transport.tcp.port

  集群间通信的端口号,默认为9300

- discovery.zen.ping.unicast.hosts

  集群的ip集合,可指定端口,默认为9300,如 ["192.168.1.101","192.168.1.102"]

- discovery.zen.minimum_master_nodes

  最少的主节点个数,为了防止脑裂,最好设置为(总结点数/2 + 1)个

- discovery.zen.ping_timeout

  主节点选举超时时间设置

- gateway.recover_after_nodes

  值为n,网关控制在n个节点启动之后才恢复整个集群

- node.max_local_storage_nodes

  值为n,一个系统中最多启用节点个数为n

- action.destructive_requires_name

  选项为true或false,删除indices是否需要现实名字
修改ES配置
  • Node1-配置
# 统一的集群名
cluster.name: my-ealsticsearch
# 当前节点名
node.name: node-1
# 对外暴露端口使外网访问
network.host: 127.0.0.1
# 对外暴露端口
http.port: 9201
#集群间通讯端口号
transport.tcp.port: 9301
#集群的ip集合,可指定端口,默认为9300
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
  • Node2-配置
# 统一的集群名
cluster.name: my-ealsticsearch
# 当前节点名
node.name: node-2
# 对外暴露端口使外网访问
network.host: 127.0.0.1
# 对外暴露端口
http.port: 9202
#集群间通讯端口号
transport.tcp.port: 9302
#集群的ip集合,可指定端口,默认为9300
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
  • Node3-配置
# 统一的集群名
cluster.name: my-ealsticsearch
# 当前节点名
node.name: node-3
# 对外暴露端口使外网访问
network.host: 127.0.0.1
# 对外暴露端口
http.port: 9203
#集群间通讯端口号
transport.tcp.port: 9303
#集群的ip集合,可指定端口,默认为9300
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]

分别启动三个ES节点 , 访问:http://127.0.0.1:9201/

连接集群

修改kibana配置
elasticsearch.url: "http://localhost:9201"

连接其中一个节点自然能连接上整个集群 , 然后启动Kibana

集群查看命令

创建索引

PUT shopping
{
    "settings":{
        "number_of_shards":5,
        "number_of_replicas":1
    }
}
  • GET _cat/nodes?v :查看Node

  • GET _cat/indices?v : 查看索引库

七.JavaApi操作ES

集成ES

导入依赖

    org.elasticsearch.client
    transport
    5.2.2


    org.apache.logging.log4j
    log4j-api
    2.7


    org.apache.logging.log4j
    log4j-core
    2.7

连接ES

编写工具

public class ESClientUtil {

    public static TransportClient getClient(){
        Settings settings = Settings.builder()
        .put("cluster.name","my-ealsticsearch")
        .put("client.transport.sniff", true).build();
        
        TransportClient client = null;
        try {
            client = new PreBuiltTransportClient(settings)
                    .addTransportAddress(
                            new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9303));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return client;
    }
}

文档CRUD

添加文档
@Test
public void testAdd() {
  //获取客户端对象
  TransportClient client = ESClientUtil.getClient();

  //创建索引
  IndexRequestBuilder indexRequestBuilder = client.prepareIndex("shopping", "user", "1");
  Map data = new HashMap<>();
  data.put("id",1);
  data.put("username","zs");
  data.put("age",11);
  //获取结果
  IndexResponse indexResponse = indexRequestBuilder.setSource(data).get();

  System.out.println(indexResponse);
  client.close();
}
9.2.2.获取文档
GetResponse response = client.prepareGet("crm", "vip", "1").get();
9.2.3.更新文档
@Test
    public void testUpdate(){
        //获取客户端对象
        TransportClient client = ESClientUtil.getClient();

        //修改索引
        UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate("shopping", "user", "1");
        Map data = new HashMap<>();
        data.put("id",1);
        data.put("username","zs");
        data.put("age",11);
        //获取结果设置修改内容
        UpdateResponse updateResponse = updateRequestBuilder.setDoc(data).get();

        System.out.println(updateResponse);
        client.close();
    }
9.2.4.删除文档
 @Test
    public void testDelete(){
        //获取客户端对象
        TransportClient client = ESClientUtil.getClient();

        DeleteRequestBuilder deleteRequestBuilder = client.prepareDelete("shopping", "user", "1");
        DeleteResponse deleteResponse = deleteRequestBuilder.get();

        System.out.println(deleteResponse);
        client.close();
    }
9.2.5.批量操作
 @Test
    public void testBuilkAdd(){
        //获取客户端对象
        TransportClient client = ESClientUtil.getClient();

        BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();

        Map data1 = new HashMap<>();
        data1.put("id",11);
        data1.put("username","zs");
        data1.put("age",11);

        bulkRequestBuilder.add(client.prepareIndex("shopping", "user", "11").setSource(data1));

        Map data2 = new HashMap<>();
        data2.put("id",22);
        data2.put("username","zs");
        data2.put("age",11);

        bulkRequestBuilder.add(client.prepareIndex("shopping", "user", "11").setSource(data2));

        BulkResponse bulkItemResponses = bulkRequestBuilder.get();
        Iterator iterator = bulkItemResponses.iterator();
        while(iterator.hasNext()){
            BulkItemResponse next = iterator.next();
            System.out.println(next.getResponse());
        }
        client.close();
    }

9.3.查询

 @Test
    public void testSearch(){
        //获取客户端对象
        TransportClient client = ESClientUtil.getClient();


        SearchRequestBuilder searchRequestBuilder = client.prepareSearch("shopping");
        searchRequestBuilder.setTypes("user");
        searchRequestBuilder.setFrom(0);
        searchRequestBuilder.setSize(10);
        searchRequestBuilder.addSort("age", SortOrder.ASC);

        //查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        List must = boolQueryBuilder.must();
        must.add(QueryBuilders.matchQuery("username" , "zs"));

        List filter = boolQueryBuilder.filter();
        filter.add(QueryBuilders.rangeQuery("age").lte(20).gte(10));
        filter.add(QueryBuilders.termQuery("id",11));

        searchRequestBuilder.setQuery(boolQueryBuilder);

        SearchResponse searchResponse = searchRequestBuilder.get();

        SearchHits hits = searchResponse.getHits();

        System.out.println("条数:"+hits.getTotalHits());
        for (SearchHit hit : hits.getHits()) {
            System.out.println(hit.getSource());

        }

        client.close();
    }

你可能感兴趣的:(ElasticSearch全文搜索引擎-简单使用)