2016-06-28 10 views
0

MySQLは、レコードIDをインクリメントする自動メカニズムを提供します。これは多くの目的では問題ありませんが、ORACLEの提供するシーケンスを使用できるようにする必要があります。明らかに、その目的のためにテーブルを作成することに意味はありません。mysql - Oracleのシーケンスと同様のメカニズムを作成

溶液が簡単でなければならない:

1)

2、ホストに必要なすべての配列表を作成)特定の配列の値を増加させ、新しい値を返す関数を作成し、

3)シーケンスの現在の値を返す関数を作成します。

理論的には

、それはシンプルに見える...しかし...(オラクルでnextvalとほぼ同じ)のシーケンスの値を大きくすると

、あなたはこの操作を実行するために他のセッションを防ぐために必要がある(または更新が完了するまで現在の値を取得することさえできます)。

二つの理論的なオプション:

- UPDATEおよびSELECTの間でテーブルをロック - シングルショットで新しい値を返す、または

BしまうUPDATEステートメントを使用します。

残念ながら、MySQLは関数/プロシージャ内のテーブルをロックすることを許可しておらず、(UPDATE ... RETURNING ...のように)機能/手順の完了後も生き残ることができる。

誰もがこれについてアイデア/ワーキングソリューションを持っていますか?

ありがとうございました。

+0

ことを明確にするシーケンスとしてあなたの心にあるものの例を与えます。そして、なぜあなたがストアドプロシージャでこれを自分で行うことができないのかを説明してください。 – Drew

答えて

4

以下は、FOR UPDATE intention lockの簡単な例です。 INNODBエンジンによる行レベルのロック。このサンプルでは、​​よく知られているINNODB Gap Anomaly(AUTO_INCREMENTの使用に失敗した後にギャップが発生する場合)に悩まされない次の利用可能なシーケンスの4つの行が示されています。

スキーマ:

-- 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、ASAPを実行します。

ストアドプロシージャ上記は:

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 

ようのみ1 2つのメカニズムを有するあなたのニーズのために応じてストアドプロシージャを変更しますシーケンス番号(OUTパラメーターまたは結果セットのいずれか)を検索します。言い換えると、OUTのパラメータの概念を簡単に破棄することができます。

LOCKのASAPリリース(更新後には明らかに不要)を遵守せず、リリース前に時間のかかるコードを実行し続けると、他のタイムアウト期間の後に次のことが起こりますシーケンス番号を待つクライアント:

ERROR 1205(HY000):ロック待ちタイムアウトを超えました。再起動してください トランザクション

これは決して問題ではありません。

show variables where variable_name='innodb_lock_wait_timeout'; 

MySQLマニュアルページinnodb_lock_wait_timeout

現在私のシステムでは、値は50(秒)です。ほとんどの状況では、おそらく1秒以上2秒以上の待ち時間は耐えられません。トランザクション中

また、関心の次のコマンドからの出力のセクションです:

SHOW ENGINE INNODB STATUS; 
+0

Hello @Drew !!!答えをありがとう(SELECT FOR UPDATEオプションを知らなかった)。私が得ることのないことが1つあります:新しいシーケンス値を返すFUNCTIONとして設定できるのであれば、なぜセッション変数 '@ mine_to_use'が必要なのですか?つまり、グローバル変数に影響を及ぼすことは、望ましくない(および潜在的に有害な)副作用となります。あなたは同意しないでしょうか? – FDavidov

+0

私はこのようなもののストアドプロシージャをプログラムします。私は通常、関数を考えるのではなく、小さな計算を考えます。これはトランザクションのためのものであり、私はprocsを操作します。これはグローバル変数ではありません。これはローカル変数(DECLARE)とは対照的に、単なるユーザー変数です。 – Drew

+0

[User Variables](http://dev.mysql.com/doc/refman/5.7/en/user-variables.html)のマニュアルページはこちら...安全です。引用符: 'ユーザ定義変数はセッション固有です。あるクライアントによって定義されたユーザー変数は、他のクライアントには表示されず、使用できません。 ' – Drew

関連する問題