开发者社区> 问答> 正文

并发减库存 redis vs mysql

并发减库存 redis和 mysql那个更快?

展开
收起
蛮大人123 2016-02-08 18:36:11 5270 0
1 条回答
写回答
取消 提交回答
  • 我说我不帅他们就打我,还说我虚伪

    商品有库存, 如10000 每买一个商品 库存就减一
    减库存可以通过mysql来实现 如
    update product_stock set stock = stock - 1 where product_id = 1 and stock > 0;
    也可以使用redis来实现 如
    decr 1_stock
    (integer) 99
    面对这种场景都说要使用redis 因为redis并发性能更好 想实际验证一下是否这样
    思路
    设置较大的并发数去更新库存 执行10次 比较redis和mysql花费的时间
    代码:

    @SpringBootApplication
    public class CocurrentUpdateStockApplication implements CommandLineRunner {
        @Autowired
        private JdbcTemplate jdbcTemplate;
        
        @Bean
        JedisConnectionFactory jedisConnectionFactory() {
            return new JedisConnectionFactory();
        }
    
        @Bean
        RedisTemplate<String, Long> redisTemplate() {
            final RedisTemplate<String, Long> template = new RedisTemplate<String, Long>();
            template.setConnectionFactory(jedisConnectionFactory());
            template.setKeySerializer(new StringRedisSerializer());
            template.setHashValueSerializer(new GenericToStringSerializer<Long>(Long.class));
            template.setValueSerializer(new GenericToStringSerializer<Long>(Long.class));
            return template;
        }
    
        public static void main(String[] args) {
            SpringApplication.run(CocurrentUpdateStockApplication.class, args);
        }
        //mysql减库存任务
        private Callable<Void> updateStockInMysqlTask = () -> {
            final String sql = "update product_stock set stock = stock-1 where product_id=1 and stock>0";
            jdbcTemplate.update(sql);
            return null;
        };
        //redis减库存任务
        private Callable<Void> updateStockInRedisTask = () -> {
            redisTemplate().execute(new RedisCallback<Long>() {
                public Long doInRedis(RedisConnection connection) throws DataAccessException {
                    Long decr = connection.decr("1_stock".getBytes());
                    return decr;
                }
            });
            return null;
        };
    
        @Override
        public void run(String... args) throws Exception {
            final String name = "mysql"; // or "redis"
            System.out.printf("start concurrent update stock in %s...%n", name);
            List<Long> timeList = new ArrayList<>();
            for (int i = 0; i < 10; i++) {//分别统计10次
                long start = System.currentTimeMillis();
                concurrentUpdateStock(name); //
                long end = System.currentTimeMillis();
                System.out.printf("Done. Take time: %d ms%n", end - start);
                timeList.add(end - start);
                Thread.sleep(1000); //休眠1秒
            }
            System.out.println(timeList.stream().collect(Collectors.summarizingLong(t -> t))); //输出统计结果
    
        }
    
        private void concurrentUpdateStock(String name) throws InterruptedException {
            // 模拟并发更新库存
            int nThreads = 500; //设置一个较大线程数
            ExecutorService pool = Executors.newFixedThreadPool(nThreads);
            List<Callable<Void>> tasks = new ArrayList<>();
            for (int i = 0; i < nThreads * 2; i++) { //2倍于线程数的减库存任务
                if ("mysql".equalsIgnoreCase(name))
                    tasks.add(updateStockInMysqlTask);
                else if ("redis".equalsIgnoreCase(name))
                    tasks.add(updateStockInRedisTask);
            }
            List<Future<Void>> futureList = pool.invokeAll(tasks); //并发去执行这些任务
    
            while (futureList.stream().anyMatch(f -> !f.isDone())); //等待任务执行完
            pool.shutdown();
    
        }
    
    }

    输出结果:
    mysql:

    LongSummaryStatistics{count=10, sum=11485, min=897, average=1148.500000, max=1458}
    redis:
    LongSummaryStatistics{count=10, sum=1706, min=95, average=170.600000, max=493}

    结论:
    并发执行1000次减库存操作 mysql要比redis慢差不多7倍

    2019-07-17 18:39:55
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
搭建电商项目架构连接MySQL 立即下载
搭建4层电商项目架构,实战连接MySQL 立即下载
PolarDB MySQL引擎重磅功能及产品能力盛大发布 立即下载

相关镜像