Sql-Server
合併導致死鎖
我們正在執行 MS SQL Server 2008 R2。
我們有一個 VendorMaster 表(大約有 8700 行),我們試圖使用 Merge 語句在兩個數據庫之間保持同步。合併程式碼在 SQL 調度程序中每 5 分鐘執行一次,但由於死鎖情況,大約每小時執行一次。sql 程式碼在 management studio 中執行時,大約需要 28 秒才能執行。
源表定義如下:
/****** Object: Table [dbo].[VendorMaster] Script Date: 09/18/2015 10:59:36 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[VendorMaster]( [VendorID] [int] IDENTITY(1,1) NOT NULL, [Name] [char](50) NOT NULL, [AddLine1] [char](50) NULL, [AddLine2] [char](50) NULL, [City] [char](25) NULL, [State] [char](3) NULL, [Zip] [char](10) NULL, [Phone] [char](20) NULL, [Fax] [char](14) NULL, [Commission] [real] NULL, [Contact] [char](50) NULL, [MemberPercent] [real] NULL, [Link_Variable] [varchar](12) NULL, [Click_To_URL] [varchar](255) NULL, [Vendor_Label] [varchar](64) NULL, [image_path] [varchar](255) NULL, [vmt_code] [int] NOT NULL, [Description] [varchar](250) NULL, [vm_date_added] [datetime] NULL, [vm_last_modified] [datetime] NULL, [vm_who_modified] [char](10) NULL, [ContactEmail] [varchar](255) NULL, [CoBrandedPage] [bit] NULL, [CoBrandedPageDesc] [varchar](8000) NULL, [CoBrandedPageLastChecked] [datetime] NULL, CONSTRAINT [PK___2__11] PRIMARY KEY CLUSTERED ( [VendorID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY], CONSTRAINT [Unq_VendorID] UNIQUE NONCLUSTERED ( [VendorID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] ) ON [PRIMARY] GO SET ANSI_PADDING OFF GO ALTER TABLE [dbo].[VendorMaster] ADD CONSTRAINT [DF_VendorMaster_vmt_code] DEFAULT (1) FOR [vmt_code] GO ALTER TABLE [dbo].[VendorMaster] ADD CONSTRAINT [DF_VendorMaster_vm_date_added] DEFAULT (getdate()) FOR [vm_date_added] GO ALTER TABLE [dbo].[VendorMaster] ADD DEFAULT ((0)) FOR [CoBrandedPage] GO
另一個數據庫中的目標表定義為:
/****** Object: Table [dbo].[VendorMaster] Script Date: 09/18/2015 11:00:55 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[VendorMaster]( [VendorID] [int] IDENTITY(1,1) NOT NULL, [Name] [varchar](50) NOT NULL, [AddLine1] [varchar](50) NULL, [AddLine2] [varchar](50) NULL, [City] [varchar](50) NULL, [State] [varchar](50) NULL, [Zip] [varchar](10) NULL, [Phone] [varchar](20) NULL, [Fax] [varchar](14) NULL, [Commission] [real] NULL, [Contact] [varchar](50) NULL, [MemberPercent] [real] NULL, [Link_Variable] [varchar](12) NULL, [Click_To_URL] [varchar](255) NULL, [Vendor_Label] [varchar](64) NULL, [image_path] [varchar](255) NULL, [vmt_code] [int] NOT NULL, [Description] [varchar](250) NULL, [vm_date_added] [datetime] NULL, [vm_last_modified] [datetime] NULL, [vm_who_modified] [varchar](10) NULL, [ContactEmail] [varchar](255) NULL, [CoBrandedPage] [bit] NULL, [CoBrandedPageDesc] [varchar](8000) NULL, [CoBrandedPageLastChecked] [datetime] NULL, CONSTRAINT [PK___2__11] PRIMARY KEY CLUSTERED ( [VendorID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY], CONSTRAINT [Unq_VendorID] UNIQUE NONCLUSTERED ( [VendorID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] ) ON [PRIMARY] GO SET ANSI_PADDING OFF GO ALTER TABLE [dbo].[VendorMaster] ADD CONSTRAINT [DF_VendorMaster_vmt_code] DEFAULT ((1)) FOR [vmt_code] GO ALTER TABLE [dbo].[VendorMaster] ADD CONSTRAINT [DF_VendorMaster_vm_date_added] DEFAULT (getdate()) FOR [vm_date_added] GO ALTER TABLE [dbo].[VendorMaster] ADD DEFAULT ((0)) FOR [CoBrandedPage] GO
這是我們的 Merge 語句的程式碼:
SET QUOTED_IDENTIFIER OFF SET IDENTITY_INSERT targetdb.dbo.vendormaster ON BEGIN TRAN; MERGE targetdb.dbo.vendormaster AS tvm USING vendormaster AS svm ON tvm.vendorid = svm.vendorid WHEN NOT MATCHED BY TARGET THEN INSERT ( VendorID ,Name ,AddLine1 ,AddLine2 ,City ,State ,Zip ,Phone ,Fax ,Commission ,Contact ,MemberPercent ,Link_Variable ,Click_To_URL ,Vendor_Label ,image_path ,vmt_code ,Description ,vm_date_added ,vm_last_modified ,vm_who_modified ,ContactEmail ,CoBrandedPage ,CoBrandedPageDesc ,CoBrandedPageLastChecked ) VALUES ( svm.VendorID ,svm.Name ,svm.AddLine1 ,svm.AddLine2 ,svm.City ,svm.State ,svm.Zip ,svm.Phone ,svm.Fax ,svm.Commission ,svm.Contact ,svm.MemberPercent ,svm.Link_Variable ,svm.Click_To_URL ,svm.Vendor_Label ,svm.image_path ,svm.vmt_code ,svm.Description ,svm.vm_date_added ,svm.vm_last_modified ,svm.vm_who_modified ,svm.ContactEmail ,svm.CoBrandedPage ,svm.CoBrandedPageDesc ,svm.CoBrandedPageLastChecked ) WHEN MATCHED AND EXISTS (SELECT svm.VendorID ,svm.Name ,svm.AddLine1 ,svm.AddLine2 ,svm.City ,svm.State ,svm.Zip ,svm.Phone ,svm.Fax ,svm.Commission ,svm.Contact ,svm.MemberPercent ,svm.Link_Variable ,svm.Click_To_URL ,svm.Vendor_Label ,svm.image_path ,svm.vmt_code ,svm.Description ,svm.vm_date_added ,svm.vm_last_modified ,svm.vm_who_modified ,svm.ContactEmail ,svm.CoBrandedPage ,svm.CoBrandedPageDesc ,svm.CoBrandedPageLastChecked EXCEPT SELECT tvm.VendorID ,tvm.Name ,tvm.AddLine1 ,tvm.AddLine2 ,tvm.City ,tvm.State ,tvm.Zip ,tvm.Phone ,tvm.Fax ,tvm.Commission ,tvm.Contact ,tvm.MemberPercent ,tvm.Link_Variable ,tvm.Click_To_URL ,tvm.Vendor_Label ,tvm.image_path ,tvm.vmt_code ,tvm.Description ,tvm.vm_date_added ,tvm.vm_last_modified ,tvm.vm_who_modified ,tvm.ContactEmail ,tvm.CoBrandedPage ,tvm.CoBrandedPageDesc ,tvm.CoBrandedPageLastChecked ) THEN UPDATE SET tvm.Name = svm.Name ,tvm.AddLine1 = svm.AddLine1 ,tvm.AddLine2 = svm.AddLine2 ,tvm.City = svm.City ,tvm.State = svm.State ,tvm.Zip = svm.Zip ,tvm.Phone = svm.Phone ,tvm.Fax = svm.Fax ,tvm.Commission = svm.Commission ,tvm.Contact = svm.Contact ,tvm.MemberPercent = svm.MemberPercent ,tvm.Link_Variable = svm.Link_Variable ,tvm.Click_To_URL = svm.Click_To_URL ,tvm.Vendor_Label = svm.Vendor_Label ,tvm.image_path = svm.image_path ,tvm.vmt_code = svm.vmt_code ,tvm.Description = svm.Description ,tvm.vm_date_added = svm.vm_date_added ,tvm.vm_last_modified = svm.vm_last_modified ,tvm.vm_who_modified = svm.vm_who_modified ,tvm.ContactEmail = svm.ContactEmail ,tvm.CoBrandedPage = svm.CoBrandedPage ,tvm.CoBrandedPageDesc = svm.CoBrandedPageDesc ,tvm.CoBrandedPageLastChecked = svm.CoBrandedPageLastChecked WHEN NOT MATCHED BY SOURCE THEN DELETE; --OUTPUT $action, Inserted.*, Deleted.*; commit TRAN; SET IDENTITY_INSERT targetdb.dbo.vendormaster OFF update targetdb.dbo.vendormaster set vmt_code = 3 where vmt_code = 1 and VendorID in ( select VendorID from VendorException ve where ve.MainVendorId in (select VendorID from targetdb.dbo.VendorMergeExceptions) ) SET QUOTED_IDENTIFIER ON GO
這是此更新的合併語句的執行計劃:
http://beta1.igive.com/test/igive.zip
大約每小時一次,由於與其他程序的死鎖,查詢被終止。除了這個之外,目標數據庫中沒有任何程序正在更新 VendorMaster 表。那麼,這似乎表明死鎖必鬚髮生在源數據庫中?正確的?合併期間源表中的行是否被鎖定?我們可以指定只對源表進行讀鎖定嗎?
我的建議是你做一個合併 upsert(只插入和更新)。如果要刪除記錄,我建議您在源和目標之間創建一個臨時表。登台表被清除、插入,然後與目標表合併。創建一個按計劃清理源表的作業(如果需要)。我建議您在您的情況下將刪除排除在合併之外。
至於你的僵局。執行計劃不會告訴你太多。您需要生成死鎖跟踪。您可以通過在 SQL-profiler 中打開跟踪(謹慎/臨時)來做到這一點。閱讀 MSDN 了解更多資訊。
死鎖跟踪可以生成一個死鎖圖,向您顯示正在發生的事情以及究竟發生了什麼死鎖。您可以分別在源表和目標表上生成 Shared-X 鎖和獨占鎖。