基于本地redis、protostuff序列化对于数据层的优化及java中对于泛型的使用
2018-03-29 17:08
896 查看
此次对于redis、protostuff的应用是在一个高并发的秒杀系统中实现的。
每次获取秒杀接口我们都要访问数据库,在高并发的系统中我们可以使用redis缓存进行优化,不需要每次都访问数据库,从而减小数据库的压力。而同时使用Protostuff对于需要传递的数据进行序列化,这样传递的数据量就会大大减小。从而减少并发时间。
2.对于秒杀操作的优化
秒杀操作是和数据库事务有关的,在秒杀操作中我们主要执行insert和update两条语句。
而对同一条数据进行update操作的时候,mysql存在行级锁等待的情况,一条update语句必须等待前一天update执行完之后才可以拿到lock锁。
所以这里优化主要针对的是行级锁事务的等待,网络的延迟和GC回收!
这次我们主要讲的是对于秒杀地址的优化。
实体类Seckill,记录了秒杀商品id,商品name,商品库存,开始时间结束时间,创建时间。
实体类Exposer,存放了是否秒杀,MD5加密,当前系统时间,秒杀开始时间、秒杀结束时间等信息
实体类:秒杀结果类,里面存放了请求是否成功,成功则返回一个泛型T的数据,失败则返回错误信息:
RedisDao中定义了put、get redis中数据的方法:
暴露秒杀接口地址的方法,会返回一个Exposer的类型:
控制器controller中
在不确定返回参数类型的时候,可以使用泛型,等返回参数确定之后,再给其绑定类型。
在高并发的秒杀系统的优化中主要有以下几个方面:
1.对于获取秒杀地址的接口的优化每次获取秒杀接口我们都要访问数据库,在高并发的系统中我们可以使用redis缓存进行优化,不需要每次都访问数据库,从而减小数据库的压力。而同时使用Protostuff对于需要传递的数据进行序列化,这样传递的数据量就会大大减小。从而减少并发时间。
2.对于秒杀操作的优化
秒杀操作是和数据库事务有关的,在秒杀操作中我们主要执行insert和update两条语句。
而对同一条数据进行update操作的时候,mysql存在行级锁等待的情况,一条update语句必须等待前一天update执行完之后才可以拿到lock锁。
所以这里优化主要针对的是行级锁事务的等待,网络的延迟和GC回收!
这次我们主要讲的是对于秒杀地址的优化。
实体类Seckill,记录了秒杀商品id,商品name,商品库存,开始时间结束时间,创建时间。
public class Seckill { private long seckillId; private String name; private int number; private Date startTime; private Date endTime; private Date createTime; public long getSeckillId() { return seckillId; } public void setSeckillId(long seckillId) { this.seckillId = seckillId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public Date getStartTime() { return startTime; } public void setStartTime(Date startTime) { this.startTime = startTime; } public Date getEndTime() { return endTime; } public void setEndTime(Date endTime) { this.endTime = endTime; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } @Override public String toString() { return "Seckill{" + "seckillId=" + seckillId + ", name='" + name + '\'' + ", number=" + number + ", startTime=" + startTime + ", endTime=" + endTime + ", createTime=" + createTime + '}'; } }
实体类Exposer,存放了是否秒杀,MD5加密,当前系统时间,秒杀开始时间、秒杀结束时间等信息
/** * Created by yliu on 18/03/27. * 暴露秒杀地址(接口)DTO */ public class Exposer { //是否开启秒杀 private boolean exposed; //加密措施 private String md5; private long seckillId; //系统当前时间(毫秒) private long now; //秒杀的开启时间 private long start; //秒杀的结束时间 private long end; //构造函数。秒杀成功则返回true,已经经过md5加密后的地址,以及商品id,表示该商品已经开启秒杀 public Exposer(boolean exposed, String md5, long seckillId) { this.exposed = exposed; this.md5 = md5; this.seckillId = seckillId; } //构造函数。若失败则返回false,可以返回商品id和系统时间、开始时间、结束时间、判断秒杀何时开启 public Exposer(boolean exposed, long seckillId,long now, long start, long end) { this.exposed = exposed; this.seckillId=seckillId; this.now = now; this.start = start; this.end = end; } //构造函数 public Exposer(boolean exposed, long seckillId) { this.exposed = exposed; this.seckillId = seckillId; } public boolean isExposed() { return exposed; } public void setExposed(boolean exposed) { this.exposed = exposed; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public long getSeckillId() { return seckillId; } public void setSeckillId(long seckillId) { this.seckillId = seckillId; } public long getNow() { return now; } public void setNow(long now) { this.now = now; } public long getStart() { return start; } public void setStart(long start) { this.start = start; } public long getEnd() { return end; } public void setEnd(long end) { this.end = end; } @Override public String toString() { return "Exposer{" + "exposed=" + exposed + ", md5='" + md5 + '\'' + ", seckillId=" + seckillId + ", now=" + now + ", start=" + start + ", end=" + end + '}'; } }
实体类:秒杀结果类,里面存放了请求是否成功,成功则返回一个泛型T的数据,失败则返回错误信息:
/** * Created by yliu on 18/03/28. */ //将所有的ajax请求返回类型,全部封装成json数据 public class SeckillResult<T> { //请求是否成功 private boolean success; private T data; private String error; public SeckillResult(boolean success, T data) { this.success = success; this.data = data; } public SeckillResult(boolean success, String error) { this.success = success; this.error = error; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getError() { return error; } public void setError(String error) { this.error = error; } }
RedisDao中定义了put、get redis中数据的方法:
/** * Created by yliu on 18/3/27. */ public class RedisDao { private final JedisPool jedisPool;//redis连接池 //redisDao的构造器,传入redis的地址和端口,实现连接池的初始化 public RedisDao(String ip, int port) { jedisPool = new JedisPool(ip, port); } //Protostuff序列化所要传入的参数 private RuntimeSchema<Seckill> schema = RuntimeSchema.createFrom(Seckill.class); public Seckill getSeckill(long seckillId) { //redis操作逻辑 try { Jedis jedis = jedisPool.getResource(); try { String key = "seckill:" + seckillId; //并没有实现哪部序列化操作 //采用自定义序列化 protostuff: pojo. //通过key得到存放在redis中经过序列化后的Seckill类,经过序列化后存放的是字节类型的数据 byte[] bytes = jedis.get(key.getBytes()); //缓存重获取到 if (bytes != null) { Seckill seckill=schema.newMessage(); ProtostuffIOUtil.mergeFrom(bytes,seckill,schema); //反序列化得到Seckill 并返回 return seckill; } }finally { //最后不要忘记关闭redis连接 jedis.close(); } }catch (Exception e) { } return null; } public String putSeckill(Seckill seckill) { try { Jedis jedis = jedisPool.getResource(); try { String key = "seckill:" + seckill.getSeckillId(); //将seckill类序列化成字节,大小为默认的大小 byte[] bytes = ProtostuffIOUtil.toByteArray(seckill, schema, LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE)); //超时缓存 int timeout = 60 * 60;//1小时 //将序列化后的字节存放在redis中,存放时间为1小时,返回string类型的result,根据返回的结果可判断存放是否成功 String result = jedis.setex(key.getBytes(),timeout,bytes); return result; }finally { jedis.close(); } }catch (Exception e) { } return null; } }
暴露秒杀接口地址的方法,会返回一个Exposer的类型:
//加入一个混淆字符串(秒杀接口)的salt,为了我避免用户猜出我们的md5值,值任意给,越复杂越好 private final String salt="shsdssljdd'l."; public Exposer exportSeckillUrl(long seckillId) { //优化点:缓存优化:超时的基础上维护一致性 //1。首先访问redis,如果redis中存放了秒杀接口的数据,则直接从redis中取出,如果没有存放,则从数据库中取出数据,并存放到redis中,方便下次获取秒杀接口。 Seckill seckill = redisDao.getSeckill(seckillId); if (seckill == null) { //2.访问数据库 seckill = seckillDao.queryById(seckillId); if (seckill == null) {//说明查不到这个秒杀产品的记录 //返回商品id 和false表示该商品未开启秒杀 return new Exposer(false, seckillId); }else { //3,放入redis redisDao.putSeckill(seckill); } } //根据返回的seckill 类中存放的秒杀开启时间判断是否开启秒杀 Date startTime=seckill.getStartTime(); Date endTime=seckill.getEndTime(); //系统当前时间 Date nowTime=new Date(); if (startTime.getTime()>nowTime.getTime() || endTime.getTime()<nowTime.getTime()) { //系统当前时间比开始时间小,或者超过结束时间,说明没有开启秒杀,返回false及其他参数 return new Exposer(false,seckillId,nowTime.getTime(),startTime.getTime(),endTime.getTime()); } //秒杀开启,返回秒杀商品的id、用给接口加密的md5 String md5=getMD5(seckillId); return new Exposer(true,md5,seckillId); } //对秒杀商品的id进行加密,通过与salt盐值的拼接,返回复杂的md5 private String getMD5(long seckillId) { String base=seckillId+"/"+salt; //调用spring中自带的md5工具 String md5= DigestUtils.md5DigestAsHex(base.getBytes()); return md5; }
控制器controller中
@RequestMapping(value = "/{seckillId}/exposer", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"}) @ResponseBody public SeckillResult<Exposer> exposer(@PathVariable("seckillId") Long seckillId) { //返回类型是seckillResult并设置了泛型Exposer,则在实体类中seckillResult所定义的泛型变量data是Exposer类型的数据 SeckillResult<Exposer> result; try{ //根据秒杀商品id返回商品exposer类变量,中存放了是否开启秒杀,秒杀开启时间,md5等数据 Exposer exposer=seckillService.exportSeckillUrl(seckillId); //在seckillResult定义了构造函数 /* public SeckillResult(boolean success, T data) { this.success = success; this.data = data; } */ result=new SeckillResult<Exposer>(true,exposer);//请求成功 返回秒杀商品的信息,即是否开启秒杀等信息 }catch (Exception e) { //请求错误,抛出异常,并返回错误信息 e.printStackTrace(); result=new SeckillResult<Exposer>(false,e.getMessage()); } return result; }
在不确定返回参数类型的时候,可以使用泛型,等返回参数确定之后,再给其绑定类型。
相关文章推荐
- java序列化/反序列化之xstream、protobuf、protostuff 的比较与使用例子
- java序列化/反序列化之xstream、protobuf、protostuff 的比较与使用例子
- java序列化/反序列化之xml、protobuf、protostuff 的比较与使用例子
- java序列化/反序列化之xstream、protobuf、protostuff 的比较与使用例子
- JAVA中使用redis+protoStuff实现数据库缓存机制
- 序列化框架的使用及性能对比Kryo、Hessian、Protostuff、java原生
- java序列化/反序列化之xml、protobuf、protostuff 的比较与使用例子
- java序列化/反序列化之xstream、protobuf、protostuff 的比较与使用例子
- Java利用protostuff实现高效序列化
- 序列化框架比较:kryo &amp; hessian &amp; Protostuff &amp; java
- 序列化框架性能对比(kryo、hessian、java、protostuff)
- java序列化Protostuff和Serializable的区别
- 基于java反射筛选List对于不同实体泛型的公共方法
- 将java对象存储到redis数据库(使用序列化和反序列化)
- Java序列化技术性能分析(JDK原生与Protostuff)
- java protostuff实现文件级数据缓存,使用文件缓存对象,java文件级数据缓存
- 对protostuff和java序列化的小测试
- 将java对象存储到redis数据库(使用序列化和反序列化)
- 序列化框架性能对比(kryo、hessian、java、protostuff)
- java(优化24) Redis主要的五种数据类型使用