2016-06-28 28 views
0

MySQL提供了一種自動機制來增加記錄ID。這對於很多目的來說是可以的,但我需要能夠使用ORACLE提供的序列。顯然,爲此目的創建一張表是沒有意義的。mysql - 製作類似於Oracle序列的機制

溶液應簡單:

1)創建一個表到主機的所有所需的序列,

2)創建增加的特定序列的值,並返回新值的函數,

3)創建一個函數,返回一個序列的當前值。

從理論上講,它看起來很簡單...但是...

當增加一個序列(大同小異作爲Oracle nextval),你需要防止其他會話執行此操作的值(或甚至獲取當前值)直到更新完成。

兩個理論選擇:

一個 - 使用UPDATE語句,將在單杆返回新值,或

b - 鎖UPDATE和SELECT的表。

不幸的是,MySQL似乎不允許在函數/過程中鎖定表,並且試圖在單個語句(如UPDATE ... RETURNING ...)中創建整個事物時,必須使用@類型變量在函數/過程完成時存活。

有沒有人有這個想法/工作解決方案?

謝謝。

+0

給的是什麼在你的心中是一個序列,以表明這一點的例子。並解釋你爲什麼不能用存儲過程自己做這件事。 – Drew

回答

4

以下是FOR UPDATE intention lock的簡單示例。 INNODB引擎的行級鎖定。該示例顯示了四行用於下一個可用序列,它們不會遇到衆所周知的INNODB間隙異常(在使用AUTO_INCREMENT失敗後出現間隙的情況)。

模式:

-- drop table if exists sequences; 
create table sequences 
( id int auto_increment primary key, 
    sectionType varchar(200) not null, 
    nextSequence int not null, 
    unique key(sectionType) 
) ENGINE=InnoDB; 

-- truncate table sequences; 
insert sequences (sectionType,nextSequence) values 
('Chassis',1),('Engine Block',1),('Brakes',1),('Carburetor',1); 

示例代碼:

START TRANSACTION; -- Line1 
SELECT nextSequence into @mine_to_use from sequences where sectionType='Carburetor' FOR UPDATE; -- Line2 
select @mine_to_use; -- Line3 
UPDATE sequences set nextSequence=nextSequence+1 where sectionType='Carburetor'; -- Line4 
COMMIT; -- Line5 

理想情況下,你不必在全部Line3或bloaty代碼會耽誤其他客戶端上的鎖等待。意思是,讓你的下一個序列使用,執行更新(增量部分),並且儘快COMMIT,儘快

上面在存儲過程:

DROP PROCEDURE if exists getNextSequence; 
DELIMITER $$ 
CREATE PROCEDURE getNextSequence(p_sectionType varchar(200),OUT p_YoursToUse int) 
BEGIN 
    -- for flexibility, return the sequence number as both an OUT parameter and a single row resultset 
    START TRANSACTION; 
    SELECT nextSequence into @mine_to_use from sequences where sectionType=p_sectionType FOR UPDATE; 
    UPDATE sequences set nextSequence=nextSequence+1 where sectionType=p_sectionType; 
    COMMIT; -- get it and release INTENTION LOCK ASAP 
    set [email protected]_to_use; -- set the OUT parameter 
    select @mine_to_use as yourSeqNum; -- also return as a 1 column, 1 row resultset 
END$$ 
DELIMITER ; 

測試:

set @myNum:= -1; 
call getNextSequence('Carburetor',@myNum); 
+------------+ 
| yourSeqNum | 
+------------+ 
|   4 | 
+------------+ 
select @myNum; -- 4 

相應地修改所存儲的程序爲你需要,例如僅具有2個機制的1對檢索序列號(OUT參數或結果集)。換句話說,很容易拋棄OUT參數的概念。

如果您不遵守LOCK的ASAP版本(在更新後顯然不需要),並且在發佈之前繼續執行耗時的代碼,那麼可能會在其他客戶端正在等待序列號:

錯誤1205(HY000):鎖定超時超時;嘗試重新啓動 交易

希望這不是問題。

show variables where variable_name='innodb_lock_wait_timeout'; 

MySQL手冊頁爲innodb_lock_wait_timeout

在我的系統上此刻它的值爲50(秒)。在大多數情況下,等待超過一秒鐘或兩秒鐘可能是無法忍受的。

另外的交易期間的利息是從以下命令輸出的那一段:

SHOW ENGINE INNODB STATUS; 
+0

你好@德魯!謝謝你的回答(不知道SELECT FOR UPDATE選項)。有一件事我沒有得到:如果可以將它設置爲返回新序列值的FUNCTION,爲什麼需要會話變量'@ mine_to_use'?我的意思是,影響全球變量將是一種不受歡迎(並且可能有害)的副作用。你不同意嗎? – FDavidov

+0

我在存儲過程中爲這樣的事情編程。我通常不會想到一個函數,但是對於小的計算。這是爲了一筆交易,我指導過程。這不是一個全局變量。它只是一個用戶變量而不是局部變量(這是一個DECLARE)。 – Drew

+0

以下是[用戶變量](http://dev.mysql.com/doc/refman/5.7/en/user-variables.html)上的手冊頁......它們很安全。引用:用戶定義的變量是特定於會話的。由一個客戶端定義的用戶變量不能被其他客戶端看到或使用。' – Drew

相關問題