SQL Server Service Broker 是SQL server里面比較獨特的一個功能。它可幫助開發人員構建異步的松散耦合應用程序
ServiceBroker入門文章:http://blogs.msdn.com/b/apgcdsd/archive/2012/07/27/sql-server-service-broker-demo.aspx
ServiceBroker的隊列存在自動激活(ACTIVATION)功能,其中內部激活可以激活數據庫存儲過程接受和處理隊列的消息,而且可以啟動激活存儲過程的多個實例(MAX_QUEUE_READERS)。當SQLServer的SCHEDULER個數大于1(即多CPU)時,會有多個實例同時去接受并處理消息。
所以,接下來以發送出庫消息,接受并更新庫存為例,模擬并發條件下的庫存檢查及更新過程。
2.實現步驟2.1 創建測試數據庫及表代碼如下:其中inventory為庫存表,表中兩個字段,產品和庫存數量
use mastercreate database wmsgouse wms gocreate table inventory( material int, quantity int)2.2 創建ServiceBroker對象,搭建基礎框架
創建ServiceBroker服務對象,包括消息類型、約定、隊列及服務等。簡化起見,演示程序是運行在同一個數據庫實例下的同一個數據庫中
use wms--創建消息類型create message type inventoryio--創建約定create contract inventory_contract( inventoryio sent by initiator)--創建客戶端隊列create queue inventory_client_queue--創建客戶端服務create service inventory_client on queue inventory_client_queue;--創建庫存隊列create queue inventoryio_queue--創建庫存更新服務create service inventoryioon queue inventoryio_queue( [inventory_contract]);2.3 開啟會話,發送出庫消息
--發送出庫消息begin transaction declare @dialog_id uniqueidentifier begin dialog conversation @dialog_id from service inventory_client to service 'inventoryio' on contract [inventory_contract] with encryption = off; send on conversation @dialog_id message type inventoryio ( '<InventoryUpdate> <material>1</material> <quantity>1</quantity> </InventoryUpdate>' ); commit transaction;
為了記下來測試并發情況,連續運行上面的代碼四次。發送完后,查詢目標服務的隊列
select * from inventoryio_queue
結果如下:
接下里創建存儲過程,該存儲過程接受隊列的出庫指令,檢查庫存,當庫存滿足時,更新庫存;庫存不足時,回滾事務,消息重新回到隊列
代碼如下:
create PRoc InventoryProc asbegin transaction declare @dialog_id uniqueidentifier declare @message_body xml declare @quantity int; declare @material int; waitfor( receive @dialog_id = conversation_handle, @message_body = message_body from [dbo].inventoryio_queue),timeout 5000; if(@dialog_id is not null) begin set @quantity = @message_body.value('(/InventoryUpdate/quantity)[1]','int'); set @material = @message_body.value('(/InventoryUpdate/material)[1]','int'); --檢查庫存是否足夠 if exists( select 1 from inventory where material = @material and quantity>=@quantity) begin print 'come here' --更新庫存 Update inventory set quantity =quantity-@quantity where material = @material; end else begin rollback return end end end conversation @dialog_id; commit transaction; go2.5 修改隊列,啟用自動激活功能
啟用隊列的自動激活功能,激活的存儲過程為上面創建的存儲,設置最大讀取器個數為5.
ALTER QUEUE [dbo].[inventoryio_queue] WITH STATUS = ON , RETENTION = OFF , ACTIVATION ( STATUS = ON , PROCEDURE_NAME = [dbo].[InventoryProc] , MAX_QUEUE_READERS = 5 , EXECUTE AS N'dbo' ), POISON_MESSAGE_HANDLING (STATUS = OFF)
注意:
POISON_MESSAGE_HANDLING的設置必須為OFF,將有害消息處理設置為 OFF 的隊列在五個連續的事務回滾之后不會被禁用。否則默認為ON,隊列在回滾5次后,會被禁用啟用自動激活后,可以查看目前激活的實例select * from sys.dm_broker_activated_tasks2.6 插入庫存,查看更新結果
insert into inventoryvalues(1,1)select * from inventoryio_queueselect * from inventory
結果如下:
可以看到隊列中已經沒有消息了,庫存數量已經減少,但更新結果不準確.原因是在更新庫存之前,多個存儲過程實例都讀取了庫存數,并判斷出庫存滿足,然后對庫存進行了更新。注:最終庫存數量不一定為-3,需視Scheduler數量以及同時有多少個存儲過程實例可以獲得CPU來執行有關解決方法: 一種是設置隊列的自動激活的最大實例數為1,即不允許并發讀取另外就是在讀取庫存時,增加提示 with(holdlock),這樣只允許一個實例讀取庫存表的一行數據,直到事務結束。可查閱這篇文章:http://www.cnblogs.com/buaaboyi/archive/2011/08/30/2159860.html代碼如下:
--檢查庫存是否足夠 if exists( select 1 from inventory with(holdlock) where material = @material and quantity>=@quantity)3. 總結
本文演示了一個ServiceBroker單數據庫的簡單實例,并介紹了自動激活機制。簡單涉及了SQLServer的CPU調度。同時,提及了SQLServer的 with(holdlock)提示
注:本文代碼僅供演示使用而非實際應用于生產環境。如有問題及建議,請指正!^_^
參考文章:http://www.cnblogs.com/markj/archive/2013/03/31/2991777.html
新聞熱點
疑難解答