在秒杀系统中遇到的问题以及想法感悟
1.以前做的项目不叫项目,太过粗糙,没有考虑的东西太多,更谈不上优化
在这次的小项目中明确了对dto,exception,enum这些层的作用
dto:类似entity都是在封装实体,entity封装的是数据库表的实体,dto是封装结果集或者dao和service需要的实体
exception:异常层,为什么要有异常层,为了让程序更加安全,健壮,在项目中封装异常会让项目变得更加友好,另外对异常进行向上抽象
enum:枚举层,封装异常对应的提示,让项目变得对程序更加友好,以前甚至没有对枚举有过性趣,在此次项目之前甚至也不会写枚举类,现贴出以下代码
public enum SeckillStatEnum { SUCCESS(1,"秒杀成功"), END(0,"秒杀结束"), REPEAT_KILL(-1,"重复秒杀"), INNOR_ERROR(-2,"系统异常"), DATA_REWRITE(-3,"数据篡改"); private int state; private String stateInfo; public int getState() { return state; } public String getStateInfo() { return stateInfo; } SeckillStatEnum(int state, String stateInfo) { this.state = state; this.stateInfo = stateInfo; } public static SeckillStatEnum stateOf(int index){ for (SeckillStatEnum state: values()) { if(state.getState()==index){ return state; } } return null; } }
一般系统为了安全考虑都会采用各种加密算法,本次采用MD5算法对用户手机号进行加密:
具体操作:
private String slet="dsfwdfeferwf3wrfwerferfer";
private String getMD5(long seckillId){ String base=seckillId+"/"+slet; String md5= DigestUtils.md5DigestAsHex(base.getBytes()); return md5; }
加入slet(颜值)为了不让别人知道算法加密规则,更加安全;
在秒杀操作之前需要有暴露秒杀接口的操作,防止被人提前知道规则,直接绕过前台提前进行秒杀,
在秒杀系统中最为重要的是秒杀操作,秒杀操作是一个事务性的,要减库存,要增加购买明细,要考虑到期间发生的各种异常
而且一般声明为运行时异常(runTimeException),spring对事物的支持是基于运行时异常的,所以想要用到spring事物,就需要声明这些运行时异常
在秒杀操作时还要注意对用户的验证,防止数据被篡改,主要是对上面暴露接口后md5进行验证,
一些优化措施
秒杀地址会有高并发:
因为用户会大量访问,会不断暴露地址
用redis,大约1秒10万访问
先去访问数据库,拿到数据之后放入redis,直接从缓存里面拿
请求地址先访问redis,然后访问mysql,因为第一次要访问mysql
,可以给缓存设置时间,比如半小时,等半小时后直接去访问mysql
秒杀操作优化:不能使用redis,因为在缓存中大量间数据,然后引起
数据不匹配,比如库存容量
某一行的热点数据会有高并发:
比如1000,秒杀iphone6,会一瞬间有大量数据
那么怎么优化秒杀高并发?
会生成一个原子计数器(记录的库存),通过redis实现
减成功后,会记录行为(也就是誰减了这一件库存),
然后放到分布式MQ当中比如kafka
后端服务消费落地
这个架构可以抗住很高的并发
到底什么让mysql变得低效
多个用户同时执行会产生行锁,当食物不提交或者不回滚
锁不会释放,大量等待
会存在GC,当gc在新生带进行工作的时候,会停止事物的操作、
优化方向:如何减少行级锁持有时间呢?
如何对update操作进行优化?
把客户端的逻辑判断放到mysql上,避免gc,因为在用java客户端和
mysql进行交互可能会引发gc
如何做?
使用存储过程整个事物放在mysql中
redis主要是对暴露接口进行优化
怎样引入redis,在pom中加入jedis得依赖
引入自定义序列化,用protostuff
在spring-dao里面注入redis
网络延迟,gc,行级锁主要构成了mysql的瓶颈
引用存储过程主要是优化事物行级锁持有的时间
放在sql中为了优化网络延迟,gc