使用 JDBC 管理与 mysql 数据库的连接
Managing Connections to mysql DB using JDBC
背景
需要使用 JDBC 处理来自 mysql 数据库的近 6000 万条记录。这里的交互只是"selection" 从DB 基于主键,id。获取的字段是 id、name 和 description。其他一些异步线程会处理抓取的数据,不涉及DB交互。
现在这个程序将一系列 id 作为参数,例如1 到 100、101 到 200 等等,这个程序的多个实例将 运行 在不同的机器上。因此每个程序实例将处理不同范围的 id。
这是创建 backfill 数据的一次性作业,但会命中生产数据库以供选择。
观察
mysql> SHOW STATUS WHERE `variable_name` = 'Threads_connected';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_connected | 12 |
+-------------------+-------+
当我使用 25 到 95 的范围时,它只有 20 条记录,但观察到的连接数是 12,并且在整个处理过程中它们仍然如此。
代码
public void processRange(String fromid, String toid) {
if (!fromid.isEmpty() && !toid.isEmpty()) {
try {
Class.forName("com.mysql.jdbc.Driver");
this.connection = DriverManager.getConnection(this.url, this.user, this.password);
this.statement = connection.prepareStatement("SELECT id, name, contents FROM sometable WHERE id >= " + fromid + " and id <= " + toid);
this.results = statement.executeQuery();
while(results.next()) {
if(name != null && !name.isEmpty() && contents != null && !contents.isEmpty()) {
//PROCESS using async futures
}
}
} catch (SQLException sqle) {
sqle.printStackTrace();
} catch (Exception exp) {
exp.printStackTrace();
} finally {
try {
if(results != null) {
this.results.close();
}
if(statement != null) {
this.statement.close();
}
if(connection != null) {
this.connection.close();
}
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
}
}
问题
这是非常简单的 JDBC 代码。现在,如果我 运行 此过程的多个实例,如 背景 中所述,我如何确保数据库不会因 "Too many connections" 错误而关闭。有什么方法可以只获取数据,关闭连接并处理异步 "offline" 以便尽快释放连接。
另一方面,我认为循环遍历范围然后为每个 id 获取数据是解决此问题的最糟糕的方法,而不是批量获取。如果有人不这么认为,请发表评论。
更新
以现在的速度,要几个月才能完成任务...!还有其他并行方法可以使整个任务更快吗?
我也在尝试使用 MAP REDUCE 来实现相同的目的,但我不确定这是否是一个有效的用例。
以上观察是基于本地机器。当我在一个相对强大的盒子上测试它时,与最大数量相比,连接数非常少。我正在 运行 在同一台机器上查看该过程的 5 个实例,查看由结果集处理的 10000 个 ID(总共 50K)的范围。我仍然有 17 个连接,允许的最大连接数为 500。但是这样完成需要花费很多时间,CPU 使用率达到 99%。因此需要进行一些调整以使其 运行 更快或分发到其他机器。
情况一:大部分代码在MySQL,不在Java:
为此进程创建一个特殊的 "user" 登录名。设置 max_user_connections = 12
。 "Catch" 尝试连接时的相关错误。稍等片刻,然后重试。
请注意,根据您使用的 MySQL 版本、您的代码的作用、您拥有的 CPU 核心数等,拥有太多线程实际上会损害性能。一些较旧的基准显示 4-8 作为实际限制。一些较新的(5.7 版)显示 64。(但这是针对特定工作负载的,而不是您的。)
完成所有这些努力后,您可能会发现拥有多个异步进程并没有您希望的那么大的帮助。
情况2:大部分处理在外部MySQL:
拥有 max_connections = 1000
并在大量服务器上生成大量线程应该没问题。这是因为每个 MySQL 线程大部分时间都处于 'Sleep' 状态。
问题不在于数据库。瓶颈是 FTP。这些图像是根据从数据库中获取的信息创建的,并且应该 FTPed 到另一台主机。因此,不是将每张图片都放在 FTP 服务器上,而是将多张图片压缩在一起,然后上传。
背景
需要使用 JDBC 处理来自 mysql 数据库的近 6000 万条记录。这里的交互只是"selection" 从DB 基于主键,id。获取的字段是 id、name 和 description。其他一些异步线程会处理抓取的数据,不涉及DB交互。
现在这个程序将一系列 id 作为参数,例如1 到 100、101 到 200 等等,这个程序的多个实例将 运行 在不同的机器上。因此每个程序实例将处理不同范围的 id。
这是创建 backfill 数据的一次性作业,但会命中生产数据库以供选择。
观察
mysql> SHOW STATUS WHERE `variable_name` = 'Threads_connected';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_connected | 12 |
+-------------------+-------+
当我使用 25 到 95 的范围时,它只有 20 条记录,但观察到的连接数是 12,并且在整个处理过程中它们仍然如此。
代码
public void processRange(String fromid, String toid) {
if (!fromid.isEmpty() && !toid.isEmpty()) {
try {
Class.forName("com.mysql.jdbc.Driver");
this.connection = DriverManager.getConnection(this.url, this.user, this.password);
this.statement = connection.prepareStatement("SELECT id, name, contents FROM sometable WHERE id >= " + fromid + " and id <= " + toid);
this.results = statement.executeQuery();
while(results.next()) {
if(name != null && !name.isEmpty() && contents != null && !contents.isEmpty()) {
//PROCESS using async futures
}
}
} catch (SQLException sqle) {
sqle.printStackTrace();
} catch (Exception exp) {
exp.printStackTrace();
} finally {
try {
if(results != null) {
this.results.close();
}
if(statement != null) {
this.statement.close();
}
if(connection != null) {
this.connection.close();
}
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
}
}
问题
这是非常简单的 JDBC 代码。现在,如果我 运行 此过程的多个实例,如 背景 中所述,我如何确保数据库不会因 "Too many connections" 错误而关闭。有什么方法可以只获取数据,关闭连接并处理异步 "offline" 以便尽快释放连接。
另一方面,我认为循环遍历范围然后为每个 id 获取数据是解决此问题的最糟糕的方法,而不是批量获取。如果有人不这么认为,请发表评论。
更新
以现在的速度,要几个月才能完成任务...!还有其他并行方法可以使整个任务更快吗?
我也在尝试使用 MAP REDUCE 来实现相同的目的,但我不确定这是否是一个有效的用例。
以上观察是基于本地机器。当我在一个相对强大的盒子上测试它时,与最大数量相比,连接数非常少。我正在 运行 在同一台机器上查看该过程的 5 个实例,查看由结果集处理的 10000 个 ID(总共 50K)的范围。我仍然有 17 个连接,允许的最大连接数为 500。但是这样完成需要花费很多时间,CPU 使用率达到 99%。因此需要进行一些调整以使其 运行 更快或分发到其他机器。
情况一:大部分代码在MySQL,不在Java:
为此进程创建一个特殊的 "user" 登录名。设置 max_user_connections = 12
。 "Catch" 尝试连接时的相关错误。稍等片刻,然后重试。
请注意,根据您使用的 MySQL 版本、您的代码的作用、您拥有的 CPU 核心数等,拥有太多线程实际上会损害性能。一些较旧的基准显示 4-8 作为实际限制。一些较新的(5.7 版)显示 64。(但这是针对特定工作负载的,而不是您的。)
完成所有这些努力后,您可能会发现拥有多个异步进程并没有您希望的那么大的帮助。
情况2:大部分处理在外部MySQL:
拥有 max_connections = 1000
并在大量服务器上生成大量线程应该没问题。这是因为每个 MySQL 线程大部分时间都处于 'Sleep' 状态。
问题不在于数据库。瓶颈是 FTP。这些图像是根据从数据库中获取的信息创建的,并且应该 FTPed 到另一台主机。因此,不是将每张图片都放在 FTP 服务器上,而是将多张图片压缩在一起,然后上传。