Xóa chậm các bản ghi khi kích hoạt kích hoạt


17

Nghĩ rằng điều này đã được giải quyết với liên kết dưới đây - công việc xung quanh hoạt động - nhưng bản vá thì không. Làm việc với bộ phận hỗ trợ của Microsoft để giải quyết.

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

Ok vì vậy tôi có một vấn đề mà tôi muốn ném ra StackOverflow để xem ai đó có ý tưởng gì không.

Lưu ý điều này là với SQL Server 2008 R2

Vấn đề: Xóa 3000 bản ghi khỏi bảng với 15000 bản ghi mất 3-4 phút khi kích hoạt kích hoạt và chỉ 3-5 giây khi kích hoạt bị vô hiệu hóa.

Thiết lập bảng

Hai bảng chúng tôi sẽ gọi Chính và Phụ. Thứ cấp chứa các bản ghi của các mục tôi muốn xóa vì vậy khi tôi thực hiện xóa, tôi tham gia vào bảng phụ. Một tiến trình chạy trước câu lệnh xóa để điền vào bảng phụ với các bản ghi sẽ bị xóa.

Xóa tuyên bố:

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

Bảng này có rất nhiều cột và khoảng 14 Chỉ số NC khác nhau. Tôi đã thử một loạt các thứ khác nhau trước khi tôi xác định kích hoạt là vấn đề.

  • Bật khóa trang (chúng tôi đã tắt theo mặc định)
  • Số liệu thống kê được thu thập bằng tay
  • Tự động thu thập số liệu thống kê
  • Xác nhận chỉ số sức khỏe và phân mảnh
  • Bỏ chỉ số cụm từ bảng
  • Đã xem xét kế hoạch thực hiện (không có gì hiển thị là các chỉ mục bị thiếu và chi phí là 70 phần trăm đối với việc xóa thực tế với khoảng 28 phần trăm cho việc nối / hợp nhất các bản ghi

Gây nên

Bảng này có 3 kích hoạt (mỗi kích hoạt cho các hoạt động chèn, cập nhật và xóa). Tôi đã sửa đổi mã cho trình kích hoạt xóa để trở lại, sau đó chọn một mã để xem nó được bắn bao nhiêu lần. Nó chỉ bắn một lần trong toàn bộ hoạt động (như mong đợi).

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

Tóm lại

  • Với Trigger on - statement mất 3-4 phút để hoàn thành
  • Khi tắt Kích hoạt - câu lệnh mất 3-5 giây để hoàn thành

Bất cứ ai có bất kỳ ý tưởng như tại sao?

Cũng lưu ý - không tìm cách thay đổi kiến ​​trúc này, thêm xóa chỉ mục, v.v. như một giải pháp. Bảng này là phần trung tâm cho một số hoạt động dữ liệu chính và chúng tôi đã phải điều chỉnh và điều chỉnh nó (chỉ mục, khóa trang, v.v.) để cho phép các hoạt động đồng thời chính hoạt động mà không bị bế tắc.

Dưới đây là kế hoạch thực hiện xml (tên đã được thay đổi để bảo vệ người vô tội)

<?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>

Câu trả lời:


12

Khung phiên bản hàng được giới thiệu trong SQL Server 2005 được sử dụng để hỗ trợ một số tính năng, bao gồm các mức cô lập giao dịch mới READ_COMMITTED_SNAPSHOTSNAPSHOT. Ngay cả khi không ai trong số các mức cô lập được kích hoạt, hàng-versioning vẫn được sử dụng cho AFTERtrigger (để tạo điều kiện thế hệ của inserteddeletedgiả bảng), MARS, và (trong một cửa hàng phiên bản riêng biệt) lập chỉ mục trực tuyến.

Theo tài liệu , công cụ có thể thêm một hậu tố 14 byte vào mỗi hàng của bảng được phiên bản cho bất kỳ mục đích nào trong số này. Hành vi này tương đối nổi tiếng, cũng như việc bổ sung dữ liệu 14 byte vào mỗi hàng của một chỉ mục được xây dựng lại trực tuyến với mức cách ly phiên bản hàng được kích hoạt. Ngay cả khi các mức cô lập không được bật, một byte bổ sung được thêm vào các chỉ mục không được nhóm chỉ khi được xây dựng lại ONLINE.

Khi có trình kích hoạt SAU và phiên bản sẽ thêm 14 byte mỗi hàng, tối ưu hóa tồn tại trong công cụ để tránh điều này, nhưng không thể xảy ra phân bổ ROW_OVERFLOWhoặc LOBphân bổ. Trong thực tế, điều này có nghĩa là kích thước tối đa có thể của một hàng phải nhỏ hơn 8060 byte. Khi tính toán kích thước hàng tối đa có thể, động cơ giả định rằng ví dụ cột VARCHAR (460) có thể chứa 460 ký tự.

Hành vi là dễ thấy nhất với một AFTER UPDATEkích hoạt, mặc dù nguyên tắc tương tự áp dụng cho AFTER DELETE. Kịch bản sau đây tạo một bảng có độ dài liên tiếp tối đa là 8060 byte. Dữ liệu phù hợp trên một trang, với 13 byte không gian trống trên trang đó. Một trình kích hoạt không tồn tại, vì vậy trang được phân chia và thêm thông tin phiên bản:

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;

Kịch bản tạo ra đầu ra được hiển thị dưới đây. Bảng một trang được chia thành hai trang và độ dài hàng vật lý tối đa đã tăng từ 57 lên 71 byte (= +14 byte cho thông tin phiên bản hàng).

Cập nhật ví dụ

DBCC PAGEcho thấy hàng duy nhất được cập nhật có Record Attributes = NULL_BITMAP VERSIONING_INFO Record Size = 71, trong khi tất cả các hàng khác trong bảng có Record Attributes = NULL_BITMAP; record Size = 57.

Kịch bản tương tự, được UPDATEthay thế bằng một hàng duy nhất DELETEtạo ra đầu ra được hiển thị:

DELETE dbo.Example
WHERE ID = 1;

Xóa ví dụ

Tổng cộng có một hàng ít hơn (tất nhiên!), Nhưng kích thước hàng vật lý tối đa không tăng. Thông tin phiên bản hàng chỉ được thêm vào các hàng cần thiết cho bảng giả kích hoạt và hàng đó cuối cùng đã bị xóa. Việc chia trang vẫn còn, tuy nhiên. Hoạt động phân tách trang này chịu trách nhiệm cho hiệu suất chậm được quan sát khi có trình kích hoạt. Nếu định nghĩa của Padding2cột được thay đổi từ varchar(8000)thành varchar(7999), trang không còn bị chia tách.

Cũng xem bài đăng blog này của SQL Server MVP Dmitri Korotkevitch, cũng thảo luận về tác động đối với sự phân mảnh.



5

Vâng, đây là phản hồi chính thức từ Microsoft ... mà tôi nghĩ là một lỗ hổng thiết kế lớn.

14/11/2011 - Phản hồi chính thức đã thay đổi. Họ không sử dụng nhật ký giao dịch như đã nêu trước đây. Họ đang sử dụng cửa hàng nội bộ (cấp hàng) để sao chép dữ liệu đã thay đổi vào. Họ vẫn không thể xác định lý do tại sao nó mất quá nhiều thời gian.

Chúng tôi quyết định sử dụng Thay vì kích hoạt thay cho sau khi xóa kích hoạt.

Phần SAU của trình kích hoạt khiến chúng ta phải đọc qua nhật ký giao dịch sau khi xóa hoàn thành và xây dựng bảng chèn / xóa kích hoạt. Đây là nơi chúng ta dành nhiều thời gian và theo thiết kế cho phần SAU của bộ kích hoạt. Trình kích hoạt INSTEAD OF sẽ ngăn hành vi quét nhật ký giao dịch này và xây dựng bảng được chèn / xóa. Ngoài ra, như đã thấy rằng mọi thứ sẽ nhanh hơn nhiều nếu chúng ta bỏ tất cả các cột bằng nvarchar (max), điều này có ý nghĩa do thực tế rằng nó được coi là dữ liệu LOB. Vui lòng xem lại bài viết dưới đây để biết thêm thông tin về dữ liệu trong hàng:

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

Tóm tắt: Trình kích hoạt SAU yêu cầu quét lại nhật ký giao dịch sau khi quá trình xóa kết thúc, sau đó chúng ta phải xây dựng và chèn / xóa bảng yêu cầu sử dụng nhiều hơn nhật ký giao dịch và thời gian.

Vì vậy, như một kế hoạch hành động, đây là những gì chúng tôi đề xuất tại thời điểm này:

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.

2

Theo kế hoạch mọi thứ đang diễn ra chính xác. Bạn có thể thử viết xóa dưới dạng THAM GIA thay vì IN sẽ cung cấp cho bạn một kế hoạch khác.

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

Tôi không chắc sẽ giúp được bao nhiêu. Khi xóa đang chạy với các kích hoạt trên bảng, loại chờ cho phiên thực hiện xóa là gì?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.