2017-05-19 70 views
3

這不是網絡上的罕見問題,但我使用MySQL服務器做了一些優化工作來解決這個問題,但沒有得到結果。所以起初我使用maven的包mysql:mysql-connector-java:6.0.6。 我只是想運行這段代碼:SELECT連接上的JDBC連接器速度太慢

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秒時,我感到非常驚訝。我GOOGLE了我的問題,我發現只有「使用池」,「調整MySQL服務器」,「嘗試解釋選擇」。但我注意到執行時間與行數有關。所以,我看着司機的代碼,發現:

TextResultsetReader ::閱讀():

 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的人告訴我請你有這些問題嗎?如果沒有,你可以張貼任何例子,我應該怎麼做才能繞過上述問題?也許你使用另一個連接器。所以請告訴我,該怎麼辦?

+0

如果我的理解正確 - 你會得到你想要的結果,但是聲明在結束之前繼續執行select?你嘗試過'valuesStatement.cancel()'嗎? [看到這裏](http://stackoverflow.com/questions/295920/how-can-i-abort-a-running-jdbc-transaction) – Michael

+0

我試圖在close()之前添加cancel(),但它沒有也幫助。其餘所有數據都由驅動程序提取。我正在使用jar,所以我不能編輯驅動程序的代碼。 –

+0

如果您只需要一行,請告訴服務器僅使用['LIMIT 1'](https://dev.mysql.com/doc/refman/5.7/en/select.html#idm140545627869536)發送一行。 – Andreas

回答

1

你的SQL查詢說

SELECT * FROM test ORDER BY id 

你是,與查詢,指示你的MySQL服務器序列化每列的test表的每一行並將其發送到您的Java程序。所以,MySQL服從。你有一張大桌子。所以你對MySQL的指示需要時間。是的,表中的行越多,所需的時間越長。這不是JDBC或驅動程序的問題;這是你使用的SQL的一個問題。

從您的示例代碼中可以看出,您希望從表中的一行(第一行)獲得一列 - 名爲value。你可以做到,使用該SQL語句:

SELECT value FROM test ORDER BY id LIMIT 1 

如果您id列是你的表的主鍵,這將是快。

SQL的整點是爲了讓您的表包含如此多的行,以至於在短時間內將它們全部提取到Java(或其他)程序中是不合理的。這就是爲什麼SQL有WHERELIMIT子句。

+0

正如我寫的,另一個MySQL驅動程序特別是PHP和本地mysql查詢從bash執行此查詢非常快。而且我有'id'列的索引,所以MySQL必須比以前更快地給我第一個記錄。我堅持認爲這不是MySQL問題,我描述了爲什麼我在這個問題中這麼想。 –

+1

第一行的時間並不是您放置在稀缺資源上的負擔的有效指標 - 您的MySQL服務器。 –

+0

@abr_stackoverflow我完全同意這個答案。如果您需要100行,請添加限制100.如果您不知道可能需要的行數,請翻頁查看結果。 – marstato