【问题标题】:Handle parallel identical insertion in MYSQL table处理 MYSQL 表中的并行相同插入
【发布时间】:2021-07-19 17:12:40
【问题描述】:

我们正在开发一个由 MySQL 数据库支持的 Spring Boot 应用程序,我有一个找不到答案的问题,尽管这里有几个类似的问题。 如果有多个并行相同的插入语句运行,问题是在数据库中只保留一个插入。

例如,如果我的实体具有(Id, parent_id and status) 属性,我们不希望允许多个具有same parent_id and status = 1 的实体 - 即使它们以这种形式来自客户端,这会向我们的端点发送垃圾邮件相同的要求。我知道其他数据库供应商允许使用称为 conditional 唯一键的东西,即像 (parent_id, status = 1) 这样的唯一键,但 MySql 不允许。有没有办法通过索引甚至存储过程来实现这一点?

类似问题(供参考):

  1. conditional unique constraint
  2. SQL can I have a "conditionally unique" constraint on a table?
  3. https://dba.stackexchange.com/questions/7443/function-based-index-as-a-conditional-unique-key
  4. Can I conditionally enforce a uniqueness constraint?
  5. https://dba.stackexchange.com/questions/43/how-to-create-a-conditional-index-in-mysql

【问题讨论】:

    标签: mysql sql multithreading stored-procedures sql-insert


    【解决方案1】:

    你可以用一个小技巧来做到这一点。

    包含一个带有 IF() 函数的虚拟永久列。 如果状态 = 1,这将存储 parent_id,否则为 NULL 所以你可以在这个字段上创建一个 UNIQUE KEY

    CREATE TABLE `cond` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `parent_id` int(11) DEFAULT NULL,
      `status` int(11) DEFAULT NULL,
      `cond_status` int(11) GENERATED ALWAYS AS (if(`status` = 1,`parent_id`,NULL)) STORED,
      PRIMARY KEY (`id`),
      UNIQUE KEY `idx_cond_status` (`cond_status`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    

    样本

    MariaDB [Bernd]> select * from cond;
    Empty set (0.10 sec)
    
    MariaDB [Bernd]> INSERT INTO cond (parent_id,`status`) VALUES(1234, 0);
    Query OK, 1 row affected (0.05 sec)
    
    MariaDB [Bernd]> INSERT INTO cond (parent_id,`status`) VALUES(1235, 0);
    Query OK, 1 row affected (0.01 sec)
    
    MariaDB [Bernd]> INSERT INTO cond (parent_id,`status`) VALUES(1235, 2);
    Query OK, 1 row affected (0.00 sec)
    
    MariaDB [Bernd]> INSERT INTO cond (parent_id,`status`) VALUES(1235, 2);
    Query OK, 1 row affected (0.02 sec)
    
    MariaDB [Bernd]> INSERT INTO cond (parent_id,`status`) VALUES(1235, 1);
    Query OK, 1 row affected (0.22 sec)
    
    MariaDB [Bernd]> INSERT INTO cond (parent_id,`status`) VALUES(1235, 4);
    Query OK, 1 row affected (0.01 sec)
    
    MariaDB [Bernd]> INSERT INTO cond (parent_id,`status`) VALUES(1235, 1);
    ERROR 1062 (23000): Duplicate entry '1235' for key 'idx_cond_status'
    MariaDB [Bernd]> 
    
    MariaDB [Bernd]> select * from cond;
    +----+-----------+--------+-------------+
    | id | parent_id | status | cond_status |
    +----+-----------+--------+-------------+
    |  1 |      1234 |      0 |        NULL |
    |  2 |      1235 |      0 |        NULL |
    |  3 |      1235 |      2 |        NULL |
    |  4 |      1235 |      2 |        NULL |
    |  5 |      1235 |      1 |        1235 |
    |  6 |      1235 |      4 |        NULL |
    +----+-----------+--------+-------------+
    6 rows in set (0.08 sec)
    
    MariaDB [Bernd]> 
    

    注意:创建表适用于 MariaDB,但在 MySQL 中几乎相同

    【讨论】:

    • 这看起来是一个很好的解决方案,但确实看起来有点“hacky”。虽然有效负载并没有真正增加,但我真正关心的是这种解决方案如何在具有大量列的生产数据库中扩展。您是否知道使用存储过程的解决方案,所以“状态”保持不变,我们只改变“行为”?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    • 2017-11-30
    • 1970-01-01
    • 2017-04-01
    • 1970-01-01
    相关资源
    最近更新 更多