使用 innoDB 存储引擎在 mysql 中获取组合密钥(年份/数字)

Obtaining a combined key (year/number) in mysql with innoDB storage engine

提问人:Alex 提问时间:9/26/2023 更新时间:9/28/2023 访问量:42

问:

我想在innoDB表中创建一个组合键,该表已经有一个“ID”列,该列具有自动递增值,用作主键。

假设我们在 “test_db” 中有以下 “test_table”:

enter image description here

我为“test_table”触发了“inc year_num”:

SET New.num_field := IFNULL(
  ( SELECT  MAX(num_field)+1
        FROM  test_table
        WHERE  year_field = New.year_field ), 1)

这个触发器(“插入之前”)似乎效果很好:

enter image description here

然而 il 让我怀疑:在多次同时插入的情况下,即使具有不同的“ID”,它也能生成相同的组合键(年份/数字)吗? 如果是这样,我该如何解决问题?

另一个问题:考虑到在多用户环境中使用,使用此触发器会减慢表速度吗?

MySQL 触发 innoDB 自动增量

评论

0赞 Rob Eyre 9/26/2023
我没有预见到问题,但为了保证(和性能提升),您可以在(year_field, num_field)
0赞 Alex 9/26/2023
@RobEyre感谢您的建议。我做到了。所以你认为没有关于可能的错误的问题吗?
1赞 user1191247 9/26/2023
根据这个(旧的)问答,“这受竞争条件的影响”。如果添加 RobEyre 建议的唯一密钥,则可以检测到重复密钥错误并重试。

答:

0赞 Rick James 9/26/2023 #1

要处理多个线程,请使用事务:

START TRANSACTION;
get the new num_field
put that value into the table
etc
COMMIT;

似乎没有必要有;只需拥有.idPRIMARY KEY(year_field, num_field)

另一个说明:将在更高版本的 MySQL 中删除。(它在 8.0.17 中已“弃用”。有一些简单的解决方法。ZEROFILL

除了默认值之外,您几乎永远不需要隔离级别,因此请忽略该课程(暂时)。

一个事务可能会也可能不会等待另一个事务完成。单个被锁定;如果没有冲突的锁,则两个事务可以并行进行。

TRIGGER并且是正交特征。例如,触发器可以包含所需的所有 SQL 语句,也可以调用封装这些语句的过程。PROCEDURE

评论

0赞 Alex 9/27/2023
谢谢。我刚刚进入MySQL,对事务不熟悉。昨天我稍微研究了这个主题,我明白了隔离有 4 个级别。您能解释一下我的情况的正确隔离级别是多少以及如何设置它吗?如果我理解正确,我需要每笔交易在上一笔交易完成之前才开始。我需要更改分隔符吗?如果/否则,回滚?如果您能发布正确的代码,我将不胜感激。最后一件事:代码是直接进入触发器,还是我需要创建一个包含调用它的触发器的存储过程?
0赞 Rick James 9/28/2023
@Alex - 我添加到我的答案中。
0赞 Alex 9/30/2023
对不起@RickJames我向你保证,这是我最后一次发消息。如果两个同时发生的事务(T1 和 T2)需要插入一行,则每个事务都将搜索最大值“num_field”,其中“year_field”的值等于要插入的值。想象一下,要插入的“year_field”的值是“2023”,而年份等于“2023”的“num_field”的最大值是“5”,那么两者都不可能通过在“year_field”中写“2023”和在“num_field”中写“6”来获取这个值,从而产生违反组合字段唯一性规则的错误?
0赞 Rick James 9/30/2023
@Alex - 交易内部的任何内容都可能需要在末尾添加一个子句。这尤其适用于“获取新num_field”-->。这会提醒其他事务正在发生某些事情。SELECTsFOR UPDATESELECT MAX(num_field)+1 FROM tbl FOR UPDATEnum_field