2

私はいくつかの奇妙な行動に遭遇しています。準備済みステートメントとバッチ挿入を使用して、MySQL テーブルにデータを永続的に書き込む Java プログラムがあります。他のプロセスLOCK TABLES t WRITEが同じテーブルに対して を発行し、その後すぐに (数分後に) リリースした場合、Java プログラムは期待どおりに続行します。ただし、ロックが長時間 (30 分以上) 続くと、Java プログラムは接続を失い、続行しません。2 時間後、通信リンク障害で失敗します。テーブル ロック中に保留中の挿入ステートメントは、ロックが解放された後に実行されますが、接続はその後失われます。

詳細は次のとおりです。

  • MySQL 5.0.51a と 5.1.66 の両方で発生します。
  • 最新の JDBC ドライバー mysql-connector-5.1.25-bin.jar を使用しています
  • wait_timeout28800 (8 時間) に設定されています。
  • 通信リンク障害のスタック トレースと Java プログラムを以下に示します。

ここで何が起こっているか知っている人はいますか?設定/増加する必要があるタイムアウトはありますか?


2 時間後にスローされる例外:

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);
        }
    }
}
4

2 に答える 2

3

やっと本当の理由がわかった。MySQLwait_timeoutは問題とは何の関係もありません。ステートメントが実行されている限り (時間の長さに関係なく)、データベース セッションはその状態SLEEPにないため、MySQL によってセッションが閉じられることはありません。

その理由は Linux オペレーティング システム (Debian 6 または 7) にあるようです。これは 2 時間アイドル状態になると tcp 接続を閉じます (具体的なメカニズムは私には不明です。誰かがこれについて詳しく説明できるでしょうか?)。

説明されているタイムアウトを回避するには、より頻繁に tcp キープアライブ パケットを送信する必要があります。そのためには、1 つ減らす必要があります/proc/sys/net/ipv4/tcp_keepalive_time(7,200 から 60 に減らしました)。cat 60 > /proc/sys/net/ipv4/tcp_keepalive_timeこれを一時的に行います。システムの再起動後も保持するには、 で設定net.ipv4.tcp_keepalive_time=60する必要があり/etc/sysctl.confます。

于 2014-07-13T16:18:26.707 に答える