2014-04-03 43 views
0

我爲兩個存儲過程設置了內部激活。一個插入一個或多個記錄,另一個記錄更新同一個表中的一個或多個記錄。所以,我有兩個發起者,兩個目標隊列。 到目前爲止,它在開發中工作正常,但我不知道當我們將它移動到prod中經常調用這兩個存儲過程時,可能會遇到什麼類型的問題。我們已經遇到了由這兩個存儲過程導致的死鎖問題。異步執行是這個實現的主要目標。SQL Service Broker內部激活問題

問題:

  1. 有沒有辦法使用一個目標隊列的存儲程序,以防止死鎖的任何機會的方法嗎?

  2. 有什麼我可以做的,讓它更可靠嗎?像一個執行錯誤不應該阻止傳入請求 到隊列?

  3. 提高可伸縮性的提示(每秒執行次數很多)?

  4. 如果存在死鎖,我可以設置RETRY嗎?

這裏是插入存儲過程的部分代碼;

CREATE QUEUE [RecordAddUsersQueue]; 
CREATE SERVICE [RecordAddUsersService] ON QUEUE [RecordAddUsersQueue]; 

ALTER QUEUE [AddUsersQueue] WITH ACTIVATION 
( STATUS   = ON, 
     MAX_QUEUE_READERS = 1, --or 10? 
     PROCEDURE_NAME = usp_AddInstanceUsers, 
     EXECUTE AS OWNER); 

CREATE PROCEDURE [dbo].[usp_AddInstanceUsers] @UsersXml xml 
AS 
BEGIN 
    DECLARE @Handle uniqueidentifier; 

    BEGIN DIALOG CONVERSATION @Handle 
    FROM SERVICE [RecordAddUsersService] 
    TO SERVICE 'AddUsersService' 
    ON CONTRACT [AddUsersContract] 
    WITH ENCRYPTION = OFF; 

    SEND ON CONVERSATION @Handle 
    MESSAGE TYPE [AddUsersXML] (@UsersXml); 
END 
GO 

CREATE PROCEDURE [dbo].[usp_SB_AddInstanceUsers] 
AS 
BEGIN 
    DECLARE @Handle uniqueidentifier; 
    DECLARE @MessageType sysname; 
    DECLARE @UsersXML xml; 

    WHILE (1 = 1) 
    BEGIN 
    BEGIN TRANSACTION; 
     WAITFOR 
     (RECEIVE TOP (1) 
     @Handle  = conversation_handle, 
     @MessageType = message_type_name, 
     @UsersXML = message_body 
     FROM [AddUsersQueue]), TIMEOUT 5000; 
     IF (@@ROWCOUNT = 0) 
     BEGIN 
     ROLLBACK TRANSACTION; 
     BREAK; 
     END 

     IF (@MessageType = 'ReqAddUsersXML') 
     BEGIN 
     --<INSERT>.... 
     DECLARE @ReplyMsg nvarchar(100); 
     SELECT 
      @ReplyMsg = N'<ReplyMsg>Message for AddUsers Initiator service.</ReplyMsg>'; 
     SEND ON CONVERSATION @Handle 
     MESSAGE TYPE [RepAddUsersXML] (@ReplyMsg); 
     END 

     ELSE 
     IF @MessageType = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog' 
     BEGIN 
     END CONVERSATION @Handle; 
     END 
     ELSE 
     IF @MessageType = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error' 
     BEGIN 
     END CONVERSATION @Handle; 
     END 
    COMMIT TRANSACTION; 
    END 
END 
GO 

謝謝

Kuzey

回答

3

有沒有辦法使用一個目標隊列的存儲程序,以防止死鎖的任何機會的方法嗎?

你可以和你應該。沒有理由有兩個目標服務/隊列/程序。發送到相同的服務,兩種不同的消息類型爲您所需的兩個操作。然後激活的過程應根據消息類型執行添加邏輯或更新邏輯。

有什麼我可以做的,使它更可靠嗎?像一個執行錯誤不應該阻止傳入的隊列請求?

SSB激活將非常可靠,這不會是一個問題。只要您嚴格遵守事務邊界(在處理完成之前不要提交出列操作),您將永遠不會丟失消息/更新。

提高可伸縮性的技巧(每秒執行次數很高)?

閱讀Writing Service Broker ProceduresReusing Conversations。要實現高吞吐量處理,您必須將批處理(TOP(1000))出列並處理爲@table變量。有關可用於處理一批消息的模式,請參閱Exception handling and nested transactions。你需要閱讀並理解Conversation Group Locks

如果存在死鎖,我可以設置RETRY嗎?

沒有必要,SSB激活將重試爲您。在回滾時,出列(RECEIVE)將回滾,從而使消息再次可用於激活,並且該過程將自動重試。請注意,連續5次回滾將觸發poison message trap

MAX_QUEUE_READERS = 1, - 或10?

如果1無法處理負載,請添加更多。只要你理解正確的對話組鎖定,並行激活的過程應處理不相關的業務項目,永不死鎖。如果在同一隊列上激活的過程的實例之間遇到死鎖,這意味着您在對話組邏輯中存在缺陷,並且允許SSB看到的消息不相關(不同組),以修改相同的數據庫記錄(相同的業務實體)陷入僵局。

順便說一句,您還必須在啓動程序服務隊列上具有激活的過程。請參閱How to prevent conversation endpoint leaks

+0

_「然後激活的過程應執行添加邏輯或更新邏輯,具體取決於消息類型。」_合併插入和更新存儲過程將需要在應用程序端進行重大更改。如果該應用仍然會調用插入和更新sprocs,但是這兩個sprocs會調用第三個sproc,這將是新的激活sproc,具有不同的參數/ message_types? – Binsel

+0

我不知道你的代碼,但通常應該是一個簡單的過程。今天你有兩個過程,每個過程都有一個塊'IF @messageType ='someType'BEGIN END'。您將簡單合併IF,例如'IF @messageType ='someType'BEGIN END IF @messageType ='otherType'BEGIN END' –

+0

使處理句柄「TOP(1000)」更加複雜,但並不多。也是一個直接的,機械的變革。在遊標循環內部接收到@table,打開遊標,然後應用與之前一樣的邏輯(IF @message Type ...)。必須小心,不要過早提交,必須在提交之前處理@table中的所有消息,否則您有可能在發生錯誤時丟失消息。 –