前言
最近快到毕业答辩的时候,我自己的论文也完成了查重,并且已经提交到知网平台。自己做的是一个电商项目,基本的功能都已实现。当时为了偷懒,直接是copy的慕课网上Spring电商的一个项目,自己在此基础改了几个星期,真心觉得代码写的烂。代码很多程度上违反了迪米特,合成复用,依赖倒置等原则。整体架构距离一致性,可用性,容错性有很大的差距。后期有时间,我会用Spring Cloud拆分整体模块,代码重构。
项目存在的问题
-
1.20张表都是基础的CRUD。表与表之间的关系没有通过连接或者是嵌套进行关联,而是很大程序依赖去lambda代码去进行连接,导致效率很低。
-
2.使用Timer进行任务调度不当(没有合理设置initialDelay时间或者是没有设置异步),会造成Eden区和Survivor区使用率太高,CPU占有率太高,严重影响性能,造成Tomcat响应速度很慢。建议用ScheduledExecutorService代替Timer,另外要注意newScheduledThreadPool的最大线程数是Integer.MAX_VALUE。使用的工作队列是DelayedWorkQueue,它是一个无界队列,会一直去消耗CPU性能直到殆尽。如果不了解线程池,最好手动创建。
-
3.没有做前后端分离,前端路由过度依赖后端Controller的转发。
-
4.自定义封装BaseMyBatisDAO,但是RowBounds分页功能效率很低,建议使用通用Mapper和PageHelper进行分页和排序。
-
5.没有统一进行返回码封装和对异常的封装以及处理。
-
6.重复代码太多,其实20张表的基础代码(Service层、Controller层、DAO层)完成可以用自动化框架生成。没必要把时间花在这些无意义的事情上,我们需要更关注于业务逻辑。
-
7.图片服务器暂时是用Tomcat。明显不是一个好的选择,应该考虑七牛云或者搭建FastDFS。
-
8.使用Redis缓存,无脑瞎B使用。设置的KEY也没有设置缓存失效时间。很多程度上没有考虑缓存穿透,缓存雪崩,缓存击穿这些场景,没有考虑到缓存数据和数据库里面的数据一致性的问题。
秒杀业务分析
在工作空余时间,也看了慕课网上关于高并发秒杀业务的解决方案,收货颇多。
-
1.商品详情页是产生高并发的一个点。中小型企业一般采用Nginx+页面静态化就能解决。我们可以把静态界面加入到CDN缓存中。CDN可以加速用户获取数据的速度,一般部署再离用户最近的网络节点上。
-
2.关于秒杀操作,我们无法去用CDN缓存。后端使用缓存比较困难,存在库存一致性的问题。在热度商品的秒杀上,存在一行数据竞争的情况。
-
3.关于秒杀地址暴露,我们也无法去用CDN缓存。适合用Redis进行缓存商品,一致性维护成本低。Redis和Mysql数据一致性维护可以采用超时穿透/主动更新策略。
-
4.关于获取秒杀时间的获取,其实不用优化。Java访问一次内存是10ns,而1秒等于=10亿ns。相当我1s的时间进行1亿次的new Date()。
-
5.比较成熟的解决方案: 原子计数器->Redis,记录行为消息->分布式MQ,消费消息并落地->MySQL。但是存在数据一致性和回滚问题,幂等性难以保证(会造成重复秒杀),这种架构不适合新手架构。
-
6.经过Jmeter压力测试,一条update商品库存语句的QPS是4W。一般用户进行秒杀操作,会受到网络延迟+GC的串行化阻塞。一般来说用户执行秒杀操作,正常的业务来说先执行减少商品库存操作,再插入用户购买明细。但是update同一行商品记录会造成行级锁。行级数会在commit事务后之后释放。在并发量集中的秒杀操作,这些操作会造成阻塞,因此我们优化的方向是减少行级锁持有的时间。我们可以先执行插入用户购买明细操作,然后更新库存操作。因为insert可以并行!
-
7.关于秒杀操作,我们可以把秒杀的业务逻辑写到MySQL端(也就是存储过程),整个事务在MySQL端完成,优化网络延迟和GC干扰。
优化总结:
- 1.前端控制:合理暴露秒杀地址,秒杀按钮防重复。
- 2.后端控制:动静态数据分离,CDN缓存,后端缓存,行级锁竞争优化,减少事务时间。
尾言
没有什么比学习和成长更为重要的事情了。
电商项目地址:https://github.com/cmazxiaoma/groupon
电商秒杀业务项目地址:https://github.com/cmazxiaoma/mallSeckill
网友评论