Java高并发秒杀——高并发优化

Java高并发秒杀——高并发优化

高并发优化:由于该系统还是存在高并发问题,并发访问量大概只有100左右,可以通过高并发优化将并发量提高到5000左右

高并发优化分析目录

  1、高并发优化分析
  2、redis后端缓存优化
  3、数据库存储结构并发优化
  4、系统架构部署

一、高并发优化分析

在这里插入图片描述

  1、CDN理解:CDN(内容分发网络)加速用户获取数据的系统,部署在离用户最近的网络节点上,命中CDN不需要访问后端服务器
  2、秒杀地址接口:无法使用CDN缓存,适合服务端缓存(redis)
  3、秒杀操作:无法使用CDN缓存,后端缓存困难(库存问题),一行数据竞争(热点商品)
  4、瓶颈分析优化:把客户端逻辑放在MySQL服务端,避免网络延迟和GC(垃圾回收)的影响——使用存储过程:整个事务在MySQL端完成

在这里插入图片描述

  5、优化总结
    (1)前端控制:暴露接口,按钮防重复
    (2)动静态数据分离:CDN缓存。后端缓存(redis)
    (3)事务竞争优化:减少事务锁时间,使用存储过程

二、redis后端缓存优化

  1、redis相当于是一个高性能的服务器,先查找redis服务器看里面有没有需要找的对象,若有就直接拿不需要连接数据库,若没有就查看数据库,并把查找到的数据库放进redis缓存中,方便下一次的查找。

在这里插入图片描述

  2、创建RedisDAO层,通给java试下redis的jedis序列化以及反序列化一个java类的对象。自定义序列化:protostuff,并在对应的Service先对redis中的数据做判断,若redis中没有,再去数据库中查,查到后直接加入到redis中,方便下一次的查找操作。
	package com.fehead.dao.cache;
	
	import com.dyuproject.protostuff.LinkedBuffer;
	import com.dyuproject.protostuff.ProtostuffIOUtil;
	import com.dyuproject.protostuff.runtime.RuntimeSchema;
	import com.fehead.bean.Seckill;
	import org.slf4j.Logger;
	import org.slf4j.LoggerFactory;
	import redis.clients.jedis.Jedis;
	import redis.clients.jedis.JedisPool;
	
	
	/**
	 * Created by xiaoaxiao on 2019/5/6
	 * Description:
	 */
	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) {
	        //jedis操作逻辑
	        try {
	            Jedis jedis = jedisPool.getResource();
	            try {
	                String key = "seckill:" + seckillId;
	                //并没有实现内部序列化操作
	                //get->byte[]->反序列化->Object(Seckill)
	                //采用自定义序列化
	                //protostuff:pojo
	                byte[] bytes = jedis.get(key.getBytes());
	                //缓存中获取对象
	                if (bytes != null) {
	                    //建立一个空对象
	                    Seckill seckill = schema.newMessage();
	                    //通过自定义序列化为该对象赋值
	                    ProtostuffIOUtil.mergeFrom(bytes, seckill, schema);
	                    //seckill被反序列化——字节数组->对象实例
	                    return seckill;
	                }
	            } finally {
	                jedis.close();
	            }
	        } catch (Exception e) {
	            logger.error(e.getMessage(), e);
	        }
	        return null;
	    }
	
	    public String putSeckill(Seckill seckill) {
	        //set Object(Seckill)->序列化->byte[]
	        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;//1小时
	                String result = jedis.setex(key.getBytes(), timeout, bytes);
	                return result;
	            } finally {
	                jedis.close();
	            }
	        } catch (Exception e) {
	            logger.error(e.getMessage(), e);
	        }
	        return null;
	    }
	}

三、数据库存储结构并发优化

  1、将insert语句放在update语句前,减少事务锁时间

在这里插入图片描述

  2、创建存储过程将整个事务放在MySQL端完成
    (1)存储过程优化:减少事务行级锁持有的时间
    (2)不要过渡依赖存储过程
    (3)简单的逻辑可以应用存储过程
    (4)QPS:一个秒杀单可以承受6000/qps
-- 秒杀执行存储过程
DELIMITER $$ -- console ; 转换为 $$
-- 定义存储过程
-- 参数:in:输入参数;out:输出参数
-- row_count():返回上一条修改类型sql(delete,insert,update)的影响行数
-- row_count: 0:未修改数据; >0:表示修改的行数; <0:sql错误/未执行修改sql操作
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, state)
    VALUES (v_seckill_id, v_phone, 0);
    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 ;

set @r_result=-3;
-- 执行存储过程
call execute_seckill(1003,1357896451,now(),@r_result);
-- 获取结果
select @r_result;

四、系统架构部署

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值