MySQL建表添加乐观锁字段_Java秒杀系统优化-Redis缓存-分布式session-RabbitMQ异步下单-页面静态化...

Java秒杀系统优化-Redis缓存-分布式session-RabbitMQ异步下单-页面静态化

项目介绍

基于SpringBoot+Mybatis搭建的秒杀系统,并且针对高并发场景进行了优化,保证线程安全的同时极大地提高了服务器的吞吐量,主要优化手段有页面静态化、Redis缓存(页面缓存、对象缓存)、RabbitMQ异步下单,项目实现的主要功能为 登录-->商品列表浏览-->秒杀-->付款或返回。本项目利用压测工具Jmeter对优化前后的性能做了详细的评测,附带完整的测试报告以及整个系统的设计思路报告。

软件说明

整个项目的结构为严格的Maven项目结构,源码包下的包名即为其主要作用的缩写名,易于理解,概不赘述。

本文重点阐述计思路及优化方案,并附在Jmeter下压测的完整结果报告。

设计方案

以下分功能模块阐述:

一、登录功能及session

登录的设计并不复杂,主要思路为两次MD5+盐化,首先为前端用固定salt+password做一次MD5然后传至后端,后端逻辑首先对用户存在与否做判断,然后取出该用户的salt值,和前端传来的已经一次MD5的password再次MD5,然后比较即可,然后生成session并存储在redis缓存中,返回cookie。值得注意的是在user表中加入随机的salt可极大降低彩虹表攻击的风险,还有就是将session值存储于redis缓存中也极大地降低了对数据库的访问。

二、商品列表、订单详情、商品详情功能

这部分功能逻辑十分简单,即为查数据,然后展示,无须赘述,需注意的是,这部分功能的优化策略,详见后文。

三、秒杀功能

该功能为系统的核心功能,既要保证程序的线程安全性,又要满足高并发场景的需求。

在未优化前,其主要的逻辑为:查库存-->查是否秒杀-->秒杀,又因为该逻辑理论上应为一个原子的操作,所以加锁,或者作为事务,但是这样会大大影响程序的并发性能,所以需做优化。

四、数据库

数据库的主要设计为五张表:goods、miaosha_goods、miaosha_user、miaosha_order、order_info

具体数据表的主要结构,在./sql/中有sql文件,并且./java/util/中有用于生成测试用户的脚本,有兴趣的读者可以查看。

以上即为主要功能,接下来阐述对应的一些优化手段。

优化方案

一、页面缓存

页面缓存的主要思路为,将一些用户经常请求的页面,例如/goods/to_list--商品列表页面,存储到redis缓存中,在用户请求的时候直接在缓存中获取并返回,如果取缓存失败,则利用thymeleaf的手动渲染,渲染后存入缓存,并且返回。我们可以很明显的知道,不使用页面缓存的请求,每次都先访问数据库,然后经thymeleaf渲染,然后返回,其中渲染的过程可能需要从磁盘中读取html模板,而使用页面缓存以后,直接在内存缓存中读取,无需查库和渲染,只有失效的情况下才需要查库渲染,所以在些用户经常请求的页面中使用页面缓存优化,可大大降低对数据库和服务器的压力。(需要注意的是合理的设置页面缓存的有效期)。

二、对象缓存

相对于页面缓存,对象缓存是个更细粒度的缓存,比如说在登录模块中的session中,我们把session对应的user对象存储到redis缓存中,那么在需要user对象的页面中,既不需要登录,也不需要更具cookie去查找数据库,只需要通过cookie在redis中获取user对象,即可使用,同理,这样类型的缓存也会减小对数据库的压力。

三、页面静态化

上述的两种缓存,都是利用redis缓存服务器来实现的,虽然可以降低对数据库和服务器的压力,但是,redis服务器的容量和处理能力也是有限的,所以我们可以考虑将页面模板直接缓存到用户的浏览器,那么每次请求用户只需要请求用于渲染的对象即可,这不仅仅减轻了redis服务器的压力,同时也减少了带宽的消耗,此即为页面静态化。

在本项目中,主要实现的是商品详情、订单详情页面、秒杀页面的静态化,主要方法是利用ajax的异步加载,请求渲染需要的对象,并且通过配置

####### spring.resources的相关参数来告诉浏览器是否缓存,缓存有效时间等等。

四、静态资源优化

主要手段包括JS/CSS压缩,CDN等,此项目中并没有尝试,但不失为优化的另外一些好的思路。

以上部分的优化手段主要为缓存、页面优化等于前端比较接近的手段,对于后端接口的优化将在以下部分阐述

五、接口优化

主要思路为Redis预减库存+RabbitMQ异步下单

具体流程如下:

1、系统初始化,加载库存到redis缓存

2、收到请求,预减库存

3、判断库存,若剩余,则入队列,否则秒杀失败

4、出队下单

分析:

一、通过将库存加载到redis中,使得每次判断、减少库存直接从内存中读取,无需访问数据库

二、收到请求预见库存,然后判断

注意这一顺序非常重要,保证了线程安全

分析:因为redis封装的decr()等函数是线程安全的,无需外加同步,所以你通过decr()减少库存后获取到的库存永远都是你刚刚减少后得到的库存,本身就是个原子操作,不会存在线程安全问题,然后根据这个库存来入队,不符合条件的秒杀请求直接返回失败,极大地减少了服务器的压力,而且整个后台逻辑中,需要保证原子性的也仅仅是decr()这一个操作,并且由于redis经过了乐观锁优化,所以整个系统的并发性相对于自己首先同步代码而言,并发性得到了极大的提高。

三、完成了上述的操作,再去实现接下来的逻辑就很简单了,唯一需要注意的是,从队列中出来的请求执行秒杀过程是一个事务,需完整执行,否则回滚。

同时,订单的详情页面做一个静态化优化,前端轮询秒杀结果,得到结果后进行渲染即可。

以上即为接口优化的阐述,接下来是压测报告。

测试参数

服务器:(Mysq、Redis、RabbitMQ等服务也均安装在本机上

CPU: Hasse/战神Z7m Intel-i7-6700HQ 2.6GHz-3.5Ghz 四核心八线程 三级cache 6M

内存: 8G DDR3L

磁盘: 5400转/s 1TB

Java版本:1.8.0_161 Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)

MySQL:5.7.20-log MySQL Community Server (GPL)

Redis:4.0.10 容量--100M

RabbitMQ:3.7.7

Mybatis、Druid等具体配置参数见./resource/application.properties文件

测试方法

测试工具使用Jmeter并发测试工具,为保证无非相关变量的影响,每次测试的并发线程数均设为1000,并发时间1s,并发循环次数为10次,如图所示:

a0d55b37a87b974e1ac37e22e5372781.png

/goods/to_list接口测试

该接口为商品列表接口,对该接口做了页面缓存的优化,分别对优化前,优化后做压测,结果如下:

3e91a85d38b80f90f2cb32fe55d2acff.png

优化前的测试结果

0f43c992b102d42058381cf67f6ef292.png

优化后的测试结果

e9164d1c7f8606d4807a61111782ec9f.png

对于上图的两个测试,我们比较关注的是聚合报告里的 ThoughPut 的值,其值可以作为吞吐量的一个较好估计。

由图可见:

优化前,系统吞吐量为:921.1/sec

优化后,系统吞吐量为:4046.9/sec

也就意味着,加速比为:4.39。

/goods/detail/{goodsId}接口测试

该接口为商品详情接口,对该接口实现了页面静态化的处理,分别对优化前,优化后做压测,结果如下:

优化前的测试结果

82bb4289df608eb25a375175b94638d5.png

优化后的测试结果

96591e8233a002824ab9b2cda6cc3444.png

对比以上两图,会发现,似乎区别并不明显,很容易让人得出优化无效的结论,其实不然

据本人分析,这种情况应该是由Jmeter自身导致的,因为Jmeter做测试的时候并不会依赖于其他浏览器,只是发起http请求,而浏览器所具备的一些功能,他并没有,比如,缓存,所有,利用Jmeter进行测试并不能得出一个如意的结果,但是,我们可以通过浏览器来大致的了解页面静态化后的一些改变。如图:

9cada2f9c7fd77ae4da80cf2dfa86c3d.png

可以清楚的看到,这个页面大部分的内容都被“已缓存”,只有400个字节左右的对象被请求并传输,这也就达到了我们优化前的目的了。

秒杀接口优化

对于接口测试,我实现准备了1000个user和cookie,具体脚本见./java/util/下代码,在Jmeter中使用参数方法可自行百度,如图:

089fa238a8f9e937b1369812e8e795c5.png

对于秒杀接口,我们只对优化后的接口进行压测,测试的情况分为两类:

1、秒杀库存充足

2、秒杀库存不足

对于秒杀库存充足的情况,我们设置初始的库存为200,然后,并发量和其他测试设置一致,结果如图:

d31a173728e9f47b6086557ab7aca969.png

对于秒杀库存不足的情况,我们设置初始的库存为0,然后,并发量和其他测试设置一致,结果如图:

bb6f1591e2059361f8c0d4b3019498ed.png

对于处理逻辑比较复杂的接口而言,最好和最坏的情况能达到这样的一个吞吐量,同时保证线程安全性,比较满意。

以上为全部测试报告,读者若有不明之处,欢迎提问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/526851.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

叶金荣mysql教程_mysql优化--叶金荣老师讲座笔记

copy to tmp table执行ALTER TABLE修改表结构时建议:凌晨执行Copying to tmp table拷贝数据到内存中的临时表,常见于GROUP BY操作时建议:创建索引Copying to tmp table on disk临时结果集太大,内存中放不下,需要将内存…

mysql tpcc 测试结果分析_mysql 数据库TPCC测试

创建数据库tpcc,导入测试表格mysql -h 192.168.0.202 -P15002 -utest -ptest -e "drop database tpcc;"mysql -h 192.168.0.202 -P15002 -utest -ptest -e "create database tpcc;"mysql -h192.168.0.202 -P15002 -utest -ptest --databasetpcc…

mysql 变量生命周期_Go: 延长变量的生命周期

![Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20191002-Go-Keeping-a-Variable-Alive/00.png)本文基于 Go 1.13。在 Go 中&#xff0…

python qqbot实现qq聊天机器人_Python QQBot库的QQ聊天机器人

本文实例为大家分享了Python QQBot库的QQ聊天机器人的具体代码,供大家参考,具体内容如下1.安装pip install qqbot2.主动发出消息from qqbot import _bot as bot# 登录QQbot.Login([-q, 2816626661])buddy 获取指定名称/备注的好友group 获取群buddy bot…

tp5 mysql实现消息队列_TP5系列 | Queue消息队列

消费信息如下ThinkPHP5 Queue消息队列优点1、Queue内置了 Redis,Database,Topthink ,Sync这四种驱动,本文使用Redis驱动2、Queue消息队列适用于大并发或者返回结果 时间有点长并需要批量操作的第三方接口,可用于短信发…

mysql表里插不进去数据_Oracle数据中表值插不进去问题(转)

相信我们在进行测试的时候,有的时候会遇上数据库表的值插不进去的情况,在执行SQL语句的时候,好像卡住一样,没有反应。但是当你把SQL语句c&#xf…

java 类默认访问权限_Java类 成员 访问权限 默认

Java中的访问权限控制符有四个.作用域 当前类 同一package 子孙类 其他packagepublic √ √ √ √protected √ …

java创建临时文件夹_java创建临时文件

[java]代码库/*** 创建临时文件** param prefix* 临时文件名的前缀* param suffix* 临时文件名的后缀* param dirName* 临时文件所在的目录,如果输入null,则在用户的文档目录下创建临时文件* return 临时文件创建成功返回true,否则返回false*…

java quartz2.1_quartz 2.1学习(一)

quartz是一种开源任务调度框架,提供了强大的任务调度机制,Quartz允许开发人员灵活地定义触发器的调度时间表,并可对触发器和任务进行关联映射。废话不多说了,介绍一下编程的基本步骤:实现Job接口,编码实现需…

java http setheader_response.setHeader各种用法详解

本文主要介绍了response.setHeader各种用法。具有很好的参考价值,下面跟着小编一起来看下吧一秒刷新页面一次 response.setHeader("refresh","1");二秒跳到其他页面 response.setHeader("refresh","2;URLotherPagename");没…

datagridview取消默认选中_C# WinForm 取消DataGridView的默认选中Cell 使其不反蓝

dataGridView1.Rows[0].Selected false;默认情况下 DataGridView绑定数据后会选中首行首列为实现其没有默认不选中(即绑定后 看不到首行首列反蓝)之前将dataGridView1.Rows[0].Selected false;放在窗体的构造函数中 怎么都看似不起效果 首行首列还是反蓝后来尝试放在窗体的Lo…

java多线程的优点_【java多线程的优点】

作者:Jakob Jenkov 翻译:古圣昌 校对:欧振聪尽管面临很多挑战,在java学习中多线程有一些优点使得它一直被使用。这些优点是:资源利用率更好程序设计在某些情况下更简单程序响应更快资源利用率更好想…

java boolean是什么_java中的boolean与Boolean有什么不同

java中的boolean与Boolean有什么不同发布时间:2020-11-11 15:59:21来源:亿速云阅读:74作者:Leah这篇文章给大家介绍java中的boolean与Boolean有什么不同,内容非常详细,感兴趣的小伙伴们可以参考借鉴&#x…

linux cmake编译安装mysql_Linux源码安装MySQL 5.6.12 (Cmake编译)

Linux源码安装MySQL 5.6.12 (Cmake编译)1.安装make编译器(默认系统自带)下载地址:tar zxvf make-3.82.tar.gzcd make-3.82./configuremakemake install2.安装bison下载地址:tar zxvf bison-2.5.tar.gzcd bison-2.5./configuremakemake install3.安装gcc-…

啊哈java_1.桶排序——啊哈算法java实现

/*** 题目:* 5个人考试得分分别为 5分,3分,5分,2分,8分;满分是10分;* 要将 5 3 5 2 8 这个数组进行降序排序;* 即排序后变为 8 5 5 3 2;* *//*** 桶排序解法: 建一个大小为11的一维数组a,a[0]~a[10]元素都初…

java成员变量的初始化_Java成员变量初始化过程

import java.util.*;public class Main{public static void main(String[] args){Student s new Student(5);s.show();}}class Person{public Person(){System.out.println("父初始化");show();}public void show(){System.out.println("父show");}}class…

java校招面试题_java校招面试编程题及答案.docx

java校招面试编程题及答案java校招面试编程题及答案  Java集合框架为Java编程语言的基础,也是Java面试中很重要的一个知识点。这里,我列出了一些关于Java集合的重要问题和答案。   集合框架是什么?说出一些集合框架的优点?   每种编程语言中都有…

合并两个有序数组 java_合并两个有序的数组

/*** 写在前面,题目要求的是将有序数组合并,那么有可能这所谓的有序是顺序或者逆序* 所以,应该在开始的时候判断一下* 然后,在比较的时候应该根据顺序逆序来写判断逻辑* 不过常规应该是顺序递增,然后就有了以下的代码&…

arp linux 清空_Linux怎么清理ARP缓存

1、系统初始arp环境[rootesx ~]# arp -nAddress HWtype HWaddress Flags Mask Iface192.168.1.175 ether 00:24:1D:97:B6:7F C vswif0192.168.1.120 ether 00:1F:C6:3A:DC:81 C vswif0192.168.1.51 (incomplete) vswif02、执行清除所有arp 缓存命令[rootesx ~]# arp -n|awk /^[…

ctf mysql hash传递_分享个 CTF 小工具 bruteHASH

别问,问就是为了 CTF思路源于一次三小时十二题的内部 CTF 竞赛,其中一道简单 MISC 给出明文范围(字母数字)和 MD5 开头,要求穷举出 flag——这当然不难,python 十几行代码搞定,但是运行出结果竟然用了近 20 分钟&#…