初识ElasticSearch
Elasticsearch 介绍
安装 ES (JDK1.8以上版本 ES v5.6.16)
Elasticsearch 的基本概念
命令行上的ES增删改查等操作
Java API(RestTemplate)访问ES
一、Elasticsearch 介绍
Elasticsearch,简称 ES。是一个全文搜索服务器,也可以作为 NoSQL 数据库,存储任意格式的文档和数据,同时,也可以做大数据的分析,是一个跨界开源产品。
ES 有如下特点:
全文搜索引擎, ES 是建立在 Lucene 上的开源搜索引擎,可以用来进行全文搜索、地理信息搜索。
文档存储和查询,可以像 NoSQL 那样存储任意格式文档,井能根据条件查询文档。
大数据分析, ES 号称能准确实时地进行大数据分析,数据量从 TB 到 PB。
ES 提供了REST API,用来简化对 ES 的操作。 因此可以使用任何语言的客户端,同 时也提供 Java API, Spring Boot 也对 REST API 进行了封装,简化了开发。
ES 常常配合传统数据库一起使用, ES 用来负责大数据的查询、搜索、统计分析。
二、安装 ES (JDK1.8以上版本 ES v5.6.16)
1.下载指定版本的ES https://www.elastic.co/cn/downloads/past-releases
2.添加启动参数:vi ~/elasticsearch-5.6.16/config/elasticsearch.yml
#network.host: 192.168.0.1
network.host: 0.0.0.0
# Set a custom port for HTTP:
http.port: 9200
3.进入 bin 目录,运行./elasticsearch, Linux 下不能使用 root 直接运行, 最好为运行 ES 创建一个新的用户。看到以下提示,表示安装成功:
[2019-07-13T10:15:13,901][INFO ][o.e.c.s.ClusterService ] [orF3ezy] new_master {orF3ezy}{orF3ezyMSImyhVqJjM35pQ}{xMMVYSYWQkyrCwveTeYtZw}{192.168.248.131}{192.168.248.131:9300}, reason: zen-disco-elected-as-master ([0] nodes joined)
[2019-07-13T10:15:13,956][INFO ][o.e.h.n.Netty4HttpServerTransport] [orF3ezy] publish_address {192.168.248.131:9200}, bound_addresses {[::]:9200}
【注:遇到的问题】[1]: max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]
[2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
解决方法:
1.使用root账户,vi /etc/security/limits.conf
在文件末尾添加:
# 最大线程个数
* hard nproc 4096
* soft nproc 4096
# 每个进程最大同时打开文件数太小
* hard nofile 65535
* soft nofile 65535
2.vi /etc/sysctl.conf文件最后添加:
vm.max_map_count=262144
3.sysctl -p
4.切换到es用户生效,在es的bin目录下./elasticsearch
三、Elasticsearch 的基本概念
1.Index,类似于数据库,是文档(Document)的集合,Index下面包含了Type,用于对Document的进一步分类。
2.Type,类似于表,一个 Index 下可以有多个 Type
3.Document,类似于表的行记录,是ES存储和搜索的基本信息
【注:ES 中可以轻易地联合 Index 和 Type 来搜索数据,数据库却不能】
---
Node(节点),节点是集群里的一台 ES Server,用于文档的存储和查询。应用可以只有一个节点,也可以由上百个节点组成集群来存储和搜索数据。 每个节点都有一个节点名字,以及所属集群的名字。
集群,同样集群名的节点将组合为 ES 集群,用来联合完成数据的存储和搜索。默认的集群名字是 elasticsearch。
分区(Shards)和复制(Replicas),每个 Index 理论上都可以包含大量的数据,超过了单个节点的存储限制,而且,单个节点处理那么大的数据,将明显限制存储和搜索性能。为了解决这个问题,ES 会进一步将 Index 在物理上细分为多个分区,而且这些分区会按照配置复制到多个节点, Index 的分区称为主分区,复制的分区称为复制分区。 这样的好处是既保证数据不会丢失,又提高了查询的性能。
---
每个分区是一个独立的工作单元,可以完成存储和搜索功能 , 每个分区能存储最多 2147483519 个文档。
四、命令行上的ES增删改查等操作
1.新增记录(product 表示 Index, book 表示 Type,数字 1 是文挡的主键,主键可以是任意形式,如果未指定主键, ES 会自动生成一个唯一主键, pretty是可选的, ES 输出的时候会格式化输出结果, 更加美观。)
curl -XPOST '192.168.248.131:9200/product/book/1?pretty' -H 'Content-Type:application/json' -d '{"name": "北京100种小吃", "type": "food", "postDate": "2019-11-15'T'12:00:00", "message":"介绍了北京小吃,如炸酱面、卤煮、驴打滚等"}'
2.查询记录
curl -XGET '192.168.248.131:9200/product/book/1?pretty'
3.更新记录(ES 会处理三种格式日期:yyyy-MM-dd,如 2019-11-15; yyyy-MM-dd'T'HH:mm:ss,如 2019-11-l5T13:14:21; yyyy-MM-dd'T'HH:mm:ss.SSS,如 2009-l1-15Tl3:14:21.389)
curl -XPOST '192.168.248.131:9200/product/book/1?pretty' -H 'Content-Type:application/json' -d '{"name": "北京108种小吃", "type": "food", "postDate": "2019-11-15T12:00:00", "message":"介绍了北京小吃,如炸酱面、卤煮、驴打滚等"}'
或 curl -XPUT '192.168.248.131:9200/product/book/1?pretty' -H 'Content-Type:application/json' -d '{"name": "北京108种小吃", "type": "food", "postDate": "2019-11-15T12:00:00", "message":"介绍了北京小吃,如炸酱面、卤煮、驴打滚等"}'
局部更新:使用_update,doc表示要更新的文档片段
curl -XPOST '192.168.248.131:9200/product/book/1/_update?pretty' -H 'Content-Type:application/json' -d '{"doc": {"message":"介绍了北京小吃,如炸酱面、卤煮、驴打滚,还有胶圈等!"}}'
注:若更新的内容未发生改变,则版本号不会增加
4.删除记录(根据主键删除)
curl -XDELETE '192.168.248.131:9200/product/book/1?pretty'
返回的结果中:
_shards 表示分区信息, total 为 2 表示有两个分区(包括主分区) , successful 为 l 表示成功复制了一份。
_version 代表了文档版本号,每一次修改都会递增,注意 ES 并不会存储文档修改的各个版本!
--1.搜索文档--
可以使用GET方法:
curl -G --data-urlencode 'q=message=驴打滚' '192.168.248.131:9200/product/book/_search?pretty'
注:get方法的url中包含中文关键字,所以需要curl进行uri编码,即使用--data-urlencode,参数-G 表示-XGET,不加-G,curl默认发出的是POST请求
POST方法:
curl -XPOST '192.168.248.131:9200/product/book/_search?pretty' -H 'Content-Type:application/json' -d '{"query": {"match":{"message":"驴打滚"}}}'
【注】搜索结果中,hits 包含了查询结果,Index 是 product, Type 是 book,score 是搜索引擎概念,表示查询相关度,分数越高,表示此文档与关键字期望的结果的匹配程度高
--2.精确搜索--
curl -XPOST '192.168.248.131:9200/product/book/_search?pretty' -H 'Content-Type:application/json' -d '{"query": {"term":{"type":"food"}}}'
--3.联合分页搜索--
curl -XPOST '192.168.248.131:9200/product/book/_search?pretty' -H 'Content-Type:application/json' -d '{"from":0, "size":5, "query": {"bool":{"must":{"term":{"type":"food"}}, "must":{"match":{"message":"驴打滚"}}}}}'
--4.联合多个索引搜索--
Index存储的文档数量有限,将文档存放至Index时候,可以使用增加日期等后缀的方式,如:product2008、product2009,那么在搜索的时候:product*/book,查询以 product 开头的所有索引。
【注:ES的修改并发控制】乐观锁
对线程对一个版本号version=2的记录做修改时,只有一个线程可以拿到锁,当该线程执行完毕后,ES对该资源的version递增,那么其他的线程将修改不到version=2的资源,修改失败报status=409的错误
命令如下:
curl -XPOST '192.168.248.131:9200/product/book/1/_update?pretty&version=2' -H 'Content-Type:application/json' -d '{"doc": {"message":"介绍了北京小吃,如炸酱面、卤煮、驴打滚,还有胶圈等等等!"}}'五、Java API(RestTemplate)访问ES
1.新建maven工程:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.es</groupId>
<artifactId>es</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>es</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.3</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>${spring-restdocs.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.新建实体类:
@Data
public class Book {
//书名
private String name;
//书简介
private String message;
//上架时间
private Date postDate;
//书分类
private String type;
}
3.新建rest接口类:
@RestController
public class RestClientController {
@RequestMapping("/rest/es/book/{id}")
public String getLogById(@PathVariable String id){
Book book = null;
RestTemplate restTemplate = new RestTemplate();
Map<String, Object> paras = new HashMap<>();
//设置参数
paras.put("id", id);
//转换访问资源的url
String response = restTemplate.getForObject("http://192.168.248.131:9200/product/book/{id}", String.class, paras);
//对象映射工具
ObjectMapper mapper = new ObjectMapper();
JsonFactory factory = mapper.getFactory();
try {
JsonParser parser = factory.createParser(response);
//获取json根节点元素
JsonNode root = mapper.readTree(parser);
//获取_source字段
JsonNode source = root.get("_source");
//转换为实体对象
book = mapper.convertValue(source, Book.class);
return book.getMessage();
}catch (Exception e){
e.printStackTrace();
}
return "";
}
}
4.启动springboot应用,访问http://localhost:8080/rest/es/book/1
返回之前curl命令插入的记录:介绍了北京小吃,如炸酱面、卤煮、驴打滚等
5.如果熟悉 ES 的 REST 接口,可以用 RestTemplate 来处理 ES 的任何查询,而可以不用Spring Data Elastic,不过 Spring Data 使得开发更为简单,容易维护。Spring Data 的缺点是不支持复杂的查询和统计分析等,最好使用 ES 的 REST 方式来完成,另外 ES 的版本演进跟其他 NoSOL 一样,特别快,新的功能 Spring Data 也暂时无法支持。
使用 spring data elasticsearch :
-- 依赖:
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-data-elasticsearch</artifactid>
</dependency>
-- 编写dao:
public interface BookDao extends CrudRepository<Book, String> {
//分页实现根据关键字查询书籍
public Page<Book> getByMessage(String key, Pageable pageable);
//根据关键字查询书籍
public List<Book> getByMessage(String key);
}
-- 编写controller:
@RestController
public class RestClientController {
@Autowired
BookDao bookDao;
@RequestMapping("/rest/es/{id}")
public Book getBookById(@PathVariable String id){
Optional<Book> byId = bookDao.findById(id);
Book book = byId.get();
return book;
}
@RequestMapping("/rest/es/search/{key}")
public List<Book> getByMessage(@PathVariable String key){
List<Book> bookList = bookDao.getByMessage(key);
return bookList;
}
@RequestMapping("/rest/es/search/{key}/{page}")
public List<Book> getByMessage(@PathVariable int page, @PathVariable String key){
//每页显示5条
int numOfPage = 5;
PageRequest request = PageRequest.of(page, numOfPage);
//全文翻页搜索
Page<Book> pages = bookDao.getByMessage(key, request);
long total = pages.getTotalElements();
long totalPage = pages.getTotalPages();
List<Book> bookList = pages.getContent();
return bookList;
}
}
-- 配置文件application.properties
spring.data.elasticsearch.cluster-nodes=192.168.248.131:9300
-- 启动项目访问:
1.分页实现根据关键字查询书籍 http://localhost:8080/rest/es/search/%E5%8C%97%E4%BA%AC/0,多条信息数组显示
[{"name":"2","message":"吃遍北京小吃","postDate":"2019-11-16T12:00:00.000+0000","type":"food"},{"name":"1","message":"介绍了北京小吃,如炸酱面、卤煮、驴打滚等","postDate":"2019-11-15T12:00:00.000+0000","type":"food"}]
2.根据关键字查询书籍 http://localhost:8080/rest/es/search/%E5%8C%97%E4%BA%AC
[{"name":"2","message":"吃遍北京小吃","postDate":"2019-11-16T12:00:00.000+0000","type":"food"},{"name":"1","message":"介绍了北京小吃,如炸酱面、卤煮、驴打滚等","postDate":"2019-11-15T12:00:00.000+0000","type":"food"}]
3.根据书籍ID查询记录
http://localhost:8080/rest/es/1
{"name":"1","message":"介绍了北京小吃,如炸酱面、卤煮、驴打滚等","postDate":"2019-11-15T12:00:00.000+0000","type":"food"}
http://localhost:8080/rest/es/2
{"name":"2","message":"吃遍北京小吃","postDate":"2019-11-16T12:00:00.000+0000","type":"food"}