首先,我对Hibernate很新,而且我已经坚持了大约一个星期左右。我自己找不到解决办法,所以我必须在这里寻求帮助。
我正在尝试使用以下堆栈构建基于位置的简单API:
API是关于寻找你周围的终端(ATM)。我有三种类型的实体:
每个终端都有多个交易(一对多关联)。交易有几个TransactionCurrencies(再次,一对多关联)。
问题是搜索终端时的CPU使用率很高(60-80%)。
终端实体:
@Entity
@Table(name="terminals",
indexes = {
@Index(name = "INDEX_terminals_id", columnList="id", unique = true)
})
@Indexed
@Spatial(name="distance", spatialMode = SpatialMode.HASH)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Terminal implements Serializable {
@Id
@Column(name = "id", nullable = false)
protected int id = 0;
@Field
@Column(name = "name", nullable = false)
protected String name;
@Field
@Column(name = "owner", nullable = false)
protected String owner;
@Field
@Column(name = "terminal_class", nullable = false)
protected int terminalClass;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "terminal", fetch = FetchType.EAGER)
@OrderColumn(name="position")
@IndexedEmbedded
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
protected List<Transaction> transactions = new ArrayList<Transaction>();
@Field
@Column(name = "description", nullable = false)
protected String description;
@Version
private long version = -1;
private Timestamp tsCreated = null;
private Timestamp tsLastModified = null;
@PrePersist
public void prePersist(){
if (transactions != null && !transactions.isEmpty()) {
for (Transaction t : transactions) {
t.setTerminal(this);
}
}
Timestamp now = new Timestamp(System.currentTimeMillis());
if(tsCreated == null)tsCreated = now;
tsLastModified = now;
}
public Terminal() {
}
...
@Override
public boolean equals(Object o) {
...
}
@Override
public int hashCode() {
...
}
}
以下是终端DAO的搜索方法:
public List<Terminal> getByParams(HashMap<String, Object> params) {
List<Terminal> terminals = new ArrayList<Terminal>();
List results = null;
if (params != null && !params.isEmpty()) {
EntityManager em = this.persistenceProvider.entityManager();
Session session = em.unwrap(Session.class);
FullTextSession fullTextSession = Search.getFullTextSession(session);
// create native Lucene query using the query DSL
QueryBuilder qb = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity(Terminal.class).get();
BooleanJunction bj = qb.bool();
Integer count = (Integer) params.get("count");
Integer offset = (Integer) params.get("offset");
Integer maxDist = (Integer) params.get("maxDist");
Double gpsLatitude = (Double) params.get("gpsLatitude");
Double gpsLongitude = (Double) params.get("gpsLongitude");
if (count != null && offset != null && maxDist != null
&& count > 0 && offset >= 0 && maxDist > 0) {
for (Map.Entry<String, Object> entry : params.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if ((value != null && value instanceof Integer && (Integer) value > -1
&& !key.equals("count") && !key.equals("offset") && !key.equals("maxDist")) ||
value != null && value instanceof String) {
bj.must(qb.keyword().onField(key).matching(value).createQuery());
}
}
bj.must(qb.spatial()
.onField("distance")
.within(maxDist, Unit.KM)
.ofLatitude(gpsLatitude)
.andLongitude(gpsLongitude)
.createQuery());
Query luceneQuery = bj.createQuery();
FullTextQuery hibQuery = fullTextSession.createFullTextQuery(luceneQuery, Terminal.class);
Sort distanceSort = new Sort(
new DistanceSortField(gpsLatitude, gpsLongitude, "distance"));
hibQuery.setSort(distanceSort);
hibQuery.setProjection(FullTextQuery.SPATIAL_DISTANCE, FullTextQuery.THIS);
hibQuery.setSpatialParameters(gpsLatitude, gpsLongitude, "distance");
hibQuery.setFetchSize(count);
hibQuery.setFirstResult(offset);
hibQuery.setReadOnly(true);
// execute search
results = hibQuery.list();
Iterator<Object[]> iterator = results.iterator();
while (iterator.hasNext()) {
Object[] resultObject = iterator.next();
if (resultObject.length == 2) {
double distanceInMeters = (Double) resultObject[0] * 1000;
Terminal terminal = (Terminal) resultObject[1];
terminal.setDistance(distanceInMeters);
terminals.add(terminal);
}
}
} else {
log.error("Empty param list passed to TerminalDAO class. Cannot find terminals.");
}
}
return terminals;
}
我使用Apache Benchmark在Core i7 2.5 Ghz和16 Gb RAM上共有10个并发和100个请求(所以硬件很好):
ab -c 10 -n 100 "http://localhost:8080/getTerminals?class=1&lat=46.2317893&lon=50.168378&count=100"
正如您所看到的,响应中包含100个终端附近的位置。查询很快,但我担心CPU使用率。
根据分析器,最强大的cpu方法是FullTextQuery.list()。
在阅读了hibernate文档和一些研究之后,我尝试了几件事:
但没有任何帮助。
并发请求是否常见问题(从db中选择)? Hibernate Search可以处理它们吗?我错过了什么吗?
可以在此处找到完整的示例项目:https://github.com/xvonabur/hib_debug
我感谢任何帮助。
答案 0 :(得分:0)
通常我会说这是预料之中的,通常人们不会将其归类为问题,而是将您的可用CPU用于充分利用。 &#34;未使用的CPU是一个浪费的CPU&#34;正如他们所说的那样。
Hibernate Search依赖于Apache Lucene,这对IO操作非常有效;开发人员努力确保您的CPU不必等待慢速磁盘。
查询评分是一项计算密集型操作,因此如果您在多个请求之间没有任何暂停,您就能够最大化CPU。还要考虑一个真正的基准测试应该模拟用户认为暂停:当完成一个Query时,你仍然会看到CPU使用率的峰值,但是这些将非常短,因此平均CPU使用率会很低。
这种方法的另一个好处是应用程序完成了大部分繁重工作,因此您的MySQL数据库只能执行繁琐的任务;这通常是一件好事,因为您可以根据需要在多个节点上扩展应用程序;虽然通常难以扩展数据库。
如果您对检查更多内容感兴趣,可以尝试使用Projections;通过使用投影,您可以完全避免查询数据库,如果您避免加载&#39;这个&#39;但只需要获取所需的字段,这样您就可以检查全文查询的性能,而无需从数据库加载方面来混淆数字。
独立优化它们;当然,仅使用索引中的投影时,二级缓存等选项毫无意义。从图片中取出JDBC相关代码应该可以更容易再次分析并确定您还可以做什么。如果您发现Hibernate Search效率低下,请打开enter link description here,我们会对您的结果非常感兴趣。