秒殺避免超賣 ,兼顧效率與質量

原文鏈接:https://www.cnblogs.com/peteremperor/p/11125000.html

redis的隊列來實現。將要促銷的商品數量以隊列的方式存入redis中,每當用戶搶到一件促銷商品則從隊列中刪除一個數據,確保商品不會超賣。這個操作起來很方便,而且效率極高

Redis是一個分佈式key-value緩存系統,value支持多種數據結構,這裏value可以選擇兩種類型,String(或者hash):主要用於記錄商品的庫存,對商品減庫存。Set集合(這裏不要用list集合,list集合是可重複的,set是不可重複的,可以保證一個用戶只賣一次,如果一個用戶可以買多次那麼可以使用list集合):用於存儲用戶的id獲取其他唯一確定一個用戶的值。

在秒殺開始前:可以使用批處理,將參加秒殺的產品信息緩存到redis中。這裏將產品的業務唯一字段作爲key,庫存作爲value。這裏的key要和前端緩存的key一致。
在秒殺開始時:用戶大量提交。根據用戶提交的產品信息,獲取到redis中需要的key值,查詢緩存 ,得到庫存量,判斷當前庫存是否大於零,如果大於零,判斷當前的set集合中是否用該用戶ID,如果沒有,減庫存並且將用戶的ID放入集合中,並對庫存減一,如果庫存爲0,提示用戶,商品已售完等文案信息,如果集合中已經存在該用戶id,則不做任何處理,直接處理下一個請求。直到最後庫存售完,上面的過程可以利用redis事務和watch功能完成對數據一致性的控制即超賣問題
庫存售完後:程序開始啓動一個有個後臺線程,可以阻塞等待商品庫存售完的通知,在上面一步,庫存一旦售完,後臺進程獲取set集合中的用戶信息,異步處理需要操作的購買等後續操作。

package com.github.distribute.lock.redis;                                                                                                          
                                                                                                                                                   
import java.util.List;                                                                                                                             
import java.util.Set;                                                                                                                              
import java.util.concurrent.ExecutorService;                                                                                                       
import java.util.concurrent.Executors;                                                                                                             
                                                                                                                                                   
import redis.clients.jedis.Jedis;                                                                                                                  
import redis.clients.jedis.Transaction;                                                                                                            
                                                                                                                                                   
/**                                                                                                                                                
 * redis樂觀鎖實例                                                                                                                                      
 * @author linbingwen                                                                                                                              
 *                                                                                                                                                 
 */                                                                                                                                                
public class OptimisticLockTest {                                                                                                                  
                                                                                                                                                   
    public static void main(String[] args) throws InterruptedException {                                                                           
        long starTime=System.currentTimeMillis();                                                                                                  
                                                                                                                                                   
        initPrduct();                                                                                                                              
        initClient();                                                                                                                              
        printResult();                                                                                                                             
                                                                                                                                                   
        long endTime=System.currentTimeMillis();                                                                                                   
        long Time=endTime-starTime;                                                                                                                
        System.out.println("程序運行時間: "+Time+"ms");                                                                                                  
                                                                                                                                                   
    }                                                                                                                                              
                                                                                                                                                   
    /**                                                                                                                                            
     * 輸出結果                                                                                                                                        
     */                                                                                                                                            
    public static void printResult() {                                                                                                             
        Jedis jedis = RedisUtil.getInstance().getJedis();                                                                                          
        Set<String> set = jedis.smembers("clientList");                                                                                            
                                                                                                                                                   
        int i = 1;                                                                                                                                 
        for (String value : set) {                                                                                                                 
            System.out.println("第" + i++ + "個搶到商品,"+value + " ");                                                                                  
        }                                                                                                                                          
                                                                                                                                                   
        RedisUtil.returnResource(jedis);                                                                                                           
    }                                                                                                                                              
                                                                                                                                                   
    /*                                                                                                                                             
     * 初始化顧客開始搶商品                                                                                                                                  
     */                                                                                                                                            
    public static void initClient() {                                                                                                              
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();                                                                        
        int clientNum = 10000;// 模擬客戶數目                                                                                                            
        for (int i = 0; i < clientNum; i++) {                                                                                                      
            cachedThreadPool.execute(new ClientThread(i));                                                                                         
        }                                                                                                                                          
        cachedThreadPool.shutdown();                                                                                                               
                                                                                                                                                   
        while(true){                                                                                                                               
            if(cachedThreadPool.isTerminated()){                                                                                                   
                System.out.println("所有的線程都結束了!");                                                                                                  
                break;                                                                                                                             
            }                                                                                                                                      
            try {                                                                                                                                  
                Thread.sleep(1000);                                                                                                                
            } catch (InterruptedException e) {                                                                                                     
                e.printStackTrace();                                                                                                               
            }                                                                                                                                      
        }                                                                                                                                          
    }                                                                                                                                              
                                                                                                                                                   
    /**                                                                                                                                            
     * 初始化商品個數                                                                                                                                     
     */                                                                                                                                            
    public static void initPrduct() {                                                                                                              
        int prdNum = 100;// 商品個數                                                                                                                   
        String key = "prdNum_100001";                                                                                                              
        String clientList = "clientList";// 搶購到商品的顧客列表                                                                                             
        Jedis jedis = RedisUtil.getInstance().getJedis();                                                                                          
                                                                                                                                                   
        if (jedis.exists(key)) {                                                                                                                   
            jedis.del(key);                                                                                                                        
        }                                                                                                                                          
                                                                                                                                                   
        if (jedis.exists(clientList)) {                                                                                                            
            jedis.del(clientList);                                                                                                                 
        }                                                                                                                                          
                                                                                                                                                   
        jedis.set(key, String.valueOf(prdNum));// 初始化                                                                                              
        RedisUtil.returnResource(jedis);                                                                                                           
    }                                                                                                                                              
                                                                                                                                                   
}                                                                                                                                                  
                                                                                                                                                   
/**                                                                                                                                                
 * 顧客線程                                                                                                                                            
 *                                                                                                                                                 
 * @author linbingwen                                                                                                                              
 *                                                                                                                                                 
 */                                                                                                                                                
class ClientThread implements Runnable {                                                                                                           
    Jedis jedis = null;                                                                                                                            
    String key = "prdNum_10001";// 商品主鍵                                                                                                            
    String clientList = "clientList";//// 搶購到商品的顧客列表主鍵                                                                                             
    String clientName;                                                                                                                             
                                                                                                                                                   
    public ClientThread(int num) {                                                                                                                 
        clientName = "編號=" + num;                                                                                                                  
    }                                                                                                                                              
                                                                                                                                                   
    public void run() {                                                                                                                            
        try {                                                                                                                                      
            Thread.sleep((int)(Math.random()*5000));// 隨機睡眠一下                                                                                      
        } catch (InterruptedException e1) {                                                                                                        
        }                                                                                                                                          
        while (true) {                                                                                                                             
            System.out.println("顧客:" + clientName + "開始搶商品");                                                                                      
            jedis = RedisUtil.getInstance().getJedis();                                                                                            
            try {                                                                                                                                  
                jedis.watch(key);                                                                                                                  
                int prdNum = Integer.parseInt(jedis.get(key));// 當前商品個數                                                                            
                if (prdNum > 0) {                                                                                                                  
                    Transaction transaction = jedis.multi();                                                                                       
                    transaction.set(key, String.valueOf(prdNum - 1));                                                                              
                    List<Object> result = transaction.exec();                                                                                      
                    if (result == null || result.isEmpty()) {                                                                                      
                        System.out.println("悲劇了,顧客:" + clientName + "沒有搶到商品");// 可能是watch-key被外部修改,或者是數據操作被駁回                                      
                    } else {                                                                                                                       
                        jedis.sadd(clientList, clientName);// 搶到商品記錄一下                                                                             
                        System.out.println("好高興,顧客:" + clientName + "搶到商品");                                                                       
                        break;                                                                                                                     
                    }                                                                                                                              
                } else {                                                                                                                           
                    System.out.println("悲劇了,庫存爲0,顧客:" + clientName + "沒有搶到商品");                                                                    
                    break;                                                                                                                         
                }                                                                                                                                  
            } catch (Exception e) {                                                                                                                
                e.printStackTrace();                                                                                                               
            } finally {                                                                                                                            
                jedis.unwatch();                                                                                                                   
                RedisUtil.returnResource(jedis);                                                                                                   
            }                                                                                                                                      
                                                                                                                                                   
        }                                                                                                                                          
    }                                                                                                                                              
                                                                                                                                                   
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章