PSQL JDBC事务导致死锁

时间:2013-10-11 00:06:52

标签: java postgresql jdbc transactions deadlock

问题:
更新: 为什么在table A中使用外键约束向table B插入一行,然后更新table B中的行,导致事务中table A引用中的插入行导致死锁?

情境:

  • reservation.time_slot_idtime_slot.id有一个外键约束。
  • 进行预订时,运行以下SQL:

    BEGIN TRANSACTION   
    INSERT INTO reservations (..., time_slot_id) VALUES (..., $timeSlotID)
    UPDATE reservations SET num_reservations = 5 WHERE id = $timeSlotID
    COMMIT
    
  • 我正在使用大约100个并发用户对我的服务器进行负载测试,每个用户都为同一时段预留(每个用户$timeSlotID相同)。

  • 如果我不使用交易(删除cn.setAutoCommit(false);cn.commit()等),则不会出现此问题。


环境:

  • PostgreSQL 9.2.4
  • Tomcat v7.0
  • JDK 1.7.0_40
  • 公地DBCP-1.4.jar
  • 公地池-1.6.jar
  • 的PostgreSQL-9.2-1002.jdbc4.jar


代码:

// endpoint start
// there are some other SELECT ... LEFT JOIN ... WHERE ... queries up here but they don't seem to be related
...

// create a reservation in the time slot then increment the count

cn.setAutoCommit(false);
try
{
    st = cn.prepareStatement("INSERT INTO reservation (time_slot_id, email, created_timestamp) VALUES (?, ?, ?)");
    st.setInt   (1, timeSlotID); // timeSlotID is the same for every user
    st.setString(2, email);
    st.setInt   (3, currentTimestamp);

    st.executeUpdate();
    st.close();

    st = cn.prepareStatement("UPDATE time_slot SET num_reservations = 5 WHERE id = ?"); // set to 5 instead of incrementing for testing
    st.setInt(1, timeSlotID); // timeSlotID is the same for every user

    st.executeUpdate();
    st.close();

    cn.commit();
}
catch (SQLException e)
{
    cn.rollback();
    ...
}
finally
{
    cn.setAutoCommit(true);
}

...
// endpoint end


PSQL错误:

ERROR:  deadlock detected
DETAIL:  Process 27776 waits for ExclusiveLock on tuple (2,179) of relation 49817 of database 49772; blocked by process 27795.
    Process 27795 waits for ShareLock on transaction 3962; blocked by process 27777.
    Process 27777 waits for ExclusiveLock on tuple (2,179) of relation 49817 of database 49772; blocked by process 27776.
    Process 27776: UPDATE time_slot SET num_reservations = 5 WHERE id = $1
    Process 27795: UPDATE time_slot SET num_reservations = 5 WHERE id = $1
    Process 27777: UPDATE time_slot SET num_reservations = 5 WHERE id = $1
HINT:  See server log for query details.
STATEMENT:  UPDATE time_slot SET num_reservations = 5 WHERE id = $1

1 个答案:

答案 0 :(得分:0)

虽然我仍然不理解,但我补充道:

SELECT * FROM time_slot WHERE id = ? FOR UPDATE

作为交易中的第一个陈述。这似乎解决了我的问题,因为我不再陷入僵局。

我仍然希望有人给出正确答案并向我解释。