1. 減庫存
一般下單減庫存的流程大概是這樣的:
1、查詢商品庫存。這里直接查的Redis中的庫存。
2、Redis中的庫存減1。這里用到的Redis命令是:incrby -1
3、扣減數據庫中的庫存。這里用數據庫樂觀鎖,不用額外加鎖
4、異步刷新Redis中的庫存
5、定時掃描超時未支付的交易,庫存加回去
總結一下這個流程就是:先減redis庫存,再減數據庫庫存,最后刷新redis庫存
用到的Redis命令可能:DECR key 或者 INCRBY key -1
更新數據庫的SQL可能是這樣的:
update 商品庫存表 set 庫存 = 庫存 - 1 where 商品ID = xxx and 庫存 > 0;
或者
update 商品庫存表 set 庫存 = 庫存 - 1 where 商品ID = xxx and version = xxx;
用樂觀鎖是一種比較好的方式,而且一遍ID字段都有索引,可以充分利用MySQL行級鎖
這種方式還有一個比較巧妙的地方是,利用redis的單線程來操作庫存,而且又是原子命令,可以避免並發問題
同時,先減redis庫存后可以防止后續因庫存不足而造成下單失敗
最后,數據庫更新完以后,再通過MQ異步刷新緩存,可以使得redis中的庫存誤差不會太大
交易系統會定時掃描超時未支付的訂單,然后用MQ異步通知訂單和商品中心,將訂單關閉,庫存再放回去
2. 加鎖
加鎖(比如:基於Redis的分布式鎖)
MQ可以把並行轉成串行,但是並不能很好的解決並發訪問的問題,只能靠鎖
加鎖會影響性能,但是影響不大。假設我們用Redisson分布式鎖,操作redis只需要幾毫秒,因此這點兒損耗不是什么大問題。都是這么玩兒的,不加鎖還能怎么辦呢。
3. 內存緩存
在cms管理后台修改數據后,同步或異步刷新redis緩存,同時利用zookeeper刷新內存緩存,這樣就可以不用等到需要用的時候再從redis中同步。
一定要避免redis大key,最常見的就是hash key,設置的時候不注意,一不小心里面就幾千個field了,這對查詢非常不利,可以取模進行分片。
一定要避免HGETALL命令,利用Pinpoint可以幫助我們分析每個請求在每個操作所消耗的時候,從而有助於我們優化
數據遷移用Canal
https://redis.io/commands/incr