Java 从非常大的表中获取数据

Java 从非常大的表中获取数据,java,mysql,multithreading,jdbc,producer-consumer,Java,Mysql,Multithreading,Jdbc,Producer Consumer,我在MySQL数据库中有一个非常大的表,表用户中有2亿条记录 我使用JDBC进行查询: public List<Pair<Long, String>> getUsersAll() throws SQLException { Connection cnn = null; CallableStatement cs = null; ResultSet rs = null; final List<Pair<

我在MySQL数据库中有一个非常大的表,表
用户中有2亿条记录

我使用JDBC进行查询:

public List<Pair<Long, String>> getUsersAll() throws SQLException {
        Connection cnn = null;
        CallableStatement cs = null;
        ResultSet rs = null;
        final List<Pair<Long, String>> res = new ArrayList<>();
        try {
            cnn = dataSource.getConnection();
            cs = cnn.prepareCall("select UserPropertyKindId, login from TEST.users;");
            rs = cs.executeQuery();
            while (rs.next()) {
                res.add(new ImmutablePair<>(rs.getLong(1), rs.getString(2)));
            }
            return res;
        } catch (SQLException ex) {
            throw ex;
        } finally {
            DbUtils.closeQuietly(cnn, cs, rs);
        }
    }
  • 在callable for each pair中,进行两次查询:

    第一个问题:

    select distinct entityId 
    from UserPropertyValue 
    where userPropertyKindId= ? and value = ? -- value its login from Users table
    
    第二个问题:

    select UserIds 
    from UserPropertyIndex 
    where UserPropertyKindId = ? and Value = ?
    
  • 可能有两种情况:

  • 第一次查询的结果为空:记录、发送通知、继续下一对
  • 第二次查询的结果不等于第一次查询的结果(已解码的varbinary数据。存储了编码的entityId)。然后记录,发送通知,转到下一对
  • 我不能改变基地的结构。我必须在Java代码端执行的所有操作。

    而不是在Java代码端执行的Lists.partition(users,2000),您应该将mysql结果集限制为每个请求2000个

    select UserPropertyKindId, login from TEST.users limit <offset>, 2000;
    
    选择UserPropertyKindId,从TEST.users limit登录,2000;
    
    更新:正如Raymond Nijland在下面的评论中提到的,如果偏移量太大,查询速度可能会显著减慢

    一种解决方法是不使用offset,而是引入where语句,例如where id>last\u user\u id

    由于@All_safe在下面进行了注释,因此不存在自动增量id,因此大限制偏移量的另一种解决方法是:仅在子查询中获取主键,然后连接回主表。这将迫使mysql不进行早期行查找,这是大偏移量限制的主要问题


    但是您最初的查询只获取主键列,我认为早期行查找不适用。

    您可以将优先级烘焙到查询中 e、 例如,
    其中my_priority=1,由my_sub_priority DESC排序

    正如雅各布所说,使用极限
    limit02000


    您可能可以打破不一致用户的逻辑,寻找特定的缺陷,然后使用解释中获得的见解优化这些查询。也许一种查找用户缺陷(defect)的方法可以帮助您以设置方式处理用户

    我也遇到过类似的情况。我正在从MySQL数据库读取数据,并将其复制到MS SQL Server数据库中。不是2亿,每天只有400万。但我收到了与通信链路故障相同的错误消息。我可以通过设置PreparedStatement.setFetchSize(Integer.MIN_值)的fetchsize来解决这个问题;
    因此,通信链路故障消失了。我知道,这并不能解决你的列表问题

    您应该在多个级别处理此问题:

    JDBC驱动程序获取大小 JDBC有一个方法,它指示在从JDBC获取行之前,JDBC驱动程序将预取多少行。请注意,MySQL JDBC驱动程序并没有真正正确地实现这一点,但是您可以设置
    setFetchSize(Integer.MIN_VALUE)
    ,以防止它一次性获取所有行

    注意,您也可以使用激活连接上的功能

    你自己的逻辑 您不应该将整个用户列表放在内存中。现在要做的是从JDBC收集所有行,然后稍后使用
    Lists.partition(users,2000)
    对列表进行分区。这正朝着正确的方向发展,但你还没有做好。相反,要:

    try(ResultSet rs=cs.executeQuery()){
    while(rs.next()){
    res.add(新的ImmutablePair(rs.getLong(1),rs.getString(2));
    }
    //处理一批行:
    如果(分辨率大小()>=2000){
    过程(res);
    res.clear();
    }
    }
    //处理剩余的行
    过程(res);
    

    这里的重要信息是不加载内存中的所有行,然后批量处理它们,但直接从JDBC传输行时处理它们。

    您正面临查询超时问题,请考虑增加SimeDon不保存所有用户。memory@user7294900,我知道,这不对。但我不知道该怎么做otherwise@All_Safe你想达到什么目标?为什么要在内存中保存2亿条记录?@user7294900,对于从该表接收到的每个用户,您都需要执行一定的处理和验证。假设在这种情况下,将有大量查询数据库是的,您正在谈论2亿条记录。没有查询不是这里的主要问题。我不明白一句话:“没有查询不是这里的主要问题”1。您试图在一个查询中获取2亿条记录,并将所有记录存储在内存中,我认为这不是一个好主意。2.在每个记录的循环处理中,您将做什么?你们会更新数据库吗?3.如果这是批处理作业,那么您可以考虑Sql DUMP,然后再使用java应用程序处理转储数据,而不是访问数据库。同样,避免将所有内容加载到内存中。我不会更新数据库。对于每个用户,我需要从其他两个表中提取数据并进行比较。当我写入:
    setFetchSize(Integer.MIN\u VALUE)
    时,我将来自数据库1行?据我所知,在
    MySQL
    中无法指定部件的大小,它会忽略it@All_Safe:是的,MySQL不支持一次抓取N行,只支持所有行或逐行抓取。请说,如果我要使用
    useCursorFetch=true
    ,我需要设置此参数:
    stmt=conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,java.sql.ResultSet.CONCUR_READ_ONLY)
    ?或者足够了:
    stmt.setFetchSize(2000);
    setFetchSize(Integer.MIN_值)
    。注意:
    MIN_值
    。我已经链接到了一些旧的堆栈溢出问题,解释了为什么在MySQL中需要使用
    MIN_值
    select UserIds 
    from UserPropertyIndex 
    where UserPropertyKindId = ? and Value = ?
    
    select UserPropertyKindId, login from TEST.users limit <offset>, 2000;