当前位置: 首页 > 知识库问答 >
问题:

Spring JPA和JDBC模板——用IN子句执行非常慢的选择查询

相温文
2023-03-14

我正在尝试从我的Java项目中执行以下查询。我正在使用MySQL和数据存储,并已将Hikari CP配置为数据源。

SELECT iv.* FROM identifier_definition id 
INNER JOIN identifier_list_values iv on id.definition_id = iv.definition_id
where 
     id.status IN (:statuses)
 AND id.type = :listType
 AND iv.identifier_value IN (:valuesToAdd)

MySQL连接字符串:

jdbc:mysql://hostname:3306/DBNAME?useSSL=true&allowPublicKeyRetrieval=true&useServerPrepStmts=true&generateSimpleParameterMetadata=true

当我从MySQL workbench执行同样的查询时,它在0.5秒内返回结果。

然而,当我在JPA存储库或Spring JDBC模板中执行相同的操作时,几乎需要50秒的时间。

此查询有2个IN子句,其中状态集合只有3个项目,而标识值集合有10000个项目。

当我使用JDBC模板在没有命名参数的情况下执行原始SQL查询时,它在2秒内得到结果。然而,这种方法对SQL注入很敏感。

JPA和JDBC模板都使用了Java PreparedStatement。我的直觉是,在添加大型参数集时,底层PreparedStatement会导致性能问题。

如何提高查询性能?

下面是我正在使用的JDBC模板代码

@Component
public class ListValuesDAO {

    private static final Logger LOGGER = LoggerFactory.getLogger(ListValuesDAO.class);

    private final NamedParameterJdbcTemplate jdbcTemplate;

    @Autowired
    public ListValuesDAO(DataSource dataSource) {
        jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
    }

    public void validateListOverlap(List<String> valuesToAdd, ListType listType) {

    String query = "SELECT iv.* FROM identifier_definition id  " +
            "INNER JOIN identifier_list_values iv on id.definition_id = iv.definition_id where " +
            "id.status IN (:statuses) AND id.type = :listType AND iv.identifier_value IN (:valuesToAdd)";

    List<String> statuses = Arrays.stream(ListStatus.values())
            .map(ListStatus::getValue)
            .collect(Collectors.toList());

    MapSqlParameterSource parameters = new MapSqlParameterSource();
    parameters.addValue("statuses", statuses);
    parameters.addValue("listType", listType.toString());
    parameters.addValue("valuesToAdd", valuesToAdd);

    List<String> duplicateValues = jdbcTemplate.query(query, parameters, new DuplicateListValueMapper());

    if (isNotEmpty(duplicateValues)) {
        LOGGER.info("Fetched duplicate list value entities");
    } else {
        LOGGER.info("Could not find duplicate list value entities");
    }
}

编辑- 1

我看到这篇文章,其他人在MSSQL服务器上使用准备语句运行选择查询时遇到了类似的问题。MySQL中是否有像“sendStringParametersAsUnicode”这样的属性?

编辑-2

已尝试启用一些与 MySQL 性能相关的属性。结果还是一样的。

jdbc:mysql://localhost:3306/DBNAME?useSSL=true&allowPublicKeyRetrieval=true&useServerPrepStmts=true&generateSimpleParameterMetadata=true&rewriteBatchedStatements=true&cacheResultSetMetadata=true&cachePrepStmts=true&cacheCallableStmts=true

共有3个答案

麹正业
2023-03-14

相反,将列表传递给IN子句,将列表作为逗号分隔的字符串传递,并在查询中使用

从string_split(:valuesToAdd,',')中选择值

因此,您的查询将如下所示

SELECT iv.* FROM identifier_definition id  
     INNER JOIN identifier_list_values iv on id.definition_id = iv.definition_id 
where id.status IN (:statuses) AND id.type = :listType AND iv.identifier_value 
    IN (select value from string_split(:valuesToAdd, ','))

string_split是SQL服务器中的一个函数,MySQL可能有类似的函数

臧兴学
2023-03-14

添加到表中的复合索引:

id:  INDEX(type, status, definition_id)
id:  INDEX(definition_id, type, status)
iv:  INDEX(identifier_value, definition_id)
iv:  INDEX(definition_id, identifier_value)

对于 jdbc,连接参数应包含如下内容

?useUnicode=yes&characterEncoding=UTF-8

如需进一步讨论,请为每个表提供SHOW CREATE TABLE,并为任何有问题的查询提供EXPLAIN SELECT…

商佑运
2023-03-14

我认为应该在JPA中启用“show_sql”为true,然后尝试,我认为它运行多个查询是因为延迟加载,因此它可能需要时间。

 类似资料:
  • 键->字符串 principal_name->string 别名->字符串集合 ....... 我还在principal_name、别名和key上添加了索引。 当我试图导入Article类型节点和Author类型节点之间的关系时,问题就出现了。 有办法用Cypher做到这一点吗?

  • 问题内容: 我有一个表,其中有一个索引(A列,B列)。我正在运行一个查询,如下所示: 这个查询很慢!该计划如下所示: Postgres似乎没有一次对5000个值进行一次索引扫描,而是一次对5000个值进行了一次索引扫描,这解释了为什么查询如此缓慢。 实际上,这样做是更快的方法: 获取结果,然后在应用程序内的B列上进行过滤(python)。 我真的更希望结果已经由Postgres在合理的运行时间下进

  • 问题内容: 我已经开发了一个用户批量上传模块。有两种情况,当数据库有零条记录时,我批量上传了20000条记录。大约需要5个小时。但是,当数据库已经有大约30 000条记录时,上传速度将非常缓慢。上载2万条记录大约需要11个小时。我只是通过fgetcsv方法读取CSV文件。 下面是运行的查询。(我正在使用Yii框架) 如果存在,请更新用户: 如果用户不存在,请插入新记录。 表引擎类型为MYISAM。

  • 问题内容: 我正在维护一个通过JDBC创建Oracle DB的应用程序。从今天开始,此查询: 由于某些oracle内部机制,开始变得非常缓慢,因为我的所有分支似乎都一样。 有人知道一个可能的原因以及如何面对吗? 问候,努齐奥 问题答案: 数据字典或固定对象统计信息可能很旧,请尝试重新收集它们: 即使这样,也不一定能收集 所有 系统对象的统计信息。有些对象(例如)必须手动收集。尽管这是一个罕见的数据

  • 问题内容: 我有一个MySQL查询(Ubu 10.04,Innodb,Core i7、16Gb RAM,SSD驱动器,优化的MySQL参数): 表em_link_data有大约700万行,em_link有数千行。此查询大约需要 18秒 才能完成。但是,如果我替换子查询的结果并执行以下操作: 那么查询将在不到1毫秒的时间内运行。仅子查询在不到1毫秒的时间内运行,因此索引了列linkid。 如果我将查

  • 问题内容: 我从table1中选择所有数据,以匹配table2中field3和field4的所有匹配唯一组合。 这是我精简的SQL: 我需要将我的SQL转换为hibernate条件。我的实体对象正确映射到了表,并将响应转换为正确的结果实体,但是我无法正确转换where子句。 我有的 我希望我的where子句类似于: 但这是hibernate所不允许的。 我尝试推出where子句以具有两个子查询,并