【问题标题】:Improving performance of Solr and MySQL pair (connected by "WHERE IN" using JPA)提高 Solr 和 MySQL 对的性能(使用 JPA 通过“WHERE IN”连接)
【发布时间】:2012-12-01 04:21:37
【问题描述】:

我有一个由 Solr 索引的 MySQL 数据库。我使用 Solr(快速)执行搜索,并使用 JPA 从数据库中检索 Solr 搜索中的每个结果。 JPA 在非常慢的数据库上运行 WHERE IN 查询。

有没有办法让这个过程更快,或者重构设计以提高性能?

我刚刚将整个应用程序从使用 MySQL 的全文搜索重构为使用 Solr,现在性能更差了。

注意:我需要立即对所有结果进行计算,因此我不能使用分页。

Java 代码:

    SolrDocumentList documentList = response.getResults();
    Collection<String> listingIds = new ArrayList<>();
    for(SolrDocument doc : documentList) {
        String listingId = (String) doc.getFirstValue("ListingId");
        listingIds.add(listingId);
    }

    Query query = em.createNamedQuery("getAllListingsWithId");
    query.setParameter("listingIds", listingIds);
    List<ListedItemDetail> listings = query.getResultList();

命名查询:

<query>Select listing from ListingSet listing where listing.listingId in :listingIds</query>

附加信息:

SHOW CREATE TABLE ListingSet 产生[缩短]:

CREATE TABLE `listingset` (
  `LISTINGID` int(11) NOT NULL,
  `STARTDATE` datetime DEFAULT NULL,
  `STARTPRICE` decimal(10,2) DEFAULT NULL,
  `TITLE` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`LISTINGID`),
  KEY `FK_LISTINGSET_MEMBER_MEMBERID` (`MEMBER_MEMBERID`),
  CONSTRAINT `FK_LISTINGSET_MEMBER_MEMBERID` FOREIGN KEY (`MEMBER_MEMBERID`) REFERENCES `member` (`MEMBERID`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1

调查生成的 SQL

查看生成的 SQL,JPA 为单个 JPA 查询运行大量 SQL 查询。 ListingSet 表有 7 个与之链接的表,并为每个表运行一个单独的 SELECT 查询以获取每个列表 ID(其中有 1,000 - 10,000 个)。所以我的一个 JPA 查询被炸成了大约 7,000 个查询!

【问题讨论】:

  • SQL 语法将是:`从 ListingSet 列表中选择列表,其中列表.listingId in (:listingIds)';鉴于 listingIds 是一个逗号列表。
  • JPA 处理这个问题。 &lt;query&gt;..&lt;/query&gt; 中的查询不是原始 SQL 查询,而是转换为 JPA 建议的形式。
  • 我知道 JPA 可以做各种各样的事情,但我希望您的问题可以通过查看生成的 SQL 来回答。在其他情况下,您必须查阅手册。
  • 我的问题不是对运行的查询感到困惑。我的问题是连接 Solr 和 MySQL 的典型方式是什么,这样性能不会很差。我已经看到 JPA 运行的查询,它是标准的 WHERE IN,带有逗号分隔的 id 列表。
  • 好吧:看看发出的 SQL,或者只是添加括号并尝试。也许您甚至需要引用/取消引用?

标签: mysql sql jpa solr


【解决方案1】:

以下只是调试问题的个人想法:

  • 开启mysql查询日志 并检查 JPA 不访问 MySQL 每个列表 ID 的每个查询。

    mysql -uroot -pYOUR-PASSWORD -e "SET GLOBAL log_output = 'FILE'; 设置 GLOBAL general_log_file = '/tmp/mysql.log'; SET GLOBAL general_log = 'ON';" tail -f /tmp/mysql.log

  • 检查性能是否是由 MySQL 引起的,在你的 MySQL 数据库中运行等效的 SQL。

    从ListingSet中选择listingId所在的listingId(把你的真实listingId放在这里);

    确保ListingId 列上有索引(很有可能索引已经存在)

  • 由于您只从 MySQL 读取行,也许您可​​以设置 Replicate 以获得更多从站, 然后将您的 ListingIds 拆分为所有从 MySQL,然后合并结果。 http://dev.mysql.com/doc/refman/5.0/en/replication-howto.html

【讨论】:

  • ListingId 是表的主键,所以应该已经被索引了。
  • 我已更新问题以包含您建议的步骤中的信息。
  • 就像我猜的那样,它确实会访问 MySQL 每个列表 ID 的每个查询。如何使用Query createNativeQuery(String sqlString, Class resultClass)
【解决方案2】:

问题是由我使用 JPA 引起的。由于我的实体有很多关系,单个查询会爆炸成 1,000-10,000 个查询。

解决办法是在JPA中使用Batch Processing来防止ORM n+1查询问题。批处理导致 JPA 一次从相关表中请求所有相关行,而不是为每个实体一次。当查询返回许多个结果并且被查询的实体有许多关系时,此解决方案是合适的。

确定 JPA 潜在问题的最简单方法是启用更精细的日志记录。对于 EclipseLink,将属性添加到 persistence.xml:

  <property name="eclipselink.logging.level" value="FINEST"/>

请注意,在 EclipseLink 的默认设置下生成的日志记录仅显示查询的 JPQL 形式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    • 1970-01-01
    • 2016-03-16
    • 2022-08-19
    • 1970-01-01
    • 2021-12-12
    相关资源
    最近更新 更多