【问题标题】:A SELECT query is executed per each row result when using JdbcTemplace.query()使用 JdbcTemplace.query() 时,每行结果都会执行一个 SELECT 查询
【发布时间】:2019-12-30 12:15:57
【问题描述】:

我意识到,当我使用 JdbcTemplace.query() 执行一个返回许多记录的简单选择时,会在后台执行数百个选择。 实际上每行一个,由于我的 ACL 表中某些用户的大量记录,这最终成为性能问题。


private final String PERMISSIONS_QUERY = "SELECT acl.user_id, acl.permission FROM v_user_acls acl WHERE acl.principal = :userId ";
private NamedParameterJdbcTemplate jdbcTemplate;

MapSqlParameterSource params = new MapSqlParameterSource(DAOConstants.USER_ID_KEY, userId);

List<UserPermissionRow> userPermissions = jdbcTemplate.query(PERMISSIONS_QUERY, params,
                new RowMapper<UserPermissionRow>() {
                    @Override
                    public UserPermissionRow mapRow(ResultSet rs, int rowNum) throws SQLException {
                        return new UserPermissionRow(rs.getString(1), rs.getString(2));
                    }
                });

我查看了显示正在执行的查询的 P6Spy 日志。如您所见,一个查询是按行执行的(我认为效率不是很高)。


READ
1566811816536|-1||resultset|SELECT acl.user_id, acl.permission FROM v_user_acls acl  WHERE acl.principal = '11582' |USER_ID = 10078, PERMISSION =
 READ
1566811816536|-1||resultset|SELECT acl.user_id, acl.permission FROM v_user_acls acl  WHERE acl.principal = '11582' |USER_ID = 10079, PERMISSION =
 READ
1566811816536|-1||resultset|SELECT acl.user_id, acl.permission FROM v_user_acls acl  WHERE acl.principal = '11582' |USER_ID = 10080, PERMISSION =
 READ
1566811816536|-1||resultset|SELECT acl.user_id, acl.permission FROM v_user_acls acl  WHERE acl.principal = '11582' |USER_ID = 10081, PERMISSION =
 READ
1566811816536|-1||resultset|SELECT acl.user_id, acl.permission FROM v_user_acls acl  WHERE acl.principal = '11582' |USER_ID = 10082, PERMISSION =
 READ
1566811816536|-1||resultset|SELECT acl.user_id, acl.permission FROM v_user_acls acl  WHERE acl.principal = '11582' |USER_ID = 10083, PERMISSION =
 READ
1566811816538|-1||resultset|SELECT acl.user_id, acl.permission FROM v_user_acls acl  WHERE acl.principal = '11582' |USER_ID = 10084, PERMISSION =
 READ
1566811816538|-1||resultset|SELECT acl.user_id, acl.permission FROM v_user_acls acl  WHERE acl.principal = '11582' |USER_ID = 10085, PERMISSION =
 READ
1566811816538|-1||resultset|SELECT acl.user_id, acl.permission FROM v_user_acls acl  WHERE acl.principal = '11582' |USER_ID = 10086, PERMISSION =
 READ
1566811816538|-1||resultset|SELECT acl.user_id, acl.permission FROM v_user_acls acl  WHERE acl.principal = '11582' |USER_ID = 10087, PERMISSION = READ

...
..
.

我想知道是否有一种更有效的方法来检索如此大量的数据,只需一次选择到数据库。

非常感谢一些反馈。

问题中原始方法的响应时间为 5726 毫秒。

请参阅下面我尝试的替代方法。 不幸的是,整体时间没有改善。

选项 1(状态)

Statement stmt = dataSource.getConnection().createStatement();
ResultSet rs = stmt.executeQuery(EXPERT_ACCESS_QUERY_2 + userId);
List<UserPermissionTest> list =new ArrayList<UserPermissionTest>();
    // ============= Expert Access Query RAW: 79 ms
while(rs.next()){
    UserPermissionTest e=new UserPermissionTest();
    e.setIdentity(rs.getString(1));
    e.setPermission(rs.getString(2));
    list.add(e);
}
    // ============ 5554 ms

选项 2(ResultSetExtractor)

List <UserPermissionTest> userPerms = jdbcTemplate.query(EXPERT_ACCESS_QUERY_2 + userId, 
    new ResultSetExtractor<List<UserPermissionTest>>(){

    @Override
    public List<UserPermissionTest> extractData(ResultSet rs) throws SQLException, DataAccessException {

        List<UserPermissionTest> list =new ArrayList<UserPermissionTest>();
        while(rs.next()){
            UserPermissionTest e=new UserPermissionTest();
            e.setIdentity(rs.getString(1));
            e.setPermission(rs.getString(2));
            list.add(e);
         }
         return list;
    }
});
    //========= 5694 ms

【问题讨论】:

  • “如您所见,一个查询是按行执行的” 我看不到。在我看来,输出就像是单个查询执行中多行的转储,它只是在每一行输出上重复查询。为什么完全相同的查询的结果(USER_ID 和 PERMISSION)会不同?
  • "问题中原始方法的响应时间为 5726 毫秒" --- 也许您在 principal 列上缺少索引,因此查询是进行全表扫描。
  • 您是否尝试过自己直接在数据库上运行该查询?
  • 是的,我在数据库客户端执行。客户端在 0.098 秒内返回 40 行。显然,该查询总共有 30544 条该 ID 的记录。
  • 由于查询返回该ID的30544条记录,并且在DB客户端运行时只显示40行,那么显然DB客户端在40之后停止,您必须滚动才能看到更多,这意味着它没有读取全部30544个结果,计时值比较无意义。再试一次,但这次测量获取所有行的时间。

标签: java database jdbc oracle11g acl


【解决方案1】:

可能是因为您在下面的 sql 脚本中使用了单个 userId:

private final String PERMISSIONS_QUERY = "SELECT acl.user_id, acl.permission FROM v_user_acls acl WHERE acl.principal = :userId ";

尝试使用 IN 并在 where 条件中使用 userID 列表作为参数?

如果有帮助,我找到了这个参考: https://www.technicalkeeda.com/spring-tutorials/spring-jdbctemplate-in-clause-example

【讨论】:

  • 不,不是这样。 acl.principal Id 不会从一个查询更改为下一个查询。正如您在我提供的 SQL 中看到的那样,分配给 acl.principal 的用户 ID 没有改变,它是登录用户的 ID。如果我包含 IN 子句,查询将执行相同的次数,但只使用 IN (ID) 而不是 = ID。
  • 澄清一下,我试过你说的,结果是一样的。
【解决方案2】:

谢谢。似乎正如 Andreas 所报告的那样,日志并不是真正对数据库的调用,而是在每次迭代中记录的结果集。 我发现提高性能的唯一解决方案是在 DB View 上工作以提高其性能。很抱歉造成混乱。

【讨论】:

    猜你喜欢
    • 2021-03-15
    • 2023-02-07
    • 2018-07-16
    • 1970-01-01
    • 1970-01-01
    • 2010-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多