美文网首页ElasticSearch
ElasticSearch系列一:安装,使用,搜索

ElasticSearch系列一:安装,使用,搜索

作者: suxin1932 | 来源:发表于2018-12-16 10:38 被阅读0次

    此处需补充: 安装cerebro及es集群

    1.windows版

    1.1安装ElasticSearch(以6.5.4版本为例)

    1、安装JDK,至少1.8.0_73以上版本
    2、下载和解压缩Elasticsearch安装包,目录结构
    3、启动Elasticsearch:bin\elasticsearch.bat,es本身特点之一就是开箱即用,如果是中小型应用,数据量少,操作不是很复杂,直接启动就可以用了
    4、检查ES是否启动成功:http://localhost:9200
    5.若想修改集群名称,配置文件等,则到bin/elasticsearch.yml中修改即可
    
    图片.png

    1.2安装Kibana(以6.5.4版本为例)

    1.下载和解压缩Kibana安装包
    2.启动Kibana:bin\kibana.bat
    3.检查是否启动成功: http://localhost:5601
    4.进入Dev Tools界面
    5.查看集群健康状况:    GET _cluster/health
    
    图片.png

    2.linux版

    2.1安装ElasticSearch(以6.5.4版本为例)

    root用户下,下载elasticsearch

    wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.5.4.tar.gz
    

    将下载包移至指定目录下

    mv /root/elasticsearch-6.5.4.tar.gz /home/zx/software/
    

    切换到该目录

    cd /home/zx/software/
    

    改变所属用户及组

    chown zx:zx elasticsearch-6.5.4.tar.gz
    

    切换至普通用户zx解压该包

    su - zx
    tar -zxf elasticsearch-6.5.4.tar.gz
    

    修改bin/elasticsearch.yml

    cluster.name: zx-es
    node.name: node-1
    network.host: 192.168.0.199 #ES所在服务器的ip
    http.port: 9200
    

    启动es

    bin/elasticsearch -d #后台静默启动es
    

    报错1

    max file descriptors [4096] for elasticsearch process is too low, 
    increase to at least [65536] #每个进程最大同时打开文件数太小
    

    解决方案1

    修改/etc/security/limits.conf文件,增加配置如下:
    
    *               soft    nofile          65536
    *               hard    nofile          65536
    
    用户退出后重新登录生效
    
    查看当前数量
    ulimit -Hn
    ulimit -Sn
    

    报错2

    max number of threads [3818] for user [es] is too low, 
    increase to at least [4096]#最大线程个数太低
    

    解决方案2

    修改/etc/security/limits.conf文件,增加配置如下:
    
    *               soft    nproc           4096
    *               hard    nproc           4096
    
    用户退出后重新登录生效
    
    查看当前数量
    ulimit -Hu
    ulimit -Su
    

    报错3

    max virtual memory areas vm.max_map_count [65530] is too low, 
    increase to at least [262144]
    

    解决方案3

    修改/etc/sysctl.conf文件,增加配置vm.max_map_count=262144
    
    vi /etc/sysctl.conf
    执行  sysctl -p 命令后生效
    执行  sysctl -a | grep "vm.max_map_count"命令查看修改是否生效
    

    再次启动es

    bin/elasticsearch -d #后台静默启动
    
    图片.png

    其他问题

    #centos7
    #查看防火墙状态
    firewall-cmd --state
    
    #停止firewall
    systemctl stop firewalld.service
    
    #禁止firewall开机启动
    systemctl disable firewalld.service 
    
    # es的config目录下的jvm.options修改:(这俩值保持一致即可, 根据实际及其内存调整)
    -Xms512m
    -Xmx512m
    

    2.2安装Linux版kibana(以6.5.4版本为例)

    下载kinban

    wget https://artifacts.elastic.co/downloads/kibana/kibana-6.5.4-linux-x86_64.tar.gz
    

    将文件移至指定目录下后,解压包

    tar -zxvf kibana-6.5.4-linux-x86_64.tar.gz
    

    修改配置文件 vim config/kibana.yml

    server.port: 5601
    server.host: "0.0.0.0"
    elasticsearch.url: "http://192.168.0.199:9200"
    kibana.index: ".kibana"
    

    后台静默启动kibana

    nohup bin/kibana & #采用相对路径启动,当然,绝对路径也可
    
    图片.png

    2.3安装cerebro(不要跟es安装在同一台机器上)

    #下载
    wget https://github.com/lmenezes/cerebro/releases/download/v0.8.3/cerebro-0.8.3.tgz
    
    # 解压即可
    tar -zxf cerebro-0.8.3.tgz
    
    #修改配置文件
    vim conf/ application.conf
    
    hosts = [
                       {
                           host = "http://192.168.10.64:9200"
                           name = "cm-elk"
                       },
                 ]
    
    #启动
    nohup ./bin/cerebro -Dhttp.port=1234 -Dhttp.address=192.168.0.190 > /dev/null 2>&1 &
    

    3.es常用命令

    3.1快速检查集群的健康状况

    es提供了一套api,叫做cat api,可以查看es中各种各样的数据

    3.1.1查看健康状况

    GET /_cat/health?v
    
    如何快速了解集群的健康状况?green、yellow、red?
    
    green:  每个索引的primary shard和replica shard都是active状态的
    yellow: 每个索引的primary shard都是active状态的,
             但是部分replica shard不是active状态,处于不可用的状态
    red:    不是所有索引的primary shard都是active状态的,部分索引有数据丢失了
    
    何时会处于一个yellow状态?
    
    我们现在就一个笔记本电脑,就启动了一个es进程,相当于就只有一个node。
    现在es中有一个index,就是kibana自己内置建立的index。
    由于默认的配置是给每个index分配5个primary shard和5个replica shard,
    而且primary shard和replica shard不能在同一台机器上(为了容错)。
    现在kibana自己建立的index是1个primary shard和1个replica shard。
    当前就一个node,所以只有1个primary shard被分配了和启动了,但是一个replica shard没有第二台机器去启动。
    

    3.1.2快速查看集群中有哪些索引

    GET /_cat/indices?v
    

    3.1.3创建索引

    自动

    PUT /test_index?pretty
    

    手动创建

    PUT /hand_index
    {
      "settings": {
        "number_of_shards": 1,  # 不可修改
        "number_of_replicas": 0 # 可修改
      },
      "mappings": {
        "hand_type_one": {
          "properties": {
            "field_one": {
              "type": "text"
            }
          }
        }
      }
    }
    

    3.1.4删除索引

    切记:将elasticsearch.yml中:
    action.destructive_requires_name: true
    使之只能按照名称删除,不可全删
    
    DELETE /test_index?pretty
    

    3.1.5修改索引

    PUT /hand_index
    {
      "settings": {
        "number_of_replicas": 1 # 修改replica, primary不可被修改,与routing有关
      }
    }
    

    3.2商品的CRUD操作

    document数据格式
    
    面向文档的搜索分析引擎
    
    (1)应用系统的数据结构都是面向对象的,复杂的
    (2)对象数据存储到数据库中,只能拆解开来,变为扁平的多张表,每次查询的时候还得还原回对象格式,相当麻烦
    (3)ES是面向文档的,文档中存储的数据结构,与面向对象的数据结构是一样的,基于这种文档数据结构,es可以提供复杂的索引,全文检索,分析聚合等功能
    (4)es的document用json数据格式来表达
    

    新增商品:新增文档,建立索引

    新增语法:
    PUT /index/type/id
    {
      "json数据"
    }
    

    如:

    PUT /ecommerce/product/1
    {
        "name" : "gaolujie yagao",
        "desc" :  "gaoxiao meibai",
        "price" :  10,
        "producer" :      "gaolujie producer",
        "tags": [ "meibai", "fangzhu" ]
    }
    

    检索商品

    es会自动建立index和type,不需要提前创建,而且es默认会对document每个field都建立倒排索引,让其可以被搜索
    
    检索语法:
    GET /index/type/id
    

    如:

    GET /ecommerce/product/1
    

    修改商品:替换文档

    POST /ecommerce/product/1/_update
    {
      "doc": {
        "name": "jiaqiangban gaolujie yagao"
      }
    }
    

    删除商品:删除文档

    DELETE /ecommerce/product/1
    

    3.3搜索语法

    3.3.1检索全部商品

    GET /ecommerce/product/_search
    

    3.3.2搜索商品名称中包含yagao的商品,而且按照售价降序排序

    GET /ecommerce/product/_search?q=name:yagao&sort=price:desc
    

    上述是query string search,下面是常用的 query Domain Specified Language

    DSL:Domain Specified Language,特定领域的语言
    http request body:请求体,可以用json的格式来构建查询语法,比较方便,可以构建各种复杂的语法,比query string search肯定强大多了
    

    3.3.3检索所有商品

    GET /ecommerce/product/_search
    {
      "query": {"match_all": {}}
    }
    

    3.3.4查询名称包含yagao的商品,同时按照价格升序排序(定制排序规则)

    默认情况下,是按照_score降序排序的
    然而,某些情况下,可能没有有用的_score,比如说filter
    当然,也可以是constant_score
    
    GET /ecommerce/product/_search
    {
      "query": {"match": {
        "name":"yagao"
      }},
      "sort": [
        {
          "price": "asc"
        }
      ]
    }
    

    3.3.5分页查询商品,总共2条商品,假设每页就显示1条商品,现在显示第2页,所以就查出来第2个商品

    GET /ecommerce/product/_search
    {
      "query": {"match_all": {}},
      "from": 1,
      "size": 1
    }
    

    3.3.6指定要查询出来商品的名称和价格就可以

    GET /ecommerce/product/_search
    {
      "query": {"match_all": {}},
      "_source": ["name", "price"]
    }
    

    3.3.7搜索商品名称包含yagao,而且售价大于25元的商品

    GET /ecommerce/product/_search
    {
      "query": {
        "bool": {
          "must": [
          {"match": {
            "name": "yagao"
          }
          }
        ],"filter": {
          "range": {
            "price": {
              "lte": 10
            }
          }
        }
        }
      }
    }
    

    3.3.8full-text search(全文检索)

    GET /ecommerce/product/_search
    {
      "query": {
        "match": {
          "producer": "yagao hei"
        }
      }
    }
    

    3.3.9highlight search(高亮搜索结果)

    GET /ecommerce/product/_search
    {
      "query": {
        "match": {
          "producer": "producer"
        }
      },
      "highlight": {
        "fields": {
          "producer": {}
        }
      }
    }
    

    3.3.10phrase search(短语搜索)

    跟全文检索相反,全文检索会将输入的搜索串拆解开来,去倒排索引里面去一一匹配,只要能匹配上任意一个拆解后的单词,就可以作为结果返回
    phrase search,要求输入的搜索串,必须在指定的字段文本中,完全包含一模一样的,才可以算匹配,才能作为结果返回
    
    GET /ecommerce/product/_search
    {
      "query": {
        "match_phrase": {
          "producer": "hei producer"
        }
      }
    }
    

    3.3.11分页及deep paging

    分页搜索

    GET /_search?size=10
    GET /_search?size=10&from=0
    GET /_search?size=10&from=20
    
    deep paging性能问题
    假设搜索第1000页,则请求首先到coordinate node-->
    各primary shard将数据放到coordinate,进行排序-->
    根据_score进行排序,选择最相关的几条数据-->
    返回数据
    耗费资源,cpu,耗费内存,不建议使用
    

    3.3.12query string基础语法(较少用)

    要包含test_field是test

    GET /test_index/test_type/_search?q=test_field:test
    

    要包含test_field是test

    GET /test_index/test_type/_search?q=+test_field:test
    

    不含test_field是test

    GET /test_index/test_type/_search?q=-test_field:test
    

    _all metadata的原理和作用

    GET /test_index/test_type/_search?q=test
    
    直接可以搜索所有的field,任意一个field包含指定的关键字就可以搜索出来。
    我们在进行中搜索的时候,难道是对document中的每一个field都进行一次搜索吗?不是的
    es中的_all元数据,在建立索引的时候,我们插入一条document,它里面包含了多个field,
    此时,es会自动将多个field的值,全部用字符串的方式串联起来,变成一个长的字符串,作为_all field的值,同时建立索引
    后面如果在搜索的时候,没有对某个field指定搜索,就默认搜索_all field,其中是包含了所有field的值的
    

    3.3.13bool组合多个搜索条件

    GET /website/article/_search
    {
      "query": {
        "bool": {
          "must": [ // 必须包含
            {
              "match": {
                "title": "elasticsearch"
              }
            }
          ],
          "should": [ // 可以包含
            {
              "match": {
                "content": "elasticsearch"
              }
            }
          ],
          "must_not": [ //不能包含
            {
              "match": {
                "author_id": 111
              }
            }
          ]
        }
      }
    }
    

    每个子查询都会计算一个document针对它的相关度分数,然后bool综合所有分数,合并为一个分数,当然filter是不会计算分数的

    GET /website/article/_search
    {
        "bool": {
            "must":     { "match": { "title": "how to make millions" }},
            "must_not": { "match": { "tag":   "spam" }},
            "should": [
                { "match": { "tag": "starred" }}
            ],
            "filter": {
              "bool": { 
                  "must": [
                      { "range": { "date": { "gte": "2014-01-01" }}},
                      { "range": { "price": { "lte": 29.99 }}}
                  ],
                  "must_not": [
                      { "term": { "category": "ebooks" }}
                  ]
              }
            }
        }
    }
    

    3.3.14filter与query的对比分析

    PUT /company/employee/1
    {
      "name": "tom",
      "age": 30,
      "join_date": "2018-12-31"
    }
    
    PUT /company/employee/2
    {
      "name": "jerry",
      "age": 23,
      "join_date": "2018-10-31"
    }
    #搜索请求:年龄必须大于等于10,同时join_date必须是2018-12-31
    GET /company/employee/_search
    {
      "query": {
        "bool": {
          "must": [
            {"match": {
              "join_date": "2018-12-31"
            }}
          ],
          "filter": {
            "range": {
              "age": {
                "gte": 10
              }
            }
          }
        }
      }
    }
    

    filter与query比较

    filter:
    (1)仅仅只是按照搜索条件过滤出需要的数据而已,不计算任何相关度分数,对相关度没有任何影响
    (2)不需要计算相关度分数,不需要按照相关度分数进行排序,同时还有内置的自动cache最常使用filter的数据,性能较好
    
    query:
    (1)会去计算每个document相对于搜索条件的相关度,并按照相关度进行排序
    (2)要计算相关度分数,按照分数进行排序,而且无法cache结果,性能较差
    
    总结:
    一般来说,
    如果你是在进行搜索,需要将最匹配搜索条件的数据先返回,那么用query;
    如果你只是要根据一些条件筛选出一部分数据,不关注其排序,那么用filter;
    除非是你的这些搜索条件,你希望越符合这些搜索条件的document越排在前面返回,那么这些搜索条件要放在query中;
    如果你不希望一些搜索条件来影响你的document排序,那么就放在filter中即可
    

    如果单纯想用filter,需加一个constant_score

    GET /company/employee/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "range": {
              "age": {
                "gte": 10,
                "lte": 25
              }
            }
          }
        }
      }
    }
    

    3.3.15multi match

    GET /test_index/test_type/_search
    {
      "query": {
        "multi_match": {
          "query": "test", # 关键词是test
          "fields": ["test_field", "test_field1"]  #test_field或test_field1包含test都行
        }
      }
    }
    

    3.3.16range,可以放到query中,也可以放到filter中

    GET /company/_search
    {
      "query": {
        "range": {
          "age": {  // 年龄大于等于10,小于30
            "gte": 10,
            "lt": 30
          }
        }
      }
    }
    

    3.3.17term查询

    与match查询不同,term中查询的字符串不会被分词,而是exact value
    GET /company/_search
    {
      "query": {
        "term": {
          "name": "jerry 1" # 此处会作为一个整串去查询
        }
      }
    }
    

    3.3.18terms查询

    与term查询类似,只是,可以对某个field指定多个搜索词
    GET /company/_search
    {
      "query": {
        "terms": {
          "name": ["jerry", "tom"] #这里可以指定多个搜索词
        }
      }
    }
    

    3.3.19将一个field索引两次来解决字符串排序问题

    创建mapping: 将title索引两次,一次用来分词,用来搜索;一次加上raw,不分词,用来排序

    PUT /website
    {
      "mappings": {
        "article": {
          "properties": {
            "title": {
              "type": "text",
              "fields": {
                "raw": {
                  "type": "keyword"
                }
              },
              "fielddata": true
            },
            "content": {
              "type": "text"
            },
            "post_date": {
              "type": "date"
            },
            "author_id": {
              "type": "long"
            }
          }
        }
      }
    }
    

    插入数据

    PUT /website/article/1
    {
      "title": "first article",
      "content": "this is my first article",
      "post_date": "2019-01-01",
      "author_id": 100
    }
    
    PUT /website/article/2
    {
      "title": "second article",
      "content": "this is my second article",
      "post_date": "2019-01-01",
      "author_id": 101
    }
    

    进行排序搜索

    GET /website/article/_search
    {
      "query": {"match_all": {}},
      "sort": [
        {
          "title.raw": {
            "order": "desc"
          }
        }
      ]
    }
    
    图片.png

    3.4聚合分析

    3.4.1计算每个tag下的商品数量

    GET /ecommerce/product/_search
    {
      "aggs": {
        "groups_by_tags": {
          "terms": {
            "field": "tags"
          }
        }
      }
    }
    

    此时发现报错如下

    图片.png
    需要设置tags的fielddata属性为true
    PUT /ecommerce/_mapping/product
    {
      "properties": {
        "tags":{
          "type": "text",
          "fielddata": true
        }
      }
    }
    

    然后再执行上述查询语句即可

    3.4.2对名称中包含yagao的商品,计算每个tag下的商品数量

    GET /ecommerce/product/_search
    {
      "size": 0,
      "query": {
        "match": {
          "name": "yagao"
        }
      },
      "aggs": {
        "all_tags": {
          "terms": {
            "field": "tags"
          }
        }
      }
    }
    

    3.4.3先分组,再算每组的平均值,计算每个tag下的商品的平均价格

    GET /ecommerce/product/_search
    {
      "size": 0,
      "aggs": {
        "group_by_tags": {
          "terms": {
            "field": "tags"
          },
          "aggs": {
            "avg_price": {
              "avg": {
                "field": "price"
              }
            }
          }
        }
      }
    }
    

    3.4.4计算每个tag下的商品的平均价格,并且按照平均价格升序排序

    GET /ecommerce/product/_search
    {
      "size": 0,
      "aggs": {
        "all_tags": {
          "terms": {
            "field": "tags",
            "order": {
              "avg_price": "asc"
            }
          },
          "aggs": {
            "avg_price": {
              "avg": {
                "field": "price"
              }
            }
          }
        }
      }
    }
    

    3.4.5按照指定的价格范围区间进行分组,然后在每组内再按照tag进行分组,最后再计算每组的平均价格

    GET /ecommerce/product/_search
    {
      "size": 0,
      "aggs": {
        "group_by_price": {
          "range": {
            "field": "",
            "ranges": [
              {
                "from": 0,
                "to": 10
              },
              {
                "from": 10,
                "to": 20
              },{
                "from": 20,
                "to": 50
              }
            ]
          },
          "aggs": {
            "group_by_tags": {
              "terms": {
                "field": "tags"
              },
              "aggs": {
                "avg_prices": {
                  "avg": {
                    "field": "price"
                  }
                }
              }
            }
          }
        }
      }
    }
    

    3.5mget批量查询

    语法一: 当_index与_type都不一样时
    GET /_mget
    {
      "docs": [
        {
        "_index": "test_index",
        "_type": "test_type",
        "_id": "4"
        },
        {
        "_index": "test_index",
        "_type": "test_type",
        "_id": "3"
        }
      ]
    }
    
    语法二:当_index与_type都一样时:
    GET /test_index/test_type/_mget
    {
      "ids":[1,2,3,4]
    }
    
    语法三:当_index一样,_type不一样时:
    GET /test_index/_mget
    {
      "docs":[
        {
          "_type": "test_type",
          "_id": 1
        },
            {
          "_type": "test_type",
          "_id": 4
        }
      ]
    }
    

    3.6multi-index和multi-type搜索模式

    /_search:所有索引,所有type下的所有数据都搜索出来
    
    /index1/_search:指定一个index,搜索其下所有type的数据
    
    /index1,index2/_search:同时搜索两个index下的数据
    
    /*1,*2/_search:按照通配符去匹配多个索引
    
    /index1/type1/_search:搜索一个index下指定的type的数据
    
    /index1/type1,type2/_search:可以搜索一个index下多个type的数据
    
    /index1,index2/type1,type2/_search:搜索多个index下的多个type的数据
    
    /_all/type1,type2/_search:_all,可以代表搜索所有index下的指定type的数据
    

    3.7查询逻辑

    1、客户端发送请求到任意一个node,成为coordinate node
    2、coordinate node对document进行路由,将请求转发到对应的node,此时会使用round-robin随机轮询算法,在primary shard以及其所有replica中随机选择一个,让读请求负载均衡
    3、接收请求的node返回document给coordinate node
    4、coordinate node返回document给客户端
    5、特殊情况:document如果还在建立索引过程中,可能只有primary shard有,任何一个replica shard都没有,此时可能会导致无法读取到document,但是document完成索引建立之后,primary shard和replica shard就都有了
    

    3.8查询结果含义剖析

    GET /_search

    结果:
    {
      "took": 6,
      "timed_out": false,
      "_shards": {
        "total": 6,
        "successful": 6,
        "failed": 0
      },
      "hits": {
        "total": 10,
        "max_score": 1,
        "hits": [
          {
            "_index": ".kibana",
            "_type": "config",
            "_id": "5.2.0",
            "_score": 1,
            "_source": {
              "buildNum": 14695
            }
          }
        ]
      }
    }
    

    3.8http协议中get是否可以带上request body

    HTTP协议,一般不允许get请求带上request body,
    但是因为get更加适合描述查询数据的操作,因此还是这么用了
    
    很多浏览器,或者是服务器,也都支持GET+request body模式
    如果遇到不支持的场景,也可以用POST /_search
    
    POST /_search
    {
      "from":0,
      "size":10
    }
    

    4.document的核心元数据以及图解剖析index创建

    {
      "_index": "test_index",
      "_type": "test_type",
      "_id": "1",
      "_version": 1,
      "found": true,
      "_source": {
        "test_content": "test test"
      }
    }
    
    took:整个搜索请求花费了多少毫秒
    
    hits.total:本次搜索,返回了几条结果
    
    hits.max_score:本次搜索的所有结果中,最大的相关度分数是多少,每一条document对于search的相关度,越相关,_score分数越大,排位越靠前
    
    hits.hits:默认查询前10条数据,完整数据,_score降序排序
    
    shards:shards fail的条件(primary和replica全部挂掉),不影响其他shard。
            默认情况下来说,一个搜索请求,会打到一个index的所有primary shard上去,
            当然了,每个primary shard都可能会有一个或多个replic shard,
            所以请求也可以到primary shard的其中一个replica shard上去。
    
    timeout:默认无timeout,latency平衡completeness,手动指定timeout,timeout查询执行机制
              timeout=10ms,timeout=1s,timeout=1m
    

    GET /_search?timeout=10m

    4.1_index元数据

    (1)代表一个document存放在哪个index中
    (2)类似的数据放在一个索引,非类似的数据放不同索引:product index(包含了所有的商品),sales index(包含了所有的商品销售数据),inventory index(包含了所有库存相关的数据)。如果你把比如product,sales,human resource(employee),全都放在一个大的index里面,比如说company index,不合适的。
    (3)index中包含了很多类似的document:类似是什么意思,其实指的就是说,这些document的fields很大一部分是相同的,你说你放了3个document,每个document的fields都完全不一样,这就不是类似了,就不太适合放到一个index里面去了。
    (4)索引名称必须是小写的,不能用下划线开头,不能包含逗号:product,website,blog
    

    4.2_type元数据

    (1)代表document属于index中的哪个类别(type)
    (2)一个索引通常会划分为多个type,逻辑上对index中有些许不同的几类数据进行分类:因为一批相同的数据,可能有很多相同的fields,但是还是可能会有一些轻微的不同,可能会有少数fields是不一样的,举个例子,就比如说,商品,可能划分为电子商品,生鲜商品,日化商品,等等。
    (3)type名称可以是大写或者小写,但是同时不能用下划线开头,不能包含逗号
    

    4.3_id元数据

    (1)代表document的唯一标识,与index和type一起,可以唯一标识和定位一个document
    (2)我们可以手动指定document的id(put /index/type/id),也可以不指定,由es自动为我们创建一个id
    

    4.3.1.手动指定document id

    put /index/type/id
    
    PUT /test_index/test_type/2
    {
      "test_content": "my test"
    }
    

    4.3.2自动生成document id

    post /index/type
    
    POST /test_index/test_type
    {
      "test_content": "my test"
    }
    
    自动生成的id,长度为20个字符,URL安全,base64编码,GUID,分布式系统并行生成时不可能会发生冲突
    

    4.4_source元数据

    是指,我们在创建一个document的时候,
    使用的那个放在request body中的json串,
    默认情况下,在get的时候,会原封不动的给我们返回回来。
    

    定制返回的结果,指定_source中,返回哪些field

    GET /test_index/test_type/1?_source=field1,field2
    

    4.5_version元数据

    ===Elasticsearch内部基于_version进行乐观锁并发控制===

    第一次创建一个document的时候,它的_version内部版本号就是1;
    以后,每次对这个document执行修改或者删除操作,都会对这个_version版本号自动加1;
    哪怕是删除,也会对这条数据的版本号加1
    

    4.5.1基于_version进行乐观锁并发控制

    基于最新的数据和版本号,去进行修改,修改后,
    带上最新的版本号,可能这个步骤会需要反复执行好几次,才能成功,
    特别是在多线程并发更新同一条数据很频繁的情况下
    
    加上?version=2,即对应的版本号即可
    PUT /test_index/test_type/7?version=2 
    {
      "test_field": "test client 2"
    }
    

    4.5.2基于external version进行乐观锁并发控制

    es提供了一个feature,就是说,你可以不用它提供的内部_version版本号来进行并发控制,
    可以基于你自己维护的一个版本号来进行并发控制。
    举个列子,假如你的数据在mysql里也有一份,然后你的应用系统本身就维护了一个版本号,
    无论是什么自己生成的,程序控制的。
    这个时候,你进行乐观锁并发控制的时候,可能并不是想要用es内部的_version来进行控制,
    而是用你自己维护的那个version来进行控制。
    
    ?version=1   #基于_version进行乐观锁并发控制
    若document的_version=1,则更新时?version=1,才能更新成功
    
    ?version=1&version_type=external   #基于external version进行乐观锁并发控制
    若document的_version=1,则更新时?version>1&version_type=external,才能成功,比如说?version=2&version_type=external
    

    5.es文档的增,改,删

    5.1 document的全量替换

    (1)语法与创建文档是一样的,如果document id不存在,那么就是创建;如果document id已经存在,那么就是全量替换操作,替换document的json串内容
    (2)document是不可变的,如果要修改document的内容,第一种方式就是全量替换,直接对document重新建立索引,替换里面所有的内容
    (3)es会将老的document标记为deleted,然后新增我们给定的一个document,当我们创建越来越多的document的时候,es会在适当的时机在后台自动删除标记为deleted的document
    

    5.2 document的强制创建

    (1)创建文档与全量替换的语法是一样的,有时我们只是想新建文档,不想替换文档,如果强制进行创建呢?
    (2)PUT /index/type/id?op_type=create,PUT /index/type/id/_create
    

    5.3 document的删除

    (1)DELETE /index/type/id
    (2)不会理解物理删除,只会将其标记为deleted,当数据越来越多的时候,在后台自动删除
    

    5.4 document的partial update

    PUT /index/type/id,创建文档&替换文档,就是一样的语法
    
    一般对应到应用程序中,每次的执行流程基本是这样的:
    
    (1)应用程序先发起一个get请求,获取到document,展示到前台界面,供用户查看和修改
    (2)用户在前台界面修改数据,发送到后台
    (3)后台代码,会将用户修改的数据在内存中进行执行,然后封装好修改后的全量数据
    (4)然后发送PUT请求,到es中,进行全量替换
    (5)es将老的document标记为deleted,然后重新创建一个新的document
    

    partial update语法

    post /index/type/id/_update 
    {
       "doc": {
          "要修改的少数几个field即可,不需要全量的数据"
       }
    }
    
    PUT /test_index/test_type/10
    {
      "test_field1": "test1",
      "test_field2": "test2"
    }
    
    POST /test_index/test_type/10/_update
    {
      "doc": {
        "test_field2": "updated test2"
      }
    }
    

    partial update内置乐观锁并发控制

    post /index/type/id/_update?retry_on_conflict=5&version=6  #5表示重试次数,6表示版本号
    

    5.5bulk:批量增删改

    每一个操作要两个json串,语法如下:
    
    {"action": {"metadata"}}
    {"data"}
    
    举例,比如你现在要创建一个文档,放bulk里面,看起来会是这样子的:
    
    {"index": {"_index": "test_index", "_type", "test_type", "_id": "1"}}
    {"test_field1": "test1", "test_field2": "test2"}
    
    有哪些类型的操作可以执行呢?
    (1)delete:删除一个文档,只要1个json串就可以了
    (2)create:PUT /index/type/id/_create,强制创建
    (3)index:普通的put操作,可以是创建文档,也可以是全量替换文档
    (4)update:执行的partial update操作
    
    bulk api对json的语法,有严格的要求,
    每个json串不能换行,只能放一行,同时一个json串和一个json串之间,必须有一个换行
    

    示例:

    POST /_bulk
    {"delete": {"_index": "test_index","_type":"test_type","_id": 3}}
    {"create": {"_index": "test_index","_type":"test_type","_id": 5}}
    {"name": "tom5","age": 35}
    {"index": {"_index": "test_index","_type": "test_type","_id": 6}}
    {"name": "tom6","age": 36}
    {"index": {"_index": "test_index","_type": "test_type","_id": 1}}
    {"name": "tom6 replace tom1","age": 36}
    {"update": {"_index": "test_index","_type": "test_type","_id": 2}}
    {"doc":{"name": "tom2 update tom2_"}}
    
    bulk操作中,任意一个操作失败,是不会影响其他的操作的,
    但是在返回结果里,会告诉你异常日志
    

    bulk size最佳大小

    bulk request会加载到内存里,如果太大的话,性能反而会下降,
    因此需要反复尝试一个最佳的bulk size。
    一般从1000~5000条数据开始,尝试逐渐增加。
    另外,如果看大小的话,最好是在5~15MB之间。
    

    5.6增删改原理

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

    5.7写一致性操作: quorum机制

    在发送任何一个增删改操作的时候,
    比如说put /index/type/id,都可以带上一个consistency参数,
    指明我们想要的写一致性是什么?
    put /index/type/id?consistency=quorum
    
    one:要求我们这个写操作,只要有一个primary shard是active活跃可用的,就可以执行
    all:要求我们这个写操作,必须所有的primary shard和replica shard都是活跃的,才可以执行这个写操作
    quorum:默认的值,要求所有的shard中,必须是大部分的shard都是活跃的,可用的,才可以执行这个写操作
    

    consistency,one(primary shard),all(all shard),quorum(default)

    quorum机制,写之前必须确保大多数shard都可用:
    
    int( (primary + number_of_replicas) / 2 ) + 1,当number_of_replicas>1时才生效
    quroum = int( (primary + number_of_replicas) / 2 ) + 1
    
    举个例子,3个primary shard,number_of_replicas=1,总共有3 + 3 * 1 = 6个shard
    quorum = int( (3 + 1) / 2 ) + 1 = 3
    
    所以,要求6个shard中至少有3个shard是active状态的,才可以执行这个写操作
    
    如果节点数少于quorum数量,可能导致quorum不齐全,进而导致无法执行任何写操作:
    
    3个primary shard,replica=1,
    要求至少3个shard是active,3个shard按照之前学习的shard&replica机制,必须在不同的节点上,
    如果说只有2台机器的话,是不是有可能出现说,3个shard都没法分配齐全,此时就可能会出现写操作无法执行的情况
    
    es提供了一种特殊的处理场景,就是说当number_of_replicas>1时才生效,因为假如说,你就一个primary shard,replica=1,此时就2个shard
    (1 + 1 / 2) + 1 = 2,要求必须有2个shard是活跃的,但是可能就1个node,此时就1个shard是活跃的,如果你不特殊处理的话,导致我们的单节点集群就无法工作
    
    (4)quorum不齐全时,wait,默认1分钟,timeout,100,30s
    等待期间,期望活跃的shard数量可以增加,最后实在不行,就会timeout
    我们其实可以在写操作的时候,加一个timeout参数,比如说put /index/type/id?timeout=30,这个就是说自己去设定quorum不齐全的时候,es的timeout时长,可以缩短,也可以增长
    

    相关文章

      网友评论

        本文标题:ElasticSearch系列一:安装,使用,搜索

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