1. 程式人生 > >高併發場景系列(一) 利用redis實現分散式事務鎖,解決高併發環境下減庫存

高併發場景系列(一) 利用redis實現分散式事務鎖,解決高併發環境下減庫存

問題描述:某電商平臺,首發一款新品手機,每人限購2臺,預計會有10W的併發,在該情況下,如果扣減庫存,保證不會超賣

方案一

利用資料庫鎖機制,對記錄進行鎖定,再進行操作 

  1. SELECT * from goods where ID =1 for update;

  2. UPDATE goods set stock = stock - 1;

利用排它鎖將並行轉化為序列操作,但該方案的效能和使用者體驗較差

方案二

利用redis 實現分散式鎖,

使用setnx命令(在key不存在時,建立並設定value 返回1,key存在時,會反回0)來獲取鎖,在業務邏輯中,我們可以通過這樣的方案來操作

  1. Jedis client = jedisPool.getResource();

  2. while(client.setnx("lock",String.valueOf(System.currentTimeMillis())) == 0){

  3. Thread.sleep(10000);

  4. }

  5. ...........................

  6.    client.del("lock")

方案二進階

考慮到死鎖問題,即現成A獲取鎖後,宕機了,導致鎖一直無法釋放,我們可以通過get命令獲取鎖的時間戳,通過他進行超時判斷,並進行釋放

  1. Long TIMEOUT_SECOUND = 120000L;

  2. Jedis client = jedisPool.getResource();

  3. while(client.setnx("lock",String.valueOf(System.currentTimeMillis())) == 0){

  4. Long lockTime = Long.valueOf(client.get("lock"));

  5. if (lockTime!=null && System.currentTimeMillis() > lockTime+TIMEOUT_SECOUND) {

  6. client.del("lock");

  7. }

  8. Thread.sleep(10000);

  9. }

  10. ...........................

  11. ...........................

  12. client.del("lock")

方案二加強

方案2的演算法中,為了確保在非超時情況下,鎖只能由有鎖的執行緒進行釋放,可以在value的時間戳中,拼上執行緒特徵碼

  1. Long TIMEOUT_SECOUND = 120000L;

  2. String featureCode = "machine01";

  3. Jedis client = jedisPool.getResource();

  4. while(client.setnx("lock",featureCode+":"+String.valueOf(System.currentTimeMillis())) == 0){

  5. Long lockTime = Long.valueOf(client.get("lock").substring(9));

  6. if (lockTime!=null && System.currentTimeMillis() > lockTime+TIMEOUT_SECOUND) {

  7. client.del("lock");

  8. }

  9. Thread.sleep(10000);

  10. }

  11. ...........................

  12. ...........................

  13. if (featureCode.equals(client.get("lock").substring(0, 8))) {

  14. client.del("lock");

  15. }