2012-02-07 15 views
2

現在、SQL Serverの取得手順で問題が発生しています。負荷の高いSQL Serverの取得エラー(同時ユーザー数200)

ストアドプロシージャは、同じパラメータに対して多数のユーザー(ユーザー数> 40)が同時に実行すると、予期しない結果を返します。 Noのユーザ< = 20の場合、期待される結果を示します。エラー率は、jMeterを使用して測定して0.07-0.10%です。

次のSQL文は、1行を返すと予想されます。しかし、重い負荷の下では、時には行が返されません。

exec RetrievingNode @Keys='one,two,three' 

ここにはprocが格納されています。ここで

CREATE PROCEDURE [dbo].[RetrievingNode] @Keys nvarchar(max) 
as 
    Begin 
     SET NOCOUNT ON; 

     --*************************************** 
     --*************************************** 
     -- Turn On or Off updation of LastAccessDateTime 
     -- To Turn on set @TurnOnUpdation=1 
     -- To Turn Off set @TurnOnUpdation=0 
     declare @TurnOnUpdation as bit 

     set @TurnOnUpdation=1 

     --*************************************** 
     --*************************************** 
     declare @Variable as table( 
     Value   nvarchar(max), 
     KeyedNodes_Id int, 
     KeyNodeValue nvarchar(max), 
     IsParent  bit, 
     IsReadOnly bit) 
     -- this table will hold ids against which LastAccessDateTime can be updated.  
     declare @tbl_Keys table( 
     id int identity(1, 1), 
     Value nvarchar(max)) 

     --following will take all the keys into @tbl_Keys 
     insert into @tbl_Keys 
        (Value) 
     select Data 
     from Split(@Keys, ',') 

     DECLARE @count as int 
     DECLARE @counter as int 

     SET @counter=1 

     SELECT @count = COUNT(*) 
     FROM @tbl_Keys 

     --Declare @ChildId as int 
     declare @ChildNodes table( 
     id   int, 
     Value   nvarchar(max), 
     ParentNode_id int) 
     declare @ParentId as int 

     WHILE(@counter <= @count) 
     BEGIN 
      DECLARE @Key as nvarchar(max) 

      SELECT @Key = Value 
      FROM @tbl_Keys 
      WHERE id = @counter 

      if(@counter = 1) 
       begin 
        if not exists(select * 
           from keyednodes(nolock) KN 
           where KN.Value = @Key 
             and KN.ParentNode_Id is null) 
        BEGIN 
         ---node does not exists 
         Break; 
        END 

        declare @ValueExistsForFirstKey as bit 

        set @ValueExistsForFirstKey=0 

        insert into @ChildNodes 
        select k2.Id, 
         k2.Value, 
         k2.ParentNode_Id 
        from keyednodes(nolock) k1 
         inner join keyednodes(nolock) k2 
          on k1.id = k2.ParentNode_id 
        where K1.value = @Key 
         and k1.ParentNode_Id is null 

        if(@count = 1) 
        BEGIN 
         IF EXISTS(select GV.Value, 
             GV.KeyedNodes_Id, 
             KN.Value 
            from keyednodes(nolock) KN 
             inner join GlobalVariables(nolock) GV 
              on KN.Id = GV.KeyedNodes_Id 
               and KN.Value = @Key 
               and KN.ParentNode_Id is null) 
          BEGIN -- If value of node exists 
           SET @ValueExistsForFirstKey=1 

           insert into @Variable 
           select GV.Value, 
            GV.KeyedNodes_Id, 
            KN.Value, 
            1, 
            GV.ReadOnly 
           from keyednodes(nolock) KN 
            inner join GlobalVariables(nolock) GV 
             on KN.Id = GV.KeyedNodes_Id 
              and KN.Value = @Key 
              and KN.ParentNode_Id is null 
          END 
         ELSE 
          IF NOT EXISTS(select * 
             from @ChildNodes) 
          BEGIN 
           insert into @Variable 
           select -1, 
             -1, 
             -1, 
             0, 
             0 
           --Node exists but No value or children exists 

           GOTO ExitFromProcedure 
          END 

         IF(@counter = @count) 
          BEGIN 
           if exists(select Id, 
               Value, 
               ParentNode_Id 
             from keyednodes(nolock) KN 
             where KN.Value = @Key 
               and KN.ParentNode_Id is null) 
           BEGIN 
            if(@ValueExistsForFirstKey = 0) 
             and (not exists(select * 
                 from @ChildNodes)) 
             BEGIN 
              insert into @Variable 
              select -1, 
               -1, 
               -1, 
               0, 
               0 

              GOTO ExitFromProcedure 
             END 
           END 
          END 
        END 
       end 
      else 
       begin 
        declare @KeyIsChild as int 

        set @KeyIsChild=0 

        if exists(select * 
          from @ChildNodes 
          where Value = @Key) 
        begin 
         set @KeyIsChild=1 
        End 
        ELSE 
        BEGIN 
         --Key path does not exist 
         --print 'key path does not exist' 
         GOTO ExitFromProcedure 
        ENd 

        if not exists(select * 
           from @ChildNodes 
           where Value = @Key) 
        BEGIN 
         set @ParentId=0 
        END 
        ELSE 
        BEGIN 
         select @ParentId = Id 
         from @ChildNodes 
         where Value = @Key 
        ENd 

        delete @ChildNodes 

        if(@KeyIsChild = 1) 
        begin 
         insert into @ChildNodes 
         select k2.Id, 
           k2.Value, 
           k2.ParentNode_Id 
         from keyednodes(nolock) k1 
           inner join keyednodes(nolock) k2 
           on k1.id = k2.ParentNode_id 
         where k2.ParentNode_Id = @ParentId 
        end 
       end 

      SET @[email protected] + 1 
     END 

     if exists(select * 
       from @ChildNodes) 
     begin 
      IF EXISTS (select GV.Value, 
           CN.id, 
           CN.Value 
         from @ChildNodes CN 
           left outer join GlobalVariables(nolock) GV 
           on CN.Id = GV.KeyedNodes_Id --children 
         union all 
         select GV.Value, 
           GV.KeyedNodes_Id, 
           KN.Value 
         from keyednodes(nolock) KN 
           inner join GlobalVariables(nolock) GV 
           on KN.Id = GV.KeyedNodes_Id 
            and KN.Id = @ParentId--children 
        ) 
       BEGIN 
        insert into @Variable 
        select GV.Value, 
         CN.id, 
         CN.Value, 
         0, 
         GV.ReadOnly 
        from @ChildNodes CN 
         left outer join GlobalVariables(nolock) GV 
          on CN.Id = GV.KeyedNodes_Id --children 
        union all 
        select GV.Value, 
         GV.KeyedNodes_Id, 
         KN.Value, 
         1, 
         GV.ReadOnly 
        from keyednodes(nolock) KN 
         inner join GlobalVariables(nolock) GV 
          on KN.Id = GV.KeyedNodes_Id 
           and KN.Id = @ParentId--children 
       END 
      ELSE 
       BEGIN 
        if(@count <> 1) 
        insert into @Variable 
        select -1, 
          -1, 
          -1, 
          0, 
          0 

        if(@count = 1) 
        and not exists(select * 
            from @Variable) 
        insert into @Variable 
        select -1, 
          -1, 
          -1, 
          0, 
          0 
       END 
     end 
     else 
     Begin 
      if (@KeyIsChild = 1) 
       BEGIN 
        IF EXISTS (select GV.Value, 
            GV.KeyedNodes_Id, 
            KN.Value 
          from keyednodes(nolock) KN 
            inner join GlobalVariables(nolock) GV 
             on KN.Id = GV.KeyedNodes_Id 
             and KN.Id = @ParentId) 
        BEGIN 
         ---IF value of the node exists 
         insert into @Variable 
         select GV.Value, 
           GV.KeyedNodes_Id, 
           KN.Value, 
           1, 
           GV.ReadOnly 
         from keyednodes(nolock) KN 
           inner join GlobalVariables(nolock) GV 
           on KN.Id = GV.KeyedNodes_Id 
            and KN.Id = @ParentId --node 
        END 
        ELSE 
        BEGIN 
         insert into @Variable 
         select -1, 
           -1, 
           -1, 
           0, 
           0 --Node exists but No value or children exists 
        END 
       END 
     End 

     ---- 
     ExitFromProcedure: 

     select KeyedNodes_Id, 
      KeyNodeValue, 
      Value, 
      IsParent, 
      Isnull(IsReadOnly, 0)as IsReadOnly 
     from @Variable 
     order by IsParent desc, 
       KeyNodeValue asc 

     --update LastAccessDateTime 
     IF(@TurnOnUpdation = 1) 
     BEGIN 
      IF EXISTS(select * 
         from @Variable) 
       BEGIN 
        UPDATE GlobalVariables 
        SET LastAccessDateTime = getdate() 
        WHERE KeyedNodes_Id IN (SELECT KeyedNodes_Id 
              FROM @Variable) 
       END 
     END 
    End 

は、上記の手順で参照し、スプリット()UDFです:

CREATE FUNCTION [dbo].[Split] (@RowData nvarchar(max), @SplitOn nvarchar(5)) 
RETURNS @RtnValue table 
( 
    Id int identity(1,1), 
    Data nvarchar(max) 
) AS 
BEGIN 
Declare @Cnt int; Set @Cnt = 1 

While (Charindex(@SplitOn,@RowData)>0) 
Begin 
    Insert Into @RtnValue (data) 
    Select Data = ltrim(rtrim(Substring(@RowData,1,Charindex(@SplitOn,@RowData)-1))) 
    Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData)) 
    Set @Cnt = @Cnt + 1 
End 

Insert Into @RtnValue (data) 
Select Data = ltrim(rtrim(@RowData)) 

Return 
END 
+1

左のショーの「関連」サイドボード、SQL ***開発***質問としてけれどもhttp://dba.stackexchange.com/ –

+2

でこれを頼むのは良い考えかもしれません、このように、ここでも歓迎します。 – RBarryYoung

+0

これはSQL Serverのように見えますが、どのバージョンですか? – RBarryYoung

答えて

0

チェックするREAD UNCOMMITTEDに分離レベルを設定してみてくださいそのブロッキングの問題であれば

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 
+3

"Just Checking"でブロッキングの問題が発生しても問題ありませんが、この方法で正常に実行することはお勧めしません。 – RBarryYoung

+0

は私が言っている状況に依存します。私は時にはそれを使用して、時々私はスナップショットの分離を使用します。しかし、はい、ここでの主な目標は、テーブルが同時ユーザーによってブロックされているかどうかを確認することです。 – Diego

+3

関連性のブロックがどのようになるか見当たりませんか?彼らは待機について不平を言っていません。おそらく、彼らはコード内で競合状態に陥っているかもしれませんが、私はすべてのことを見て気にすることはできません。 –

4

このほぼ確実にトランザクション分離レベルの問題です。私は実際にprocのロジックを掘り下げるのに時間はかかりませんでしたが、ちょっと見てみると、このプロシージャは状態を更新し、他の接続によって汚れた読み込みをして断続的なデータ問題を引き起こすようです。

何か試してみてください(あなたのTESTデータベース上で!)、すべてのNOLOCKヒントを削除し、トランザクション内でこのプロシージャを実行します。私が言ったように、私は本当にロジックを解析していませんが、これを試してみてください。それはあなたのスループットが下がる可能性が高いです

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; --Increase isolation level 
BEGIN TRAN 
... your proc ... 
COMMIT 
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; --Set back to the default on this connection 

が、あなたの結果が正しいとなります。適切なトランザクション分離レベルを見つけるためにprocを慎重に分析します。 SET TRANSACTION ISOLATION LEVEL READ COMMITTED SNAPSHOTがより高いスループットで適切な回答を返すことがありますが、それは最初に分析してテストするものです。

+1

はい。彼が言ったこと。 – RBarryYoung

+0

(NoLock)ヒントの約半分がINSERT..SELECTステートメントにあることに注意してください。ステートメントは、とにかく効果がありません。 – RBarryYoung

+0

そして、残りのNOLOCKヒントのほとんどは、同じINSERT文の前にあり、それらを複製するEXISTS()節にあります。したがって、コードを正しく構造化することで排除することができます。 – RBarryYoung

3

まず、IMO David Markleの回答が正しいです。しかし、FWIWは、(nolock)ヒントのすべてが削除された、コードのクリーンアップ、修正(IMHO)の例を提供するために、この回答を投稿しています。

私はIsolationとTransactionコンテナステートメントを実装していませんが、要求された場合は実行できます。私は一般的にSNAPSHOT Isolationを推奨しています。並行性の問題が多いためです。

以下のコードでは、多くの多くの重複した不要なパスが削除されています。以下の頻繁に使用される(アンチ)パターン:

IF EXISTS(
     SELECT ...{complex query} 
     ) 
begin 
     INSERT ... SELECT ...{same complex query} 
end 
ELSE 
begin 
    {do something else} 
end 

は、はるかに簡単かつ迅速に変更されました:

INSERT ... SELECT ...{same complex query} 

IF @@ROWCOUNT = 0 
begin 
    {do something else} 
end 

最後に、私は、もちろん、コードをテストするかがあることを確認することはできませんいくつかの間違いではありません。だからあなたがそれを使うならば、あなたはそれを徹底的にテストするべきです。

CREATE PROCEDURE [dbo].[RetrievingNode] @Keys nvarchar(max) as 
Begin 
    SET NOCOUNT ON; 
    -- Turn On or Off updation of LastAccessDateTime 
    DECLARE @TurnOnUpdation as bit; SET @TurnOnUpdation=1 

    DECLARE @Variable as table 
     (
      Value nvarchar(max), 
      KeyedNodes_Id int, 
      KeyNodeValue nvarchar(max), 
      IsParent bit, 
      IsReadOnly bit 
     )-- this table will hold ids against which LastAccessDateTime can be updated. 
    DECLARE @tbl_Keys table 
     (
      id int identity(1,1), 
      Value nvarchar(max) 
     ) 

    --following will take all the keys into @tbl_Keys 
    INSERT INTO @tbl_Keys(Value) SELECT Data FROM Split(@Keys,','); 

    DECLARE @key_count as int; SELECT @key_count=COUNT(*) FROM @tbl_Keys 
    DECLARE @key_depth as int; 

    DECLARE @ChildNodes table 
     (
      id int, 
      Value nvarchar(max), 
      ParentNode_id int 
     ) 
    DECLARE @ParentId as int 
    DECLARE @KeyIsChild as int;  SET @KeyIsChild=0; 

    --====== Process the First(root) Key ====  
    SET  @key_depth=1; 
    DECLARE @Key as nvarchar(max); SELECT @Key=Value FROM @tbl_Keys WHERE [email protected]_depth 
    IF @key_count >= 1 
    AND EXISTS(SELECT * 
       FROM keyednodes KN 
       WHERE [email protected] 
        and KN.ParentNode_Id is null 
      ) 
    BEGIN 
     DECLARE @ValueExistsForFirstKey as bit; SET  @ValueExistsForFirstKey=0; 

     --Load @Key's child nodes 
     INSERT INTO @ChildNodes 
     SELECT  chld.Id, chld.Value, chld.ParentNode_Id 
     FROM  keyednodes prnt 
     INNER JOIN keyednodes chld ON prnt.id=chld.ParentNode_id 
     WHERE  [email protected] 
      And  prnt.ParentNode_Id is null 

     -- Load first @Key's global Variables 
     INSERT INTO @Variable 
     SELECT  GV.Value,GV.KeyedNodes_Id,KN.Value,1,GV.ReadOnly 
     FROM  keyednodes KN 
     INNER JOIN GlobalVariables GV 
      on KN.Id=GV.KeyedNodes_Id 
      and [email protected] 
      and KN.ParentNode_Id is null 

     IF @@ROWCOUNT > 0 
      SET @ValueExistsForFirstKey=1   
     ELSE IF NOT EXISTS(SELECT * FROM @ChildNodes) 
     BEGIN 
      INSERT INTO @Variable SELECT -1,-1,-1,0,0 --Node exists but No value or children exists 
      GOTO ExitFROMProcedure 
     END 
    END 

--====== the tree-traversal Loop ==== 
    WHILE @key_depth <= @key_count 
    BEGIN 
     SET @[email protected]_depth+1 

     -- get the current Key 
     SELECT @Key=Value FROM @tbl_Keys WHERE [email protected]_depth 

     --Does Key path exist? 
     IF NOT EXISTS(SELECT * FROM @ChildNodes WHERE [email protected]) GOTO ExitFROMProcedure 

     SELECT @ParentId=Id FROM @ChildNodes WHERE [email protected] 
     SET  @KeyIsChild=1 

     delete @ChildNodes 

     INSERT INTO @ChildNodes 
     SELECT  chld.Id, chld.Value, chld.ParentNode_Id 
     FROM  keyednodes prnt 
     INNER JOIN keyednodes chld ON prnt.id=chld.ParentNode_id 
     WHERE  [email protected] 
    END 

--BREAK: jumps to here 
    if exists(SELECT * FROM @ChildNodes) 
    begin 

     INSERT INTO @Variable 
     SELECT  GV.Value, CN.id, CN.Value, 0, GV.ReadOnly 
     FROM  @ChildNodes CN 
     left join GlobalVariables GV ON CN.Id=GV.KeyedNodes_Id --children 
     union all 
     SELECT  GV.Value, GV.KeyedNodes_Id, KN.Value, 1, GV.ReadOnly 
     FROM  keyednodes KN 
     inner join GlobalVariables GV ON KN.Id=GV.KeyedNodes_Id and [email protected] 

     IF @@ROWCOUNT = 0 
     BEGIN 
      IF @key_count <> 1 
      OR NOT EXISTS (SELECT * FROM @Variable) 
       INSERT INTO @Variable SELECT -1,-1,-1,0,0 
     END 

    end 
    else 
    Begin 

     IF @KeyIsChild = 1 
     BEGIN 
      INSERT INTO @Variable 
      SELECT  GV.Value, GV.KeyedNodes_Id, KN.Value, 1, GV.ReadOnly 
      FROM  keyednodes KN 
      inner join GlobalVariables GV 
       ON KN.Id=GV.KeyedNodes_Id 
       and [email protected] --node 

      IF @@ROWCOUNT = 0 
        INSERT INTO @Variable SELECT -1,-1,-1,0,0 --Node exists but No value or children exists 
     END 
    End 

---- 
ExitFromProcedure: --Not Found jumps here 

    SELECT  KeyedNodes_Id, KeyNodeValue, Value, IsParent, Isnull(IsReadOnly,0) as IsReadOnly 
    FROM  @Variable 
    ORDER by IsParent desc, KeyNodeValue asc 

    IF(@TurnOnUpdation=1) 
    BEGIN 
     --update LastAccessDateTime 
     UPDATE GlobalVariables 
     SET  LastAccessDateTime = getdate() 
     WHERE KeyedNodes_Id IN (SELECT KeyedNodes_Id FROM @Variable) 
    END 

End 

Go 
+1

'IF ... EXISTS'はとにかく競合状態です。チェックの時点でそれがまだ2回目に行われることが保証されているわけではありません。特に「NOLOCK」だけでなく、read committedにおいても。 –

+0

ここでは奇妙な更新です。 私たちは、次のバージョンのいずれかのエラーが見つかりませんでした: のMicrosoft SQL Server 2008の(RTM) - 10.0.1600.22(インテルX86) \t 2008年7月9日午後02時43分34秒 \t著作権(C)1988年から2008年マイクロソフト株式会社 \t Enterprise EditionのWindows上でのNT 6.1 (7600ビルドします) エラーは、次のバージョンである: のMicrosoft SQL Server 2008 R2の(RTM) - 10.50.1600.1(X64) \t 2010年4月2日午後03時48分: 46 \t著作権(c)Windows NT 6.1のMicrosoft Corporation標準版(64ビット)(ビルド7601:サービスパック1) – user1021582

+0

SQLを実行しているコアの数はいくつですか? MAXDOPは何に設定されましたか? – RBarryYoung

関連する問題