zoukankan      html  css  js  c++  java
  • 秒杀系统-高并发的优化

    秒杀系统面临着如下问题:
    (1)无法使用cdn缓存,因为系统逻辑不可能放在cdn中。
    (2)后端缓存困难:库存问题,因为运用到了mysql事务操作(设置联合主键)。
    (3)一行数据竞争:热点商品,因为多个用户同时对数据库某条数据进行操作。
    秒杀系统的优化方案:
    (1)前端控制:暴露接口,按钮防重复提交。
    (2)动静态数据分离:cdn缓存,后端Redis缓存。
    (3)事务竞争优化:减少事务锁时间,把客户端逻辑放在mysql服务端,避免网络延迟和GC的影响。 GC(Garbage Collection)垃圾回收机制
    使用Redis优化地址暴露接口,在dao包下新建一个cache包,然后创建一个RedisDao.java函数,在spring-dao.xml中配置redisdao构造器。
    RedisDao.java:
    public class RedisDao {
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
        private final JedisPool jedisPool;
        public RedisDao(String ip, int port) {
            jedisPool = new JedisPool(ip, port);
        }
    private RuntimeSchema<Seckill> schema = RuntimeSchema.createFrom(Seckill.class);
        public Seckill getSeckill(long seckillId) {
            try {
                Jedis jedis = jedisPool.getResource();
                try {
                    String key = "seckill:" + seckillId;
                    byte[] bytes = jedis.get(key.getBytes());
                    if (bytes != null) {
                        Seckill seckill = schema.newMessage();
                        ProtostuffIOUtil.mergeFrom(bytes, seckill, schema);
                        return seckill;
                    }
                } finally {
                    jedis.close();
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
            return null;
        }
        public String putSeckill(Seckill seckill) {
            try {
                Jedis jedis = jedisPool.getResource();
                try {
                    String key = "seckill:" + seckill.getSeckillId();
                    byte[] bytes = ProtostuffIOUtil.toByteArray(seckill, schema,
                        LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
                    int timeout = 60 * 60; 
                    String result = jedis.setex(key.getBytes(), timeout, bytes);
                    return result;
                } finally {
                    jedis.close();
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
            return null;
        }
    }
    针对网络延迟和GC导致的并发问题,使用存储过程,将整个事务放在mysql端完成,秒杀存储过程实现如下。
    DELIMITER $$ 
    CREATE PROCEDURE `seckill`.`execute_seckill`
      (in v_seckill_id bigint,in v_phone bigint,
        in v_kill_time timestamp,out r_result int)
      BEGIN
        DECLARE insert_count int DEFAULT 0;
        START TRANSACTION;
        insert ignore into success_killed
          (seckill_id,user_phone,create_time)
          values (v_seckill_id,v_phone,v_kill_time);
        select row_count() into insert_count;
        IF (insert_count = 0) THEN
          ROLLBACK;
          set r_result = -1;
        ELSEIF(insert_count < 0) THEN
          ROLLBACK;
          SET R_RESULT = -2;
        ELSE
          update seckill
          set number = number-1
          where seckill_id = v_seckill_id
            and end_time > v_kill_time
            and start_time < v_kill_time
            and number > 0;
          select row_count() into insert_count;
          IF (insert_count = 0) THEN
            ROLLBACK;
            set r_result = 0;
          ELSEIF (insert_count < 0) THEN
            ROLLBACK;
            set r_result = -2;
          ELSE
            COMMIT;
            set r_result = 1;
          END IF;
        END IF;
      END;
    $$
    DELIMITER ;
    最后,在ServiceService.java中建立存储执行秒杀操作的函数,在SeckillDao.java实现该接口,在SeckillDao.xml中使用mybatis调用存储过程,在服务端的SeckillServiceImpl.java完成配置即可。
  • 相关阅读:
    算法笔记--数据结构--并查集
    帮助
    八数码难题神奇!!!
    题解 P1197 【[JSOI2008]星球大战】
    线段树1对于Pushdown的理解
    最短路问题之SPFA
    并查集(路径压缩)
    并查集(KRUSKAL算法)
    初识单调栈
    初识单调队列
  • 原文地址:https://www.cnblogs.com/smuxiaolei/p/8979786.html
Copyright © 2011-2022 走看看