ES学习
一、ElasticSearch介绍
- ES是基于Lucene的搜索服务器,提供了分布式多用户能力的全文搜索引擎,支持开箱即用
- ES隐藏了Lucene的复杂性,对外提供Restful接口来操作索引、搜索
- ES是分布式文档存储
二、ElasticSearch优点
- 扩展性好,可部署上百台服务器集群,处理PB级数据
- 分布式的实时文档存储,实时分析搜索引擎,每个字段都可以被索引
三、相关概念介绍
3-1、文档概念
- ES是分布式文档存储,由于ES存储的是JSON格式的数据,所以文档也就是存储JSON格式数据的文件,以.doc结尾。其实文档也就是以文本形式存在的存储对象,可以是WORD也可以是XML或者PDF等。
- 文档都会有自己的唯一编号:DocID。
- 多个文档可以构成一个文档集合
3-2、倒排索引
倒排索引就是实现“单词-文档矩阵”的一种具体存储形式。通过倒排索引可以根据单词快速定位包含该单词的文档列表。所以倒排索引也就主要由两部分组成:单词列表和倒排文件。
但既然有倒排索引,肯定也就有正向索引,对比更好理解倒排索引:
- 正向索引:根据文档找单词,也就是拿到一个关键字单词,然后搜索每一个文档,看是否包含单词
- 反向索引(倒排索引):根据单词找文档,也就是拿到一个关键字单词,根据单词获取单词映射的每一个包含其的文档。其实反向索引也是从正向索引给重构出来的。
这样对比会发现,如果使用正向索引,在互联网那么大规模的文档中,速度是极其慢的
3-2-1、单词词典
单词词典就是就是文档集合中出现过的所有单词构成的字符串集合,也就是单词的集合,并且每一条信息不仅包含单词的内容还包含指向到排列表的指针。就类似与词典,不仅包含这个字的读音,还包含页码。
3-2-2、倒排文件
倒排文件就是所有单词的倒排列表按顺序存储的文件,也就是倒排索引的物理文件。倒排列表指的是记载某个单词出现过的所有文档列表和单词具体出现的位置,每条记录是一个倒排项目,根据倒排列表就能知道哪些文档包含要查找的单词以及出现的位置。
四、ElasticSearch的原理架构
4-1、ElasticSearch的架构
ElasticSearch其实也是分布式集群式的,也就是分为主节点和从节点,只不过ElasticSearch可以实现自己进行协调,也就是无需Zookeeper进行协调管理,所以ElasticSearch的节点主要分为以下三类:
- cluster.name节点:主要负责集群的增删索引、增删节点,也就是元信息的更改
- node.master节点:存储数据和对应倒排索引,具体的查询工作由其完成。
- 协调节点:类似于Zookeeper作用,动态协调节点的负载,相应用户请求。
ElasticSearch的分片:
ElasticSearch的分片就是将文档数据水平分为多份,存储在不同的node.master节点上,形成负载均衡,当然也会存在副本。默认情况下一个文档分为5个主分片,且每个节点如果保留住主分片会同时保留其对应的一个副本分片。
4-2、ElasticSearch的读写过程
4-2-1、ElasticSearch的写过程
用户提交了新的文档后,协调节点会根据公式:shard = hash(routing) % number_of_primary_shards 决定将该文档存储在哪一个分片中,然后根据该分片所在的节点执行写操作,同时进行分片副本同步。
过程其实就是:先写内存,同时写入translog日志文件,当内存快满的时候,refresh到一个segment file中,当translog达到一定长度后就进行commit写入os cache然后持久化到磁盘。当segment file达到一定数量后就要进行merge
4-2-2、ElasticSearch的读过程
客户端会选择某个节点发送读请求,协调节点根据routing计算,确定所需文档所属的分片,多个节点都包含此文档的话,就要采用轮询的形式实现负载均衡,最后将文档通过计算的节点返回客户端。
4-2-3、ElasticSearch的更新删除过程
由于ES的索引是不可以被修改的,所以实际上每个segment文件都会维护一个del文件,也就是说,如果进行了修改和删除,其实是在他们对应的del文件上进行标记,然后在查询时会略过del文件被打标机的文件,然后再下一次segment合并时将他们真的删除。所以就有可能删除的文件仍然可以访问到3
五、ElasticSearch的API
5-1、使用RestClient操作
- 创建索引
public boolean index(String id, String jsonString) {
//设置相关参数,请求类型、终结点(索引名/type名还有具体的id)、请求体
String method = "PUT";
String endpoint = new StringBuilder().append("/").append(INDEX_NAME).append("/").append(TYPE_NAME).append("/").append(id)
.toString();
HttpEntity entity = new NStringEntity(jsonString, ContentType.APPLICATION_JSON);
Response response = null;
try {
//请求并返回内容
response = restClient.performRequest(method, endpoint, Collections.emptyMap(), entity);
//判断是否成功返回
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED
|| response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
LOGGER.info("index success");
return true;
} else {
LOGGER.error("index error : {}", response.toString());
return false;
}
} catch (IOException e) {
LOGGER.error("system error, id={}, jsonString={}", id, jsonString, e);
return false;
}
}
- 查询操作:
public class ES_Demo_API {
RestClient restClient;
public ES_Demo_API() {
restClient = EsClientUtils.getEsRestClient();
}
public static void main(String[] args) {
//第一步、创建客户端
ES_Demo_API es_demo = new ES_Demo_API();
//第二步、执行相关方法
es_demo.query();
}
//查询方法
public void query(){
//创建查询体
String queryJson = "查询体";
/**
* HttpEntity:实现客户端最新的HTTP标准和建议
*为基本的HTTP协议提供强大的支持
* 目标是发送和接受HTTP报文
*/
HttpEntity entity = new NStringEntity(queryJson, ContentType.APPLICATION_JSON);
/**
* restClient.performRequest发送请求,参数是:"请求方式","终结点","可选查询字符串参数","org.apache.http.HttpEntity对象中包含的请求主体"
*/
try {
Response response = restClient.performRequest("GET","/终结点/_search", Collections.<String, String>emptyMap(),entity);
HttpEntity resultEntity = response.getEntity();
System.out.println(resultEntity);
} catch (IOException e) {
e.printStackTrace();
}
}
- 删除索引
public static void deleteIndex(RestClient client) {
try {
String method = "DELETE";
String endpoint = "索引位置";
HttpEntity entity = new NStringEntity("", ContentType.APPLICATION_JSON);
Response response = client.performRequest(method, endpoint,entity);
System.out.println(EntityUtils.toString(response.getEntity()));
}catch (Exception e) {
e.printStackTrace();
}finally {
try{
client.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
- 更新索引
public static void updateIndex(RestClient client) {
try {
// doc_as_upsert :使用doc_as_upsert可以在文档不存在的时候,把doc中的内容插入到文档中
String method = "POST";
String endpoint = "/索引地址/_update";
HttpEntity entity = new NStringEntity("要修改的索引", ContentType.APPLICATION_JSON);
//要修改的索引例子如下:
/**
*{\n" +
* " \"doc\": {\n" +
* " \"name\":\"要修改的索引\"\n" +
* " }\n" +
* "}
*/
Response response = client.performRequest(method,endpoint,entity);
System.out.println(EntityUtils.toString(response.getEntity()));
}catch (Exception e) {
e.printStackTrace();
}finally {
try{
client.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
5-1、ElasticSearch的相关参数概念
- 相关的配置属性
- shardId:指定需要执行的分片信息
- index:索引库,类似关系型数据库的database
- type:类型名,类似于关系数据库的table(表)
- id :文档ID,所谓的文档,类似于关系数据库的行,id,类似于关系数据库的主键ID。也就是第几行
- routing:分片值,默认为id的值,可用在elasticsearch的分片路由算法上:hashcode(routing) % primary_sharding_count(主分片个数) )
- fetchSourceContext:执行更新操作后,如果命中,需要返回_source的上下文配置
- version :版本号
- retryOnConflict :更新冲突时重试次数
- refreshPolicy :刷新策略,NONE表示不重试
- waitForActiveShards :执行操作之前需要等待激活的副本数
- doc:使用的该请求进行操作