用 MySQL 个事务强制死锁
Forcing a deadlock with MySQL transactions
我正在尝试为演示和 Oracle 强制造成死锁,它似乎有效,但在 MySQL 中,出于某种原因,查询通过...
为什么我的例子没有导致死锁?
@EventListener(ApplicationReadyEvent.class)
public void executeStartupTask() throws Exception {
LOGGER.info("Connecting to MySQL via: " + databaseUrl);
LOGGER.info("Seeding database...");
seedDatabase();
Thread providerResetThread = new Thread(() -> {
try {
Connection connection = getMySQLDataSource().getConnection();
connection.setAutoCommit(false);
// 1st device model always GMETER
updateDeviceModel("E000000001", "GMETER", connection);
LOGGER.info("Sleep for " + genericTimeoutLength + " milliseconds");
Thread.sleep(Integer.parseInt(genericTimeoutLength));
// 2st device model always EMETER
updateDeviceModel("E000000002", "EMETER", connection);
connection.commit();
}
catch(Exception e) {
e.printStackTrace();
}
});
providerResetThread.start();
// Resetting the model type
try {
Connection connection = getMySQLDataSource().getConnection();
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
updateDeviceModel("E000000002", "NONE", connection);
updateDeviceModel("E000000001", "NONE", connection);
connection.commit();
}
catch(Exception e) {
e.printStackTrace();
}
providerResetThread.join();
LOGGER.info("EXECUTION SUCCESSFUL!");
}
public void updateDeviceModel(String deviceId, String model, Connection connection) throws SQLException {
LOGGER.info("Updating model for device " + deviceId);
PreparedStatement statement = connection.prepareStatement("UPDATE device_entity SET model = '" + model + "' WHERE deviceId = '" + deviceId + "'");
statement.execute();
}
日志如下:
2022-02-08 09:59:32,950 INFO method: [Thread-2] interview.InterviewApplication (InterviewApplication.java:126) - Updating model for device E000000001
2022-02-08 09:59:32,950 INFO method: [main] interview.InterviewApplication (InterviewApplication.java:126) - Updating model for device E000000002
2022-02-08 09:59:32,955 INFO method: [Thread-2] interview.InterviewApplication (InterviewApplication.java:56) - Sleep for 5000 milliseconds
2022-02-08 09:59:37,965 INFO method: [Thread-2] interview.InterviewApplication (InterviewApplication.java:126) - Updating model for device E000000002
2022-02-08 09:59:37,975 INFO method: [main] interview.InterviewApplication (InterviewApplication.java:126) - Updating model for device E000000001
2022-02-08 09:59:43,046 INFO method: [main] interview.InterviewApplication (InterviewApplication.java:92) - EXECUTION SUCCESSFUL!
使用您指定的存储库中的代码,我得到了预期的行为,如下图所示:
我用过 MySQL 5.6(在最新版本中也是如此)。
此外,存储库中的代码与您在此处发布的代码不同。使用您在此处发布的代码时,不会发生这种行为。
仔细观察的话:
updateDeviceModel("E000000001", "GMETER", connection);
updateDeviceModel("E000000001", "NONE", connection);
正在使用数据库中不存在的 ID。
您的种子数据库使用 ID“E1”和“E2”。
实际上你没有更新任何东西。
将您的执行语句更改为
final int update = statement.executeUpdate();
您可以看到有多少记录受到影响(零)。
所以不会出现死锁。
在您的存储库代码中,如果您执行相同的操作,您将看到每次调用都更新了一条记录。
我正在尝试为演示和 Oracle 强制造成死锁,它似乎有效,但在 MySQL 中,出于某种原因,查询通过...
为什么我的例子没有导致死锁?
@EventListener(ApplicationReadyEvent.class)
public void executeStartupTask() throws Exception {
LOGGER.info("Connecting to MySQL via: " + databaseUrl);
LOGGER.info("Seeding database...");
seedDatabase();
Thread providerResetThread = new Thread(() -> {
try {
Connection connection = getMySQLDataSource().getConnection();
connection.setAutoCommit(false);
// 1st device model always GMETER
updateDeviceModel("E000000001", "GMETER", connection);
LOGGER.info("Sleep for " + genericTimeoutLength + " milliseconds");
Thread.sleep(Integer.parseInt(genericTimeoutLength));
// 2st device model always EMETER
updateDeviceModel("E000000002", "EMETER", connection);
connection.commit();
}
catch(Exception e) {
e.printStackTrace();
}
});
providerResetThread.start();
// Resetting the model type
try {
Connection connection = getMySQLDataSource().getConnection();
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
updateDeviceModel("E000000002", "NONE", connection);
updateDeviceModel("E000000001", "NONE", connection);
connection.commit();
}
catch(Exception e) {
e.printStackTrace();
}
providerResetThread.join();
LOGGER.info("EXECUTION SUCCESSFUL!");
}
public void updateDeviceModel(String deviceId, String model, Connection connection) throws SQLException {
LOGGER.info("Updating model for device " + deviceId);
PreparedStatement statement = connection.prepareStatement("UPDATE device_entity SET model = '" + model + "' WHERE deviceId = '" + deviceId + "'");
statement.execute();
}
日志如下:
2022-02-08 09:59:32,950 INFO method: [Thread-2] interview.InterviewApplication (InterviewApplication.java:126) - Updating model for device E000000001
2022-02-08 09:59:32,950 INFO method: [main] interview.InterviewApplication (InterviewApplication.java:126) - Updating model for device E000000002
2022-02-08 09:59:32,955 INFO method: [Thread-2] interview.InterviewApplication (InterviewApplication.java:56) - Sleep for 5000 milliseconds
2022-02-08 09:59:37,965 INFO method: [Thread-2] interview.InterviewApplication (InterviewApplication.java:126) - Updating model for device E000000002
2022-02-08 09:59:37,975 INFO method: [main] interview.InterviewApplication (InterviewApplication.java:126) - Updating model for device E000000001
2022-02-08 09:59:43,046 INFO method: [main] interview.InterviewApplication (InterviewApplication.java:92) - EXECUTION SUCCESSFUL!
使用您指定的存储库中的代码,我得到了预期的行为,如下图所示:
我用过 MySQL 5.6(在最新版本中也是如此)。
此外,存储库中的代码与您在此处发布的代码不同。使用您在此处发布的代码时,不会发生这种行为。
仔细观察的话:
updateDeviceModel("E000000001", "GMETER", connection);
updateDeviceModel("E000000001", "NONE", connection);
正在使用数据库中不存在的 ID。
您的种子数据库使用 ID“E1”和“E2”。 实际上你没有更新任何东西。
将您的执行语句更改为
final int update = statement.executeUpdate();
您可以看到有多少记录受到影响(零)。
所以不会出现死锁。
在您的存储库代码中,如果您执行相同的操作,您将看到每次调用都更新了一条记录。