CFLOCK多人共用資源鎖定/獨占
WEB是個多人共用的執行環境。
好處是多人多工執行緒,不會互相干擾。
缺點是同時搶資源時處理上就很傷腦筋。
什麼叫『搶資源』,白話說搶票啦,簡單說就是希望讓多人執行可以排隊做"某件事"。
假如資料庫裡面有一個表Tickets,裡面裡面有500筆資料,每一筆都是獨立票號。
當客人要來取票時,系統會找出Tickets裡面沒有被使用的票號發給客人。
所以程式碼會寫成:
邏輯上看似沒有問題,但是請別忘記WEB是多人操作的,
結果就發生同時有2個人很湊巧的同一秒鐘按下取票,
因此上面程式碼就被兩個執行緒同時執行。
此時,兩個客人就會取到同一個票號 !!!
也許兩個執行緒有微秒之差,
但是在先進入的執行緒還沒有執行UPDATE前,第二個執行緒已經在執行SELECT了
所以,兩方都會取得相同的票號。
如何避免?
一、在SELECT前先執行SQL table lock,像是LOCK Tables...,但很遺憾,不是每一種資料庫都支援這樣的指令,當有一天要更換資料庫時,你的程式碼就得再大風吹一次,很可憐的.
二、在資料庫裡面寫個獨占式StoreProcedure,這個可以,但是你要再學資料庫的T-SQL語言,另外如果你公司很大,有獨立的DBA部門,搞不好DBA不想作這段程式碼,又得三請四託的很麻煩。
三、連線設定改成autocommit = false,這可能是大大大問題,應用程式也許這樣玩可以,但是WEB程式就不建議這樣作,其中有諸多原因,大致上說只要任一個使用者的commit沒執行,或者是程式碼忘了寫Commit就會大大大當機啦!!!
四、就是今天要介紹的,CFML有提供特殊指令CFLOCK,利用此指令可以鎖定(獨占)程式碼方塊、執行緒、甚至到整個網站都可以。所以現在就是利用這個指令來作比較方便。
我的作法,把需要動用到這個資料表程式碼便片段用CFLOCAK包起來(紅色)。
CFLOCK設定TYPE="EXCLUSIVE"為獨占式,一次只允許一個執行緒執行。其他執行緒就是排隊等待被執行。
也就是說不管多少人執行這個網頁,只要遇到 blockTickets 這個 CFLOCK 區塊就是得排隊等待前面一個人執行結束。
所以,簡單的方式就可以解決搶票重複的問題了(大家都來排隊)。
好處是多人多工執行緒,不會互相干擾。
缺點是同時搶資源時處理上就很傷腦筋。
什麼叫『搶資源』,白話說搶票啦,簡單說就是希望讓多人執行可以排隊做"某件事"。
假如資料庫裡面有一個表Tickets,裡面裡面有500筆資料,每一筆都是獨立票號。
當客人要來取票時,系統會找出Tickets裡面沒有被使用的票號發給客人。
所以程式碼會寫成:
<cfquery name="check1" datasource="SQLDB">
Select top 1 tick_no
From Tickets
Where tick_used = 'N'
</cfquery>
<cfif check1.recordcount GT 0>
<cfset newTicket = check1.tick_no>
<cfquery name="update1" datasource="SQLDB">
Update Tickets
Set tick_used = 'Y' , cust_id = '#LoginCust#'
Where Tick_no = '#newTicket#'
</cfquery>
</cfif>
<cftransaction action="commit" />
<cfif isdefined("newTicket")>
<cfoutput>You Got Tick No : #newTicket#</cfoutput>
<cfelse>
<cfoutput>No More Tickets can get !</cfoutput>
</cfif>
邏輯上看似沒有問題,但是請別忘記WEB是多人操作的,
結果就發生同時有2個人很湊巧的同一秒鐘按下取票,
因此上面程式碼就被兩個執行緒同時執行。
此時,兩個客人就會取到同一個票號 !!!
也許兩個執行緒有微秒之差,
但是在先進入的執行緒還沒有執行UPDATE前,第二個執行緒已經在執行SELECT了
所以,兩方都會取得相同的票號。
如何避免?
一、在SELECT前先執行SQL table lock,像是LOCK Tables...,但很遺憾,不是每一種資料庫都支援這樣的指令,當有一天要更換資料庫時,你的程式碼就得再大風吹一次,很可憐的.
二、在資料庫裡面寫個獨占式StoreProcedure,這個可以,但是你要再學資料庫的T-SQL語言,另外如果你公司很大,有獨立的DBA部門,搞不好DBA不想作這段程式碼,又得三請四託的很麻煩。
三、連線設定改成autocommit = false,這可能是大大大問題,應用程式也許這樣玩可以,但是WEB程式就不建議這樣作,其中有諸多原因,大致上說只要任一個使用者的commit沒執行,或者是程式碼忘了寫Commit就會大大大當機啦!!!
四、就是今天要介紹的,CFML有提供特殊指令CFLOCK,利用此指令可以鎖定(獨占)程式碼方塊、執行緒、甚至到整個網站都可以。所以現在就是利用這個指令來作比較方便。
我的作法,把需要動用到這個資料表程式碼便片段用CFLOCAK包起來(紅色)。
<cflock name="blockTickets" timeout="180" type="exclusive">
<cfquery name="check1" datasource="SQLDB">
Select top 1 tick_no
From Tickets
Where tick_used = 'N'
</cfquery>
<cfif check1.recordcount GT 0>
<cfset newTicket = check1.tick_no>
<cfquery name="update1" datasource="SQLDB">
Update Tickets
Set tick_used = 'Y' , cust_id = '#LoginCust#'
Where Tick_no = '#newTicket#'
</cfquery>
</cfif>
<cftransaction action="commit" />
<cfif isdefined("newTicket")>
<cfoutput>You Got Tick No : #newTicket#</cfoutput>
<cfelse>
<cfoutput>No More Tickets can get !</cfoutput>
</cfif>
</cflock>
此處用的是程式碼區塊獨占方式,可以把這段寫到一個固定CFC裡面或是CFM裡面都可以,反正只要操作這個Tickets表的就是用這個區塊(不要有做其他區塊操作同樣的動作)。CFLOCK設定TYPE="EXCLUSIVE"為獨占式,一次只允許一個執行緒執行。其他執行緒就是排隊等待被執行。
也就是說不管多少人執行這個網頁,只要遇到 blockTickets 這個 CFLOCK 區塊就是得排隊等待前面一個人執行結束。
所以,簡單的方式就可以解決搶票重複的問題了(大家都來排隊)。
留言
找個機會想與你交流一下,是否可以?
Hi Allen Yoe,這是沒問題的,目前台灣用的人也很少,也希望有人可以交流,否則絕大部分技巧都得找國外的文章,目前我還在用也是因為系統需求,但是很多東西都漸漸被C#取代了。