某天用户反馈集群负载很高,CPU资源处于持续被打满的状态,对于elasticsearch集群的业务请求也频繁超时,大量请求失败。
通过监控,我们发现集群重要任务均为查询任务,通过集群慢日志我们发现集群在大量运行wildcard请求。耗时从数十秒到数分钟不等。在大量请求期间,集群的CPU load,磁盘IO均处于高位运行。
样例语句如下:
GET index/_search
{
"query": {
"wildcard": {
"name_zh": {
"wildcard": "*xx科技*"
}
}
},
"from": 0,
"size": 10
}
我们对慢查询语句通过profile解析后发现,耗时主要集中于wildcard模糊匹配阶段。
wildcard通配符模糊匹配查询需要使用正排索引,类似于关系型数据库中的“like”操作。在wildcard查询对数据进行匹配的过程中需要匹配很多类型的数据,所以整体耗时都会很长。通过匹配字符串的方式对数据进行过滤查询。与elasticsearch使用倒排索引加速查询的理念背道而驰。虽然elasticsearch提供了wildcard这种字符串模糊匹配的能力,但是我们不建议使用该方法对elasticsearch进行查询。
因此,通配符查询应该谨慎使用,尽量避免在大型索引上使用,并且应该使用更精确的查询方式来提高查询性能和结果的准确性。
public WildcardQueryBuilder(String fieldName, String value) {
if (Strings.isEmpty(fieldName)) {
throw new IllegalArgumentException("field name is null or empty");
}
if (value == null) {
throw new IllegalArgumentException("value cannot be null");
}
this.fieldName = fieldName;
this.value = value;
}
在该构造函数中判断传入wildcard Query中的字段名与搜索关键字是否为空,如果字段名为空,则抛出 IllegalArgumentException
异常。如果值为null
,则抛出IllegalArgumentException
异常。否则,将字段名和值赋值给相应的成员变量。 以便于后续构造QueryBuilder。
public static WildcardQueryBuilder fromXContent(XContentParser parser) throws IOException {
String fieldName = null;
String rewrite = null;
String value = null;
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
boolean caseInsensitive = DEFAULT_CASE_INSENSITIVITY;
String queryName = null;
String currentFieldName = null;
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
throwParsingExceptionOnMultipleFields(NAME, parser.getTokenLocation(), fieldName, currentFieldName);
fieldName = currentFieldName;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else {
if (WILDCARD_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
value = parser.text();
} else if (VALUE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
value = parser.text();
} else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
boost = parser.floatValue();
} else if (REWRITE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
rewrite = parser.textOrNull();
} else if (CASE_INSENSITIVE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
caseInsensitive = parser.booleanValue();
} else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
queryName = parser.text();
} else {
throw new ParsingException(
parser.getTokenLocation(),
"[wildcard] query does not support [" + currentFieldName + "]"
);
}
}
}
} else {
throwParsingExceptionOnMultipleFields(NAME, parser.getTokenLocation(), fieldName, parser.currentName());
fieldName = parser.currentName();
value = parser.text();
}
}
在构造WildcardQueryBuilder的这段代码中,从XContentParser解析WildcardQueryBuilder的字段名、值和其他参数,并使用这些参数构建一个WildcardQueryBuilder对象。它根据字段名匹配相应的参数,并将其赋值给相应的变量。最后,将构造的WildcardQueryBuilder对象返回。
public WildcardQueryBuilder caseInsensitive(boolean caseInsensitive) {
this.caseInsensitive = caseInsensitive;
return this;
}
通过该方法来控制匹配的敏感度,如果传入的参数为true
,则表示进行大小写不敏感的匹配;如果传入的参数为false
,则表示进行大小写敏感的匹配。
GET company/_search
{
"query": {
"match": {
"name_zh.word": {
"query": "xx科技"
}
}
},
"from": 0,
"size": 10
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。