Elasticsearch入门

Elasticsearch 和 solr

  Elasticsearch更加精简,大多高级功能是依靠插件完成的。而solr很全面,包含了大部分的高级功能,可以从下载大小就可以看出来。
  2者都是基于Lucene这个基于java的搜索类库。
  根据文档(未测试)Elasticsearch的实时搜索效率明显高于solr。
  由于小且精简,因此入门简单。

Elasticsearch 安装

  本次安装基于Windows10。
  官网下载:https://www.elastic.co/downloads/elasticsearch
  下载的zip包,下载解压,然后运行bin\elasticsearch.bat这个Windows批处理文件。
使用下面命令启动es,你可以运行多次这个命令,只要node.name不一样,cluster.name一样,那么就是一个集群。

elasticsearch.bat -E node.name=node2 -E cluster.name=dh -E path.data=node2  -d

  然后打开浏览器输入 http://localhost:9200/。看看是否返回了有效信息。
  安装node.js,grunt(网上搜索)。
  下载head插件。https://github.com/mobz/elasticsearch-head
  修改Elasticsearch的配置文件config/elasticsearch.yml
  修改一下ES的监听地址,这样别的机器也可以访问
  network.host: 0.0.0.0
  增加新的参数,这样head插件可以访问es
  http.cors.enabled: true
  http.cors.allow-origin: “*”
  然后在下载的head插件的解压文件夹的根路径下,运行,npm install 安装其所依赖的各种插件。
  最后启动 grunt server。访问http://localhost:9100/
  安装完毕。

ps:kabana,logstash配合使用时版本必须一致。

健康颜色

绿色就是在这个集群上,可以分配所有的主分片和副本分片
黄色,意味着只能分配主分片
红色,意味着主分片也无法分配

面向文档(Nosql)

  Elasticsearch 是 面向文档 的,意味着它是以文档来进行存储数据的。Elasticsearch 不仅存储文档,而且能索引每个文档的内容使之可以被检索。在 Elasticsearch 中,你是对文档进行索引、检索、排序和过滤–而不是对行列数据(关系数据库)。这是一种完全不同的思考数据的方式,也是 Elasticsearch 能支持复杂全文检索的原因。

索引

  在Elasticsearch里面,索引有很多个意思。
  1.名词:一个索引就是类似于关系数据库的一个数据库,是一个存储关系型文档的地方。
  2.动词:索引一个文档就是存储一个文档到索引(名词)中以便于它可以被检索和查询到,类似于关系数据库的insert关键字。
  3.倒排索引:如果说关系数据库的索引的数据结构是B+树,那么Elasticsearch采用了一个叫倒排索引的结构达到相同的目的。

创建和基本查询

  打开Elasticsearch的head插件,然后连接Elasticsearch。在复合查询中,输入url http://localhost:9200/megacorp/employee/3/,提交方式选择put,内容:{}(json)。即可创建一个索引文档。其中 http://localhost:9200 是Elasticsearch的地址。而megacorp上上面提到的名词索引。employee是类型。3为该数据的特定id。
  插入成功后,可以GET /megacorp/employee/3 查询出你插入的数据。
  往里面多插入几条数据,以便于学习测试。
   /megacorp/employee/_search 这个就是查出这个索引这个类型的所有数据。
  在head这个插件的复合查询里面,有2个行,第一行就是输入上面的url,下面的行,加入查询参数:?q=last_name:Smith
  后面有更加复杂的ELS查询:
  http://localhost:9200/
  megacorp/employee/_search/
  post
  {
“query” : {
“bool”: {
“must”: {
“match” : {
“last_name” : “smith”
}
},
“filter”: {
“range” : {
“age” : { “gt” : 30 }
}
}
}
}
}
  也可以用上面的方式(match)实现全文检索。结果会按照匹配度按顺序排下来。但是想要不全文检索,具体查询,那么就用这个(match_phrase)

文档的更新

在 Elasticsearch 中文档是 不可改变 的,不能修改它们。 相反,如果想要更新现有的文档,需要 重建索引 或者进行替换,在内部,Elasticsearch 已将旧文档标记为已删除,并增加一个全新的文档。 尽管你不能再对旧版本的文档进行访问,但它并不会立即消失。当继续索引更多的数据,Elasticsearch 会在后台清理这些已删除文档。删除逻辑也是。这点和mysql的删除很类似,都是逻辑删除!但是意义不一样~~~

冲突处理

  当多个端同时处理一个文档的值的时候,可能出现值丢失的情况。因此我们需要加锁,就像对mysql的处理一样。有2个方式,第一个就是悲观加锁,要获取这个数据,那么你要先获得锁,但是效率存在问题,第二个就是用乐观锁,类似java原子类的CAS的思想,我假设你没有变化。有变化,我就重新获取值进行处理,而我们的文档存在一个叫version的字段,可以用来判断。我们可以通过api来指定version来确认。

PUT /website/blog/1?version=1 
{
  "title": "My first blog entry",
  "text":  "Starting to get the hang of this..."
}

  当然大部分情况下,我们使用mysql作为主要的数据存储,而Elasticsearch主要作为检索用的,如果多个进程进行数据同步,就会出现那个问题。我们在数据库里面就有了版本号,那么我们也可以用这个版本号作为判断条件,是否更新。

请求合并

  es可以将多个请求合并成一个,避免单独处理每个请求花费的网络延时和开销。 如果你需要从 Elasticsearch 检索很多文档,那么使用 multi-get 或者 mget API 来将这些检索请求放在一个请求中,将比逐个文档请求更快地检索到全部文档。

_mget
{
       "docs" : [
  {
     "_index" : "website",
     "_type" :  "blog",
     "_id" :    2
  },
  {
     "_index" : "website",
     "_type" :  "pageviews",
     "_id" :    1,
     "_source": "views"
  }
       ]
}

分布式存储

  我们知道我们一个索引对应多个分片,每个分片又有多个副本,那么我们索引一个文档时,文档会被存储到主分片里面,但是应该存到具体哪个分片里面呢?是有个hash算法,根据id计算出一个值,然后对分片数取模,即可。

  以下是在主副分片和任何副本分片上面 成功新建,索引和删除文档所需要的步骤顺序:
  1.客户端向 Node 1 发送新建、索引或者删除请求。
  2.节点使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3,因为分片 0 的主分片目前被分配在Node 3 上。
  3.Node 3 在主分片上面执行请求。如果成功了,它将请求并行转发到 Node 1 和 Node 2 的副本分片上。一旦所有的副本分片都报告成功, Node 3 将向协调节点报告成功,协调节点向客户端报告成功。

  以下是从主分片或者副本分片检索文档的步骤顺序:

  1、客户端向 Node 1 发送获取请求。

  2、节点使用文档的 _id 来确定文档属于分片 0 。分片 0 的副本分片存在于所有的三个节点上。 在这种情况下,它将请求转发到 Node2 。

  3、Node 2 将文档返回给 Node 1 ,然后将文档返回给客户端。

  在处理读取请求时,协调结点在每次请求的时候都会通过轮询所有的副本分片来达到负载均衡。

  在文档被检索时,已经被索引的文档可能已经存在于主分片上但是还没有复制到副本分片。 在这种情况下,副本分片可能会报告文档不存在,但是主分片可能成功返回文档。 一旦索引请求成功返回给用户,文档在主分片和副本分片都是可用的。

  以下是部分更新一个文档的步骤:

  1.客户端向 Node 1 发送更新请求。
  2.它将请求转发到主分片所在的 Node 3 。
  3.Node 3 从主分片检索文档,修改 _source 字段中的 JSON ,并且尝试重新索引主分片的文档。 如果文档已经被另一个进程修改,它会重试步骤 3 ,超过 retry_on_conflict 次后放弃。
  4.如果 Node 3 成功地更新文档,它将新版本的文档并行转发到 Node 1 和 Node 2 上的副本分片,重新建立索引。 一旦所有副本分片都返回成功, Node 3 向协调节点也返回成功,协调节点向客户端返回成功。

集群

  Elasticsearch可以垂直扩展(购买性能更好的机器),也可以水平扩展(扩展多个节点),一个运行中的Elasticsearch实例就是一个节点,具有多个相同cluster.name配置的节点组成。其共同承担数据和负载的压力。
  集群有个叫集群健康的值,3个状态。
  green:所有主分片和副分片都正常。
  yellow:所有主分片正常,但是副分片有的不正常。
  red:存在主分片不正常。
  我们上面提到的名词索引实际上指向一个或者多个分片。一个分片是底层的工作单元,保存了全部数据的一部分,Elasticsearch利用分片将数据分发到集群各处的,分片是数据的容器,文档保存在分片中。分片分配到集群的各个节点里面。
  一个分片可以是主分片和副本分片,索引内的任何一个文档都是属于主分片的,所以主分片决定了索引能保存的最大数据量。副本分片是主分片的copy,为了保障数据安全的。
  水平扩容后,分片后自动重新分配的。
  一个Elasticsearch实例就是一个节点。一个索引默认五个主分片,五个副分片。
  当你只有一个节点的时候,创建一个索引的集群健康值一定是黄色。因为副本分片都是 unassigned —— 它们都没有被分配到任何节点。 在同一个节点上既保存原始数据又保存副本是没有意义的,因为一旦失去了那个节点,我们也将丢失该节点上的所有副本数据。当你再启动一个名字一样的节点的时候,集群健康值就是绿色了。
  然后我们在启动第三个节点,发现一个索引的10个分片均匀分配到3个节点里面了。
因此我们发现Elasticsearch的动态扩展能力极强。
  写操作我们只能通过主分片进行,读操作我们可以从主副分片都可以。当我们创建的节点吵过10个后,我们可以将副本重新设置,来获取更大的并发量。

java 操作 Elasticsearch

  代码如下:
  maven依赖:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>6.3.0</version>
</dependency>

  代码:

import java.net.InetAddress;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
public class TestEsClient {
    public static void main(String[] args) {
        try {
            Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
            // 创建client
            TransportClient client = new PreBuiltTransportClient(settings)
                    .addTransportAddress(new TransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
            // 搜索数据
            GetResponse response = client.prepareGet("megacorp", "employee", "1").execute().actionGet();
            // 输出结果
            System.out.println(response.getSourceAsString());
            // 关闭client
            client.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  结果输出:

{"first_name":"John","last_name":"Smith","age":25,"about":"I love to go rock climbing","interests":["sports","music"]}

小结

  大致这样,具体要用的时候细看吧。ps:关于java代码操作Elasticsearch,要切记,maven版本和Elasticsearch版本不能差距太大哦,不然是连不上的