2011年6月2日木曜日

IDENTITYが重複した値を返す

SQLServerでは、Oracleと違いCREATE SEQUENCEコマンドがない。
代替方法として、IDENTITYで連番が採番されるテーブルを用意しておき、
ストアド側で以下のように呼ぶ。

-- トランザクション
BEGIN TRANSACTION @TRAN;

-- セーブポイント
SAVE TRANSACTION SP;

-- 連番取得
INSERT INTO 採番テーブル名 DEFAULT VALUES;
SET @連番 = SCOPE_IDENTITY();

-- セーブポイントにロールバック
ROLLBACK TRANSACTION SP;

-- コミット
COMMIT TRANSACTION @TRAN;

上記のやり方で、シーケンス番号が取得できる。

が、しかしである。ある環境だと希に奇妙な問題が発生する。
シーケンス番号のはずが、重複した値が返るのだ・・・

これは、どういうことかというと2CPU(2ソケット)以上を
搭載しているサーバー機であると、ストアドのシーケンス
番号取得部分(SCOPE_IDENTITY)がパラレル実行され
瞬間的にCPUそれぞれで同じ番号を取得できてしまうらしい。
複数コアCPUの場合では、まだ発生を確認できていない。

この問題は、SQLServer 2008 Expressで確認しており
2005など下位バージョンでも発生する可能性がある。

【回避方法】
1CPUのみ利用する設定に変更することで回避できるようだ。
以下のようなコマンドを実行する。ただし、パフォーマンスが
少し落ちるため、実行する前に影響範囲を検討頂きたい。

■DB単位で設定する場合
sp_configure 'show advanced options', 1;
GO
RECONFIGURE WITH OVERRIDE;
GO
sp_configure 'max degree of parallelism', 1;
GO
RECONFIGURE WITH OVERRIDE;
GO

■ストアド単位で設定する場合
OPTION (MAXDOP 1)

より詳しい解説については、以下のサイトでコメントがある。

■SCOPE_IDENTITY() sometimes returns incorrect value
http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=328811

■You may receive incorrect values when using SCOPE_IDENTITY() and @@IDENTITY
http://support.microsoft.com/default.aspx?scid=kb;en-US;2019779

■SQL SERVER ? 2008 ? SCOPE_IDENTITY Bug with Multi Processor Parallel Plan and Solution
http://blog.sqlauthority.com/2009/03/24/sql-server-2008-scope_identity-bug-with-multi-processor-parallel-plan-and-solution/

発生時のインパクトが大きいだけに、なるべく早く根本的に解決してほしいものだ。