Java 从非常大的表中获取数据
我在MySQL数据库中有一个非常大的表,表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<
用户中有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;