mysql auto\u increment不回滚

piah890a  于 2021-06-18  发布在  Mysql
关注(0)|答案(11)|浏览(444)

我使用mysql的自动增量字段和innodb来支持事务。我注意到当我回滚事务时,auto\u increment字段没有回滚?我发现它是这样设计的,但是有什么解决办法吗?

rlcwz9us

rlcwz9us1#

这样不行。考虑:
程序一,打开一个事务并插入一个表foo,该表有一个autoinc主键(任意地,我们说它的键值是557)。
程序2启动后,它打开一个事务并插入到表foo558中。
将两个插入程序编程到表栏中,表栏中有一列是foo的外键。所以现在558同时位于foo和bar。
程序二现在提交。
程序3启动并从表foo生成一个报告。558记录被打印出来了。
之后,程序一回滚。
数据库如何回收557值?它是否进入foo并减少所有其他大于557的主键?它是如何修复酒吧的?它是如何擦除558上打印的报表程序三次输出的?
出于同样的原因,oracle的序列号也独立于事务。
如果你能在固定的时间内解决这个问题,我相信你能在数据库领域赚很多钱。
现在,如果您有一个要求,即您的自动增量字段永远不会有间隙(例如,出于审计目的)。那么就不能回滚事务。相反,您需要在记录上有一个状态标志。在第一次插入时,记录的状态是“未完成”,然后开始事务,完成工作并将状态更新为“compete”(或任何您需要的)。当你提交时,记录是实时的。如果事务回滚,则不完整的记录仍在那里进行审核。这会给您带来许多其他的麻烦,但这是处理审计跟踪的一种方法。

v8wbuo2f

v8wbuo2f2#

为什么你在乎它是否被回滚?自动递增键字段不应该有任何意义,所以你真的不应该关心使用什么值。
如果您有要保存的信息,可能需要另一个非键列。

xuo3flqw

xuo3flqw3#

如果你设置 auto_increment1 在回滚或删除之后,在下一次插入时,mysql将看到 1 已使用,将改为获取 MAX() 值并加1。
这将确保如果删除了具有最后一个值的行(或者回滚了插入),那么它将被重用。
要将自动增量设置为1,请执行以下操作:

ALTER TABLE tbl auto_increment = 1

这并不像简单地继续下一个数字那样有效,因为 MAX() 可能会很昂贵,但是如果不经常删除/回滚,并且痴迷于重用最高值,那么这是一种现实的方法。
请注意,这并不能防止中间删除的记录出现间隙,或者在将auto\u increment设置回1之前发生另一次插入。

eqfvzcg8

eqfvzcg84#

解决方案:
让我们使用“tbl\u test”作为示例表,并假设字段“id”具有auto\u increment属性

CREATE TABLE tbl_test (
Id int NOT NULL AUTO_INCREMENT ,
Name varchar(255) NULL ,
PRIMARY KEY (`Id`)
)
;

假设表已经插入了上千行,并且您不想再使用自动递增;因为在回滚事务时,字段“id”总是在自动增量值中加上+1。为了避免你可能会这样做:
让我们从列“id”中删除自动增量值(这不会删除插入的行):

ALTER TABLE tbl_test MODIFY COLUMN Id  int(11) NOT NULL FIRST;

最后,我们创建一个before insert触发器来自动生成一个'id'值。但是使用这种方法不会影响您的id值,即使您回滚任何事务。

CREATE TRIGGER trg_tbl_test_1
BEFORE INSERT ON tbl_test
FOR EACH ROW
  BEGIN
    SET NEW.Id= COALESCE((SELECT MAX(Id) FROM tbl_test),0) + 1;
  END;

就这样!你完了!
不客气。

xtfmy6hx

xtfmy6hx5#

我有一个客户需要id在发票表上回滚,其中订单必须是连续的
我在mysql中的解决方案是删除自动增量并从表中提取最新的id,添加一个(+1),然后手动插入它。
如果表名为“tablea”,自动递增列为“id”

INSERT INTO TableA (Id, Col2, Col3, Col4, ...)
VALUES (
(SELECT Id FROM TableA t ORDER BY t.Id DESC LIMIT 1)+1,
Col2_Val, Col3_Val, Col4_Val, ...)
h7appiyu

h7appiyu6#

如果需要按数字顺序分配ID,并且不留间距,则不能使用自动递增列。您需要定义一个标准整数列,并使用一个存储过程来计算插入序列中的下一个数字,并在事务中插入记录。如果insert失败,那么下次调用该过程时,它将重新计算下一个id。
话虽如此,但依赖于ID的某种特定顺序而没有间隙是个坏主意。如果您需要保留顺序,您可能应该在insert时(也可能在更新时)为行添加时间戳。

mo49yndu

mo49yndu7#

INSERT INTO prueba(id) 
VALUES (
(SELECT IFNULL( MAX( id ) , 0 )+1 FROM prueba target))

如果表不包含值或零行
在选择时为错误mysql type update from添加目标

v6ylcynt

v6ylcynt8#

$masterConn = mysql_connect("localhost", "root", '');
mysql_select_db("sample", $masterConn);

for($i=1; $i<=10; $i++) {
    mysql_query("START TRANSACTION",$masterConn);
    $qry_insert = "INSERT INTO `customer` (id, `a`, `b`) VALUES (NULL, '$i', 'a')";
    mysql_query($qry_insert,$masterConn);
    if($i%2==1) mysql_query("COMMIT",$masterConn);
    else mysql_query("ROLLBACK",$masterConn);

    mysql_query("ALTER TABLE customer auto_increment = 1",$masterConn);

}

echo "Done";
44u64gxh

44u64gxh9#

对于这一具体困境(我也有过)的具体答案如下:
1) 创建一个表,为不同的文档(发票、收据、rma等)保存不同的计数器;为每个文档插入一条记录,并将初始计数器添加到0。
2) 在创建新文档之前,请执行以下操作(例如,对于发票):

UPDATE document_counters SET counter = LAST_INSERT_ID(counter + 1) where type = 'invoice'

3) 获取刚刚更新到的最后一个值,如下所示:

SELECT LAST_INSERT_ID()

或者使用php(或其他)mysql\u insert\u id()函数来获得相同的结果
4) 插入您的新记录以及您刚从数据库中获得的主id。这将覆盖当前的自动增量索引,并确保您的记录之间没有id间隔。
当然,这整件事需要 Package 在一个事务中。此方法的优点在于,当回滚事务时,步骤2中的update语句将被回滚,并且计数器将不再更改。其他并发事务将被阻止,直到第一个事务被提交或回滚,因此在所有其他事务首先完成之前,它们将无法访问旧计数器或新计数器。

nxowjjhe

nxowjjhe10#

让我指出一些非常重要的事情:

永远不要依赖自动生成的键的数字特性。

也就是说,除了比较它们是否相等(=)或不一致(<>),您不应该做任何其他事情。没有关系运算符(<,>),没有按索引排序,等等。如果您需要按“添加日期”排序,有一个“添加日期”列。
把它们当作苹果和橘子:问苹果和橘子是不是一样有意义?对。问苹果是否比橘子大有意义吗?不。(事实上,是的,但你明白我的意思。)
如果您坚持这个规则,自动生成的索引的连续性中的间隙将不会导致问题。

ruoxqz4g

ruoxqz4g11#

我不知道怎么做。根据mysql文档,这是预期的行为,在所有innodb\u autoinc\u lock\u mode锁模式下都会发生。具体文本为:
在所有锁模式(0、1和2)中,如果生成自动增量值的事务回滚,则这些自动增量值将“丢失”。一旦为自动增量列生成了值,则无法回滚该值,无论“insert like”语句是否完成,以及是否回滚包含该事务的事务。这些丢失的值不会被重用。因此,存储在表的自动增量列中的值可能存在间隙。

相关问题