秒杀系统,库存优化

直接上代码

 1 public class RedisShop {
 2 
 3     private final static String PRODUCT_STOCK_KEY = "productStockKey";
 4     private static ConcurrentHashMap<Long, Boolean> productSoldOutMap = new ConcurrentHashMap<>();
 5     private static ConcurrentHashMap<Long, Boolean> getProductSoldOutMap() {
 6         return productSoldOutMap;
 7     }
 8 
 9     @Autowired
10     private ProductService productService;
11     @Autowired
12     private StringRedisTemplate stringRedisTemplate;
13     @Autowired
14     private Zookeeper zookeeper;
15 
16     /**
17      * 将数据添加至redis缓存
18      */
19     public void init() {
20 
21         List<PoductInfo> products = productService.getProductList();
22 
23         for (PoductInfo productInfo : products) {
24 
25             stringRedisTemplate.opsForValue().set(PRODUCT_STOCK_KEY + productInfo.getProductId, productInfo.getStock());
26         }
27 
28     }
29 
30     public String soleProduct(Long productId) {
31 
32         //优化3:创建一个ConcurrentHashMap集合(Jvm线程同步),不等于空==已售完
33         if (productSoldOutMap.get(productId) != null) {
34 
35             return "商品已售完";
36         }
37 
38         //优化2:将redis缓存中的数据-1,并返回减1后的值
39         Long stock = stringRedisTemplate.opsForValue().decrement(PRODUCT_STOCK_KEY + productId);
40         if (stock < 0) {
41 
42             //优化3:库存不足时,向ConcurrentHashMap添加值
43             getProductSoldOutMap().put(productId, true);
44 
45             //优化2:缓存数据+1(这时stock=-1,还原为0,需要+1)
46             stringRedisTemplate.opsForValue().increment(PRODUCT_STOCK_KEY + productId);
47 
48             //优化4:ConcurrentHashMap是jvm线程同步,如果是多集群分布式服务,还是会造成并发,可以用消息中间件或zooKeeper解决
49             //获取productId在Zookeeper中的路径
50             String zkSoldProductPath = Constants.getZkSoldProductPath(productId);
51             //如果不存在值为true的zookeeper,则添加一个
52             if (zookeeper.exists(zkSoldProductPath, ture) == null) {
53                 zookeeper.create(zkSoldProductPath, "true".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode);
54             }
55 
56             return "商品已售完";
57         }
58 
59         try {
60 
61             //业务逻辑
62 
63             //优化1:操作数据库,-1操作在sql中执行,防止并发(数据库乐观锁)
64 
65         } catch (Exception ex) {
66 
67             //优化2:秒杀失败,还原stock,需要+1
68             stringRedisTemplate.opsForValue().increment(PRODUCT_STOCK_KEY + productId);
69 
70             //优化3:秒杀失败,这时库存至少为1,移除ConcurrentHashMap值
71             if (productSoldOutMap.get(productId) != null) {
72                 productSoldOutMap.remove(productId);
73             }
74 
75             //优化4:修改zk的商品售完标记为false
76             if (zookeeper.exists(zkSoldProductPath, ture) != null) {
77                 zookeeper.setData(Constants.getZkSoldProductPath(productId), "false".getBytes(), -1);
78             }
79 
80             return "创单失败";
81         }
82         
83         return "";
84 
85     }

优化一共分为四个方面:

1.数据库优化:对库存的数据库操作,直接在sql中进行-1,mySql数据库自带乐观锁。(缺点:并发在500-1000左右,最大并发连接数是16384。)

2.redis缓存:将要对数据库操作的数据放入redis缓存中,先判断缓存中数据是否满足,不行直接return,大大减少了数据库压力。(并发数能达到十万左右,缺点:要考虑redis缓存和mysql数据的一致性)

3.ConCorrectHashMap集合:线程同步的集合,库存不足时向ConCorrectHashMap集合中添加一个值,先检查ConCorrectHashMap集合是否有值,再检查stock。(解决了redis和mysql数据的一致性,缺点:JVM级别的线程同步,在多集群分布式服务中并不适用)

4.消息中间件或ZooKeeper:库存不足时向zookeeper中添加一个值,客户端进行监听数据

原文地址:https://www.cnblogs.com/BounceGuo/p/13384334.html