zoukankan      html  css  js  c++  java
  • 秒杀避免超卖 ,兼顾效率与质量

    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);                                                                                                   
                }                                                                                                                                      
                                                                                                                                                       
            }                                                                                                                                          
        }                                                                                                                                              
                                                                                                                                                       
    }                                                                                                                                                  
                                                                                                                                                       


     
  • 相关阅读:
    约合¥1720 LG法国称G Watch将于6月开卖
    c语言中的位移位操作
    兼容的动态加载JS【原】
    Oracle 10g AND Oracle 11g手工建库案例--Oracle 10g
    面向对象思想----不看懊悔!
    学习设计模式--观察者模式(C++)
    使用POI 导入excel
    机器学习 F1-Score, recall, precision
    阿里无线11.11:手机淘宝移动端接入网关基础架构演进之路
    ICE 迁移64位安装问题
  • 原文地址:https://www.cnblogs.com/peteremperor/p/11125000.html
Copyright © 2011-2022 走看看