【发布时间】:2017-10-19 13:47:22
【问题描述】:
这个问题在网上并不鲜见,但是我对MySQL服务器做了一些优化工作来解决这个问题,但没有得到结果。所以一开始我使用maven的包mysql:mysql-connector-java:6.0.6。 我试着运行这段代码:
try {
mysqlConnection = DriverManager.getConnection(DatabaseUtils.mysqlUrl, DatabaseUtils.mysqlUser, DatabaseUtils.mysqlPassword);
PreparedStatement valuesStatement = "SELECT * FROM `test` ORDER BY `id`"
ResultSet cursor = valuesStatement.executeQuery();
double value = 0;
if (cursor.next())
value = cursor.getDouble("value");
} catch (SQLException sqlEx) {
sqlEx.printStackTrace();
} finally {
cursor.close();
pricesStatement.close();
}
我的表中有很多记录。它大约是百万,但每天都会增加大约一千条记录。所以当这个简单的例子执行了 30 秒时,我感到非常惊讶。我用谷歌搜索了我的问题,我发现只有“使用池”、“调整 mysql 服务器”、“尝试解释选择”。但我注意到执行时间与行数有关。于是我查看了驱动的代码,发现:
TextResultsetReader::read():
while(true) {
if(row == null) {
rows = new ResultsetRowsStatic(rowList, cdef);
break;
}
if(maxRows == -1 || rowList.size() < maxRows) {
rowList.add(row);
}
row = (ResultsetRow)this.protocol.read(ResultsetRow.class, trf);
}
这意味着即使我只想获取一个行,驱动程序也会获取所有查询的行并让我排在第一位。手册建议使用“setFetchSize”仅获取 n 条记录。但它不起作用。无论如何,驱动程序代码都会获取所有数据。于是我发现有两个记录集:ResultRowsStatic 和 ResultSetStreaming。第二个似乎只在我需要查询数据时才获取数据。如何使用 ResultRowsStreaming?我发现它只在代码中。参数“fetchSize”必须等于 -2147483648。我确实尝试过,它奏效了!现在“executeQuery()”的执行时间大约为 0.0007 秒。这对我来说非常快。但是等等..我的脚本无论如何都需要 30 秒。为什么?我在执行查询后调试了代码。之后只有两种“关闭”方法。有什么问题?这是真的,“cursor.close()”占用了剩下的时间。我再次查看了库代码并到达了ResultsetRowsStreaming::close():
boolean hadMore = false;
int howMuchMore = 0;
synchronized(mutex) {
while(this.next() != null) {
hadMore = true;
++howMuchMore;
if(howMuchMore % 100 == 0) {
Thread.yield();
}
}
if(conn != null) {
if(!((Boolean)this.protocol.getPropertySet().getBooleanReadableProperty("clobberStreamingResults").getValue()).booleanValue() && ((Integer)this.protocol.getPropertySet().getIntegerReadableProperty("netTimeoutForStreamingResults").getValue()).intValue() > 0) {
int oldValue = this.protocol.getServerSession().getServerVariable("net_write_timeout", 60);
this.protocol.clearInputStream();
try {
this.protocol.sqlQueryDirect((StatementImpl)null, "SET net_write_timeout=" + oldValue, (String)this.protocol.getPropertySet().getStringReadableProperty("characterEncoding").getValue(), (PacketPayload)null, -1, false, (String)null, (ColumnDefinition)null, (GetProfilerEventHandlerInstanceFunction)null, this.resultSetFactory);
} catch (Exception var9) {
throw ExceptionFactory.createException(var9.getMessage(), var9, this.exceptionInterceptor);
}
}
if(((Boolean)this.protocol.getPropertySet().getBooleanReadableProperty("useUsageAdvisor").getValue()).booleanValue() && hadMore) {
ProfilerEventHandler eventSink = ProfilerEventHandlerFactory.getInstance(conn.getSession());
eventSink.consumeEvent(new ProfilerEventImpl(0, "", this.owner.getCurrentCatalog(), this.owner.getConnectionId(), this.owner.getOwningStatementId(), -1, System.currentTimeMillis(), 0L, Constants.MILLIS_I18N, (String)null, (String)null, Messages.getString("RowDataDynamic.2") + howMuchMore + Messages.getString("RowDataDynamic.3") + Messages.getString("RowDataDynamic.4") + Messages.getString("RowDataDynamic.5") + Messages.getString("RowDataDynamic.6") + this.owner.getPointOfOrigin()));
}
}
}
此代码无条件地获取所有其余数据,仅用于记录我未获取的记录数。真奇怪。如果附加了记录器,那将是合理的。但在我的情况下,这段代码在 30 秒内计算未获取的行数,然后......什么也不做。而这个问题我无法解决,因为没有参数可以告诉代码不要计算行数。
现在我不知道下一步该做什么。查询时间对我来说很慢。例如 php 的 mysql 驱动程序在 0.0004-0.001 秒内执行此查询。
所以那些使用mysql-connector for Java的人,请告诉我你有这些问题吗?如果没有,您能否发布任何示例来绕过上述问题?也许您使用其他连接器。那么请告诉我,该怎么办?
【问题讨论】:
-
如果我的理解是正确的 - 你得到了你想要的结果,但语句在关闭之前继续执行选择?你试过
valuesStatement.cancel()吗? See here -
我尝试在 close() 之前添加 cancel() 但它也无济于事。所有其余数据均由驱动程序获取。我正在使用jar,所以我无法编辑驱动程序的代码。
-
如果你只想要一行,告诉服务器只发送一行,使用
LIMIT 1。 -
就我而言,我不知道应该设置什么限制。我的目的比这个例子更复杂。我选择所有数据,通过非线性算法制作几个数组,当我得到需要的数组数量时,我就停止获取数据。所以我不知道我需要多少行:一万行。
-
尊重您关于不知道需要多少行的评论:请edit 您的问题告诉我们您决定需要哪些行的逻辑。解决此问题后,您将在 SQL 中使用
WHERE、ORDER BY和LIMIT子句来实现该逻辑。获取大表中的所有行通常是此类问题的错误解决方案。 thedailywtf.com 中提到你的工作就是这种错误。
标签: java mysql jdbc mysql-connector