Lock 在 Rails 中的實務應用
喜歡作者的文章嗎?馬上按「關注」,當作者發佈新文章時,思書™就會 email 通知您。
思書是公開的寫作平台,創新的多筆名寫作方式,能用不同的筆名探索不同的寫作內容,無限寫作創意,如果您喜歡寫作分享,一定要來試試! 《 加入思書》
思書™是自由寫作平台,本文為作者之個人意見。
文章資訊
本文摘自:
Categories:
Tags:
Date:
Published: 2020/02/20 - Updated: 2020/03/07
Total: 1473 words
給本文個喜歡
或不
關於作者
很久以前就是個「寫程式的」,其實,什麼程式都不熟⋯⋯
就,這會一點點,那會一點點⋯⋯
看看作者的其他文章
看看思書的其他文章
這篇比較少程式碼,我想要寫的對 rails 的 「Optimistic 樂觀鎖」 與 「Pessimistic 悲觀鎖」的看法。
鎖住(lock)一筆資料(raw),或甚至是鎖住一個資料表(table),都是 Mutex 的一種實用,Mutex 還有一位長得很像的兄弟,叫 semaphore,唸 Sam-mer-for,在英文中原來是指旗號,我在讀書的時候,對這些都很感興趣,這些也都是學電腦的人打屁聊天的題材,難嗎?如果要自己寫低階的 Lock,很難,真的很難,但是要搞懂,用它,倒是還好。
先說笑話:你知道 Mutex 跟 semaphore 的差別嗎?其實很簡單啦,Mutex 就是飯店房間內的廁所,只有一間,一個人進去後,其他人就要等。 semaphore 就像是很多間的公廁,大家排隊,只要有空的就可以進去,所以說,Mutex 就是 semaphore 的一種,就是只有一間的公廁啦。
再來讓大家腦筋轉轉彎,下面有兩題:
先不要看下去,要想想,其實,第二題是真的需要腦筋轉轉彎的。
只限數量
好吧,我們先來看第一題,限量,通常,我們會有一個折價卷的資料表,我們就叫他「Coupon」,這次發的 100 張某某折價卷就會是他的一筆資料(raw),限量 100 張就會先寫在這筆資料中的「剩餘」欄位,每發出一張,就扣一張,扣到零就發完了,簡單。
實務應用上,如果程式不鎖住的話,在有很多人搶的情況下,很可能 100 張發到最後變成發出 150 張,但是如果都沒人搶,一點問題都沒有,所以,實務上,很多人沒寫,反正發多了就算了(笑)。
在 Rails 中使用 lock 非常簡單,只是 Rails 有兩種鎖,悲觀鎖與樂觀鎖,你要用那一種?這有點難,我們就不介紹悲觀鎖與樂觀鎖了,網路上很多人寫,這篇不錯,我們要考慮的是上鎖後怎麼改,很多人都說樂觀鎖的效率好,真的嗎?我做了一個簡單的表:
Optimistic 樂觀鎖
可以
只有第一個能改,其他人
就出「exception 錯誤」
以這一題來說,我會用悲觀鎖,原因就只有簡單,如果我用樂觀鎖,我需要:
用悲觀鎖就什麼都不用加,一群人來索取折價卷時,就排隊等,實務上這不是很正常嗎?伺服器也不用再一直重讀重寫,不用寫 Timeout。
限量又限人
這一題看起來很簡單,就跟上面一樣,上個鎖慢慢排對就好了,可惜不對,這個難在一人限一張,只要有人在瀏覽器上一次開個十個 tab 一起搶,很容易就破功。 所以我們的程式,在同一段上鎖的時間內:
難就難在「只能新增一筆」這點上,很多人想到的都是鎖住整個資料表,也可以啦,只是你會發現原來 Rails 不提供鎖整個資料表,我想也是因為這樣做太不合理了,不是不能做,真的很怪,一人佔住整個大樓,不好,真要......
鎖住整個資料表
可以用這個 gem,我是能不用 gem 就不用的人啦,不想用 gem 就用 raw SQL 吧,如下:
Mutex 鎖
鎖住整個資料表真的很糟,那就來個 Mutex 或是 semaphore 如何?也就可以用來控制每個人只能進一次,沒想到 Rails 真有奇人異士,就有人寫了這個 with_advisory_lock gem,這個好,只是好像用的人不多,為什麼?難道限量又限人的應用不多?原來不是,正解是鎖父母,讓我們看下去,不過,我想我以後一定有地方會用到這個好玩的 Mutex gem。
鎖住父母
原來,如果要限制資料庫的新增,最簡單的辦法就是把資料的 parent 鎖住,以這個例子來說,就是將這位使用者的使用者資料用「悲觀鎖」鎖住,當他用其他 tab 來搶折價卷時,因為使用者的資料被鎖住,他就只能等,當第一筆新增完成後,排隊進來的都會發現已經領過了,想搶兩張,門都沒有。
這一定要用悲觀鎖,不然會寫很多碼,沒必要啦.
話說
好像我都沒說樂觀鎖的好話呢,well,也不是這樣說,樂觀鎖在有大量讀取、又要能鎖定更新的場景,是有很重要的應用場合的,例如購物商品中的庫存,實務上就絕不能把商品光是有人看就鎖住,畢竟看的人多買的人少,但是實務上,樂觀鎖確實不好寫,很多例外與 timeout 要可慮,用的時候辛苦多了。