在多线程Spring应用中避免MySQL死锁

Ham*_*med 7 java mysql spring multithreading

场景很简单。
我有一个包含两个表的大型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    |    ...
   ...
Run Code Online (Sandbox Code Playgroud)
  • 在table1上只有id一个主键。该表包含大约1200万个条目。
  • 在table2 id_srcid_trg上都是主键,并且都在table1上具有外键约束,id并且还DELETE ON CASCADE启用了该选项。该表包含大约1.1亿个条目。

好的,现在我要做的只是创建id要从表1中删除的s 列表,然后执行一个简单的DELETE FROM table1 WHERE id IN (<the list of ids>);

正如您可能已经猜到的那样,后一个过程也会从table2中删除相应的id。到目前为止,还不错,但是问题是,当我在多线程环境中运行它时,我得到了很多Deadlocks

一些注意事项:

  • 目前没有其他进程在同时运行(暂时)
  • 我希望这个速度很快!我大约有24个线程(如果这确实对答案有所帮助)
  • 我已经尝试了几乎所有的事务隔离级别(TRANSACTION_NONE除外)Java sql连接事务隔离
  • 订购/排序ID我认为这无济于事!
  • 我已经尝试过SELECT ... FOR UPDATE,但是一个简单的过程DELETE可能需要30秒!(因此没有使用它的用途):

    DELETE FROM table1 
    WHERE id IN ( 
        SELECT id FROM (
            SELECT * FROM table1 
            WHERE id='some_id' 
            FOR UPDATE) AS x);  
    
    Run Code Online (Sandbox Code Playgroud)

我怎样才能解决这个问题?

我将不胜感激,在此先感谢:)

编辑:

  1. 使用InnoDB引擎
  2. 在单个线程上,此过程将花费十几个小时,甚至可能需要一整天,但我的目标是几个小时!
  3. 我已经在使用连接池管理器: java.util.concurrent
  4. 有关双嵌套SELECTs的说明,请参阅MySQL无法在FROM子句中指定目标表进行更新
  5. 要从数据库中删除的列表可能总共包含几百万个条目,分为200个块
  6. FOR UPDATE子句是我听说它锁定一行而不是锁定整个表
  7. 该应用程序使用Spring的batchUpdate(String sqlQuery)方法,从而自动管理事务
  8. 所有ID均已启用索引,且ID最多可包含50个字符!
  9. DELETE ON CASCADEon id_srcid_trg(分别单独)将意味着对table1的每次删除id=x都会导致对table2 id_src=xid_trg=x
  10. 一些要求的代码:

    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);
           }
    }
    
    Run Code Online (Sandbox Code Playgroud)

并且myJdbcTemplate只是一个扩展的抽象类JdbcDaoSupport

Zaf*_*lik 3

首先,您在其中传递 ids 的第一个简单删除查询,如果您将 ids 传递到像 1000 这样的限制(子表中的总行数也应该接近但不会太多,比如 10,000 等),则不应产生问题。 ),但如果你传递了 50,000 或更多,那么它可能会产生锁定问题。

为了避免死锁,您可以按照以下方法来处理此问题(假设批量删除不会成为生产系统的一部分)-

步骤1:通过选择查询获取所有id并保留在光标中。

Step2:现在将存储过程中cursor中存储的这些id一一删除。

注意:要检查为什么删除会获取锁,我们必须检查几项内容,例如您传递了多少个 id、在数据库级别设置的事务级别是什么、my.cnf 中的 Mysql 配置设置是什么等...