質問

これは以下のリンクで解決されたと考えました - 回避は機能します - しかし、パッチはそうではありません。 Microsoftサポートと協力して解決します。

http://support.microsoft.com/kb/2606883

わかりましたので、誰かがアイデアを持っているかどうかを確認するためにStackoverflowに捨てたいという問題があります。

これは、SQL Server 2008 R2を使用しています

問題:15000のレコードを備えたテーブルから3000のレコードを削除するには、トリガーが有効になると3〜4分かかり、トリガーが無効になっている場合は3〜5秒しかかかりません。

テーブルのセットアップ

メインとセカンダリと呼ばれる2つのテーブル。セカンダリには、削除するアイテムのレコードが含まれているため、削除を実行するときにセカンダリテーブルに結合します。プロセスは、削除ステートメントの前に実行され、削除されるレコードをセカンダリテーブルに入力します。

ステートメントの削除:

DELETE FROM MAIN 
WHERE ID IN (
   SELECT Secondary.ValueInt1 
   FROM Secondary 
   WHERE SECONDARY.GUID = '9FFD2C8DD3864EA7B78DA22B2ED572D7'
);

このテーブルには、多くの列と約14の異なるNCインデックスがあります。トリガーが問題であると判断する前に、私はさまざまなことを試しました。

  • ページロックをオンにします(デフォルトでオフにしました)
  • 手動で統計を収集しました
  • 統計の無効自動収集
  • 検証済みのインデックスの健康と断片化
  • テーブルからクラスター化されたインデックスを削除しました
  • 実行計画を検討しました(インデックスが欠落しているために表示されず、コストは実際の削除に対して70%であり、レコードの結合 /マージの約28%

トリガー

テーブルには3つのトリガーがあります(挿入、更新、および削除操作用にそれぞれ1つ)。削除トリガーのコードを変更して、それを選択して、それが何回起動されるかを確認しました。操作全体(予想どおり)中に1回だけ発射します。

ALTER TRIGGER [dbo].[TR_MAIN_RD] ON [dbo].[MAIN]
            AFTER DELETE
            AS  
                SELECT 1
                RETURN

要点をまとめると

  • トリガーをオンにすると、ステートメントが完了するのに3〜4分かかります
  • トリガーがオフになると - 声明は完了するのに3〜5秒かかります

誰かが理由について何かアイデアを持っていますか?

また、このアーキテクチャを変更することを検討せず、インデックスの削除などをソリューションとして追加しようとしています。このテーブルは、いくつかの主要なデータ操作の中心的な部分であり、微調整してチューニング(インデックス、ページロックなど)を調整してチューニングする必要がありました。

これが実行プランXMLです(無実を保護するために名前が変更されました)

<?xml version="1.0" encoding="utf-16"?>
<ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.1" Build="10.50.1790.0" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
  <BatchSequence>
    <Batch>
      <Statements>
        <StmtSimple StatementCompId="1" StatementEstRows="185.624" StatementId="1" StatementOptmLevel="FULL" StatementOptmEarlyAbortReason="GoodEnoughPlanFound" StatementSubTreeCost="0.42706" StatementText="DELETE FROM MAIN WHERE ID IN (SELECT Secondary.ValueInt1 FROM Secondary WHERE Secondary.SetTMGUID = '9DDD2C8DD3864EA7B78DA22B2ED572D7')" StatementType="DELETE" QueryHash="0xAEA68D887C4092A1" QueryPlanHash="0x78164F2EEF16B857">
          <StatementSetOptions ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" ARITHABORT="false" CONCAT_NULL_YIELDS_NULL="true" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="true" />
          <QueryPlan CachedPlanSize="48" CompileTime="20" CompileCPU="20" CompileMemory="520">
            <RelOp AvgRowSize="9" EstimateCPU="0.00259874" EstimateIO="0.296614" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="185.624" LogicalOp="Delete" NodeId="0" Parallel="false" PhysicalOp="Clustered Index Delete" EstimatedTotalSubtreeCost="0.42706">
              <OutputList />
              <Update WithUnorderedPrefetch="true" DMLRequestSort="false">
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_02]" IndexKind="Clustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[PK_MAIN_ID]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[UK_MAIN_01]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_03]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_04]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_05]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_06]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_07]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_08]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_09]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_10]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_11]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[UK_MAIN_12]" IndexKind="NonClustered" />
                <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[IX_MAIN_13]" IndexKind="NonClustered" />
                <RelOp AvgRowSize="15" EstimateCPU="1.85624E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="185.624" LogicalOp="Top" NodeId="2" Parallel="false" PhysicalOp="Top" EstimatedTotalSubtreeCost="0.127848">
                  <OutputList>
                    <ColumnReference Column="Uniq1002" />
                    <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="RelationshipID" />
                  </OutputList>
                  <Top RowCount="true" IsPercent="false" WithTies="false">
                    <TopExpression>
                      <ScalarOperator ScalarString="(0)">
                        <Const ConstValue="(0)" />
                      </ScalarOperator>
                    </TopExpression>
                    <RelOp AvgRowSize="15" EstimateCPU="0.0458347" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="185.624" LogicalOp="Left Semi Join" NodeId="3" Parallel="false" PhysicalOp="Merge Join" EstimatedTotalSubtreeCost="0.12783">
                      <OutputList>
                        <ColumnReference Column="Uniq1002" />
                        <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="RelationshipID" />
                      </OutputList>
                      <Merge ManyToMany="false">
                        <InnerSideJoinColumns>
                          <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Column="ValueInt1" />
                        </InnerSideJoinColumns>
                        <OuterSideJoinColumns>
                          <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="ID" />
                        </OuterSideJoinColumns>
                        <Residual>
                          <ScalarOperator ScalarString="[MyDatabase].[dbo].[MAIN].[ID]=[MyDatabase].[dbo].[Secondary].[ValueInt1]">
                            <Compare CompareOp="EQ">
                              <ScalarOperator>
                                <Identifier>
                                  <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="ID" />
                                </Identifier>
                              </ScalarOperator>
                              <ScalarOperator>
                                <Identifier>
                                  <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Column="ValueInt1" />
                                </Identifier>
                              </ScalarOperator>
                            </Compare>
                          </ScalarOperator>
                        </Residual>
                        <RelOp AvgRowSize="19" EstimateCPU="0.0174567" EstimateIO="0.0305324" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="15727" LogicalOp="Index Scan" NodeId="4" Parallel="false" PhysicalOp="Index Scan" EstimatedTotalSubtreeCost="0.0479891" TableCardinality="15727">
                          <OutputList>
                            <ColumnReference Column="Uniq1002" />
                            <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="ID" />
                            <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="RelationshipID" />
                          </OutputList>
                          <IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" NoExpandHint="false">
                            <DefinedValues>
                              <DefinedValue>
                                <ColumnReference Column="Uniq1002" />
                              </DefinedValue>
                              <DefinedValue>
                                <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="ID" />
                              </DefinedValue>
                              <DefinedValue>
                                <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Column="RelationshipID" />
                              </DefinedValue>
                            </DefinedValues>
                            <Object Database="[MyDatabase]" Schema="[dbo]" Table="[MAIN]" Index="[PK_MAIN_ID]" IndexKind="NonClustered" />
                          </IndexScan>
                        </RelOp>
                        <RelOp AvgRowSize="11" EstimateCPU="0.00392288" EstimateIO="0.03008" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="3423.53" LogicalOp="Index Seek" NodeId="5" Parallel="false" PhysicalOp="Index Seek" EstimatedTotalSubtreeCost="0.0340029" TableCardinality="171775">
                          <OutputList>
                            <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Column="ValueInt1" />
                          </OutputList>
                          <IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" NoExpandHint="false">
                            <DefinedValues>
                              <DefinedValue>
                                <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Column="ValueInt1" />
                              </DefinedValue>
                            </DefinedValues>
                            <Object Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Index="[IX_Secondary_01]" IndexKind="NonClustered" />
                            <SeekPredicates>
                              <SeekPredicateNew>
                                <SeekKeys>
                                  <Prefix ScanType="EQ">
                                    <RangeColumns>
                                      <ColumnReference Database="[MyDatabase]" Schema="[dbo]" Table="[Secondary]" Column="SetTMGUID" />
                                    </RangeColumns>
                                    <RangeExpressions>
                                      <ScalarOperator ScalarString="'9DDD2C8DD3864EA7B78DA22B2ED572D7'">
                                        <Const ConstValue="'9DDD2C8DD3864EA7B78DA22B2ED572D7'" />
                                      </ScalarOperator>
                                    </RangeExpressions>
                                  </Prefix>
                                </SeekKeys>
                              </SeekPredicateNew>
                            </SeekPredicates>
                          </IndexScan>
                        </RelOp>
                      </Merge>
                    </RelOp>
                  </Top>
                </RelOp>
              </Update>
            </RelOp>
          </QueryPlan>
        </StmtSimple>
      </Statements>
    </Batch>
  </BatchSequence>
</ShowPlanXML>
役に立ちましたか?

解決 2

ここにMicrosoftからの公式の回答があります...これは大きな設計上の欠陥だと思います。

11/14/2011-公式の対応が変更されました。前述のように、トランザクションログを使用していません。内部ストア(行レベル)を使用して、変更されたデータをコピーします。彼らはまだそれがそんなに時間がかかった理由を判断することができません。

トリガーを削除した後の代わりにトリガーの代わりに使用することにしました。

トリガーの一部は、削除が完了し、トリガー挿入/削除されたテーブルを構築した後、トランザクションログを読み取る必要があります。これは私たちが膨大な時間を費やしており、トリガーの一部の後に設計することです。トリガーの代わりに、トランザクションログのスキャンと挿入/削除されたテーブルの構築のこの動作を防ぎます。また、すべての列をNvarChar(MAX)でドロップすると、物事がはるかに高速であることが観察されたように、LOBデータと見なされるという事実のために理にかなっています。列のデータに関する詳細については、以下の記事でAlookを使用してください。

http://msdn.microsoft.com/en-us/library/ms189087.aspx

概要:トリガーでは、削除仕上げ後にトランザクションログをスキャンする必要がある後、トランザクションログと時間の使用が必要なテーブルをビルドして挿入/削除する必要があります。

行動計画として、これは私たちが現時点で提案するものです:

A) Limit the number of rows deleted in each transaction or
B) Increase timeout settings or
C) Don't use AFTER trigger or trigger at all or
D) Limit usage of nvarchar(max) datatypes.

他のヒント

SQL Server 2005で導入された行バージョンフレームワークは、新しいトランザクション分離レベルを含む多くの機能をサポートするために使用されます READ_COMMITTED_SNAPSHOTSNAPSHOT. 。これらの分離レベルのいずれも有効になっていない場合でも、行バージョンはまだ使用されています AFTER トリガー(生成を促進するため inserteddeleted 擬似テーブル)、火星、および(別のバージョンストアで)オンラインインデックス。

として 文書化, 、エンジンは、これらの目的のいずれかにバージョンされたテーブルの各行に14バイトのポストフィックスを追加する場合があります。この動作は比較的よく知られており、14バイトのデータがインデックスのすべての行に追加されているように、 オンラインで再建されました 列を除く分離レベルが有効になっています。分離レベルが有効になっていない場合でも、1つの追加バイトが追加されます クラスタリングされていないインデックスのみ 再構築されたとき ONLINE.

アフタートリガーが存在し、バージョンが行ごとに14バイトを追加する場合、エンジン内に最適化が存在します。 避ける これは、ここでa ROW_OVERFLOW また LOB 割り当ては発生できません。実際には、これは行の最大サイズが8060バイト未満でなければならないことを意味します。計算で 最大 可能な行サイズでは、エンジンは、たとえば、Varchar(460)列に460文字が含まれている可能性があると想定しています。

動作は、で見るのが最も簡単です AFTER UPDATE トリガー、同じ原則が適用されます AFTER DELETE. 。次のスクリプトは、最大列の長さ8060バイトのテーブルを作成します。データは単一のページに適合し、そのページには13バイトの空きスペースがあります。 NO-OPトリガーが存在するため、ページが分割され、バージョン情報が追加されています。

USE Sandpit;
GO
CREATE TABLE dbo.Example
(
    ID          integer NOT NULL IDENTITY(1,1),
    Value       integer NOT NULL,
    Padding1    char(42) NULL,
    Padding2    varchar(8000) NULL,

    CONSTRAINT PK_Example_ID
    PRIMARY KEY CLUSTERED (ID)
);
GO
WITH
    N1 AS (SELECT 1 AS n UNION ALL SELECT 1),
    N2 AS (SELECT L.n FROM N1 AS L CROSS JOIN N1 AS R),
    N3 AS (SELECT L.n FROM N2 AS L CROSS JOIN N2 AS R),
    N4 AS (SELECT L.n FROM N3 AS L CROSS JOIN N3 AS R)
INSERT TOP (137) dbo.Example
    (Value)
SELECT
    ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM N4;
GO
ALTER INDEX PK_Example_ID 
ON dbo.Example 
REBUILD WITH (FILLFACTOR = 100);
GO
SELECT
    ddips.index_type_desc,
    ddips.alloc_unit_type_desc,
    ddips.index_level,
    ddips.page_count,
    ddips.record_count,
    ddips.max_record_size_in_bytes
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID(N'dbo.Example', N'U'), 1, 1, 'DETAILED') AS ddips
WHERE
    ddips.index_level = 0;
GO
CREATE TRIGGER ExampleTrigger
ON dbo.Example
AFTER DELETE, UPDATE
AS RETURN;
GO
UPDATE dbo.Example
SET Value = -Value
WHERE ID = 1;
GO
SELECT
    ddips.index_type_desc,
    ddips.alloc_unit_type_desc,
    ddips.index_level,
    ddips.page_count,
    ddips.record_count,
    ddips.max_record_size_in_bytes
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID(N'dbo.Example', N'U'), 1, 1, 'DETAILED') AS ddips
WHERE
    ddips.index_level = 0;
GO
DROP TABLE dbo.Example;

スクリプトは、以下に示す出力を生成します。シングルページテーブルは2ページに分割され、最大 物理的 行の長さは57バイトから71バイト(= +14バイト)増加しました。

Update example

DBCC PAGE 単一の更新された行があることを示しています Record Attributes = NULL_BITMAP VERSIONING_INFO Record Size = 71, 、一方、テーブル内の他のすべての行があります Record Attributes = NULL_BITMAP; record Size = 57.

同じスクリプト、 UPDATE 単一の行に置き換えられました DELETE 表示されている出力を生成します:

DELETE dbo.Example
WHERE ID = 1;

Delete example

合計で1つの行が少ない(もちろん!)が、最大の物理行サイズは増加していません。行バージョンの情報は、トリガーの擬似テーブルに必要な行にのみ追加され、その行は最終的に削除されました。ただし、ページの分割は残ります。このページスプリッティアクティビティは、トリガーが存在したときに観察される遅いパフォーマンスの原因です。の定義の場合 Padding2 列はから変更されます varchar(8000)varchar(7999), 、ページが分割されなくなりました。

これも参照してください ブログ投稿 SQL ServerによるMVP Dmitri Korotkevitchは、断片化への影響についても説明しています。

計画によれば、すべてが正しく進んでいます。別の計画を提供する代わりに、削除を結合として書くことを試みることができます。

DELETE m
FROM MAIN m
JOIN Secondary s ON m.ID = s.ValueInt1
AND s.SetTMGUID = '9DDD2C8DD3864EA7B78DA22B2ED572D7'

しかし、それがどれだけ役立つかはわかりません。テーブル上のトリガーで削除が実行されているとき、セッションの待機タイプは削除を実行しますか?

ライセンス: CC-BY-SA帰属
所属していません dba.stackexchange
scroll top