本文目录一览:
- 1、Java代码查询es 的索引是yellow的状态,怎么可以查询不报错?
- 2、怎么用spring获取es数据
- 3、java中如何直接读取es语言
- 4、es查询数据的工作原理是什么?
- 5、ES 常见面试问题
- 6、如何在Spring中注入ElasticSearch实例
Java代码查询es 的索引是yellow的状态,怎么可以查询不报错?
建议提前检查,为yellow直接提醒运维去维护为green。不过我这边用的es6.2.3yellow是正常查询的。建议你检查一下环境配置应该不是yellow的问题。试了一下;为red都可以正常查询的(java代码查询结果和下图es-head查询结果一致)
怎么用spring获取es数据
1. ES和solr都是作为全文搜索引擎出现的。都是基于Lucene的搜索服务器。
2. ES不是可靠的存储系统,不是数据库,它有丢数据的风险。
3. ES不是实时系统,数据写入成功只是trans log成功(类似于MySQL的bin log),写入成功后立刻查询查不到是正常的。因为数据此刻可能还在内存里而不是进入存储引擎里。同理,删除一条数据后也不是马上消失。写入何时可查询?ES内部有一个后台线程,定时将内存中的一批数据写入到存储引擎,此后数据可见。默认后台线程一秒运行一次。该线程运行的越频繁,写入性能越低。运行的频率越低,写入的性能越高(不会无限高)。
4. 目前已知的单ES集群可以存储PB级别的数据,不过这个就非常费劲了。TB级别数据没压力。
5. 如果使用ES官方提供的jar包访问,需要JDK1.7及以上。
6. 使用对应的版本访问ES server。如果ES server端的版本是1.7,那么请使用ES 1.7的client。如果ES server是2.1,请使用2.1的client。
7. ES索引存在Linux服务器的文件系统之上(背后是文件系统,不是类似于HDFS的分布式文件系统)
8. ES Java client是线程安全的,全局构建一个即可满足读写需求,不要每次都创建ES client。每次访问ES都构建新的es client即会抛出次异常。
9. 非常不建议使用ES的动态识别和创建的机制,因为很多情况下这并非你所需要。推荐的做法是在写数据之前仔细的创建mapping。
10. 强烈不建议在ES中使用深分页。可能会导致集群不可用。
11. ES是静态分片,一旦分片数在创建索引时确定那么后继不能修改。
12. ES里提供了type,很多人以为type是物理表,一个type的数据是独立存储的;但是在ES内部并不是这样,type在ES内部仅仅是一个字段。所以在很多数据能分为独立index的情况下,不要放到一个index里用type去分。只有嵌套类和父子类的情况下使用type才是合理的。
13. ES并不提供原生的中文分词的能力。有第三方的中文分词的插件,比如ik等。Ik是个toy分词器,有严肃的分词需求的话,请在使用ES之前使用独立的分词器分好词后向ES写入。
14. ES中的index,首先会进行分片,每一个分片数据一般都会有自己的副本数据,ES分配分片的策略会保证同一个分片数据和自己的副本不会分配到同一个节点上。当集群中的某一节点宕机后,ES的master在ping该节点时通过一定的策略会发现该节点不存活;会开启ES的恢复过程
15. ES没有update的能力。所有的update都是标记删除老文档,然后重新insert一条新文档。
java中如何直接读取es语言
Elasticsearch不是java语言开发的,所以必须要通过api才能调用。你可以看看有没有第三方包可以直接操作。
es查询数据的工作原理是什么?
查询,GET某一条数据,写入了某个document,这个document会自动给你分配一个全局唯一的id,doc id,同时也是根据doc id进行hash路由到对应的primary shard上面去。也可以手动指定doc id,比如用订单id,用户id。
我们可以通过doc id来查询,会根据doc id进行hash,判断出来当时把doc id分配到了哪个shard上面去,从那个shard去查询
1)客户端发送请求到任意一个node,成为coordinate node(协调结点)
2)coordinate node进行hash后对document进行路由,将请求转发到对应的node,此时会使用round-robin 随机轮询算法 ,在primary shard以及其所有replica node中 随机选择一个 ,让读请求负载均衡
3)接收请求的node返回document给coordinate node
4)coordinate node返回document给客户端
es最强大的是做全文检索,就是比如你有三条数据
java真好玩儿啊
java好难学啊
j2ee特别牛
你根据java关键词来搜索,将包含java的document给搜索出来
es就会给你返回:java真好玩儿啊,java好难学啊
1)客户端发送请求到一个coordinate node
2)协调节点 将搜索请求转发到 所有的shard 对应的primary shard或replica shard
3)query phase: 每个shard将自己的搜索结果 (其实就是一些 doc id ), 返回给协调节点 ,由协调节点进行数据的 合并、排序、分页 等操作,产出最终结果
4)fetch phase:接着由 协调节点,根据doc id去各个节点上拉取实际的document数据 ,最终返回给客户端
尤其要注意的这里是先拿的id哟
ES 常见面试问题
1 增大内存: es性能优化的杀手锏: filesystem cache(OS cache): 也就是说 尽量让内存可以容纳所有的索引数据文件,那么搜索的时候就基本都是走内存的,性能会非常高。磁盘和OS cache扫描速度相差近一个数量级,可能一个是1到几百毫秒,另一个是秒。最佳的情况下,就是单机机器的内存,至少可以容纳单机数据量的一半。另一个方面就是写数据的时候,仅仅写入要用来检索的少数几个字段就可以了,其余的数据放到hbase或者mysql上
2 数据预热
假设机器内存达到上面的要求,比如 内存是100G,数据是200G。那么有一半的数据存放在磁盘上,那么这个时候可以设计一个 数据预热子系统, 就是对热数据每隔一段时间,就提前访问一下,让热数据进入 filesystem cache 里面去。这样下次别人访问的时候,性能一定会好很多。
3 document 模型设计
document 模型设计是非常重要的,很多操作,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作,尽量存放单纯的数据放到ES上去,不要考虑用 es 做一些它不好操作的事情,比如 join/nested/parent-child 搜索都要尽量避免,性能都很差的。
4 分页性能优化
分页性能差的原因:
解决方案1:跟产品经理说,你系统不允许翻那么深的页,默认翻的越深,性能就越差。
解决方案2:类似于 app 里的推荐商品不断下拉出来一页一页的
就像淘宝商品一样,一页一页往下刷,不能从第一页跳到100页,从100页跳到50页,不能这样操作。
可以使用 scroll api 来实现,scroll 会一次性给你生成所有数据的一个快照,然后每次滑动向后翻页就是通过游标 scroll_id 移动,获取下一页下一页这样子,性能会比上面说的那种分页性能要高很多很多,基本上都是毫秒级的。
初始化时必须指定 scroll 参数,告诉 es 要保存此次搜索的上下文多长时间。你需要确保用户不会持续不断翻页翻几个小时,否则可能因为超时而失败。
除了用 scroll api,你也可以用 search_after 来做,search_after 的思想是使用前一页的结果来帮助检索下一页的数据,显然,这种方式也不允许你随意翻页,你只能一页页往后翻。初始化时,需要使用一个唯一值的字段作为 sort 字段。参考:
见
先写入内存 buffer,在 buffer 里的时候数据是搜索不到的;同时将数据写入 translog 日志文件。
如果 buffer 快满了,或者到一定时间,就会将内存 buffer 数据 refresh 到一个新的 segment file 中,但是此时数据不是直接进入 segment file 磁盘文件,而是先进入 os cache 。这个过程就是 refresh。只要数据进入了OS cache那么就可以被访问到了。
每隔 1 秒钟,es 将 buffer 中的数据写入一个新的 segment file (如果 buffer 里面此时没有数据,那当然不会执行 refresh 操作) ,每秒钟会产生一个新的磁盘文件 segment file,这个 segment file 中就存储最近 1 秒内 buffer 中写入的数据。
这里就解释了为什么叫 es 是 准实时 的? NRT,全称 near real-time。默认是每隔 1 秒 refresh 一次的,所以 es 是准实时的,因为写入的数据 1 秒之后才能被看到。可以通过 es 的 restful api 或者 java api,手动执行一次 refresh 操作,就是手动将 buffer 中的数据刷入 os cache中(但是这样会影响ES批量插入数据的效率),让数据立马就可以被搜索到。只要数据被输入 os cache 中,buffer 就会被清空了,因为不需要保留 buffer 了,数据在 translog 里面已经持久化到磁盘去一份了。
重复上面的步骤,新的数据不断进入 buffer 和 translog,不断将 buffer 数据写入一个又一个新的 segment file 中去,每次 refresh 完 buffer 清空,translog 保留。随着这个过程推进,translog 会变得越来越大。当 translog 达到一定长度的时候,就会触发 commit 操作。
commit 操作发生第一步,就是将 buffer 中现有数据 refresh 到 os cache 中去,清空 buffer。然后,将一个 commit point 写入磁盘文件,里面标识着这个 commit point 对应的所有 segment file,同时强行将 os cache 中目前所有的数据都 fsync 到磁盘文件中去。最后清空 现有 translog 日志文件,重启一个 translog,此时 commit 操作完成。
这个 commit 操作叫做 flush。默认 30 分钟自动执行一次 flush,但如果 translog 过大,也会触发 flush。flush 操作就对应着 commit 的全过程,我们可以通过 es api,手动执行 flush 操作,手动将 os cache 中的数据 fsync 强刷到磁盘上去。
translog 日志文件的作用是什么?你执行 commit 操作之前,数据要么是停留在 buffer 中,要么是停留在 os cache 中,无论是 buffer 还是 os cache 都是内存,一旦这台机器死了,内存中的数据就全丢了。所以需要将数据对应的操作写入一个专门的日志文件 translog 中,一旦此时机器宕机,再次重启的时候,es 会自动读取 translog 日志文件中的数据,恢复到内存 buffer 和 os cache 中去 这里和Redis持久化机制是类似的 。
translog 其实也是先写入 os cache 的,默认每隔 5 秒刷一次到磁盘中去,所以默认情况下,可能有 5 秒的数据会仅仅停留在 buffer 或者 translog 文件的 os cache 中,如果此时机器挂了,会丢失 5 秒钟的数据。但是这样性能比较好,最多丢 5 秒的数据。也可以将 translog 设置成每次写操作必须是直接 fsync 到磁盘,但是性能会差很多。
总结一下,数据先写入内存 buffer,然后每隔 1s,将数据 refresh 到 os cache,到了 os cache 数据就能被搜索到(所以我们才说 es 从写入到能被搜索到,中间有 1s 的延迟)。每隔 5s,将数据写入 translog 文件(磁盘里面)(这样如果机器宕机,内存数据全没,最多会有 5s 的数据丢失),translog 大到一定程度,或者默认每隔 30mins,会触发 commit 操作,将缓冲区的数据都 flush 到 segment file 磁盘文件中。
可以通过 doc id 来查询,会根据 doc id 进行 hash,判断出来当时把 doc id 分配到了哪个 shard 上面去,从那个 shard 去查询。
客户端发送请求到任意一个 node,成为 coordinate node。
coordinate node 对 doc id 进行哈希路由,将请求转发到对应的 node,此时会使用 round-robin 随机轮询算法,在 primary shard 以及其所有 replica 中随机选择一个,让读请求负载均衡。
接收请求的 node 返回 document 给 coordinate node。
coordinate node 返回 document 给客户端。
搜索
es 最强大的是做全文检索,就是比如你有三条数据:
java真好玩儿啊
java好难学啊
j2ee特别牛
你根据 java 关键词来搜索,将包含 java的 document 给搜索出来。es 就会给你返回:java真好玩儿啊,java好难学啊。
1 客户端发送请求到一个 coordinate node。
2 协调节点将搜索请求转发到所有的 shard 对应的 primary shard 或 replica shard,都可以。
3 query phase:每个 shard 将自己的搜索结果(其实就是一些 doc id)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果。
4 fetch phase:接着由协调节点根据 doc id 去各个节点上拉取实际的 document 数据,最终返回给客户端。
如果是更新操作,就是将原来的 doc 标识为 deleted 状态,然后新写入一条数据。
buffer 每 refresh 一次,就会产生一个 segment file,所以默认情况下是 1 秒钟一个 segment file,这样下来 segment file 会越来越多,此时会定期执行 merge。每次 merge 的时候,会将多个 segment file 合并成一个 (这里类似于Redis的RDB文件重写) ,同时这里会将标识为 deleted 的 doc 给 物理删除掉 ,然后将新的 segment file 写入磁盘,这里会写一个 commit point,标识所有新的 segment file,然后打开 segment file 供搜索使用,同时删除旧的 segment file。
如何在Spring中注入ElasticSearch实例
在
企业级项目开发中,大多数公司都会集成Spring来简化开发成本,要使用Spring自然少不了一大堆需要依赖注入的Bean,通常情况下,我们会选择
在spring的xml中,配置一些类的实例,比如连接池,或者配置文件初始化类,或者集成duboo时配置一些Service的引用等等。
有
些类的实例生成比较复杂,直接在xml中,是没法进行配置的,比如我想在Spring注入ElasticSearch的Client实例,注意(这里并不
是使用的spring-data-elasticsearch项目),而是使用原始的ElasticSearch的API。
我们先看下,在非Spring的项目中,如何初始化ElasticSearch的Client实例:
Java代码
//es的客户端实例
static Client client=null;
static {
//设置集群名字
Settings settings = ImmutableSettings.settingsBuilder()
.put("cluster.name", "search")
.put("client.transport.sniff", true)
. build();
//连接单台机器,注意ip和端口号,不能写错
client=new TransportClient(settings)
.addTransportAddress(new InetSocketTransportAddress("192.168.1.187", 9300))
.addTransportAddress(new InetSocketTransportAddress("192.168.1.121", 9300))
.addTransportAddress(new InetSocketTransportAddress("192.168.1.122", 9300));
}
由
于ElasticSearch的Client实例是通过各种组装加工而成,并没法直接在Spring的xml中使用Bean标签进行初始化,这时候我们可
以使用@Configuration注解,来在Java类中,生成bean实例,这个注解和使用spring的xml进行注入功能大致相同,只不过能直接
在Java类生成一个类实例,比较灵活而已。最终的代码如下:
Java代码
package cn.bizbook.product.elk.config;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Created by qindongliang on 2016/4/6.
*/
@Configuration
public class FactoryBean {
//配置文件工具类
@Autowired
private ESConf esConf;
//注入的ElasticSearch实例
@Bean(name = "client")
public Client getESClient(){
//设置集群名字
Settings settings = ImmutableSettings.settingsBuilder()
.put("cluster.name", esConf.getClusterName())
.build();
Client client=new TransportClient(settings);
//读取的ip列表是以逗号分隔的
for(String ip:esConf.getIps().split(",")){
((TransportClient)client).addTransportAddress(new InetSocketTransportAddress(ip,esConf.getPort()));
}
return client;
}
}
最后来看下,如何在DAO层,引用client实例,非常easy:
Java代码
@Resource(name = "client")
private Client client;