ElasticSearch

作者: 紫晶葫芦 | 来源:发表于2021-07-16 18:59 被阅读0次

    1. ElasticSearch的介绍

    ElasticSearch的简称是ES. ES是内存数据库的一种,其核心的内容是,分词,全文检索,高性能.使用的场景一般有商城,订单,日志收集等.日志收集一般会搭配logstash与Kibana一起使用,简称ELK.logstash的作用是收集数据,Kibana的作用是ES数据库的管理工具并且附带报表生成的功能.本文后半段会附带ELK的搭建.

    2. ElasticSearch提供的查询方式

    1. 关键字匹配:Match , Mulit Match.

    2. 条件过滤(Filter):Term , Terms ,Range ,Geo Distance .

    3. 字段排序: Sort(on specific fields ) , Geo Distance Sorting .

    4. 打分排序: Script Based Sorting , Function Score , Rescoring .

    5. 或与非(Bool Query) : must , filter , should , must_not .

    3. ElasticSearch的结构

    为了更好的解释ES的结构,现在通过与关系型数据库的对比说明 .

    以下是ES与关系型数据库的结构对应关系:

    关系型数据库 ElasticSearch
    database(数据库) index(索引库)
    table(表) type(类型)
    row(行) document(文档)
    column(列) field(字段)

    注:

    1. 在ES8版本之后对type(类型)去掉了 , 库下面是直接放的document(文档) .

    2. 对某一字段做分词的时候,最好字段内容短一些 .

    4. ElasticSearch的调用

    ElasticSearch是由java写的, 底层是Lucene,封装后的ElasticSearch 具有了并发能力.

    ElasticSearch 提供的接口风格是rest 的所以它可以跨语言使用 . 以下是它所提供的接口:

    method URL地址 描述
    PUT localhost:9200/索引名称/类型名称/文档id 创建文档(指定文档ID)
    POST localhost:9200/索引名称/类型名称 创建文档(随机文档ID)
    POST localhost:9200/索引名称/类型名称/文档id/_updata 修改文档
    DELETE localhost:9200/索引名称/类型名称/文档id 删除文档
    GET localhost:9200/索引名称/类型名称/文档id 查询文档通过文档ID
    POST localhost:9200/索引名称/类型名称/_search 查询所有数据

    5. 环境的搭建

    现在提供是Docker的部署:

    5.1. 单节点集群部署

    docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms100m -Xmx200m" elasticsearch:7.2.0

    说明:

    1. ES_JAVA_OPTS="-Xms100m -Xmx200m" 是内存使用

    2. "discovery.type=single-node" 单点集群

    安装成功后:访问(http://IP:9200/)

    返回如下内容表示成功:

    {
      "name" : "695644ec6d0d",
      "cluster_name" : "docker-cluster",
      "cluster_uuid" : "3BtyZmyJSDeoJwYZ7x3q8g",
      "version" : {
        "number" : "7.2.0",
        "build_flavor" : "default",
        "build_type" : "docker",
        "build_hash" : "508c38a",
        "build_date" : "2019-06-20T15:54:18.811730Z",
        "build_snapshot" : false,
        "lucene_version" : "8.0.0",
        "minimum_wire_compatibility_version" : "6.8.0",
        "minimum_index_compatibility_version" : "6.0.0-beta1"
      },
      "tagline" : "You Know, for Search"
    }
    

    通过网页插件Elasticvue 登录界面


    1.jpg

    Kibana 的安装

    docker run -p 5601:5601 -d -e ELASTICSEARCH_URL=http://IP:9200 -e ELASTICSEARCH_HOSTS=http://IP:9200 kibana:7.2.0

    说明:

    1. 其中的IP为上一步安装ES的IP地址.

    安装成功后:访问(http://IP:5601/)

    2.png
    可以在Console 中执行简单的命令 : 具体查看(4. ElasticSearch的调用)的调用集合.
    例如:增加  .其他的查看 4. ElasticSearch的调用
    PUT myes/_doc/1
    {
        "Name" = "sss",
    }
    

    6. ElasticSearch的基本操作

    6.1 创建索引插入数据

    #删除索引库
    DELETE /testindex
    #创建索引库"type": "keyword"字符串类型不做分词,"type": "text"字符串类型做分词
    PUT /testindex
    {
       "mappings": {
         "properties": {
         "name":{
           "type": "keyword"
         },
         "address":{
            "type": "text"
         },
         "age":{
            "type": "integer"
         }
         }
       }
    }
    #插入数据 可以自己生成id //  
    POST  /testindex/_doc
    {
       "name":"张三",
       "address":"山西",
       "age":18
      
    }
    POST  /testindex/_doc
    {
       "name":"李四",
       "address":"河北",
       "age":19
      
    }
    POST  /testindex/_doc
    {
       "name":"王五",
       "address":"山东",
       "age":37
      
    }
    POST  /testindex/_doc
    {
       "name":"赵六",
       "address":["陕西","山东"],
       "age":25
    }
    

    6.2 排序

    以下是排序的查询:

    GET testindex/_doc/_search
    {
      "query":{
         
         "match": {
           "address": "山西"
         }
      },
     "_source":["name", "age" ,"address"],
      "sort":[
        {"age":{"order":"desc"}},
        {"name":{"order":"asc"}}
        ]
    }
    

    注意:

    1. 查询条件是 address ,而address text类型的数据,ES中会自动的做分词,所以出来的结果,有山西,山东。

    6.3 分页查询

    分页查询多了两个字段:from :开始位置,size :条数

    GET testindex/_doc/_search
    {
      "query":{
         "match": {
           "address": "山西"
         }
      },
      "_source":["name"],
      "sort":[
        {"age":{"order":"desc"}},
        {"name":{"order":"asc"}}
        ],
       "from":0,
       "size":10
    }
    

    6.4 BOOL值查询

    and 逻辑使用 must

    GET testindex/_doc/_search
    {
      "query":{
          "bool": {
            "must": [
              {
         "match": {
           "address": "山西"
         }
              },{
                "match": {
           "name": "张三"
         }    
              }
            ] 
          }
      },
      "_source":["name"],
      "sort":[
        {"age":{"order":"desc"}},
        {"name":{"order":"asc"}}
        ],
       "from":0,
       "size":2
    }
    

    or 逻辑使用 should

    GET testindex/_doc/_search
    {
      "query":{
          "bool": {
            "should": [
              {
         "match": {
           "address": "魏国"
         }    
              },{
                "match": {
           "name": "曹操"
         }
              }
            ]
          }
      },
      "_source":["name"],
      "sort":[
        {"age":{"order":"desc"}},
        {"name":{"order":"asc"}}
        ],
       "from":0,
       "size":10
    }
    

    6.5 排除查询

    must_not 与之不匹配的查询

    #查询名字不是张三的信息
    GET testindex/_doc/_search
    {
      "query":{
        "bool": {
          "must_not": [
            {
               "match": { "name": "张三"}
            }
          ]
        }
      }
    }
    

    6.6 FILTER数据过滤

    gt 大于, gte 大于等于, lt 小于 ,lte 小于等于

    GET testindex/_doc/_search
    {
      "query":{
        "bool": {
          "must": [
            {
               "match": {"name": "张三"}
            }
          ],
          "filter": {
            "range": {
              "age": {
                "gte": 10,
                "lte": 20
              }
            }
          }
        }
        
      }
    }
    

    6.7 配备多个条件查询

    查询是山东,又是山西的

    GET testindex/_doc/_search
    {
      "query":{
        "match":{"address":"山东 山西 "}
      }
    }
    

    6.8 高亮查询

    1. 高亮查询
    2. GET testindex/_doc/_search
      {
        "query":{
        "match": {
          "name": "张三"
        }
        },
        "highlight":{
          "fields": {
            "name": {}
          }
        }
      }
      # 返回结果是:<em> 张三</em>
      
    1. 自定义高亮风格

      就是在返回的结果中,加上自定义的标签,属性。

      GET testindex/_doc/_search
      {
        "query":{
        "match": {
          "name": "张三"
        }
        },
        "highlight":{
      # 前面加的内容
          "pre_tags": "<p class='zhangs'>", 
      # 后面加的内容      
          "post_tags": "</p>", 
          "fields": {
            "name": {}
          }
        }
      }
      # 返回结果是:<p class='zhangs'> 张三</p>
      
    1. 多条件多字段高亮查询
    ```json
    GET testindex/_doc/_search
    {
      "query":{
       "bool": {
            "must": [
              {
         "match": {
           "address": "山西"
         }
              },{
                "match": {
           "name": "张三"
         }  
              }
            ]
            
          }
      },
      "highlight":{
        
        "pre_tags": "<p class='zhangs'>", 
        "post_tags": "</p>", 
        "fields": [
          {"name": {}},
           {"address": {}}
       ]
      }
    }
    ```
    

    6.9 SQL 查询

    1. .Net代码对接中使用的方式。
    2. 进入容器 docker exec -it 容器ID /bin/bash ,然后进入 bin 文件夹执行:elasticsearch-sql-cli, 然后就可以执行SQL语句了。
    3. 注意,可以进行跨库子查询,但是不能做连接查询,像左连接,右连接等。

    7. 聚合查询

    准备数据创建索引库,以音乐库为例:ID ,作者,歌名,点赞数,语言等。

    PUT /music
    {
          "mappings": {
            "properties": {
              "id": {
                "type": "keyword"
              },
              "author": {
                "type": "text",
                "analyzer": "english",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "content": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "language": {
                "type": "text",
                "analyzer": "english",
                "fielddata": true
              },
              "tags": {
                "type": "text",
                "analyzer": "english"
              },
              "length": {
                "type": "long"
              },
              "likes": {
                "type": "long"
              },
              "isRelease": {
                "type": "boolean"
              },
              "releaseDate": {
                "type": "date"
              }
            }
          }
      }
    

    增加数据:使用批量增加,_bulk。注意批量增加每条数据之前的加上 { "index": {}}。

    POST /music/_doc/_bulk
    { "index": {}}
    { "author" : "zhangsan", "name" : "red", "content" : "honda", "language":"ch","tags":"tags","length":3,"likes":10,"isRelease":true,"releaseDate" : "2021-10-28" }
    { "index": {}}
    { "author" : "lisi", "name" : "blue", "content" : "honda", "language":"en","tags":"tags","length":3,"likes":100,"isRelease":true,"releaseDate" : "2021-10-28" }
    { "index": {}}
    { "author" : "zhangsan1", "name" : "red", "content" : "honda", "language":"ch","tags":"tags","length":30,"likes":10,"isRelease":true,"releaseDate" : "2021-11-28" }
    { "index": {}}
    { "author" : "lisi1", "name" : "blue", "content" : "honda", "language":"en","tags":"tags","length":30,"likes":100,"isRelease":true,"releaseDate" : "2021-11-28" }
    { "index": {}}
    { "author" : "zhangsan2", "name" : "red", "content" : "honda", "language":"ch","tags":"tags","length":80,"likes":10,"isRelease":true,"releaseDate" : "2021-11-28" }
    { "index": {}}
    { "author" : "lisi2", "name" : "blue", "content" : "honda", "language":"en","tags":"tags","length":80,"likes":100,"isRelease":true,"releaseDate" : "2022-10-28" }
    { "index": {}}
    { "author" : "zhangsan2", "name" : "red", "content" : "honda", "language":"ch","tags":"tags","length":80,"likes":10,"isRelease":true,"releaseDate" : "2022-10-28" }
    { "index": {}}
    { "author" : "lisi2", "name" : "blue", "content" : "honda", "language":"en","tags":"tags","length":80,"likes":100,"isRelease":true,"releaseDate" : "2022-10-28" }
    
    1. 统计每种语言的歌曲数量

      GET /music/_doc/_search
      {
        "size": 0,
        "aggs": {
          "song_qty_by_language": {
            "terms": {
              "field": "language"
            }
          }
        }
      } 
      
      • size:0 表示只要统计后的结果,原始数据不展现,如果是大于0的,则会返回原数据
      • aggs:固定语法 ,聚合分析都要声明aggs
      • song_qty_by_language:聚合的名称,可以随便写,建议规范命名
      • terms:按什么字段进行分组
      • field:具体的字段名称
    1. 按语种统计歌曲的平均时长,最大时长,最短时长,总时长

      GET /music/_doc/_search
      {
        "size": 0,
        "aggs": {
          "color": {
            "terms": {
              "field": "language"
            },
            "aggs": {
              "length_avg": {
                "avg": {
                  "field": "length"
                }
              },
              "length_max": {
                "max": {
                  "field": "length"
                }
              },
              "length_min": {
                "min": {
                  "field": "length"
                }
              },
              "length_sum": {
                "sum": {
                  "field": "length"
                }
              }
            }
          }
        }
      }GET /music/_doc/_search
      {
        "size": 0,
        "aggs": {
          "lang": {
            "terms": {
              "field": "language"
            },
            "aggs": {
              "length_avg": {
                "avg": {
                  "field": "length"
                }
              }
            }
          }
        }
      } 
      
    1. 按时长分段统计统计歌曲平均时长

      以30秒为一段,看各段区间的平均值。interval:30表示分的区间段为[0,30),[30,60),[60,90),[90,120)

      GET /music/_doc/_search
      {
        "size": 0,
        "aggs": {
          "sales_price_range": {
            "histogram": {
              "field": "length",
              "interval": 30
            },
            "aggs": {
              "length_avg": {
                "avg": {
                  "field": "length"
                }
              }
            }
          }
        }
      }
      
    1. 按日期分段统计歌曲数量

      GET /music/_doc/_search
      {
        "size": 0,
        "aggs": {
          "sales": {
            "date_histogram": {
              "field": "releaseDate",
              "interval": "month",
              "format": "yyyy-MM-dd",
              "min_doc_count": 0,
              "extended_bounds": {
                "min": "2000-10-01",
                "max": "2088-12-31"
              }
            }
          }
        }
      } 
      

    8. .NET 的代码对接

    Nuget .安装Nest 下面是以单节点集群连接

    1. 原始方式的查询

      var settings = new ConnectionSettings(new Uri(“http://IP:9200/”)).DefaultIndex("people");
      var client = new ElasticClient(settings);
      //查询张三
      var searchResponse = client.Search<Person>(s => s.From(0).Size(10).Query(q => q.Match(m => m.Field(f => f.FirstName).Query("张三"))));   
      var people = searchResponse.Documents;
      foreach (var item in people)
      {
          Console.WriteLine($"id:{item.Id},firstname:{item.FirstName},lastname:{item.LastName}");
      } 
      

      缺点:学习查询方式成本高。虽然可以使用ElasticHD.exe 负责但是成本还个很高。

    2. 以ES提供的SQL模式查询:
      使用:http://IP:9200_xpack/sql?format=csv

        //调用:SendByWebRequest.GetDataBySql("select * from testindex where  address like '%山%' ");
        
        public static DataTable GetDataBySql(string sql, string url = "http://IP:9200/_xpack/sql?format=csv")
        {
            DataTable dataTable = new DataTable();
            try
            {
                // 调用通过sql去查询结果
                string jsonstr = Post(sql,url);
                var lines = jsonstr.Split("\r\n");
                foreach (string item in lines[0].Split(","))
                {
                    //可以做类型转换
                    dataTable.Columns.Add(item, typeof(System.String));
                }
                for (int i = 1; i < lines.Length - 1; i++)
                {
                    var filedvalue = lines[i].Split(",");
                    var row = dataTable.NewRow();
                    for (int j = 0; j < dataTable.Columns.Count; j++)
                    {
                        row[j] = filedvalue[j];
                    }
                    dataTable.Rows.Add(row);
                }
            }
            catch (Exception ex)
            {
                throw;
            }
            return dataTable;
        }
        
        public static string Post(string queryParam, string url )
        {
            HttpWebRequest request = null;
            try
            {
                request = (HttpWebRequest)WebRequest.Create(url);
                var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(queryParam));
                request.Method = "POST";
                request.ContentType = "application/json"; // 设置请求数据的ContentType
                request.ContentLength = data.Length;
                request.Timeout = 90000;
                // 设置入参
                using (var stream = request.GetRequestStream())
                {
                    stream.Write(data, 0, data.Length);
                }
                // 发送请求
                var response = (HttpWebResponse)request.GetResponse();
                // 读取出参
                using (var resStream = response.GetResponseStream())
                {
                    using (var reader = new StreamReader(resStream, Encoding.UTF8))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }
            catch (Exception ex)
            {
                return null;
            }
            finally
            {
                // 释放连接
                if (request != null) request.Abort();
            }
        }
       
    

    缺点:
    1. "select * from testindex where address like '%山%' " 。不能写出 * 需要写出具体的字段。
    2. 可以进行跨库子查询,但是不能做连接查询,像左连接,右连接等。

    1. 使用插件

      下载插件地址:https://github.com/NLPchina/elasticsearch-sql注意插件对应与ES版本。

      把下载SQL插件拷贝到home/es/sql 然后docker 挂载到plugins/sql下面。

      docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -v /home/es/sql:/usr/share/elasticsearch/plugins/sql -e ES_JAVA_OPTS="-Xms100m -Xmx200m" elasticsearch:7.6.1

        调用:Post("select * from testindex where address like '%山%'");
        
        public static string Post(string queryParam, string url = "http://IP:9200/_nlpcn/sql")
        {
            HttpWebRequest request = null;
            try
            {
                request = (HttpWebRequest)WebRequest.Create(url);
                var data = Encoding.UTF8.GetBytes(queryParam);
                request.Accept = "application/json; charset=UTF-8"; // 设置响应数据的ContentType
                request.Method = "POST";
                request.ContentType = "application/json"; // 设置请求数据的ContentType
                request.ContentLength = data.Length;
                request.Timeout = 90000;
                // 设置入参
                using (var stream = request.GetRequestStream())
                {
                    stream.Write(data, 0, data.Length);
                }
                // 发送请求
                var response = (HttpWebResponse)request.GetResponse();
                // 读取出参
                using (var resStream = response.GetResponseStream())
                {
                    using (var reader = new StreamReader(resStream, Encoding.UTF8))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }
            catch (Exception ex)
            {
                return null;
            }
            finally
            {
                // 释放连接
                if (request != null) request.Abort();
            }
        }
    

    9. 分词

    由于原ES做分词是针对英文的,针对汉语分词必须的加载iK分词器

    9.1 分词器的安装

    1. 下载分析器:
        wget [https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.6.12/elasticsearch-analysis-ik-5.6.12.zip](https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.6.12/elasticsearch-analysis-ik-5.6.12.zip)
    
    1. 进入 ES 容器 docker exec -it b246c /bin/bash

      找到plugins 目录 创建 iK目录,

    2. 把下载的iK分析器解压到iK中。

    3. 重启ES .

    以下是示例:

    {
     "analyzer": "iK_max_word",
     "test":"北京天安门"
    }
    

    9.2 自定义扩展分词

    1. 进入iK分词器下面的config目录

    2. 创建my.doc 文件在其中加入要分的词

    3. 在IKAnalyzer.cfg.xml中加入自己的创建的分词文件

    4. 重启ES。

    以下是截图示例:


    111.png

    10. 倒排索引

    倒排索引是相对于正排索引而言的。
    
    正排索引,就是一般SQL保存数据的格式,Key ,Value 的结构。如下面的结构:
    
    Key Value
    青花瓷 天青色等烟雨.........
    菊花台 你的笑容以泛黄.....

    正排索引,可以通过《青花瓷》来检索到歌词具体的内容。但是经常遇到的问题就是,通过歌词中的某一个词去检索整首歌曲,这样传统的正排索引就比较费劲了,如果数据较少还可以通过比对内容去实现,但是数据成千上百万,用这种方式就不现实了。而ES实现的就是全文索引。所以这种方式是不可取的。而它采取的是倒排索引的数据保存方式。如下面的结构:

    key Value
    天青色等烟雨.........
    青色 天青色等烟雨.........
    烟雨 天青色等烟雨.........
    天青色等烟雨.........

    采用倒排的方式就可以解决了,但是随之而来的问题就是,这样的拆分会导致数据量的成倍的增长。数据量大同样的导致数据查询慢的问题,为了解决这一问题,ES使用通过大量使用内存,咋就是做索引压缩。

    11. 索引压缩

    压缩Key :索引压缩称为FST,

    假设对aaz,abyz,acdyz,be构建FST。做一个排序如下图:


    3.png

    使用共享的首尾对应硬盘的地址。比如:要针对aaz ,查询,通过排序就包含了,abyz,acdyz,查询的范围增大,但是也会有个范围不是全盘去扫描,如果是针对a*yz 去查,则检索范围进一步缩小。返回在缩小的范围进一步去检索,就可以快速的找到对应的key保存在磁盘中的位置。

    压缩Value:

    对于Value巨大的数据量也是需要压缩的压缩的方式如下图:

    4.png

    如图所示:保存 73 300 302 332 343 372 如果是用一般的方式就是长度为6的int类型的数组,所用的空间是24个字节。

    第二步:通过连加的方式:缩小了数据的大小,可以用6个short类型的数组保存数据,所用的空间就减小了一半成为了12个字节。

    第三步:对于一些突变的数据可以做跟段处理。然后在一段之内数据选择最大的,申请bit数组如上图左边,最大是227 就是8个bit ,三个数组就是 3个字节,同理右半段就是2个字节,加上前面的一个数字代表的bit长度,一共就是占用7个字节。

    整个算下来就是压缩了1/3空间大小。

    12. 联合查询

    由于倒排索引的保存方式会导致,主键Key 的不同但是Value相同。所以联合查询的时候会导致查询的结果是相同的。例如:查询条件是,where key=‘天’ and key = ‘青色’ 的就会导致相同的结果,所以用到了取交集的方式,同理也会遇到取并集、补集的方式。ElasticSearch 的实现思路和Redis 的Zset 实现的思路是一致的可以查看Redis那篇。也是通过跳跃表完成的。

    13. 分布式原理

    13.1 分部式的搭建

    以下是es的yml 文件,配置文件的说明。

    # 配置es的集群名称, es会自动发现在同一网段下的es,如果在同一网段下有多个集群,就可以用这个属性来区分不同的集群。
    cluster.name: elasticsearch
    # 节点名称
    node.name: node-001
    # 指定该节点是否有资格被选举成为node
    node.master: true
    
    cluster.initial_master_nodes: ["127.0.0.1:9300"]
    
    # 指定该节点是否存储索引数据,默认为true。
    node.data: true
    # 设置绑定的ip地址还有其它节点和该节点交互的ip地址,本机ip
    network.host: 127.0.0.1
    # 指定http端口,你使用head、kopf等相关插件使用的端口
    http.port: 9200
    # 设置节点间交互的tcp端口,默认是9300。
    transport.tcp.port: 9300
    #设置集群中master节点的初始列表,可以通过这些节点来自动发现新加入集群的节点。
    #因为下两台elasticsearch的port端口会设置成9301 和 9302 所以写入两台#elasticsearch地址的完整路径
    discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
    #如果要使用head,那么需要解决跨域问题,使head插件可以访问es
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    

    以下提供三个yml文件做三个节点的集群示例:

    示例的内容:是在同一宿主机上的三个不同的端口开启三个节点,生产环境中是不同的宿主机部署。

    es1.yml

    cluster.name: elasticsearch-cluster
    node.name: es-node1
    network.bind_host: 0.0.0.0
    network.publish_host: 192.168.1.101
    http.port: 9200
    cluster.initial_master_nodes: ["192.168.1.101:9300"]
    transport.tcp.port: 9300
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    node.master: true
    node.data: true
    discovery.zen.ping.unicast.hosts: ["192.168.1.101:9300","192.168.1.101:9301","192.168.1.101:9302"]
    discovery.zen.minimum_master_nodes: 2
    

    es2.yml

    cluster.name: elasticsearch-cluster
    node.name: es-node2
    network.bind_host: 0.0.0.0
    network.publish_host: 192.168.1.101
    http.port: 9201
    cluster.initial_master_nodes: ["192.168.1.101:9300"]
    transport.tcp.port: 9301
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    node.master: true
    node.data: true
    discovery.zen.ping.unicast.hosts: ["192.168.1.101:9300","192.168.1.101:9301","192.168.1.101:9302"]
    discovery.zen.minimum_master_nodes: 2
    

    es3.yml

    cluster.name: elasticsearch-cluster
    node.name: es-node3
    network.bind_host: 0.0.0.0
    network.publish_host: 192.168.1.101
    http.port: 9202
    cluster.initial_master_nodes: ["192.168.1.101:9300"]
    transport.tcp.port: 9302
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    node.master: true
    node.data: true
    discovery.zen.ping.unicast.hosts: ["192.168.1.101:9300","192.168.1.101:9301","192.168.1.101:9302"]
    discovery.zen.minimum_master_nodes: 2
    

    以下是执行docker :

    # 没有挂载的
    docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 -v /root/es/es1.yml:/usr/share/elasticsearch/config/elasticsearch.yml --name es01 elasticsearch:7.2.0
    
    docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9201:9201 -p 9301:9301 -v /root/es/es2.yml:/usr/share/elasticsearch/config/elasticsearch.yml --name es02 elasticsearch:7.2.0
    
    docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9202:9202 -p 9302:9302 -v /root/es/es3.yml:/usr/share/elasticsearch/config/elasticsearch.yml --name es03 elasticsearch:7.2.0
    
    #有挂载的
    docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 -v /home/es/config/es1.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /home/es/data1:/usr/share/elasticsearch/data --name es01 docker.elastic.co/elasticsearch/elasticsearch:7.10.0
    
    docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9201:9201 -p 9301:9301 -v /home/es/config/es2.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /home/es/data2:/usr/share/elasticsearch/data --name es02 docker.elastic.co/elasticsearch/elasticsearch:7.10.0
    
    docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9202:9202 -p 9302:9302 -v /home/es/config/es3.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /home/es/data3:/usr/share/elasticsearch/data --name es03 docker.elastic.co/elasticsearch/elasticsearch:7.10.0
    

    注意:

    1. 必须在宿主机创建data文件夹,然后配置777权限,文件夹单独配置

    2. 调高jvm线程数限制

    3. 方法1:

      vim /etc/sysctl.conf

      加入

      vm.max_map_count=262144

      执行生效

      sysctl –p

      方法2:

      sysctl -w vm.max_map_count=262144

    13.2 主节点的职责

    1. 索引

      创建索引

      删除索引

      删除索引Template

      删除索引Mapping

      Mapping更新

      Mapping创建

      删除索引Warmer

      索引Warmer存在

      获取索引Warmer

      创建Warmer

      索引别名

      索引存在

      打开索引

      关闭索引

    2. 节点或集群

      集群信息

      集群健康

      集群状态

      集群配置

      搜索分片

      节点关闭

      集群路由

    3. 发布集群状态

    由于主节点职责多,所以主节点一般不保存数据。

    14. 工作机制

    14.1 节点发现

    节点的发现是通过配置文件中实现的,它们每个节点之间都是都是相互发现的如下图:


    5.png

    如果其中有一个节点断开其他的节点是知道的。

    14.2 节点故障

    1. 节点故障,

      当主节点与节点有连续三次以上的心跳不通后,主节点就会把该节点移除,认为该节点故障,然后重新的分配片区。如下图:


      6.png
    2. 主节点故障

    当主节点与所有节点断开后,默认主节点宕机,主节点宕机后,其他的剩余的节点重新选举主节点。如下图:


    7.png

    14.3 Master选举

    主节点的选举,不是按照票数的多少来进行的,而是按照节点的开启创建的cluster_uuid来的,cluster_uuid越小就越有机会选举成主节点。

     "cluster_uuid" : "3BtyZmyJSDeoJwYZ7x3q8g",
    
    public MasterCandidate electMaster(Collection<MasterCandidate> candidates) {
            assert hasEnoughCandidates(candidates);
            List<MasterCandidate> sortedCandidates = new ArrayList<>(candidates);
            sortedCandidates.sort(MasterCandidate::compare);
            return sortedCandidates.get(0);
    }
    public static int compare(MasterCandidate c1, MasterCandidate c2) {
        // we explicitly swap c1 and c2 here. the code expects "better" is lower in a sorted
        // list, so if c2 has a higher cluster state version, it needs to come first.
        int ret = Long.compare(c2.clusterStateVersion, c1.clusterStateVersion);
        if (ret == 0) {
            ret = compareNodes(c1.getNode(), c2.getNode());
        }
        return ret;
    }
    

    14.4 脑裂

    配置文件中配置 discovery.zen.minimum_master_nodes: 2(形成集群所需要的最小节点数)

    1. 一般是(所有节点数-主节点数)/2 + 1
    2. 尽量多的节点放到和主节点一个网段下面

    14.5 集群状态更新

    集群的状态更新,是由主节点主动的推送到分节点,再由节点去完成相应的任务,如下图:


    8.png

    15. 分片

    集群搭建完成最重要的就是负载均衡,也就是做分片。elasticsearch的分片原理和Kafka一致的。基本的原则就是:首先,满足节点数等于分片数,其次一个分片最好保存20G左右的数据,再次就是一个节点最多两个分片(过多会影响性能)。

    #Kibana 执行
    PUT /my_temp_index/_settings
    {
     "number_of_shards": 1,
     "number_of_replicas": 5
    }
    
    1. 分片机制弹性扩容

      分片的扩容:新的节点接入的时候会从新的分片,做到任何一个节点宕机后,不会影响数据的完整性。

    2. 分片机制节点故障

      旧的节点宕机后,剩下的节点会从新的分片,保证剩下的节点任何一个节点宕机后不会影响数据的完整性。

    16. 集群的代码连接方式

    var uris = new[]
    {
        new Uri("http://localhost:9200"),
        new Uri("http://localhost:9201"),
        new Uri("http://localhost:9202"),
    };
    var connectionPool = new SniffingConnectionPool(uris);
    var settings = new ConnectionSettings(connectionPool).DefaultIndex("people");
    var client = new ElasticClient(settings);
    

    把连接字符串变成集群就可以了。

    相关文章

      网友评论

        本文标题:ElasticSearch

        本文链接:https://www.haomeiwen.com/subject/gpbbpltx.html