2013-07-12 89 views
1

我遇到了一些奇怪的行爲。我有一個Java程序,它使用預處理語句和批量插入將數據永久寫入MySQL表。如果其他進程在同一個表上發出LOCK TABLES t WRITE,並在此後不久(幾分鐘後)釋放它,Java程序會按預期繼續。但是,當鎖定持續較長時間(超過30分鐘)時,Java程序失去連接並且不會繼續。 2小時後,它會因通信鏈路故障而失敗。在表鎖定期間掛起的插入語句在鎖釋放後執行,但之後連接消失。在長時間鎖定表中通信鏈接失敗到MySQL

下面是詳細信息:

  • 它的MySQL都和5.0.51a 66年5月1日
  • 情況我使用的是最新的JDBC驅動程序使用mysql-connector-5.1.25-bin.jar
  • wait_timeout被設置爲28800(8小時)
  • 通訊鏈路故障和Java程序的堆棧跟蹤下面給出

有人知道這裏發生了什麼嗎?是否有任何超時需要設置/增加?


兩小時後拋出的異常:

Exception in thread "main" java.sql.BatchUpdateException: Communications link failure 

The last packet successfully received from the server was 7.260.436 milliseconds ago. The last packet sent successfully to the server was 7.210.431 milliseconds ago. 
    at com.mysql.jdbc.PreparedStatement.executeBatchedInserts(PreparedStatement.java:1836) 
    at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1456) 
    at com.mysql.jdbc.CallableStatement.executeBatch(CallableStatement.java:2499) 
    at main.LockTest.main(LockTest.java:26) 
Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure 

The last packet successfully received from the server was 7.260.436 milliseconds ago. The last packet sent successfully to the server was 7.210.431 milliseconds ago. 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) 
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) 
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513) 
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) 
    at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1121) 
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3670) 
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3559) 
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4110) 
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2570) 
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2731) 
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2815) 
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155) 
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2458) 
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2375) 
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2359) 
    at com.mysql.jdbc.PreparedStatement.executeBatchedInserts(PreparedStatement.java:1792) 
    ... 3 more 
Caused by: java.net.SocketTimeoutException: Read timed out 
    at java.net.SocketInputStream.socketRead0(Native Method) 
    at java.net.SocketInputStream.read(SocketInputStream.java:129) 
    at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:114) 
    at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:161) 
    at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:189) 
    at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3116) 
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3570) 
    ... 13 more 

完整的Java程序:

package main; 

import java.sql.Connection; 
import java.sql.DriverManager; 
import java.sql.PreparedStatement; 
import java.sql.SQLException; 
import java.util.Date; 

public class LockTest { 

    private static final String CONNECTION_PATTERN = "jdbc:mysql://%s/?user=%s&password=%s" 
      + "&autoReconnect=true&rewriteBatchedStatements=true&characterEncoding=utf8"; 
    private static final String QUERY = "INSERT INTO test.lock_test (random_text) VALUES (?)"; 

    public static void main(String[] args) throws SQLException, InterruptedException { 
     Connection con = DriverManager.getConnection(String.format(CONNECTION_PATTERN, "host", "user", "pw")); 
     PreparedStatement ps = con.prepareCall(QUERY); 
     int i = 0; 
     while (true) { 
      i++; 
      ps.setString(1, Long.toString(System.currentTimeMillis())); 
      ps.addBatch(); 
      if (i % 10 == 0) { 
       ps.executeBatch(); 
       System.out.println(new Date() + ": inserting 10 rows"); 
      } 
      Thread.sleep(5000); 
     } 
    } 
} 

回答

3

最後,我發現了真正的原因。 MySQL的wait_timeout與問題無關 - 只要語句正在運行(不管多長時間),數據庫會話不會處於SLEEP狀態,因此MySQL永遠不會關閉會話。

原因似乎是Linux操作系統(Debian 6或7),它在閒置2小時後關閉tcp連接(具體機制對我來說是未知的,也許有人可以詳細闡述這一點?)。

爲了避免描述的超時,需要發送更頻繁的tcp keep alive數據包。要做到這一點,必須減少​​(我把它從7,200減少到60)。 cat 60 > /proc/sys/net/ipv4/tcp_keepalive_time暫時這樣做;要在系統重啓後保留它,必須在/etc/sysctl.conf中設置net.ipv4.tcp_keepalive_time=60

+0

我不能使用'vi'來更改我的保存文件'tcp_keepalive_time「E667:Fsynch失敗的內容,我必須這樣做:'root @ sd-53310:/ home/ocatelin#echo 60 | sudo dd =/proc/sys/net/ipv4/tcp_keepalive_time 0 + 1註冊lus 0 + 1註冊表編輯 3字節(3 B)copiés,2,4948e-05 s,120 kB/s root @ sd -53310:/ home/ocatelin#cat/proc/sys/net/ipv4/tcp_keepalive_time 60' – Stephane

0

我只是搜索同一個問題。我想我的答案可以幫助你。

  1. 查看例外

從服務器成功接收最後一個包是7.260.436毫秒以前。

這顯然意味着,2,如果您的應用程序將引發異常,因爲WAIT_TIMEOUT太小7.260.436毫秒< WAIT_TIMEOUT設置爲28800

。該異常顯示這樣的:

 
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 46,208,149 milliseconds ago. The last packet sent successfully to the server was 46,208,150 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem. 
; SQL []; The last packet successfully received from the server was 46,208,149 milliseconds ago. 
The last packet sent successfully to the server was 46,208,150 milliseconds ago. 
is longer than the server configured value of 'wait_timeout'. 
You should consider either expiring and/or testing connection validity before use in your application, 
increasing the server configured values for client timeouts, 
or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.; 

這意味着46208150毫秒前> WAIT_TIMEOUT

  • 回答!!!
  • 要注意以下幾個關鍵詞「通信鏈路故障」,這主要是關於MySQL變量:net_write_timeout或net_read_timeout

     
    com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure 
    

    net_write_timeout和net_read_timeout的默認值是小,你可以執行

     mysql> show variables like '%timeout%';
    來查找結果。

    相關問題