IT TIP

MySQL AUTO_INCREMENT가 롤백되지 않습니다.

itqueen 2020. 11. 22. 21:03
반응형

MySQL AUTO_INCREMENT가 롤백되지 않습니다.


트랜잭션을 지원하기 위해 MySQL의 AUTO_INCREMENT 필드와 InnoDB를 사용하고 있습니다. 트랜잭션을 롤백 할 때 AUTO_INCREMENT 필드가 롤백되지 않는다는 것을 알았습니다. 이 방식으로 설계되었다는 것을 알았지 만 이에 대한 해결 방법이 있습니까?


매우 중요한 것을 지적하겠습니다.

자동 생성 키의 숫자 기능에 의존해서는 안됩니다.

즉, 동등 (=) 또는 불평등 (<>)을 비교하는 것 외에는 다른 작업을 수행해서는 안됩니다. 관계 연산자 (<,>), 색인 별 정렬 없음 등이 없습니다. "추가 된 날짜"별로 정렬해야하는 경우 "추가 된 날짜"열을 사용하십시오.

사과와 오렌지로 취급 : 사과가 오렌지와 같은지 묻는 것이 합리적입니까? 예. 사과가 오렌지보다 큰지 묻는 것이 합리적입니까? 아니요. (사실 그렇습니다.하지만 제 요지는 이해합니다.)

이 규칙을 고수하면 자동 생성 된 인덱스의 연속성에 차이가 있어도 문제가 발생하지 않습니다.


그런 식으로 작동 할 수 없습니다. 중히 여기다:

  • 프로그램 1에서 트랜잭션을 열고 autoinc 기본 키가있는 테이블 FOO에 삽입합니다 (임의로 키 값에 대해 557을 얻습니다).
  • 프로그램 2가 시작되면 트랜잭션이 열리고 558을 얻는 테이블 FOO에 삽입됩니다.
  • FOO에 대한 외래 키인 열이있는 BAR 테이블에 두 개의 삽입을 프로그래밍하십시오. 이제 558은 FOO와 BAR에 모두 있습니다.
  • 프로그램 2는 이제 커밋합니다.
  • 프로그램 3이 시작되고 테이블 FOO에서 보고서를 생성합니다. 558 레코드가 인쇄됩니다.
  • 그 후 프로그램 1이 롤백됩니다.

데이터베이스는 557 값을 어떻게 회수합니까? FOO에 들어가서 557보다 큰 다른 모든 기본 키를 줄입니까? BAR을 어떻게 수정합니까? 보고서 프로그램 3 출력에 인쇄 된 558을 어떻게 지우나요?

Oracle의 시퀀스 번호는 동일한 이유로 트랜잭션과 독립적입니다.

이 문제를 일정한 시간에 해결할 수 있다면 데이터베이스 분야에서 많은 돈을 벌 수있을 것입니다.

이제 자동 증가 필드에 간격이 없어야한다는 요구 사항이있는 경우 (예 : 감사 목적으로). 그러면 트랜잭션을 롤백 할 수 없습니다. 대신 레코드에 상태 플래그가 있어야합니다. 처음 삽입 할 때 레코드의 상태는 "미완료"이고 트랜잭션을 시작하고 작업을 수행하고 상태를 "경쟁"(또는 필요한 모든 것)으로 업데이트합니다. 그런 다음 커밋하면 레코드가 라이브입니다. 트랜잭션이 롤백되면 불완전한 레코드가 감사를 위해 여전히 남아 있습니다. 이로 인해 다른 많은 골칫거리가 발생하지만 감사 추적을 처리하는 한 가지 방법입니다.


주문이 연속되어야하는 송장 테이블에서 롤백하려면 고객이 ID가 필요했습니다.

MySQL의 내 솔루션은 AUTO-INCREMENT를 제거하고 테이블에서 최신 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, ...)

롤백되는 경우 왜 관심이 있습니까? AUTO_INCREMENT 키 필드는 의미가 없으므로 어떤 값이 사용되는지는 신경 쓰지 않아도됩니다.

보존하려는 정보가있는 경우 키가 아닌 다른 열이 필요할 수 있습니다.


나는 그것을 할 방법을 모른다. MySQL 문서 에 따르면 이것은 예상되는 동작이며 모든 innodb_autoinc_lock_mode 잠금 모드에서 발생 합니다. 구체적인 텍스트는 다음과 같습니다.

모든 잠금 모드 (0, 1 및 2)에서 자동 증가 값을 생성 한 트랜잭션이 롤백되면 해당 자동 증가 값은 "손실"됩니다. 자동 증가 열에 대한 값이 생성되면 "INSERT-like"문이 완료되었는지 여부 및 포함 된 트랜잭션이 롤백되었는지 여부에 관계없이 롤백 할 수 없습니다. 이러한 손실 된 값은 재사용되지 않습니다. 따라서 테이블의 AUTO_INCREMENT 열에 저장된 값에 간격이있을 수 있습니다.


당신이 설정 한 경우 auto_increment1롤백 또는 삭제 한 후, 다음 삽입에, MySQL은 그이 볼 1이미 사용하고, 대신 얻을 것이다 MAX()값을하고 여기에 1을 추가합니다.

이렇게하면 마지막 값이있는 행이 삭제되거나 삽입이 롤백되는 경우 다시 사용됩니다.

auto_increment를 1로 설정하려면 다음과 같이하십시오.

ALTER TABLE tbl auto_increment = 1

MAX()비용이 많이들 수 있기 때문에 단순히 다음 번호를 계속 사용하는 것만 큼 효율적이지는 않지만 , 자주 삭제 / 롤백하고 가장 높은 값을 재사용하는 데 집착한다면 이는 현실적인 접근 방식입니다.

이것은 중간에 삭제 된 레코드의 간격이나 auto_increment를 다시 1로 설정하기 전에 다른 삽입이 발생해야하는 경우를 방지하지 않습니다.


해결책:

'tbl_test'를 예제 테이블로 사용하고 'Id'필드에 AUTO_INCREMENT 속성이 있다고 가정합니다.

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

테이블에 houndred 또는 수천 개의 행이 이미 삽입되어 있고 더 이상 AUTO_INCREMENT를 사용하지 않는다고 가정합니다. 트랜잭션을 롤백 할 때 'Id'필드는 항상 AUTO_INCREMENT 값에 +1을 추가하기 때문입니다. 따라서 이것을 피하려면 다음을 수행하십시오.

  • 'Id'열에서 AUTO_INCREMENT 값을 제거하겠습니다 (삽입 된 행은 삭제되지 않음).
ALTER TABLE tbl_test MODIFY COLUMN Id int (11) NOT NULL FIRST;
  • 마지막으로 BEFORE INSERT 트리거를 생성하여 'Id'값을 자동으로 생성합니다. 그러나이 방법을 사용하면 트랜잭션을 롤백하더라도 Id 값에 영향을주지 않습니다.
트리거 생성 trg_tbl_test_1
tbl_test에 삽입하기 전에
각 행
  시작
    SET NEW.Id = COALESCE ((SELECT MAX (Id) FROM tbl_test), 0) + 1;
  종료;

그게 다야! 끝났습니다!

천만에요.

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

테이블에 값이 없거나 행이없는 경우

SELECT에서 오류 mysql 유형 업데이트에 대한 대상 추가


If you need to have the ids assigned in numerical order with no gaps, then you can't use an autoincrement column. You'll need to define a standard integer column and use a stored procedure that calculates the next number in the insert sequence and inserts the record within a transaction. If the insert fails, then the next time the procedure is called it will recalculate the next id.

Having said that, it is a bad idea to rely on ids being in some particular order with no gaps. If you need to preserve ordering, you should probably timestamp the row on insert (and potentially on update).


Concrete answer to this specific dilemma (which I also had) is the following:

1) Create a table that holds different counters for different documents (invoices, receipts, RMA's, etc..); Insert a record for each of your documents and add the initial counter to 0.

2) Before creating a new document, do the following (for invoices, for example):

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

3) Get the last value that you just updated to, like so:

SELECT LAST_INSERT_ID()

or just use your PHP (or whatever) mysql_insert_id() function to get the same thing

4) Insert your new record along with the primary ID that you just got back from the DB. This will override the current auto increment index, and make sure you have no ID gaps between you records.

This whole thing needs to be wrapped inside a transaction, of course. The beauty of this method is that, when you rollback a transaction, your UPDATE statement from Step 2 will be rolled back, and the counter will not change anymore. Other concurrent transactions will block until the first transaction is either committed or rolled back so they will not have access to either the old counter OR a new one, until all other transactions are finished first.


$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";

참고URL : https://stackoverflow.com/questions/449346/mysql-auto-increment-does-not-rollback

반응형