官网:https://www.elastic.co/
下载:https://www.elastic.co/downloads/elasticsearch
文档:https://www.elastic.co/guide/index.htmlhtml
操做系统:CentOS release 6.8 (Final)
ES版本:6.1.1java
先确认安装了Java运行时环境:node
[es@localhost]$ java -version java version "1.8.0_121" Java(TM) SE Runtime Environment (build 1.8.0_121-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
解压ES压缩包:linux
[es@localhost]$ curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.1.1.zip [es@localhost]$ unzip elasticsearch-6.1.1.zip
[es@localhost]$ cd elasticsearch-6.1.1 [es@localhost]$ ./bin/elasticsearch # 在前台启动,能够经过CTRL + C中止 [es@localhost]$ ./bin/elasticsearch -d # 在后台以守护进程模式运行,经过信号通知关闭: kill -SIGTERM $pid
固然,对于在后台以守护进程模式运行的ES,能够在启动时将进程ID保存到指定文件,在中止时读取进程ID,发送SIGTERM信号给进程进行关闭。git
[es@localhost]$ cd elasticsearch-6.1.1 [es@localhost]$ ./bin/elasticsearch -p /tmp/elasticsearch-pid -d [es@localhost]$ kill -SIGTERM `cat /tmp/elasticsearch-pid`
实际上,咱们还能够直接将这个启动和中止ES的操做命令写到一个脚本文件中来实现,这样就避免了每次都输入命令。
注意: 在Shell脚本不能直接使用信号SIGRTMIN,须要使用信号编号15代替,如:程序员
#!/bin/bash pid=`cat /tmp/elasticsearch-pid` kill -15 $pid
验证是否启动成功:github
[es@localhost]$ curl http://localhost:9200/?pretty { "name" : "4t5PbHS", "cluster_name" : "elasticsearch", "cluster_uuid" : "l7iMk0Y2QOWWu5UG-MWlpA", "version" : { "number" : "6.1.1", "build_hash" : "bd92e7f", "build_date" : "2017-12-17T20:23:25.338Z", "build_snapshot" : false, "lucene_version" : "7.1.0", "minimum_wire_compatibility_version" : "5.6.0", "minimum_index_compatibility_version" : "5.0.0" }, "tagline" : "You Know, for Search" }
另外,须要注意的是经过上述方式启动的ES只能在本地访问,即:只能经过localhost方式访问。web
[es@localhost]$ netstat -anpt|grep 92 (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) tcp 0 0 192.168.80.133:22 192.168.80.1:52710 ESTABLISHED - tcp 0 0 192.168.80.133:22 192.168.80.1:52730 ESTABLISHED - tcp 0 64 192.168.80.133:22 192.168.80.1:52742 ESTABLISHED - tcp 0 0 ::ffff:127.0.0.1:9200 :::* LISTEN 4512/java tcp 0 0 ::1:9200 :::* LISTEN 4512/java
一般咱们的ES是须要可以在外部访问的,因此须要修改elasticsearch.yml中的network.host参数为network.host: 0.0.0.0
。算法
节点(node)是一个运行着的Elasticsearch实例。集群(cluster)是一组具备相同cluster.name的节点集合,他们协同工做,共享数据并提供故障转移和扩展功能,固然一个节点也能够组成一个集群。必须设置一个合适的名字来替代cluster.name的默认值,这样能够防止一个新启动的节点加入到相同网络中的另外一个同名的集群中。能够经过修改文件elasticsearch.yml中参数cluster.name值,而后重启ES来作到这一点。
ES默认的集群名称为:elasticsearch。spring
关于与ES的交互方式,总结起来为2种:Java API和RESTful接口。
其中,Java API比较混乱,不一样版本之间没法兼容。下面,咱们对在不一样版本的客户端进行详细说明。
(1)[v0.90, v2.2]:在ES2.2及以前的版本中,ES为Java用户提供了两种内置客户端:
节点客户端(Node Client)
节点客户端以无数据节点身份加入集群,换言之,它本身不存储任何数据,可是它知道数据在集群中的具体位置,而且可以直接转发请求到对应的节点上。
传输客户端(Transport Client)
这个更轻量的传输客户端可以发送请求到远程集群。它本身不加入集群,只是简单转发请求给集群中的节点。
两种Java客户端都经过9300端口与集群交互,使用ES传输协议(ElasticsearchTransport Protocol)。集群中的节点之间也经过9300端口进行通讯。若是此端口未开放,你的节点将不能组成集群。
具体来讲这两种Java客户端的使用方式不相同:
<dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>4.5.1</version> </dependency>
// Embedded node clients behave just like standalone nodes, // which means that they will leave the HTTP port open! Settings settings = Settings.settingsBuilder() .put("http.enabled", false) .put("path.home","D:\\elasticsearch-2.2.2") .build(); Node node = new NodeBuilder() .settings(settings ) .clusterName("elasticsearch") .client(true) // should not hold data .node(); Client client = node.client(); // 添加文档 Map<String, Object> source = new HashMap<String, Object>(); source.put("first_name", "John"); source.put("last_name", "Smith"); source.put("age", 25); source.put("about", "I love to go rock climbing"); source.put("interests", Arrays.asList(new String[] {"sports", "music"})); IndexRequest req = new IndexRequest().index("megacorp").type("employee") .id("1") .source(source); client.index(req); // on shutdown node.close();
关于节点客户端,须要作几点说明:
Settings settings = Settings.settingsBuilder() .put("cluster.name", "elasticsearch") .put("client.transport.sniff", true) .build(); Client client = TransportClient.builder().settings(settings).build() .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300)); //.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("test.com"), 9300)); // 添加文档 Map<String, Object> source = new HashMap<String, Object>(); source.put("first_name", "Jane"); source.put("last_name", "Smith"); source.put("age", 32); source.put("about", "I like to collect rock albums"); source.put("interests", Arrays.asList(new String[] {"music"})); IndexRequest req = new IndexRequest().index("megacorp").type("employee") .id("2") .source(source); client.index(req); // on shutdown client.close();
很显然,传输客户端实际上是使用轮训算法与ES集群中的全部节点创建链接,从而达到负载均衡的效果。
(2)[v2.3, v2.4]:在ES2.3和2.4版本中,ES提供的Java客户端被分别叫作:
这里的Transport Client
与以前的传输客户端是同一个东西。相比起ES2.2及以前的版本,在ES2.3和ES2.4版本中引入了一个专门用于路由请求的客户端节点Client Node
,暂且称之为“路由客户端节点”。这个“路由客户端节点”与以前的节点客户端相似,须要加入ES集群,可是其不能参与Master选举,也不能保存数据,本质上来说它就是一个轻量级的“节点客户端”。可是它不能独立为客户端服务,而是经过Transport Client
链接到Client Node
。
Client Node
的使用模式:
<dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>2.4.1</version> </dependency> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>4.5.1</version> </dependency>
/** * 启动路由客户端节点 */ public static void startClientNode() { Settings settings = Settings.settingsBuilder() .put("node.master", false) .put("node.data", false) .put("path.home","D:\\elasticsearch-2.4.1") .build(); Node node = new NodeBuilder() .settings(settings ) .clusterName("elasticsearch") .node(); node.start(); System.out.println("Client Node Started"); } /** * 传输客户端与客户端节点通讯 * @throws UnknownHostException */ public static void transportClient() throws UnknownHostException { System.out.println("Do transport client"); // 传输客户端直接与"路由客户端节点"通讯 Client client = TransportClient.builder().build() .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9301)); // 添加文档 Map<String, Object> source = new HashMap<String, Object>(); source.put("first_name", "John"); source.put("last_name", "Smith"); source.put("age", 25); source.put("about", "I love to go rock climbing"); source.put("interests", Arrays.asList(new String[] {"sports","music"})); IndexRequest req = new IndexRequest().index("megacorp").type("employee") .id("1") .source(source); client.index(req); // on shutdown client.close(); }
(3)[v5.0,v6.1]:从ES5.0版本开始,到如今的6.1版本,ES提供的Java客户端被统一为以下2种:
从ES5.0版本开始,引入了一个新的协调节点:Coordinating Only Node
,专门用于路由请求,分发批量索引操做。与Client Node
相似,该协调节点不能参与master选举,也不能保存数据。
<dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>6.1.1</version> </dependency>
/** * 从5.0版本开始,传输客户端的使用方式与以前不一样(不一样版本的ES Java API使用方式可能不一样) * @throws UnknownHostException */ public static void transportClient() throws UnknownHostException { System.out.println("Do transport client"); TransportClient client = new PreBuiltTransportClient(Settings.EMPTY) .addTransportAddress(new TransportAddress(InetAddress.getByName("localhost"), 9300)); // 添加文档 Map<String, Object> source = new HashMap<String, Object>(); source.put("first_name", "John"); source.put("last_name", "Smith"); source.put("age", 25); source.put("about", "I love to go rock climbing"); source.put("interests", Arrays.asList(new String[] {"sports","music"})); IndexRequest req = new IndexRequest().index("megacorp").type("employee") .id("1") .source(source); client.index(req); // on shutdown client.close(); }
总结起来,关于ES提供的Java API,到目前(v6.1.1)为止一共提供了4种客户端:Transport Client
,Node Client
,Client Node
,Coordinating Only Node
,他们分别对应在不一样的版本中使用。其中,Transport Client
和Node Client
均可以独立使用,而Client Node
和Coordinating Only Node
都不能独立提供查询服务,他们做为一个功能节点加入ES集群,而后经过Transport Client
进行链接。
注意:
另外,从ES5.0版本开始,还提供了一个Java REST Client
,这个客户端能够兼容全部的ES版本。而且,从ES5.6版本开始,这个Java REST Client
又细分为两个版本:Java Low Level REST Client
和Java High Level REST Client
。
其中,Java Low Level REST Client
能兼容全部ES版本;而Java High Level REST Client
只能兼容主版本号与之相同的ES版本,而且ES的小版本号必须大于等于Java High Level REST Client
的对应小版本号。举个例子,6.0版本的Java High Level REST Client
能够兼容全部6.x版本的ES,可是6.1版本的Java High Level REST Client
可能没法兼容6.0版本的ES,由于ES的小版本号(0)小于Java High Level REST Client
的小版本号(1)。关于更多Java REST Client
信息,详见:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/5.0/index.html。
Java API虽然对Java程序员提供了必定的便利性,可是并不友好,对于非Java栈的应用来讲就不能使用Java API。与之相对应的,RESTful风格的API就不能再这个限制,并且更加灵活,自由度更大。固然,ES官方提供了多种程序语言的客户端,如:Groovy,JavaScript,.NET,PHP,Perl,Python以及Ruby。关于Java API的更多信息详见:https://www.elastic.co/guide/en/elasticsearch/client/index.html
基于HTTP协议,以JSON为数据交互格式的RESTful API。
其余全部程序语言均可以使用RESTful API,经过9200端口的与ES进行通讯,可使用任何你喜欢的WEB客户端。事实上,如你所见,你甚至能够经过curl命令与ES通讯。
上面谈到的ES交互方式都是ES官方提供的API或接口,基于这些API或接口还存在一些第三方组件,对于在应用开发或调试过程当中很是有用。
(1)spring-data-elasticsearch
该组件是Spring官方提供的一个与ES交互的组件库,便于在基于Spring框架的应用程序中操做ES。
详见:https://github.com/spring-projects/spring-data-elasticsearch
(2)ElasticSearch Query Builder
这是一个Chrome插件,在进行简单的调试查询时使用该插件很是便利和高效。
https://chrome.google.com/webstore/detail/elasticsearch-query-build/cioobgbmiehdijndbmjkbjlkdeijdibp?utm_source=chrome-ntp-icon
(3)ElasticSearch Head
Chrome插件,用于编写DSL查询,对于学习DSL查询很是有帮助。
https://chrome.google.com/webstore/detail/elasticsearch-head/ffmkiejjmecolpfloofpjologoblkegm
以下示例均已RESTful接口说明。
语法:PUT http://host:port/index/type/id,文档内容使用json格式做为http请求body。
curl -i -XPUT http://localhost:9200/megacorp/employee/1 -d ' { "first_name": "John", "last_name": "Smith", "age": 25, "about": "I love to go rock climbing", "interests": [ "sports", "music" ] } '
curl -i -XGET http://localhost:9200/megacorp/employee/1
返回:
HTTP/1.1 200 OK Content-Type: application/json; charset=UTF-8 Content-Length: 205 { "_index": "megacorp", "_type": "employee", "_id": "1", "_version": 1, "found": true, "_source": { "about": "I love to go rock climbing", "last_name": "Smith", "interests": [ "sports", "music" ], "first_name": "John", "age": 25 } }
curl -i -XGET http://localhost:9200/megacorp/employee/_search
返回:
HTTP/1.1 200 OK Content-Type: application/json; charset=UTF-8 Content-Length: 501 { "took": 16, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 2, "max_score": 1, "hits": [ { "_index": "megacorp", "_type": "employee", "_id": "2", "_score": 1, "_source": { "about": "I like to collect rock albums", "last_name": "Smith", "interests": [ "music" ], "first_name": "Jane", "age": 32 } } ] } }
语法:http://host:port/index/type/_search?q=querystring
示例:查询姓氏为Smith的员工
curl -i -XGET http://localhost:9200/megacorp/employee/_search?q=last_name:Smith
返回:
HTTP/1.1 200 OK Content-Type: application/json; charset=UTF-8 Content-Length: 522 { "took": 9, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 2, "max_score": 0.30685282, "hits": [ { "_index": "megacorp", "_type": "employee", "_id": "2", "_score": 0.30685282, "_source": { "about": "I like to collect rock albums", "last_name": "Smith", "interests": [ "music" ], "first_name": "Jane", "age": 32 } } ] } }
查询字符串搜索便于经过命令行完成特定(ad hoc)的搜索,可是它也有局限性。Elasticsearch提供丰富且灵活的查询语言叫作DSL查询(Query DSL),它容许你构建更加复杂、强大的查询。DSL(Domain Specific Language特定领域语言)以JSON请求体的形式出现。咱们能够这样表示以前关于“Smith”的查询:
curl -i -H 'Content-Type: application/json' -XPOST http://localhost:9200/megacorp/employee/_search -d ' { "query": { "match": { "last_name": "Smith" } } } '
这会返回与以前查询相同的结果。你能够看到有些东西改变了,咱们再也不使用查询字符串作为参数,而是使用请求体代替。这个请求体使用JSON表示,其中使用了match语句。
显然,在DSL查询中,须要传递消息体,因此只能使用POST方法。
Q1. can not run elasticsearch as root
[root@localhost ~]# ./bin/elasticsearch [2017-12-22T19:08:28,283][WARN ][o.e.b.ElasticsearchUncaughtExceptionHandler] [] uncaught exception in thread [main] org.elasticsearch.bootstrap.StartupException: java.lang.RuntimeException: can not run elasticsearch as root at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:125) ~[elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:112) ~[elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86) ~[elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124) ~[elasticsearch-cli-6.1.1.jar:6.1.1] at org.elasticsearch.cli.Command.main(Command.java:90) ~[elasticsearch-cli-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:92) ~[elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:85) ~[elasticsearch-6.1.1.jar:6.1.1] Caused by: java.lang.RuntimeException: can not run elasticsearch as root at org.elasticsearch.bootstrap.Bootstrap.initializeNatives(Bootstrap.java:104) ~[elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:171) ~[elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:322) ~[elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:121) ~[elasticsearch-6.1.1.jar:6.1.1] ... 6 more
缘由: ES不能使用root用户启动
解决: 使用非ROOT用户登陆并启动ES便可
Q2: CONFIG_SECCOMP not compiled into kernel, CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER are needed
[es@localhost]$ ./bin/elasticsearch [2017-12-22T19:20:25,868][WARN ][o.e.b.JNANatives ] unable to install syscall filter: java.lang.UnsupportedOperationException: seccomp unavailable: CONFIG_SECCOMP not compiled into kernel, CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER are needed at org.elasticsearch.bootstrap.SystemCallFilter.linuxImpl(SystemCallFilter.java:341) ~[elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.SystemCallFilter.init(SystemCallFilter.java:616) ~[elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.JNANatives.tryInstallSystemCallFilter(JNANatives.java:258) [elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Natives.tryInstallSystemCallFilter(Natives.java:113) [elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Bootstrap.initializeNatives(Bootstrap.java:109) [elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:171) [elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:322) [elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:121) [elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:112) [elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86) [elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124) [elasticsearch-cli-6.1.1.jar:6.1.1] at org.elasticsearch.cli.Command.main(Command.java:90) [elasticsearch-cli-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:92) [elasticsearch-6.1.1.jar:6.1.1] at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:85) [elasticsearch-6.1.1.jar:6.1.1]
当咱们使用非root用户启动ES时,在启动日志中看到一段异常日志:CONFIG_SECCOMP not compiled into kernel, CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER are needed
。
缘由: seccomp是linux kernel从2.6.23版本开始所支持的一种安全机制,详见:https://en.wikipedia.org/wiki/Seccomp
而个人主机操做系统是:CentOS release 6.8 (Final)
[root@localhost ~]# cat /etc/redhat-release CentOS release 6.8 (Final) [root@localhost ~]# uname -a Linux centosx64_tomcat1 2.6.32-642.el6.x86_64 #1 SMP Tue May 10 17:27:01 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
虽然我使用的CentOS 6.8 Final使用的内核版本为2.6.32-642,可是彷佛没有支持seccomp。然而ES默认将利用内核的seccomp机制,因此报错,详见:https://github.com/elastic/elasticsearch/issues/22899
解决: ES是经过配置参数bootstrap.system_call_filter: true
使用内核seccomp机制的,在开发环境下能够将该参数值设为false。
[es@localhost]$ vim config/elasticsearch.yml bootstrap.system_call_filter: false # 默认该参数值不在elasticsearch.yml配置文件中,添加并设置为false便可。
Q3:bootstrap checks failed
当修改了参数network.host: 0.0.0.0以后再启动ES时将会报错:
[es@localhost]$ ./bin/elasticsearch [2017-12-22T23:16:23,511][INFO ][o.e.n.Node ] initialized [2017-12-22T23:16:23,515][INFO ][o.e.n.Node ] [4t5PbHS] starting ... [2017-12-22T23:16:24,421][INFO ][o.e.t.TransportService ] [4t5PbHS] publish_address {192.168.80.133:9300}, bound_addresses {[::]:9300} [2017-12-22T23:16:24,494][INFO ][o.e.b.BootstrapChecks ] [4t5PbHS] bound or publishing to a non-loopback or non-link-local address, enforcing bootstrap checks [2017-12-22T23:16:24,509][ERROR][o.e.b.Bootstrap ] [4t5PbHS] node validation exception [3] bootstrap checks failed [1]: max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536] [2]: max number of threads [1024] for user [es] is too low, increase to at least [4096] [3]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] [2017-12-22T23:16:24,524][INFO ][o.e.n.Node ] [4t5PbHS] stopping ... [2017-12-22T23:16:24,620][INFO ][o.e.n.Node ] [4t5PbHS] stopped [2017-12-22T23:16:24,620][INFO ][o.e.n.Node ] [4t5PbHS] closing ... [2017-12-22T23:16:24,672][INFO ][o.e.n.Node ] [4t5PbHS] closed
缘由: 在启动日志中给出了很是清楚的提示:
[1]: max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536] [2]: max number of threads [1024] for user [es] is too low, increase to at least [4096] [3]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
详见:https://serverfault.com/questions/681745/unable-to-change-vm-max-map-count-for-elasticsearch
解决: 以root用户身份登陆
1.修改最大文件描述符数
# 先查看一下当前的最大文件描述符数 [root@localhost]# ulimit -n 1024 # 修改最大文件描述符数 [root@localhost]# vim /etc/security/limits.conf # 添加以下2行配置 es hard nofile 65536 es soft nofile 65536
2.修改最大线程数:非root用户容许的最大线程数默认为1024
[root@localhost]# vim /etc/security/limits.conf # 添加如2行配置 es hard nproc 4096 es soft nproc 4096
上述2项修改完毕以后须要重启系统。
3.修改vm.max_map_count:在虚拟机上运行ES时才须要修改这个值
# 先查看当前值 [root@localhost]# cat /proc/sys/vm/max_map_count 65530 # 修改 [root@localhost]# echo 262144 > /proc/sys/vm/max_map_count
该参数在修改以后重启系统后又恢复为默认值了,每次都须要从新设置。
【参考】 https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html Elasticsearch: 权威指南 https://www.gitbook.com/book/looly/elasticsearch-the-definitive-guide-cn/details es gitbook https://www.gitbook.com/book/fuxiaopang/learnelasticsearch/details Elasticsearch 权威指南 http://itindex.net/detail/54168-elasticsearch-%E4%BC%98%E5%8C%96 亿级规模的Elasticsearch优化实战 https://www.elastic.co/guide/index.html Elastic Stack and Product Documentation