只要抛出异常事务就会回滚吗?
Spring 的事务默认是发生了RunTimeException才会回滚,发生了其他异常不会回滚
不是所有的方法都需要事务,如只有一条修改操作、只读操作
Service层中的抛出异常是为了让Spring能够回滚,Controller层中捕获异常是为了将异常转换为对应的json供前台使用。
return "detail"; //我认为是没有意义的,只是一个字符串而已
优化总结
- 前端控制
暴露接口,按钮防重复(点击一次按钮后就变成灰色,禁止重复点击按钮)
- 动静态数据分离
CDN缓存,后端缓存
- 事务竞争优化
减少事务行级锁的持有时间
Redis属于NoSQL,即非关系型数据库,它是key-value型数据库,是直接在内存中进行存取数据的,所以有着很高的性能。
利用Redis可以减轻MySQL服务器的压力,减少了跟数据库服务器的通信次数。秒杀的瓶颈就在于跟数据库服务器的通信速度(MySQL本身的主键查询非常快)
Redis有很多客户端,我们的项目是用Java语言写的,自然选择对应Java语言的客户端,而官网最推荐我们的Java客户端是Jedis,在pom.xml里配置了Jedis依赖就可以使用它了,记得要先开启Redis的服务器,Jedis才能连接到服务器。
由于Jedis并没有实现内部序列化操作,而Java内置的序列化机制性能又不高,我们是一个秒杀系统,需要考虑高并发优化,在这里我们采用开源社区提供的更高性能的自定义序列化工具protostuff。
使用Redis优化地址暴露接口
原本查询秒杀商品时是通过主键直接去数据库查询的,选择将数据缓存在Redis,在查询秒杀商品时先去Redis缓存中查询,以此降低数据库的压力。如果在缓存中查询不到数据再去数据库中查询,再将查询到的数据放入Redis缓存中,这样下次就可以直接去缓存中直接查询到。
原本查询秒杀商品时是通过主键直接去数据库查询的,选择将数据缓存在Redis,在查询秒杀商品时先去Redis缓存中查询,以此降低数据库的压力。如果在缓存中查询不到数据再去数据库中查询,再将查询到的数据放入Redis缓存中,这样下次就可以直接去缓存中直接查询到。
第一:通过Jedis储存对象的方式有大概三种
- 本项目采用的方式:将对象序列化成byte字节,最终存byte字节;
- 对象转hashmap,也就是你想表达的hash的形式,最终存map;
- 对象转json,最终存json,其实也就是字符串
3. 秒杀操作——并发优化
3.1 简单优化
为什么要先insert再update
首先是在更新操作的时候给行加锁,插入并不会加锁,如果更新操作在前,那么就需要执行完更新和插入以后事务提交或回滚才释放锁。而如果插入在前,更新在后,那么只有在更新时才会加行锁,之后在更新完以后事务提交或回滚释放锁。
在这里,插入是可以并行的,而更新由于会加行级锁是串行的。
也就是说是更新在前加锁和释放锁之间两次的网络延迟和GC,如果插入在前则加锁和释放锁之间只有一次的网络延迟和GC,也就是减少的持有锁的时间。
这里先insert并不是忽略了库存不足的情况,而是因为insert和update是在同一个事务里,光是insert并不一定会提交,只有在update成功才会提交,所以并不会造成过量插入秒杀成功记录。