Java 正则表达式中的性能问题
我有我的REST服务,它在重载下运行,这意味着它每天会收到大约一百万个读取呼叫的大量流量。我的REST服务将根据用户ID从数据库中进行查找,并检索与该用户ID对应的几列 因此,我目前在代码中看到了高性能问题。我怀疑下面的方法将是我应该首先开始优化的方法之一 下面的方法将接受一个Java 正则表达式中的性能问题,java,regex,optimization,pattern-matching,matcher,Java,Regex,Optimization,Pattern Matching,Matcher,我有我的REST服务,它在重载下运行,这意味着它每天会收到大约一百万个读取呼叫的大量流量。我的REST服务将根据用户ID从数据库中进行查找,并检索与该用户ID对应的几列 因此,我目前在代码中看到了高性能问题。我怀疑下面的方法将是我应该首先开始优化的方法之一 下面的方法将接受一个attributeName,然后在此基础上使用正则表达式为我提供匹配 让我们举个例子——如果attrName是technology.profile.financial 然后,下面的方法将以technology.profil
attributeName
,然后在此基础上使用正则表达式为我提供匹配
让我们举个例子——如果attrName
是technology.profile.financial
然后,下面的方法将以technology.profile
的形式返回我。这种方法也适用于其他情况
private String getAttrDomain(String attrName){
Pattern r = Pattern.compile(CommonConstants.VALID_DOMAIN);
Matcher m = r.matcher(attrName.toLowerCase());
if (m.find()) {
return m.group(0);
}
return null;
}
在CommonConstants
类文件中
String VALID_DOMAIN = "(technology|computer|sdc|adj|wdc|pp|stub).(profile|preference|experience|behavioral)";
我只是想看看,这里是否有一些性能问题,或者不使用上面的正则表达式?如果是的话,那么在考虑性能问题的情况下,重新编写此内容的最佳方法是什么
谢谢你的帮助。我用卡钳测试了这个和这个,结果是:如果你在每次方法调用之前编译模式,这将是最快的方法 正则表达式方法是最快的方法,但他唯一需要做的改变是 预先计算模式,而不是每次:
private static Pattern p = Pattern.compile(VALID_DOMAIN);
然后在您的方法中:
Matcher matcher = pattern.matcher(input); ...
对于感兴趣的人来说,这是我用于卡尺的设置:--warmupMillis 10000--runMillis 100
package stackoverflow;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.caliper.Param;
import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
public class RegexPerformance extends SimpleBenchmark {
private static final String firstPart = "technology|computer|sdc|adj|wdc|pp|stub";
private static final String secondPart = "profile|preference|experience|behavioral";
private static final String VALID_DOMAIN = "(technology|computer|sdc|adj|wdc|pp|stub)\\.(profile|preference|experience|behavioral)";
@Param({"technology.profile.financial", "computer.preference.test","sdc.experience.test"})
private String input;
public static void main(String[] args) {
Runner.main(RegexPerformance.class, args);
}
public void timeRegexMatch(int reps){
for(int i=0;i<reps;++i){
regexMatch(input);
}
}
public void timeGuavaMatch(int reps){
for(int i=0;i<reps;++i){
guavaMatch(input);
}
}
public void timeRegexMatchOutsideMethod(int reps){
for(int i=0;i<reps;++i){
regexMatchOutsideMethod(input);
}
}
public String regexMatch(String input){
Pattern p = Pattern.compile(VALID_DOMAIN);
Matcher m = p.matcher(input);
if(m.find()) return m.group();
return null;
}
public String regexMatchOutsideMethod(String input){
Matcher matcher = pattern.matcher(input);
if(matcher.find()) return matcher.group();
return null;
}
public String guavaMatch(String input){
Iterable<String> tokens = Splitter.on(".").omitEmptyStrings().split(input);
String firstToken = Iterables.get(tokens, 0);
String secondToken = Iterables.get(tokens, 1);
if( (firstPart.contains(firstToken) ) && (secondPart.contains(secondToken)) ){
return firstToken+"."+secondToken;
}
return null;
}
}
两点:
正如注释中提到的,除了在函数外部编译表达式外,还可以使()
不捕获,这样就不会保存每个函数匹配的内容,即
String VALID_DOMAIN = "(?:technology|computer|sdc|adj|wdc|pp|stub)\\.(?:profile|preference|experience|behavioral)";
如果有效域必须始终出现在属性名称的开头,您可以使用lookingAt
方法而不是find
,这样匹配会更快失败,即
if (m.lookingAt()) {
如果表达式是在函数外部编译的,您可以添加
模式。不区分大小写
,这样您就不必每次在attrName
上调用toLowerCase()
。有什么原因不能将正则表达式保存为模式而不是字符串?如果regex从未更改,那么每次使用它时都会浪费大量时间重新编译regex。对于这样一个简单的模式,编译正则表达式可能比实际匹配要花费更多的时间
至于正则表达式本身,我建议进行一些更改。这些更改将使正则表达式稍微更有效率,但可能还不足以引起注意。目的是使其更加健壮
- 将其括在单词边界中,以避免字符串出现误报,如
或foo\u technology.profile
。我相信你知道在你的情况下会发生这样的事情,但是为什么要冒这么小的风险呢technology.profile\u bar
- 避开圆点,如图所示
- 使用非捕获组而不是捕获组。(假设您实际上不需要分解属性名的各个组件。)
只是说,但是
匹配任何字符。如果要匹配实际的
,必须将其转义。是否确定性能问题是与regex有关,而不是与数据库有关?否,不是与数据库有关。我在每个组件级别都安装了仪器。所以在数据库方面,它看起来不错。但只有在这个部分,我看到了很多变化。在这个类中,我知道一个问题可能是多线程,但另一个问题我怀疑这个正则表达式匹配。这就是我想确定使用类regex是否会导致性能下降的原因。@Farhan尝试一个粗糙的System.nanoTime()
benchmark?转义
。表达式本身没有任何性能问题。尝试在函数之外编译表达式,而不是在每次函数调用中编译表达式。您是否故意拼写错误sdc.experince.test
以测试不匹配项,或者这只是一个输入错误?我想给您一个优雅的解决方法。;)真的应该至少有一次考试不及格,你不觉得吗?特别是涉及正则表达式时;通常在失败的比赛尝试中,你会看到巨大的表现惩罚。@Alanmore当我再仔细想想时,我认为你是对的。威尔也加入了他们。thx@Eugene,我猜,您没有在实际代码中将模式声明为静态。@FarhanJamal darn您是对的:),但它很简单-->private static final Pattern patter=new Pattern(VALID_DOMAIN);谢谢迈克的建议。唯一的问题是我不知道如何使用模式。这里不区分大小写?你能提供一个关于我的场景的例子吗?谢谢你的帮助。@FarhanJamal<代码>模式r=Pattern.compile(CommonConstants.VALID\u域,Pattern.CASE\u不区分大小写)代码>
if (m.lookingAt()) {
static final Pattern VALID_DOMAIN_PATTERN = Pattern.compile(
"\\b(?:technology|computer|sdc|adj|wdc|pp|stub)\\.(?:profile|preference|experience|behavioral)\\b");