2009-04-30 1 views
10

InnoDBストレージエンジンとトランザクションでMySQLを使用しており、問題が発生しました.MySQLでOracleのSEQUENCEをエミュレートするには良い方法が必要です。要件は次のとおりです。 - 同時実行をサポート - トランザクションの安全性 - 最大のパフォーマンス(最小限のロックとデッドロックを意味する)MySQLでトランザクション安全なシーケンスをエミュレートする

値の一部が使用できない場合我々は、順番に、すなわちギャップがOKです、気にしません。カウンタを使って別々のInnoDBテーブルを作成することで簡単に達成することができますが、これはトランザクションに参加し、ロックを導入して待機することを意味します。手動ロック、その他のアイデアやベストプラクティスのMyISAMテーブルを試してみたいと思っていますか?

答えて

14

自動インクリメントがニーズには十分でない場合は、あなたのようなNという名前の配列と、原子シーケンスメカニズムを作成することができますこれは:あなたは「FOO」の行を持っていると仮定すると

CREATE TABLE sequence (
    seq_name varchar(20) unique not null, 
    seq_current unsigned int not null 
); 

あなたのシーケンスを格納するテーブルを作成します。

UPDATE sequence SET seq_current = (@next := seq_current + 1) WHERE seq_name = 'foo'; 
SELECT @next; 

はロックに必要な:テーブルであなたがアトミックこのような次のシーケンスIDを取得することができます。どちらのステートメントも同じセッションで実行する必要があるため、selectが発生したときにローカル変数@nextが実際に定義されます。

+0

ありがとう - それはほぼ正確にそれを行うすべてのストアドプロシージャの後に我々が思い付いたものです。 –

+1

レコードが存在しない場合は、このクエリを使用します。 (seq_name、seq_current)VALUE( 'foo'、(@next:= 1)) UPDATE seq_current =(@next:= seq_current + 1);次にSELECT @next;を呼び出します。あなたはqu1j0t3の答えでIWATさんのコメントを結合することもできます – iwat

+0

: 'DUPLICATE KEY UPDATEのseq_current = LAST_INSERT_ID(seq_current + 1)ON sequences2(SEQ_NAME、seq_current)VALUES( 'foo' で、LAST_INSERT_ID(1))。INSERT INTO;' 'SELECT LAST_INSERT_ID();'を呼び出すか、アプリケーション・ドライバーを使用している場合は、組み込みの自動インクリメント・コールを使用してください(例えば、jdbcの 'Statement#getGeneratedKeys')。 – Alden

0

テーブルのMySQL Identity列ではこれを処理できませんか?

テーブルtable_name ( ID INTEGER AUTO_INCREMENT PRIMARY KEY )

を作成するか、またはあなただけの別のテーブルに挿入する以外の何かのためにそれを使用するために探していますか?

(SQLだけでなく)手続き型言語を使用して作成している場合、もう1つのオプションは、単一の整数(または長整数)値と、それをロックし、選択されたストアドプロシージャそれをインクリメントして値を返す前にロックを解除しました。

(注 - 常にインクリメントあなたが値を返す前に、 - それはエラーがある場合は重複を得ていないのチャンスを最大限に - 。またはトランザクションで全体を包む)

あなたはその後、独立して、あなたのこれを呼ぶだろうメインの挿入/更新(呼び出しメカニズムによって自動的に作成されたトランザクションにキャッチされずに)、それをパラメータとして渡して、それを使用したい場所に渡します。

これは他のものとは独立しているため、すばやくロックしないようにする必要があります。ロックでエラーが発生した場合でも(データベースがオーバーロードされている場合を除き)、2回目または3回目にすることができます。

+0

おかげで、あなたの第二のアプローチは、私は、多かれ少なかれ考えていたものはおそらくです詳細は多くの助けになります。そのようなテーブルに最適なストレージエンジンに関するコメント –

+0

私は実際にMySQLをよく知っているわけではありませんので、私はそれについてアドバイスすることはできません(私はSQL Server上でそれをやっています)。 私が言っていることは、それが複雑だとは思わないということです。そのため、最良の方法は、あなたの残りの部分に使用しているものを使用し、複雑さを軽減することです。 –

9

これはMySQL manualで与えられ行うための正しい方法:

UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1); 
SELECT LAST_INSERT_ID(); 
+1

私はここで論理を見ることができません。 'LAST_INSERT_ID'は自動インクリメントのフィールドにのみ意味があります。しかし、すでに自動インクリメントの場合は、手動でインクリメントする必要があるのはなぜですか? – devios1

+0

手動で設定されていない場合、更新ステートメントはLAST_INSERT_IDに影響しません。これは、個別のユーザー定義変数を使用せずに更新ステートメントからデータを取得するためのトリックとして使用できます。 –

+0

このアプローチは正しいですし、@deviosコメントは誤解を招くことです。 'LAST_INSERT_ID(expr)'は自動インクリメントとは関係ありません。 – baf

4

私たちは高いトランザクションゲーミング企業であり、私たちのニーズにあったこの種のソリューションが必要です。 Oracleシーケンスの機能の1つは、設定可能な増分値でした。

このソリューションでは、DUPLICATE KEYが使用されます。ストアド・プロシージャまたは関数sp_seq_next_val(VARCHAR)と、次の

要約:

CREATE TABLE sequences (
    id BIGINT DEFAULT 1, 
    name CHAR(20), 
    increment TINYINT, 
    UNIQUE KEY(name) 
); 

は、次のインデックスを取得するには

INSERT INTO sequences (name) VALUES ("user_id") ON DUPLICATE KEY UPDATE id = id + increment;<br/> 
SELECT id FROM sequences WHERE name = "user_id"; 
+0

面白いです。 ON DUPLICATE KEYはMySQLで常にINSERTでアトミックですか? –

+0

@Michael:はい、ON DUPLICATE KEYはアトミックです。 – kopos