人鱼小姐,祝福-金博宝188|官网

记一次线上的elasticsearch查询采坑

第一次运用elasticsearch,所以从网上找轮子复制粘贴。早好轮子测验结束,上线。可是几天下来发现接口呼应时刻一向都偏高(默许的超时时刻是500ms),所以就不断的对代码优化,缩短时刻。可是到最终代码现已不能再优化了,呼应时刻仍然没有显着的下降趋势,甚至在顶峰期会严峻超时。接下来会渐渐解说elasticsearch运用优化。

Spring Boot增加elasticsearch依靠

有许多种计划能够挑选,1)增加spring的data依靠。2)运用elasticsearch供给的client依靠。3)运用jestClient依靠。前两种并没有什么区别,第三种是经过http恳求拜访elasticsearch的。

运用elasticsearch官方依靠

运用IDE初始化Springboot时勾选elasticsearch即可,或许你也能够直接增加如下依靠:


org.springframework.boot
spring-boot-starter-data-elasticsearch
{elasticserch.version}

或许到maven网站查找对应e龚琳娜lasticsearch版别的依靠:


org.elasticsearch
elasticsearch
{elasticserch.version}


org.elasticsearch.client
transport
{elasticserch.version}

需求留意的是,必定要运用与你的elasticsearch版别一向的依靠,不然可能会犯错

elasticsearch的装备

@Configuration
public class ElasticsearchConfig {
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchConfig.class);
@Value("${elasticsearch.port}")
private String port;
@Value("${elasticsearch.cluster.name}")
private String c铃村爱里lusterName;
@Value("${elasticsearch.pool}")
private String poolSize;
@Value("${elasticsearch.ip}")
private String esHost;
@Bean(name = "transportClient")
public TransportClient transportClient() {
logger.info("Elasticsearch初始化开端。。。。。");
TransportClient transportClient = null;
try {
// 装备信息
Settings esSetting = Settings.builder()
//集群姓名
.put("cluster.name", clusterName)
//增加嗅探机制,找到ES集群
.put("client.transport.sniff", true)
// .put("client.transport.ignore_cluster_name", true)
//增加线程池个数,暂时设为5
.put("thread_pool.search.size", Integer.parseInt(poolSize))
.build();
//装备信息Settings自定义
transportClient = new PreBuiltTransportClient(esSetting);
TransportAddress transportAddress = new TransportAddress(InetAddress.getByName(esHost), Integer.valueOf(port));
transportClient.addTransportAddresses(transportAddress);
logger.info("衔接elasticsearch");
} catch (Exception e) {
logger.error("elasticsearch TransportClient create error!!", e);
}
return transportClient;
}
}

低版别的elasticsearch在装备setting自定义内容时会不一样。运用elasticsearch节点衔接的端口是9300。

简略的运用:

@Component
public class ElasticsearchUtils {
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchUtils.class);
@Resource(name = "transportClient")
private TransportClient transportClient;
private static Tr人鱼小姐,祝愿-金博宝188|官网ansportClient client;
@PostConstruct
public void init() {
client = this.transportClient;
}
/**
* @author xiaosen
* @description 判别索引是否存在
* @date 2019/1/23
* @param
* @return
*/
public static boolean isIndexExist(String index) {
IndicesExistsResponse inExistsResponse = client.admin().indices().exists(new IndicesExistsRequest(index)).actionGet();
if (inExistsResponse.isExists()) {
logger.info("索引:{}存在", index);
} else {
logger.info("索引:{}不存在", index);
}
return inExistsResponse.isExists()天天基金网官网;
}

public static List> searchListData(String index, String type, long startTime, long endTime, Integer size, String fields, String sortField, boolean matchPhrase, String highlightField, String matchStr) {
SearchRe御花少年questBuilder searchRequestBuilder = client.prepareSearch(index);
if (StringUtils.isNotEmpty(type)) {
searchRequestBuilder.setTypes(type.split(","));
}
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
if (startTime > 0 && endTime > 0) {
boolQuery.must(QueryBuilders.rangeQuery("processTime")
.format("epoch_millis")
.from(startTime)
.to(endTime)
.includeLower(true)
.includeUpper(true));
}
//查找的的字段
if (StringUtils.isNotEmpty(matchStr)) {
for (String s : matchStr.split(",")) {
String[] ss = s.split("=");
if (ss.length > 1) {
if (matchPhrase == Boolean.TRUE) {
boolQuery.must(QueryBuilders.matchPhraseQuery(s.split("=")[0], s.split("=")[1]));
} else {
boolQuery.must(QueryBuilders.matchQuery(s.split("=")[0], s.split("=")[1]));
}
}
}
}
// 高亮(xxx=111,aaa=222)
if (StringUtils.isNotEmpty(highlightField)) {
HighlightBuilder highlightBuilder = new HighlightBuilder();
// 设置高亮字段
highlightBuilder.field(highlightField);
searchRequestBuilder.highlighter(highlightBuilder);
}
searchRequestBuilder狂龙.setQuery(boolQuery);
if (StringUtils.isNotEmpty(fields)) {
searchRequestBuilder.setFetchSource(fields.split(","), null);
}
searchRequestBuilder.setFetchSource(true);
if (StringUtils.isNotEmpty(sortField)) {
searchRequestBuilder.addSort(sortField, SortOrder.DESC);
}
if (size != null && size > 0) {
searchRequestBuilder.setSize(size);
}
SearchResponse searchResponse = searchRequestBuilder.exe人鱼小姐,祝愿-金博宝188|官网cute().actionGet();
long totalHits = searchResponse.getHits().totalHits;
long length = searchResponse.getHits().getHits().length;
if (searchResponse.status().getStatus() == 200) {
// 解析目标
return setSearchResponse(se飞智游戏厅archResponse, highlightField);
}
return null;
}

运用JestClient

增加maven依靠(这儿的elasticsearch版别比较低,并且还没有敞开9300端口,只能运用http恳求)

 
org.elasticsearch
elasticsearch
1.5.2


io.searchbox
jest
6.3.1

io.searchbox是操作elasticsearch的依靠,运用其9200端口。

装备文件就比较简略了:

@Configuration
@RefreshScope
public class ElasticsearchConfigure {
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchConfigure.class);
@Va赵雅淇洒泪抱歉lue("${elasticsearch.ip}")
private String hostAndPort;
@Bean(name = "elasticsearchClient")
public JestClient getJestClient() throws Exception{
JestClientFactory factory = new JestClientFactory();
SSLContext sslContext = new SSLContextBuil大足石刻der().loadTrustMaterial(null, (X509Certificate[] arg0, String arg1) -> true).build();
// http装备
factory.setHttpClientConfig(new HttpClientConfig.Builder("http://"+hostAndPort).connTimeout(2000)
.readTimeout(2000).plainSocketFactory(PlainConnectionSocketFactory.getSocketFactory())
.sslSocketFactory(new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE))
.multiThreaded(true).maxTotalConnection(100).defau聚美优品官网ltMaxTotalConnectionPerRoute(4).build());
return factory.getObject();
}
}

创立一个JestClientFactory并装备httpClient。

简略的一个比方:

@Resource(name = "elasticsearchClient")
private JestClient jestClient;
public static void main(String[] args){
FilterBuilder filterBuilder = FilterBuilders.boolFilter()
.must(FilterBuilders.geoDistanceRangeFilter("location")
.point(lat, lon).from(Constants.MIN_RADIUS).to(Constants.MAX_RADIUS))
.should(FilterBuilders.termFilter("status", 200), FilterBuilders.termFilter("status", 201));
FilteredQueryBuilder filteredQueryBuilder = new FilteredQueryBuilder(null, filterBuil国家开发银行der);
// 按在线时刻排序,先按时刻再按间隔排序
FieldSortBuilder sortBuilderField = SortBuilders.fieldSort("time").order(SortOrder.DESC);
// 按间隔排序,为回来客户端间隔,回来的单位:米
GeoDistanceSortBuilder sortBuilderDis = SortBuilders.geoDistanceSort("location").point(lat, lonfoobar2000).unit(DistanceUnit.KILOMETERS).order(SortOrder.ASC).geoDistance(GeoDistance.SLOPPY_ARC);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(filteredQueryBuilder).sort(sortBuilderField).sort(sortBuilderDis)
.from((queryNearbyDto.getCurrentPage()-1)*queryNearbyDto.getPageSize())
.size(queryNearbyDto.getPageSize()).fetchSource(Constants.QUERY_FIELD_TTID, null);
String query = searchSourceBuilder.toString();
r人鱼小姐,祝愿-金博宝188|官网esult = search(jestClient, index, Constants.ES_NEARBY_TYPE, 人鱼小姐,祝愿-金博宝188|官网query);

}
private List> search(JestClient jestClient, String indexName, String typeName, String query) throws Exception {
Search search = new Search.Builder(query).setSearchType(SearchType.QUERY_THEN_FETCH)
.addIndex(indexName)
.addType(typeName)
.build();
SearchResult jr = jestClient.execute(search);
if (!jr.isSucceeded()||jr.getResponseCode()!=200){
return null;
}
Long total = jr.getTotal();
List>裘怡 maps = jr.getHits(Map.class, false);
List
> sourceList = 我是maps.stream().map(source -> {
source.source.put("sort", Double.valueOf(source.sort.get(1)));
return (Map)source.source;
}).collect(Collectors.toList());
return sourceList;
}

其间的变量query是查询的elasticsearch的句子,假如你知道elasticsearch的语法也能够直接写一个json替代。

间隔人鱼小姐,祝愿-金博宝188|官网排序

在jestClient中有一个按间隔和时刻排序的比方,是先按时刻排序再按间隔排序,意图是人鱼小姐,祝愿-金博宝188|官网回来间隔。es是能够按多个字段排序的,靠前的为优先匹配排序,最终的排序成果会在回来的sort数组中回来,数组中的方位即排序的匹配方位,我这儿将回来的间隔提取出来放到map中。

5.2的elasticsearch的api的间隔排序办法如下:

GeoDistanceSortBuilder sortBuilderDis = SortBuilders.geoDistanceSort("location", lat, lon).point(lat, lon).unit(DistanceUnit.METERS).order(SortOrder.ASC).geoDistance(GeoDistance.ARC);

这儿假如不想让elasticsearch核算间隔也能够用他供给的办法自己核算,条件知道二者的经纬度,调用GeoDistance的calculate办法,详细运用的精确度能够依照事务要求挑选,不过我有做过测验,自己核算间隔和elasticsearch核算的耗时简直相差不多,假如是额定的核算间隔能够不再查一遍elasticsearch削减io耗费。

分页

关于elasticsearch不太熟悉的同学,分页也是一个坑。

浅分页

elasticsearch的的浅分页from&size,from是查询的索引方位,size是每页数量,长处类似于mysql的limit和start。

现在咱们能够假定在一个有 5 个主分片的索引中查找。 当咱们恳求成果的第一页(成果从 1 到 10 ),每一个分片产生前 10 的成果,并且回来给 和谐节点 ,和谐节点对 50 个成果排序得到悉数成果的前 10 个。现在假定咱们恳求第 1000 页--成果人鱼小姐,祝愿-金博宝188|官网从 10001 到 10010 。一切都以相同的办法作业除了每个分片不得不产生前10010个成果以外。 然后和谐节点对悉数 50050 个成果排序最终丢掉掉这些成果中的 50040 个成果。能够看到,在分布式体系中,对成果排序的本钱随分页的深度成指数上升。这便是 web 查找引擎对任何查询都不要回来超越 1000 个成果的原因。你翻页的时分,翻的越深,每个 Shard 回来的数据就越多,并且和谐节点处理的时刻越长,十分坑爹。所以用 ES 做分页的时分,你会发现越翻到后边,就越是慢。咱们之前也是遇到过这个问题,用 ES 作分uber页,前几页就几十毫秒,翻到 10 页或许几十页的时分,基本上就要 5~10 秒才干查出来一页数据了。

运用from&size的最大查询量是10000条数据,这个值能够在elasticsearch中装备文件中设置。

scroll 深分页

为了处理上面的问题,elasticsearch提出了一个scroll翻滚的办法。scroll 类似于sql中的cursor,运用scroll,每次只能获取一页的内容,然后会回来一个scroll_id。依据回来的这个scroll_id能够不断地获取下一页的内容,所以scroll并不适用于有跳页的情形.

POST /twitter/_search?scroll=1m
{
"size": 100,
"query": {
"match" : {
"title" : "elasticsearch"
}
}
}
  1. scroll=1m表明设置scroll_id保存1分钟可用。
  2. 运用scroll有必要要将from设置为0。
  3. size决议后边每次调用_search查找回来的数量
POST /_search/scroll 
{
"scroll" : "1m",
"scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ=="
}

然后咱们能够经过数据回来的_scroll_id读取下一页内容,每次恳求将会读取下10条数据,直到数据读取结束或许scroll_id保存时刻赤色官权截止。恳求的接口不再运用索引名了,而是 _search/scroll,其间GET和POST办法都能够运用。

search_after

Scroll 被引荐用于深度查询,可是contexts的价值是贵重的,不引荐用于实时用户恳求,而更适用于后台批处理使命,比方群发。search_after 供给了一个实时的光标来防止深度分页的问题,其思维是运用前一页的成果来协助检索下一页。search_after不能自在跳到一个随机页面,只能依照 sort values 跳转到下一页。运用 search_after 参数的时分,from参数有必要被设置成 0 或 -1 (当然你也能够不设置这个from参数)

search_after 需求运用一个仅有值的字段作为排序字段,不然不能运用search_after办法

引荐运用_uid 作为仅有值的排序字段。

GET twitter/_search
{
"size": 10,
"query": {
"match" : {
"title" : "ela呃sticsearch"
}
},
"sort": [
{"date": "asc"},
{"tie_breaker_id": "asc"}
]
}

鄙人一次查询的时分讲回来的最终的一条数据的sort的数组放放到search_after中。

GET twitter/_search
{
"size": 10,
"query": {
"match" : {
"title" : "elasticsearch"
}
},
"search_after": [1463538857, "654323"],
"sort": [
{"date": "asc"},
{"tie_breaker_id": "asc"}
]
}

总结

  • 深度分页不管是联系型数据库仍是Elasticsearch仍是其他查找引擎,都会带来巨大功能开支,特别是在分布式情况下。
  • 有些问题能够考事务处理而不是靠技能处理,比方许多事务都对页码有约束,google 查找,往后翻到必定页码就不行了。
  • scroll 并不合适用来做实时查找,而更适用于后台批处理使命,比方群发。
  • search_after不能自在跳到一个随机页面,只能依照 sort values 跳转到下一页。

排序与相关性

默董芝豆认情况下,回来的成果是依照 相关性 进行排序的——最相关的文档排在最前。每个文档都有相关性评分,用一个正浮点数字段 _score 来表明 。 _scor皇帝的新装e 的评分越高,相关性越高。

查询句子会为每个文档生成一个 _score 字段。评分的核算办法取决于查询类型 不同的查询句子用于不同的意图: fuzzy 查询会核算与关键词的拼写类似程度,terms 查询会核算 找到的内容与关键词组成部分匹配的百分比,可是一般咱们说的 relevance 是咱们用来核算全文本字段的值相关于全文本检索词类似程度的算法。

详细score算法能够到官网查询。

在代码中设置:

// 设置是否按查询匹配度排序
searchRequestBuilder.setExplain(true);

留意:

相关项排序耗费资源十分大,假如不是对文本精确度要求特别高的情况下,出产环境不主张按相关性排序。

参阅:

https://www.elastic.co/guide/en/elasticse馆官能奇谭arch/reference/6.7/search-request-search-after.html

https://blog.csdn.net/yiyiholic/article/details/81661919

https://www.souyunku.com/2017/11/06/ElasticSearch-example/

https://www.cnblogs.com/yangzhenlong/p/9118089.html

https://www.elastic.co/guide/cn/elasticsearch/guide/current/relevance-intro.html

原文链接:https://dwz.cn/dveJbW41

转载原创文章请注明,转载自金博宝188|官网,原文地址:http://sjlike.com/articles/2048.html

上一篇:年的传说,广州妈妈网-金博宝188|官网

下一篇:个人简历表,十月围城-金博宝188|官网