ElasticSearch初探
2015 年 06 月 27 日
elasticsearch

    在现实生活中,大多数面向用户的系统,都应该提供一些基本的查询搜索功能, 这将更好帮助用户找到自己想要的数据,不论从性能还是查询能力, 都会比DB更强大,从而也是减轻DB压力的一种途径, 本文将对ES进行一些初探。

  • 简介

  • ElasticSearch(下文称ES)是一款分布式且高可扩展的全文搜索和分析引擎,本身是基于Lucene进行开发, 同类的应该还有SolrHibernate Search等, 但ES足够简单易用,提供丰富的API,部署容易。

  • 常用案例

  • 这是一些常用的案例
  • 1. 典型的电商网站,可以让用户搜索商品,类目等。
    2. 日志查询及分析,ES有一套专门的日志收集,分析,聚合的技术栈:Elasticsearch/Logstash/Kibana
    3. 预警平台,比如商品价格预警,用户希望在某类某件商品价格降到某个价位时得到通知,可以利用ES的Percolator来实现。
    ...

  • 基本概念

  • ES中有一些关键的抽象及概念,理解它们也是十分必要的。
  • Near Realtime(NRT)

  • ES是一个近似实时的搜索平台,也就是说,从你索引一个文档开始,到其能被搜索,延迟会很低(通常是1s)。

  • 集群(Cluster)

  • 一个集群由多个节点(Server)构成,共同对外提供索引和查询服务。集群默认以elasticsearch作为标识, 该标识将决定一个节点属于哪一个集群,通常开发和测试保持默认,生产环境再进行定义。

  • 节点(Node)

  • 节点是集群中的单个Server,可以存储数据,并提供索引和查询服务。同集群一样,节点也会用字符串来标识自己, 若局域网内多个节点以相同的集群名称配置启动,将会自动形成一个集群。

  • 索引(Index)

  • 索引就是一些具有相似属性的文档集合。一个索引也通过名称来唯一标识,可以类比为数据库中的Scheme。

  • 类型(Type)

  • 类型就是文档所属的类,可以类比为数据库中的表。

  • 文档(Document)

  • 文档是索引信息的基本单位,并以JSON格式进行表示。

  • 分片(Shards)及备份(Replicas)

  • 单个索引本质上可以存储大量数据(可能超出单个节点的硬件限制),比如,一个索引里包含了十亿个文档, 并占用了1TB磁盘空间,这将不利于单个节点提供查询服务。为了解决这种问题, ElasticSearch具有将索引细分为多个分片(Shards)的能力。 当你创建索引时,可以定义想要的分片数。分片即是一个具有独立功能的"索引",可以位于集群中的任何节点上。 分片主要有两个原因:

    1. 水平分割扩展容量。
    2. 跨分片(本质是节点)分发和并行操作,因此提升性能和吞吐量。

    分片如何被分布以及它的文档如何被聚合到搜索请求中,都被ElasticSearch隐藏起来,对用户完全透明。 在分布式网络环境中,故障任何时候都可能发生,通过分片实现容错机制是十分有用并被推荐的。为此, ElasticSearch允许对索引分片进行备份,即备份分片。备份主要有两个原因:

    1. 当分片失败时提供高可用,需要注意备份分片永远不会和其主分片分配在同一节点上。
    2. 这将扩展集群的搜索吞吐量,因为所有搜索可以在备份分片上并行执行。

  • 大致的总结就是
  • 每个索引可以分割为多个分片,也可以有0个或多个备份,一旦备份, 每个索引将有主分片备份分片, 分片数和备份数可以在索引被创建时定义,索引创建后,可以动态更新备份数,但不能更新分片数。 ElasticSearch默认将每个索引分成5个分片,作1个备份,也就是说,集群中至少有2个节点, 那么索引将有5个主分片,5个备份分片。如

  • 两个节点分别有5个分片,粗线表示的是主分片,细线表示的是备份分片。
  • 安装部署

  • ElasticSearch(1.5.2)的安装部署比较简单,只需JAVA环境(1.7以上), 将安装包解压缩,进入bin,执行elasticsearch即可, 这是自己使用的一个标准包,其中集成了 BigdeskHeadIK分词器
  • 下面是一份简明的ES脚本
  • #!/usr/bin/env bash
    
    ES_HOME=~/Envir/server/es/es1
    ES_PID_FILE=~/Envir/server/es/es1/es.pid
    # java options such as: -Xmx512m -Xms512m
    JAVA_OPTIONS="-Xmx512m -Xms512m"
    # -d run in background
    BACKGROND="-d"
    REST_URL="localhost:9200"
    
    start()
    {
        local script=$ES_HOME/bin/elasticsearch
        $script -p $ES_PID_FILE  $JAVA_OPTIONS $BACKGROND
        echo "Started"
        return 0
    }
    
    stop()
    {
        if [ ! -f $ES_PID_FILE ]; then
            echo "$ES_PID_FILE isn't exist, maybe elasticsearch isn't running."
            return 1
        fi
        local pid=$(cat $ES_PID_FILE)
        if [ "$pid" = "" ]; then
            echo "pid is empty in file $ES_PID_FILE, maybe elasticsearch isn't running."
            return 1
        fi
        kill -9 $pid
        rm $ES_PID_FILE
        echo "Stopped."
        return 0
    }
    
    nodes()
    {
        curl "$REST_URL/_cat/nodes?v"
        return 0
    }
    
    indexs()
    {
        curl "$REST_URL/_cat/indices?v"
        return 0
    }
    
    master()
    {
        curl "$REST_URL/_cat/master?v"
        return 0
    }
    
    alloc()
    {
        curl "$REST_URL/_cat/allocation?v"
        return 0
    }
    
    kat()
    {
        curl -XGET "$REST_URL/$1?pretty"
        return 0
    }
    
    case "$1" in
    
        'start')
            start
            ;;
    
        'stop')
            stop
            ;;
    
        'restart')
            stop
            start
            ;;
    
        'nodes')
            nodes
            ;;
    
        'indexs')
            indexs
            ;;
    
        'master')
            master
            ;;
    
        'alloc')
            alloc
            ;;
    
        'kat')
            kat $2
            ;;
        *)
            echo "Usage:
            start   : start es.
            stop    : stop es.
            restart : restart es.
            nodes   : nodes informations.
            indexes : all index informations.
            master  : master information.
            alloc   : disk allocation informastions.
            kat     : cat an document, such as: kat /persons/person/{document ID}
            "
    esac
    
    unset ES_HOME
    unset ES_PID_FILE
    unset JAVA_OPTIONS
    unset BACKGROND
    unset REST_URL
    
        
  • 此外封装了一个简明的Java客户端工具
  • 一些常见的配置建议

  • 节点类型

  • 在配置集群中的节点时, 应该明确指定各节点的角色类型。如
  • 1. node.mater:true & node.data:true: 既为master,也为data节点,负责集群协调工作和数据存储等
    2. node.mater:true & node.data:false: 为master节点,负责集群协调工作等。
    3. node.mater:false & node.data:true: 为data节点,负责索引数据和搜索等。
    4. node.mater:false & node.data:false: 既不是master,也不是data节点,负责搜索负载均衡(从data节点抓取数据并作处理)。

  • 启用mlockall

  • 启用mlockall后,ES使用的内存将不会进入Swap分区,这将大大降低磁盘I/O带来的性能问题, 但需保证ES具有足够的内存,可通过下面的API查询是否启用mlockall:
  •     http://localhost:9200/_nodes/process?pretty
        
  • discovery.zen配置集群发现机制

  • Zen discover作为ES集群中节点间进行发现和交流的协议, 可通过discovery.zen.*等属性进行配置, 单播广播都作为发现协议的一部分
  • 1. 广播是指集群中的节点向其他所有节点发送请求。
    2. 单播是指在 discovery.zen.ping.unicast.hosts指定的主机列表之间进行通信。

  • 为了开启单播,需要设置 discovery.zen.ping.multicast.enabledfalse
  • 对于单播,必须指定属性discovery.zen.ping.unicast.hosts(单播的主机列表), discovery.zen.minimum_master_nodes 用于设置一个节点在操作集群时能看到的最小的maser节点数,建议设置为 N/2+1(N为master节点数)。

  • 数据节点和主节点通过两种方式发现彼此:
  • 1. 主节点ping集群中的所有节点,以验证它们是否状态正常。
    2. 其他节点ping主节点,验证它们是否状态正常,或者是否须要启动选举进程。

  • 小心删除所有索引

  • 为了防止误操作删除掉所有索引,应该禁止该这种操作
  •     action.disable_delete_all_indices: true
        
  • 限制Field数据缓存

  • Field数据缓存主要用于某个字段的排序和集合,应该作一定的限制,如
  • # 缓存大小30%
    indices.fielddata.cache.size: 30%
    # 5分钟过期
    indices.fielddata.cache.expire: 5m
        
  • 该值设置不合理,将影响查询和排序性能,或者ES对大量索引进行facet查询时内存溢出。
  • 索引优化

  • 索引相关的配置可见这里,根据实际生产环境可做相应变动。
  • 索引分片刷新时间

  • 当Document被建立之后,仅当索引分片刷新后,该文档才可被搜索,可通过index.refresh_interval分片刷新时间。
  • 事务日志刷新

  • 1. index.translog.flush_threshold_ops: 多少次操作后刷新事务日志,默认无限制。
    2. index.translog.flush_threshold_size: 一旦日志文件超出该大小,将刷新日志,默认512m。
    3. index.translog.flush_threshold_period: 当一段时间没有发生过刷新时,超过该时间将强制刷新一次。
    4. index.translog.interval: 多久检查一次是否刷新事务日志,将随机取值 interval ~ interval * 2,默认5s。

好人,一生平安。