Java 在多线程Spring应用程序中避免MySQL死锁
情况很简单。Java 在多线程Spring应用程序中避免MySQL死锁,java,mysql,multithreading,spring,Java,Mysql,Multithreading,Spring,情况很简单。 我有一个很大的MySQL数据库,其中包含两个表: -- Table 1 id (primary key) | some other columns without constraints -----------------+-------------------------------------- 1 | foo 2 | bar 3 | foobar
我有一个很大的MySQL数据库,其中包含两个表:
-- Table 1
id (primary key) | some other columns without constraints
-----------------+--------------------------------------
1 | foo
2 | bar
3 | foobar
... | ...
-- Table 2
id_src | id_trg | some other columns without constraints
-------+--------+---------------------------------------
1 | 2 | ...
1 | 3 | ...
2 | 1 | ...
2 | 3 | ...
2 | 5 | ...
...
- 在表1中,只有
是主键。此表包含大约1200万条条目id
- 在表2中,
和id\u src
都是主键,在表1的id\u trg
上都有外键约束,并且它们还启用了id
选项。该表包含约1.1亿个条目DELETE On CASCADE
id
s列表,然后执行一个简单的DELETE from table1,其中id IN()代码>
您可能已经猜到,后一个过程也会从表2中删除相应的id。到目前为止还不错,但问题是当我在多线程环境上运行它时,会出现许多死锁
一些注意事项:
- 没有其他进程同时运行,也不会(暂时)运行
- 我想快点!我有大约24个线程(如果这对答案有任何影响的话)
- 我已经尝试了几乎所有的事务隔离级别(除了transaction\u NONE)
- 订购/整理id我认为不会有帮助李>
- 我已经尝试了
选择。。。对于更新
,但是一个简单的删除
最多需要30秒!(因此,使用它是没有用的):
我怎样才能解决这个问题
我将感谢任何帮助和提前感谢:)
编辑:
使用InnoDB引擎
在一个线程上,这个过程可能需要十几个小时甚至一整天,但我的目标是几个小时李>
我已经在使用连接池管理器:java.util.concurrent
有关双嵌套选择的说明,请参阅
要从数据库中删除的列表可能总共包含数百万个条目,这些条目被分为200个块
UPDATE的子句是,我听说它锁定一行,而不是锁定整个表
该应用程序使用Spring的batchUpdate(String sqlQuery)方法,因此事务是自动管理的
所有ID都启用了索引,并且ID是唯一的,最多50个字符李>
id\u src
和id\u trg
(分别)上的级联删除将意味着表1id=x
上的每次删除将导致表2id\u src=x
和id\u trg=x
按要求提供一些代码:
public void write(List data){
try{
Arraylist idsToDelete = getIdsToDelete();
String query = "DELETE FROM table1 WHERE id IN ("+ idsToDelete + " )";
mysqlJdbcTemplate.getJdbcTemplate().batchUpdate(query);
} catch (Exception e) {
LOG.error(e);
}
}
而myJdbcTemplate
只是一个抽象类,它扩展了JdbcDaoSupport
首先,在传递ID的第一个简单删除查询中,如果将ID传递到1000这样的限制(子表中的行总数也应该接近但不超过10000等),应该不会产生问题,但如果你通过了50000或更多,那么它可能会产生锁定问题
为了避免死锁,您可以按照下面的方法来处理这个问题(假设批量删除不是生产系统的一部分)-
步骤1:通过选择查询获取所有ID并保留在游标中
步骤2:现在逐个删除存储过程中游标中存储的这些ID
注意:要检查删除为什么会获得锁,我们必须检查以下几项内容,如您传递的ID数量、在DB级别设置的事务级别、my.cnf中的Mysql配置设置等。删除多个(>10000)父记录可能是危险的,每个父记录都有通过级联删除的子记录,由于一次删除的记录最多,因此锁冲突导致死锁或回滚的可能性最大
如果可以接受(意味着您可以直接与数据库建立JDBC连接),您应该(此处不涉及线程):
- 计算要删除的ID列表
- 每提交100或1000条记录,按批删除它们(先验值介于10和100之间)
由于更重的工作应该在数据库部分,我毫不怀疑线程在这里会有所帮助。如果您想尝试,我建议您:
- 一个线程(具有专用的数据库连接)计算要删除的ID列表,并与之保持同步队列
- 少量线程(4个或8个),每个线程都有自己的数据库连接:
- 使用准备好的
从表1中批量删除,其中id=?
- 从队列中获取ID并准备批
- 每10或100条记录向数据库发送一批
- 每10或100批进行一次提交
我无法想象整个过程可能需要几分钟以上
经过一些其他的阅读,看起来我已经习惯了旧的系统,我的数据非常保守。好的,这就是我所做的,它实际上可能不会避免死锁,但它是我当时唯一的选择
这个解决方案实际上是一种使用Spring处理MySQL死锁的方法
捕获并重试死锁:
public void write(List data){
try{
Arraylist idsToDelete = getIdsToDelete();
String query = "DELETE FROM table1 WHERE id IN ("+ idsToDelete + " )";
try {
mysqlJdbcTemplate.getJdbcTemplate().batchUpdate(query);
} catch (org.springframework.dao.DeadlockLoserDataAccessException e) {
LOG.info("Caught DEADLOCK : " + e);
retryDeadlock(query); // Retry them!
}
} catch (Exception e) {
LOG.error(e);
}
}
public void retryDeadlock(final String[] sqlQuery) {
RetryTemplate template = new RetryTemplate();
TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
policy.setTimeout(30000L);
template.setRetryPolicy(policy);
try {
template.execute(new RetryCallback<int[]>() {
public int[] doWithRetry(RetryContext context) {
LOG.info("Retrying DEADLOCK " + context);
return mysqlJdbcTemplate.getJdbcTemplate().batchUpdate(sqlQuery);
}
});
} catch (Exception e1) {
e1.printStackTrace();
}
}
公共无效写入(列表数据){
试一试{
Arraylist idsToDelete=getIdsToDelete();
String query=“从表1中删除,其中id位于(“+idsToDelete+”);
试一试{
mysqlJdbcTemplate.getJdbcTemplate().batchUpdate(查询);
}catch(org.springframework.dao.deadlocklesserDataAccessException){
LOG.info(“捕捉到的死锁:+e”);
retryDeadlock(query);//重试!
}
}捕获(例外e){
日志错误(e);
}
}
public void retryDeadlock(最终字符串[]sqlQuery){
RetryTemplate=新RetryTemplate();
TimeoutRetryPolicy policy=新的TimeoutRetryPolicy();
策略设置超时(30000L);
template.setRetryPo
public void write(List data){
try{
Arraylist idsToDelete = getIdsToDelete();
String query = "DELETE FROM table1 WHERE id IN ("+ idsToDelete + " )";
try {
mysqlJdbcTemplate.getJdbcTemplate().batchUpdate(query);
} catch (org.springframework.dao.DeadlockLoserDataAccessException e) {
LOG.info("Caught DEADLOCK : " + e);
retryDeadlock(query); // Retry them!
}
} catch (Exception e) {
LOG.error(e);
}
}
public void retryDeadlock(final String[] sqlQuery) {
RetryTemplate template = new RetryTemplate();
TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
policy.setTimeout(30000L);
template.setRetryPolicy(policy);
try {
template.execute(new RetryCallback<int[]>() {
public int[] doWithRetry(RetryContext context) {
LOG.info("Retrying DEADLOCK " + context);
return mysqlJdbcTemplate.getJdbcTemplate().batchUpdate(sqlQuery);
}
});
} catch (Exception e1) {
e1.printStackTrace();
}
}
Step1: Delete id_trg from child table;
Step2: Delete id_src from child table;
Step3: Delete id from parent table;