我们先看一下秒杀系统的操作流程
我用红色框写的部分都是高并发的部分,所以要也别注意!
如果一个商品很火,很多人看,会对detail页进行大量获取,我们则需将其部署到CDN上,主要是将其静态资源css,js部署上去。而由于我们是单独向服务器获取当前时间,所以每次访问都回调用服务器来获取系统时间。我想这是为了避免用户修改本地时间来进行提前秒杀,但由于获取系统时间仅仅只是只需要很少时间,不考虑网络问题基本没什么影响。
好了看完上面一段话,就来说说cdn
CDN的理解:
1、用户在浏览器输入URL
2、浏览器向本地DNS请求域名解析
3、如果本地DNS缓存有该域名的解析结果,则直接将解析结果返回给浏览器
4、如果本地DNS缓存中无该域名的解析结果,则以递归方式向整个DNS系统请求域名解析,在获得应答后将解析结果返回给浏览器
5、浏览器获得解析结果,提取出IP信息,使用IP向服务器请求数据
6、服务器返回数据给浏览器
1、用户在浏览器中输入URL
2、浏览器向本地DNS请求域名解析,DNS会将域名解析权转交给CNAME指向的CDN专用的DNS服务器
3、CDN专用的DNS服务器将CDN的全局负载均衡设备的IP返回给浏览器
4、浏览器向CDN全局负载均衡设备发起URL请求
5、CDN全局负载均衡设备根据请求的URL和用户的IP地址,将用户请求转发到用户所在区域的区域负载均衡设备
6、区域负载均衡设备,根据用户IP、请求URL、缓存服务器的负载情况等,返回一台合适的服务器IP给用户
7、用户向缓存服务器发起请求
8、缓存服务器响应用户请求,如果用户请求的内容缓存服务器上不存在,则缓存服务器要向上一级缓存服务器请求内容,直到追溯到网站的源服务器
原文:https://blog.csdn.net/mountains2001/article/details/52711601
理解了上面一大段话后就基本懂CDN是个什么东西了
1.无法使用CDN缓存(上面知道CDN只适合内容不变的东西例如css js,而秒杀地址返回的数据是变化了,从未开始的null,到开始后的地址)
2.但它适合服务器缓存:redis
优化后: 请求地址-------->redis-----(redis中无数据)------>Mysql
1.无法使用CDN缓存(理由如上)
2.后端缓存困难:库存问题(redis中减了 数据库没减 不一致的问题)
3.一行数据竞争:热点商品
优化方案1:
(1)用redis或者NoSQL实现一个计数器,用来记录数量,当你秒杀一个商品的时候,数量就减一,直到0。
(2)计数器中数量减成功后就会记录行为,然后放到一个分布式的MQ(消息队列)之中。比如RabbitMQ ,RocketMQ,ActiveMQ,Kafka.
(3)最后再有后台去将其存储的mysql中
这就是不让用户的请求卡在数据库操作的底层,一般没有使用这个,当许多用户同时秒杀,会造成事务锁的释放慢,同时导致其他用户秒杀慢的情况,而用了这个优化方案,由于是直接减redis中的数量,并且记录信息。操作数据库的部分是在你用户秒杀完后,在队列里慢慢执行,将用户对数据库的操作请求,转移到了redis上。
优化方案2:直接用mysql处理
这里出现的问题java-数据库中通信有延迟。所以优化方向我们把客户端逻辑放到Mysql服务器端,避免网络延迟和GC
如何放到Mysql服务端
1.定制SQL方案,淘宝天猫是修改mysql源码,在update后加一手回滚操作。 需要修改mysql源码
2.使用存储过程,整个事务在Mysql端完成。
优化总结:
前端控制:暴露接口,按钮防止重复
动态数据分离:CDN缓存,后端缓存
事务竞争优化:减少事务锁持续时间
Redis的用法,貌似我说过redis就是一个容器,里面的存储是按照键值对存储的。
使用redis我们最好就是去redis官网找一个redis客户端来用。我们这里使用的是Jedis,直接在pom.xml文件中引入其依赖
想获取redis中的数据,只需用jedisPool生成一个连接池对象,然后调用其方法就可以获取了。注意对象存储在里面是需要实现序列化的。 get->byte[]->(反序列化)object
那么如何实现序列化和反序列化呢,
java里面有个方法:就是需要被序列化的类实现serializable接口。然后通过对象.getByte就可以转化为byte[]。
但是这个方法性能不高。
课程里使用了protostuff来自定义序列化。据说性能比实现接口好。
直接pom.xml引入依赖,
private RuntimeSchemaschema=RuntimeSchema.createFrom(Seckill.class);//通过此做一个seckill对象的模型
具体获取过程就是这样:
//从redis中获取缓存 byte[] bytes=jedis.get(key.getBytes()); //缓存重获取到 if (bytes!=null){ //空对象 Seckill seckill=schema.newMessage(); //将序列化对象缓存反序列化成对象 ProtostuffIOUtil.mergeFrom(bytes,seckill,schema); return seckill; }
存储进redis过程
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); //restult 成功则返回ok ,不成功则返回其他
了解完对redis的操作,剩下的就很简单啦,至于存储过程=,=说实话我数据库也就只会crud,跟着老师来敲,感觉也挺好理解的就是把后台的逻辑用sql语句写出来,剩下跟着视频做ok了。多谢各位观看。
到此秒杀系统记录blog就结束了,就我自己而言我觉得是有点马虎了。。。当我学完这4个课程,再回头写第一篇blog的还是有些忘了的细节。。不过通过这样一路写blog下来,我对ssm的理解 以及这个秒杀系统的理解也更加深刻了。前天看到我的dao层的blog下有个网友评论了一句加油,真的很暖心。接下来我会更加努力,写出更加有深度,有用的文章,谢谢大家了