S_a_k_Uの日記みたいなDB

~サクゥーと呼ばないで~

デッドロックとタイムアウト

【パターン1】
同じスレッドで、2つのConnectionオブジェクトによりデッドロックを起こそうとすると、タイムアウトのエラーになる。

KFPA11770-I ROW [ HOGE_T RDDATA10 ] currently in use,resource …


【パターン2】
別のスレッドで、2つのConnectionオブジェクトによりデッドロックを起こそうとすると、デッドロックのエラーになる。

KFPA11911-E Deadlock occurred on ROW [ HOGE_T RDDATA10 ] resource …


DBサーバからしたら、2つのコネクションからデッドロックSQLが要求されただけなのに、どうやってエラーを判別しょんじゃろ?
クライアント側のスレッドが同じかどうか判る仕組みなんじゃろか?
例えばクライアントID(スレッドIDが含まれる?)みたいなのが、JDBC経由でやり取りしょんじゃねーかと思ってみたり。


【パターン1】
同じスレッドで、2つのConnectionオブジェクトによりデッドロックを起こそうとするプログラム

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class DeadlockTest1 {
    
    private String URL = "jdbc:hitachi:hirdb://DB=HiRDB,DBID=22200,DBHOST=xxx.xxx.xxx.xxx";    

    public static void main(String[] args) {
        new DeadlockTest1().test();
    }
    
    public void test() {
        
        Connection con1 = null;
        Connection con2 = null;
        
        try {
            
            Class.forName("JP.co.Hitachi.soft.HiRDB.JDBC.HiRDBDriver");
            PreparedStatement pstmt1 = null;
            PreparedStatement pstmt2 = null;
            String sql = null;
            int res = -1;
        
            // コネクション1を取得
            con1 = DriverManager.getConnection(URL, "HOGE", "HOGE");
            con1.setAutoCommit(false);
            
            // コネクション2を取得
            con2 = DriverManager.getConnection(URL, "HOGE", "HOGE");
            con2.setAutoCommit(false);

            // コネクション1でレコード1をUPDATE
            sql = "UPDATE HOGE_T SET COL_C='X' WHERE COL_A='A' AND COL_B='1'";
            pstmt1 = con1.prepareStatement(sql);
            res = pstmt1.executeUpdate();

            // コネクション2でレコード2をUPDATE
            sql = "UPDATE HOGE_T SET COL_C='X' WHERE COL_A='B' AND COL_B='2'";
            pstmt2 = con2.prepareStatement(sql);
            res = pstmt2.executeUpdate();

            // コネクション1でレコード2をUPDATE【ここでデッドロック(実際はタイムアウト)】
            sql = "UPDATE HOGE_T SET COL_C='X' WHERE COL_A='B' AND COL_B='2'";
            pstmt1 = con1.prepareStatement(sql);
            res = pstmt1.executeUpdate();
            
            // コネクション1をコミット
            con1.commit();                
            con1.close();

            // コネクション2でレコード1をUPDATE
            sql = "UPDATE HOGE_T SET COL_C='X' WHERE COL_A='A' AND COL_B='1'";
            pstmt2 = con2.prepareStatement(sql);
            res = pstmt2.executeUpdate();
            
            // コネクション2をコミット
            con2.commit();                
            con2.close();

        } catch (Throwable t) {
            t.printStackTrace();
        } finally {
            try {
                if((con1 != null) && (con1.isClosed() == false)) {
                    con1.close();
                }
                if((con2 != null) && (con2.isClosed() == false)) {
                    con2.close();
                }
            } catch(Throwable t) {
                t.printStackTrace();
            }
        }

    }
    
}


【パターン2】
別のスレッドで、2つのConnectionオブジェクトによりデッドロックを起こそうとするプログラム

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class DeadlockTest2 extends Thread implements Runnable {
    
    private String URL = "jdbc:hitachi:hirdb://DB=HiRDB,DBID=22200,DBHOST=xxx.xxx.xxx.xxx";    
    
    public DeadlockTest2() {
    }
    
    public static void main(String[] args) {
        new DeadlockTest2().test();
    }
    
    private void test() {
        try {
            Class.forName("JP.co.Hitachi.soft.HiRDB.JDBC.HiRDBDriver");
            new Test1().start();
            new Test2().start();
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
    
    private class Test1 extends Thread implements Runnable {
        
        public void run() {
            
            Connection con = null;

            try {
                
                // スレッド1のコネクションを取得
                con = DriverManager.getConnection(URL, "HOGE", "HOGE");
                con.setAutoCommit(false);
                
                PreparedStatement pstmt = null;
                String sql = null;
                int res = -1;
                
                // スレッド1でレコード1をUPDATE
                sql = "UPDATE HOGE_T SET COL_C='X' WHERE COL_A='A' AND COL_B='1'";
                pstmt = con.prepareStatement(sql);
                res = pstmt.executeUpdate();
                
                Thread.sleep(100);

                // スレッド1でレコード2をUPDATE【ここでデッドロック】
                sql = "UPDATE HOGE_T SET COL_C='X' WHERE COL_A='B' AND COL_B='2'";
                pstmt = con.prepareStatement(sql);
                res = pstmt.executeUpdate();

                con.commit();                
                con.close();

            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
               try {
                    if((con != null) && (con.isClosed() == false)) {
                        con.close();
                    }
                } catch(Throwable t) {
                    t.printStackTrace();
                }
            }
            
        }
        
    }
    
    private class Test2 extends Thread implements Runnable {
        
        public void run() {
             
            Connection con = null;
           
            try {
                
                // スレッド2のコネクションを取得
                con = DriverManager.getConnection(URL, "HOGE", "HOGE");
                con.setAutoCommit(false);
                
                PreparedStatement pstmt = null;
                String sql = null;
                int res = -1;

                // スレッド2でレコード2をUPDATE
                sql = "UPDATE HOGE_T SET COL_C='X' WHERE COL_A='B' AND COL_B='2'";
                pstmt = con.prepareStatement(sql);
                res = pstmt.executeUpdate();
                
                Thread.sleep(100);
                
                // スレッド2でレコード1をUPDATE
                sql = "UPDATE HOGE_T SET COL_C='X' WHERE COL_A='A' AND COL_B='1'";
                pstmt = con.prepareStatement(sql);
                res = pstmt.executeUpdate();

                con.commit();                
                con.close();
                
            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
               try {
                    if((con != null) && (con.isClosed() == false)) {
                        con.close();
                    }
                } catch(Throwable t) {
                    t.printStackTrace();
                }
            }
            
        }
        
    }
    
}