ここにはたくさんあることを知っています。私はそれをコード内とあちこちでかなりうまく文書化しようとしました。ストアドプロシージャを使用します。あなたは自然にコードを引き出して、その方法を使わないことができます。次に使用可能なインクリメンターを格納するメインテーブルを使用します。安全なINNODB
を使用します 並行性のための意図ロック。再利用テーブルとそれをサポートするストアドプロシージャがあります。
とにかくテーブルmyTable
を使用しません 。それはあなたの質問の下のコメントに基づいたあなた自身の想像力のためにそこに示されています。その要約は、DELETE
にギャップがあることを知っているということです 。それらのスロット、それらのシーケンス番号を再利用するための整然とした方法が必要です。したがって、DELETE
行、それに応じてストアドプロシージャを使用して、その番号を追加します。当然、再利用などのために次のシーケンス番号を取得するためのストアドプロシージャがあります。
テストの目的で、sectionType
='デバイス'
そして何よりも、それはテストされています!
スキーマ:
create table myTable
( -- your main table, the one you cherish
`id` int auto_increment primary key, -- ignore this
`seqNum` int not null, -- FOCUS ON THIS
`others` varchar(100) not null
) ENGINE=InnoDB;
create table reuseMe
( -- table for sequence numbers to reuse
`seqNum` int not null primary key, -- FOCUS ON THIS
`reused` int not null -- 0 upon entry, 1 when used up (reused)
-- the primary key enforces uniqueness
) ENGINE=InnoDB;;
CREATE TABLE `sequences` (
-- table of sequence numbers system-wide
-- this is the table that allocates the incrementors to you
`id` int NOT NULL AUTO_INCREMENT,
`sectionType` varchar(200) NOT NULL,
`nextSequence` int NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `sectionType` (`sectionType`)
) ENGINE=InnoDB;
INSERT sequences(sectionType,nextSequence) values ('devices',1); -- this is the focus
INSERT sequences(sectionType,nextSequence) values ('plutoSerialNum',1); -- not this
INSERT sequences(sectionType,nextSequence) values ('nextOtherThing',1); -- not this
-- the other ones are conceptuals for multi-use of a sequence table
ストアドプロシージャ:uspGetNextSequence
DROP PROCEDURE IF EXISTS uspGetNextSequence;
DELIMITER $$
CREATE PROCEDURE uspGetNextSequence(p_sectionType varchar(200))
BEGIN
-- a stored proc to manage next sequence numbers handed to you.
-- driven by the simple concept of a name. So we call it a section type.
-- uses SAFE INNODB Intention Locks to support concurrency
DECLARE valToUse INT;
START TRANSACTION;
SELECT nextSequence into valToUse from sequences where sectionType=p_sectionType FOR UPDATE;
IF valToUse is null THEN
SET valToUse=-1;
END IF;
UPDATE sequences set nextSequence=nextSequence+1 where sectionType=p_sectionType;
COMMIT; -- get it and release INTENTION LOCK ASAP
SELECT valToUse as yourSeqNum; -- return as a 1 column, 1 row resultset
END$$
DELIMITER ;
-- ****************************************************************************************
-- test:
call uspGetNextSequence('devices'); -- your section is 'devices'
uspGetNextSequence()を呼び出した後、そのシーケンスを確認するのはあなたの責任です#
myTable
に追加されます (確認することにより)、または失敗した場合は、に挿入します
uspAddToReuseList()を呼び出した再利用テーブル。すべての挿入が成功するわけではありません。この部分に焦点を当てます。
このコードでは、sequences
に「戻す」ことができないためです。
並行性、他のユーザー、およびすでに渡された範囲。したがって、単純に、挿入が失敗した場合、
番号をreuseMe
に入れます uspAddToReuseList()を介して
...
ストアドプロシージャ:uspAddToReuseList:
DROP PROCEDURE IF EXISTS uspAddToReuseList;
DELIMITER $$
CREATE PROCEDURE uspAddToReuseList(p_reuseNum INT)
BEGIN
-- a stored proc to insert a sequence num into the reuse list
-- marks it available for reuse (a status column called `reused`)
INSERT reuseMe(seqNum,reused) SELECT p_reuseNum,0; -- 0 means it is avail, 1 not
END$$
DELIMITER ;
-- ****************************************************************************************
-- test:
call uspAddToReuseList(701); -- 701 needs to be reused
ストアドプロシージャ:uspGetOneToReuse:
DROP PROCEDURE IF EXISTS uspGetOneToReuse;
DELIMITER $$
CREATE PROCEDURE uspGetOneToReuse()
BEGIN
-- a stored proc to get an available sequence num for reuse
-- a return of -1 means there aren't any
-- the slot will be marked as reused, the row will remain
DECLARE retNum int; -- the seq number to return, to reuse, -1 means there isn't one
START TRANSACTION;
-- it is important that 0 or 1 rows hit the following condition
-- also note that FOR UPDATE is the innodb Intention Lock
-- The lock is for concurrency (multiple users at once)
SELECT seqNum INTO retNum
FROM reuseMe WHERE reused=0 ORDER BY seqNum LIMIT 1 FOR UPDATE;
IF retNum is null THEN
SET retNum=-1;
ELSE
UPDATE reuseMe SET reused=1 WHERE seqNum=retNum; -- slot used
END IF;
COMMIT; -- release INTENTION LOCK ASAP
SELECT retNum as yoursToReuse; -- >0 or -1 means there is none
END$$
DELIMITER ;
-- ****************************************************************************************
-- test:
call uspGetOneToReuse();
ストアドプロシージャ:uspCleanReuseList:
DROP PROCEDURE IF EXISTS uspCleanReuseList;
DELIMITER $$
CREATE PROCEDURE uspCleanReuseList()
BEGIN
-- a stored proc to remove rows that have been successfully reused
DELETE FROM reuseMe where reused=1;
END$$
DELIMITER ;
-- ****************************************************************************************
-- test:
call uspCleanReuseList();
ストアドプロシージャ:uspOoopsResetToAvail:
DROP PROCEDURE IF EXISTS uspOoopsResetToAvail;
DELIMITER $$
CREATE PROCEDURE uspOoopsResetToAvail(p_reuseNum INT)
BEGIN
-- a stored proc to deal with a reuse attempt (sent back to you)
-- that you need to reset the number as still available,
-- perhaps because of a failed INSERT when trying to reuse it
UPDATE reuseMe SET reused=0 WHERE seqNum=p_reuseNum;
END$$
DELIMITER ;
-- ****************************************************************************************
-- test:
call uspOoopsResetToAvail(701);
ワークフローのアイデア:
GNS uspGetNextSequence()
の呼び出しを意味します 。
RS 再利用シーケンスを意味します uspGetOneToReuse()
の呼び出しを介して
新しいINSERT
の場合 必要な場合は、 RSに電話してください :
A. RSの場合 -1を返す場合、再利用されるものはないため、 GNSを呼び出します。 これはNを返します。正常にINSERT
できる場合 myTable.seqNum=N
を使用 確認すると、完了です。正常にINSERT
できない場合 次に、uspAddToReuseList(N)
を呼び出します。 。
B. RSの場合> 0を返します。スロットには、reuseMe.reused=1
があることに注意してください。 、覚えておくとよいことです。そのため、再利用に成功していると想定されます。そのシーケンス番号をNと呼びましょう。正常にINSERT
できる場合 myTable.seqNum=N
を使用 確認すると、完了です。正常にINSERT
できない場合 次に、uspOoopsResetToAvail(N)
を呼び出します。 。
uspCleanReuseList()
を呼び出しても安全だと思われる場合 そうする。 DATETIME
を追加する reuseMe
に テーブルは、myTable
からの行がいつであるかを示す良いアイデアかもしれません。 元々削除してreuseMe
を引き起こしていました 元のINSERT
を取得する行 。