传统商城的秒杀设计简单实现

*秒杀的实现
一.秒杀的特点
1.秒杀的特点是在规定时间内,用户在同一时间进行抢购,网站流量激增。
2.秒杀的请求量远远大于库存量。
3.抢购支付成功从数据库减少库存。

二.秒杀的设计
限流:由于有很多个线程抢夺资源,只有少数线程抢成功,所以要对大部分线程进行分流处理,只允许少数的线程进入秒杀的服务器端。
削峰:队友秒杀系统会有大量的线程涌入,所以在抢购一开始的时候会有很高的峰值,可能会对系统产生巨大的冲击力,把高流浪峰值变成平稳的流量也是秒杀系统很重要的思路,实现削峰的常用方法利用缓存技术和消息中间件(mq,kafaka)等技术,将线程全放在一个消息队列中,按照进栈出栈进行流量的削峰处理。
异步处理:就是流量削峰处理的一种处理方式。
内存缓存:秒杀系统最大的瓶颈就是对数据库的读写,由于数据库读写属于磁盘IO,性能很低,所以我们只对系统的业务逻辑和系统数据转移到缓存里,避免对数据库的直接操作,会提高系统的性能。
拓展:想支持更多用户,更大的并发,将系统设计成弹性的,或进行集群,可提高系统的性能。

三.设计思路
将请求拦截在系统的上游,降低下游的压力减少数据库的压力。如果不在前段拦截可能造成数据库读写锁冲突,甚至死锁,最终请求超时
冲锋利用缓存:避免对数据库直接进行读写操作,提高系统的读写速度。
消息队列:消息队列可以削峰,将拦截大量的请求,这也是异步处理,后台从消息队列主动拉取消息进行业务处理。

四.秒杀的架构
秒杀界面–>服务器网关(限流,削峰)–>秒杀服务器层–>操作数据库

五.前段处理
1.页面静态化:将活动页面上的所有可以静态的元素全部静态化,尽量减少动态元素,通过CDN来抗峰。
2.进制重复提交:用户提高完后将按钮设为灰色,进制重复提交。
3.用户限流:在某一时间里只允许用户提交一次,可以采用ip限流。

六.后端方案
例如当秒杀量很大时,即使用户只有一个请求,到服务的请求数量还是很大,比如10w用户同时抢1000个手机,服务器的请求压力至少为10w。
1.采用消息队列缓存技术:可以先把这些请求全部都写到消息队列中缓存一下,数据库订阅消息减库存,减库存成功的请求返回秒杀成功,是白的返回秒杀结束。
2.采用缓存应对读请求:对于读多写少的业务,大部分是查询的请求,可以利用缓存分担数据库的压力。
3.利用缓存续写请求:缓存也是可以应对读写请求的,我们可以把数据库中的库存数转移到Redis缓存中,所有减少库存的操作都在Redis中,然后通过后天进程中的用户秒杀请求同步到数据库中。

数据库层
一般设计在上游就进行拦截过滤,数据库值承担“能力范围”内的请求,所有通过在服务器层引入队列和缓存,让最底层的数据库没有压力。

利用消息中间件和缓存实现简单的秒杀系统
Redis是一个分布式缓存系统,支持多种数据结构。我们采用Key-value的数据结构,用一个原子类型的的变量值作为key,把用户id作为value,我们使用RPUSH key-value插入秒杀请求,当插入的秒杀请求达到上限请求时停止所有的后续插入,然后用LPOP key读取秒杀成功的用户的id,然后在数据库操作最后的订单库存操作。

二redis的事务实现秒杀
redis的事务通常使用watch,mutil,exec,discard来完成,redis的事务不支持回滚,这样会阻塞其他客户端的请求
watch:监视一个事务(key);
mutil:开启一个事务;
exec:执行事务的操作;
discard:结束一个事务;

@Service
public class MiaoShaServiceImpl implements MiaoShaService{

	@Autowired
	private JedisPool jedisPool;

	@Override
	public void miaosha() {
		Jedis jedis = jedisPool.getResource();		
		jedis.watch("productum");
		//得到當前的數量
		String string = jedis.get("productnum");
		//轉化成iteger
		Integer currentNum = Integer.parseInt(string);
		
	}
	
	/*@Override
	public void miaosha() {
		Jedis jedis = jedisPool.getResource();
		Boolean exists = jedis.exists("productnum");
		if(exists) {
			Long decr = jedis.decr("productnum");
			if(decr>0) {
				System.out.println(Thread.currentThread().getName()+"抢到商品"+decr+"抢购人数为:"+(100-decr+1));
				//想mq发送信息取更新mysql数据库
				System.out.println("想mq发送信息更新mysql数据库");
			}else {
				System.out.println(Thread.currentThread().getName()+"库存不足");
			}
		}else {
			System.out.println(Thread.currentThread().getName()+"redis数据出现异常");
		}*/
	
	
	}
	

你可能感兴趣的:(redis)