学习秒杀的过程以及优化思路、框架的使用整合技巧
Mysql
Mybatis
Spring
Spring MVC
高并发
数据落地:
MySQL和NoSQL(对事务机制支持不太好)
事务机制依然是目前最可靠的落地解决方案。
难点问题-“竞争”:多个人同时秒杀一件商品的时候如何处理这种情况下的竞争问题。
图1.3.1
事务
行级锁的概念!
行级锁最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。
行级锁在INNODB中使用,如果有很多的用户同时对一行数据进行修改的话,此时当第一个用户正在修改未提交之前,它是占有锁的,其他的用户都必须等待第一个用户commit之后释放锁以后才能够获取锁来得修改数据的机会。这就是行级锁的大致含义。
虽然能够在并发处理能力上面有较大的优势,但是行级锁也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁也最最容易发生死锁。
使用行级锁的主要是InnoDB存储引擎!
秒杀的难点是如何高线的处理竞争问题!
代码开发阶段
具体的SQL脚本:SQL脚本
mybatis的主要作用就是实现数据库中的数据与程序中对象之间的映射,与Hibernate一样,都是映射的工具,但是MyBatis需要提供的有参数和SQL,而Hibernate只需要提供参数即可。Mybatis是自己手动写SQL语句,比较灵活,Hibernate是依照封装来定的,只需要提供参数即可。
Mybatis的特点:
SQL书写的位置
如何实现Dao接口
mybatis只写接口,不用实现。
更少的配置
更少的配置Dao实现:
mybatis足够的灵活性
整合之后
mybatis整合Spring
主要就是编写XML文件做配置。
主要的配置编写步骤:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties">context:property-placeholder>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driver}">property>
<property name="jdbcUrl" value="${url}">property>
<property name="user" value="${user}">property>
<property name="password" value="${password}">property>
<property name="maxPoolSize" value="30">property>
<property name="minPoolSize" value="10">property>
<property name="checkoutTimeout" value="10000">property>
<property name="autoCommitOnClose" value="false">property>
<property name="acquireRetryAttempts" value="2">property>
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource">property>
<property name="configLocation" value="classpath:mybatis-config.xml">property>
<property name="mapperLocations" value="classpath:Mapper/*.xml">property>
<property name="typeAliasesPackage" value="classpath:org.seckill.entity">property>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory">property>
<property name="basePackage" value="org.seckill.dao">property>
bean>
beans>
Dao层工作演变为:接口设计+SQL编写
代码和SQL的分离,方便Review
Dao拼接等逻辑在Service层完成
秒杀接口的设计,需要站在使用者的角度来设计接口,这样才会让接口的作用最大。
响应的数据传输类使用Dto定义
如果有针对项目中的异常抛出问题,需要自己自定义异常然后编写异常。
秒杀接口设计中的异常注意:
Spring中的事务管理只有运行时异常才会执行回滚操作!
Spring管理Service
Spring IOC功能理解
对象工厂+依赖管理=一致的访问接口
业务对象依赖
SeckillService依赖SeckillDao以及SuccessKilledDao
SeckillDao以及SuccessKilledDao依赖SqlSessionFactory会话工厂
SQLSessionFactory会话工厂依赖DataSource
为什么使用IOC
Spring-IOC注入方式和场景
项目中IOC使用
声明式事务
流程:开启事务->修改SQL-1->修改SQL-2->修改SQL-n->提交/回滚事务
主要目的就是解脱事务代码!
声明式事务使用方式
第三种方式可以告诉同事或者团队内成员,这里有事务方法。
使用注解控制事务方法的优点
执行事务
抛出运行期异常的时候执行事务!
事务方法嵌套
声明式事务独有的概念:传播行为
列表页-详情页-login-yes-展示逻辑
no-登录操作-写入cookie-展示逻辑
详情页流程逻辑
Restful
Restful规范
其中POST与PUT只是幂等性有所区别:
HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。
URL设计格式
/模块/资源/{标示}/集合1/......
SpringMVC运行流程:
比较完整的!
客户端请求被DispatcherServlet(前端控制器)接收
DispatcherServlet请求HandlerMapping查询Handler
HandlerMapping根据请求URL查找Handler,将Handler和HandlerInterceptor以HandlerExecutionChain的形式一并返回给DispatcherServlet
DispatcherServlet请求HandlerAdapter执行Handler
HandlerAdapter调用Handler的方法做业务逻辑处理
HandlerAdapter处理完Handler会生成一个ModelAndView对象
将ModelAndView对象返回给DispatcherServlet
DispatcherServlet将获取的ModelAndView对象传给ViewResolver视图解析器,请求进行视图解析
ViewResolver将逻辑视图解析成物理视图View,返回给DispatcherServlet
DispatcherServlet根据View进行视图渲染(将模型数据填充到视图中)
DispatcherServlet将渲染后的视图响应给客户端
HTTP请求地址映射原理
http请求 > Servlet容器(Tomcat、Jetty等)>SpringMVCHandlerMapping注解、xml编程等。 ==> Handler处理方法
请求方法细节处理
配置整合Spring框架,需要根据他的原理图来,首选需要有一个重要控制器,负责拦截所有的请求,配置DispatcherServlet
,然后需要HandlerMapping来解析处理URL,并查找到可以获取到处理业务方法的HandlerAdapter,DispatcherServlet调用HandlerAdapter执行业务逻辑,返回ModelAndView给DispatcherServlet,此时Dispatcher将ModelAndView交由ViewResolver处理,返回物理视图View,此时DispatherServlet将渲染后的视图返回给客户端。
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<servlet>
<servlet-name>seckill-servletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring/spring-*.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>seckill-servletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>encodingfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven>mvc:annotation-driven>
<mvc:default-servlet-handler>mvc:default-servlet-handler>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView">property>
<property name="prefix" value="/WEB-INF/jsp/">property>
<property name="suffix" value=".jsp">property>
bean>
<context:component-scan base-package="org.seckill.web">context:component-scan>
beans>
(1)客户端(浏览器)发送请求,直接请求到DispatcherServlet。
(2)DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。
(3)解析到对应的Handler后,开始由HandlerAdapter适配器处理。
(4)HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。
(5)处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View。
(6)ViewResolver会根据逻辑View查找实际的View。
(7)DispaterServlet把返回的Model传给View。
(8)通过View返回给请求者(浏览器)
秒杀系统的整体流程如下:
进入详情页==>获取系统时间 ==>如果时间满足的话,则暴露秒杀接口的地址,如果时间不满足的话,先倒计时,然后暴露秒杀地址接口 ==> 执行秒杀操作 ==> 返回结果。.
详情页中将会有大量的用户刷新详情页,此时可以通过CDN(内容分发网络)将detail页静态化,以及存储静态资源css,js等,然后再到秒杀系统。
CDN的理解
秒杀地址接口分析
秒杀地址接口优化
可以将请求地址通过redis与mysql之间的一致性维护,通过超时穿透/主动更新策略来优化。
秒杀操作优化分析
延迟分析
同城机房与异地机房有关系
如何放到MySQL服务端
优化总结
并发深度优化
事务SQL在MySQL端执行(使用存储过程)
系统部署架构
系统可能用到的服务
常规的架构
高并发优化总结
1:使用Redis做后端缓存处理,将商品的信息存入redis中,如果在redis中查找到了,直接返回,否则查找mysql,返回对象,将对象放入到redis中。
2:为了减少网络延迟以及GC的时间,减少GC次数,由于如果先执行减库存,再执行插入,最后commit/rollback的话,会经历两次网络延迟以及GC,此时考虑减少GC的次数,将插入提前,减库存的操作放在后面,如果插入不成功,直接rollback,插入成功的话,执行减库存,最后commit,可以在竞争热点处解决这个问题。
3:存储过程优化,直接通过mysql的存储过程来实现事务,从而减少服务器端的事务处理,交由mysql存储过程函数处理。简单的数据逻辑可以使用存储过程优化,在阿里巴巴开发手册中,是禁止使用存储过程函数来处理mysql的。
更改:添加了jedis也就是redis的依赖,在linux上安装redis来处理。添加了存储过程处理秒杀的逻辑。
Dao层
主要学习了数据库相关的知识
业务层service
Web层
并发优化