Spring Boot项目中使用ElasticSearch
Spring Boot项目中使用ElasticSearch
当项目中的数据库数据量过大,查询速度等都非常影响体验,提升速度方式有采用分库,分表,读写分离等方式。当然采用其他办法也可以减缓数据库压力。
Elasticsearch的优点
- 速度快
- 分布式,可扩展,高实时
- 可以分析数据
- 跨平台(基于RESTful web接口)
传统关系型数据库和es的对应关系
开发环境
- JDK1.8
- Spring Boot 2.4.2
- Elasticsearch 7.6.2
1.依赖添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.2</version>
</dependency>
2.配置设置
spring.elasticsearch.rest.uris = 192.168.99.100:9200,192.168.99.100:9201,192.168.99.100:9202
3.es使用RestHighLevelClient操作
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.Objects;
/**
* es 配置文件
*
* @author jun
*/
@Slf4j
@Configuration
public class ElasticSearchConfig {
@Value("${spring.elasticsearch.rest.uris}")
String[] ipAddress;
private static final int ADDRESS_LENGTH = 2;
private static final String HTTP_SCHEME = "http";
@Bean
public RestHighLevelClient restHighLevelClient() {
return new RestHighLevelClient(
RestClient.builder(
Arrays.stream(ipAddress)
.map(this::makeHttpHost)
.filter(Objects::nonNull)
.toArray(HttpHost[]::new)
)
);
}
private HttpHost makeHttpHost(String s) {
assert !StringUtils.isEmpty(s);
String[] address = s.split(":");
if (address.length == ADDRESS_LENGTH) {
String ip = address[0];
int port = Integer.parseInt(address[1]);
log.info(ip + ":" + port);
return new HttpHost(ip, port, HTTP_SCHEME);
} else {
return null;
}
}
}
4.操作
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
/**
* @author song
* @date 2021-01-04
* @desc
*/
@Component
public class ElasticSearchUtil {
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
@Resource
private ApplicationContext ctx;
/**
* 创建索引
*
* @param index 索引
* @return
* @throws Exception
*/
public boolean createIndex(String index) throws Exception {
//创建索引
CreateIndexRequest request = new CreateIndexRequest(index);
// 2、设置索引的settings
request.settings(Settings.builder()
// 分片数
.put("index.number_of_shards", 1)
// 副本数
.put("index.number_of_replicas", 1)
// 默认分词器
//.put("analysis.analyzer.default.tokenizer", "ik_max_word")
);
// 3、设置索引的mapping 默认文档类型_doc
request.mapping(//"_doc",
"{\n" +
" \"properties\":{\n" +
" \"create_time\":{\n" +
" \"type\":\"date\"\n" +
" },\n" +
" \"id\":{\n" +
" \"type\":\"long\"\n" +
" },\n" +
" \"user_id\":{\n" +
" \"type\":\"long\"\n" +
" }\n" +
" }\n" +
"}",
XContentType.JSON);
// 5、 发送请求
// 5.1 同步方式发送请求
CreateIndexResponse createIndexResponse = client.indices()
.create(request, RequestOptions.DEFAULT);
// 6、处理响应
boolean acknowledged = createIndexResponse.isAcknowledged();
boolean shardsAcknowledged = createIndexResponse.isShardsAcknowledged();
return acknowledged;
}
public BulkResponse request(BulkRequest request, RequestOptions aDefault) throws IOException {
request.timeout(new TimeValue(6000, TimeUnit.SECONDS));
return client.bulk(request, RequestOptions.DEFAULT);
}
/**
* 查询数据
*
* @param dto 条件
* @return
* @throws IOException
*/
public List<Map<String, Object>> getRecordData(MessageDTO dto, String index, String type) throws IOException {
// init
if (dto.getCurrent() == null) {
dto.setCurrent(1);
}
if (dto.getSize() == null) {
dto.setSize(10);
}
Integer pageSize = dto.getSize();
Integer from = (dto.getCurrent() - 1) * pageSize;
if (from <= 0) {
from = 0;
}
// 1、创建search请求
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.types(type);
// 2、用SearchSourceBuilder来构造查询请求体 ,请仔细查看它的方法,构造各种查询的方法都在这。
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 集合查询
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.rangeQuery("create_time").gte(dto.getFromTime()).lte(dto.getEndTime()));
sourceBuilder.query(queryBuilder);
// 设置from确定结果索引以开始搜索的选项。预设为0。
sourceBuilder.from(from);
// 设置size用于确定要返回的搜索命中次数的选项。默认为10
sourceBuilder.size(pageSize);
// 设置一个可选的超时时间,以控制允许搜索的时间。
sourceBuilder.timeout(new TimeValue(300, TimeUnit.SECONDS));
// 指定排序
sourceBuilder.sort(new FieldSortBuilder("id").order(SortOrder.DESC));
sourceBuilder.trackTotalHits(true);
// 将请求体加入到请求中
searchRequest.source(sourceBuilder);
// 3、发送请求
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 4、处理响应
// 搜索结果状态信息
RestStatus status = searchResponse.status();
// 处理搜索命中文档结果
SearchHits hits = searchResponse.getHits();
TotalHits totalHits = hits.getTotalHits();
SearchHit[] searchHits = hits.getHits();
List<Map<String, Object>> mapList = new ArrayList<>();
for (SearchHit hit : searchHits) {
// 取_source字段值
mapList.add(hit.getSourceAsMap());
}
long total = hits.getTotalHits().value;
final List<Map<String, Object>> result = getMaps(mapList);
return result;
}
/**
* 查询数据数量
*
* @param dto 条件
* @return
* @throws IOException
*/
public long queryRecordCount(MessageDTO dto, String index) {
// 构建判断条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 集合查询
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.rangeQuery("create_time").gte(dto.getFromTime()).lte(dto.getEndTime()));
searchSourceBuilder.query(queryBuilder);
CountRequest countRequest = new CountRequest().indices(index).source(searchSourceBuilder);
try {
CountResponse count = client.count(countRequest, RequestOptions.DEFAULT);
return count.getCount();
}
catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}