建立一筆訂單時,我們會給予一串 serial number,作為訂單唯一識別。我們會寫出如下的代碼:
1 | # app/models/model_name.rb |
Query 是使用搜尋效益較高的 exists
1 | User Exists? (0.8ms) SELECT 1 AS one FROM `users` WHERE `users`.`status` = 0 LIMIT 1 |
比用 where 或 find_by 好
1 | User Load (0.9ms) SELECT `users`.* FROM `users` WHERE `users`.`status` = 0 LIMIT 1 |
基本的阻擋會在 DB column 上 UNIQUE,在 model 層 validates 做 uniqueness
且 token 上索引(index)那效能大部分沒什麼問題
而假設商業邏輯需要複雜的運算,導致在 new 和 save 之間需要 lock tables 等
而 lock 很多 tables,或者大型 tables 時
我們需要避免鎖太久,以及高併發導致 dead locked 之類
這時候還想要做效能優化,想到是提前一步確定 「token 唯一性」
看到滿不錯的方法,筆記一下
核心使用 $redis.incr(key)
特性
採取不嚴謹的準確性,來換取時間
其實是一種 Tradeoff 跟我之前文章提到的「布隆過濾器」類似
不管空間,降低時間消耗
使用 $redis.incr(key)
特性
先取得一組數組(111),直接往 redis 塞拼出來的 Hash Key
1 | [13] pry(main)> $redis.incr("Gmi_token:111") |
只第一次的 key 值,會得到 1,重複就會累加
這樣就可以在 lock tables 之前,先驗證 是否產生過?
衍生問題一: 可能製造一堆沒被訂單使用的 Hash key 在 redis
透過
$redis.expire(key, EXPIRE_TIME)
2 - 3 天內過期掉已經 generated Hash key
衍生問題二: 既然會過期舊的,How to ensure generated token with hash key would be unique?
透過
timestamp.strftime('%Y%m%d')
拼上一組時間戳
1 | module RedisUnique |
這樣就可以透過下方調用,做到用 redis 空間去換時間
並取得唯一數值,不怕鎖表太久等問題
1 | RedisUnique.check("20210427H118709") |
這是我個人淺見,算是易懂的設計
有任何更好得想法、做法,歡迎底下留言討論喔!