当然了你也可以用Spring自带的事务注解来实现悲观锁的操作,因为用了@Transactional就可以实现通过事务来控制,要么全部成功,要么全部失败,用事务时有两点需注意:

  1. 尽可能将MySQL执行语句往方法体后面靠,因为MySQL事务的commit语句是在第一次执行MySQL相关语句开始,一直到方法的结束。

  2. 设置事务的超时时间,如果不设置默认是-1是无限长。并且事务中设置的耗时timeout = 最后一个MySQL语句耗时 + 以及最后一个MySQL之前的所有耗时。

需注意:悲观锁状态下会保证商品卖出去,如果没拿到锁的线程会阻塞的等待拿锁。但是他的阻塞也会给用户带来非常不良好的体验。

4 第3版-乐观锁


我们为每个数量的已售数据配备个版本号,在 Service层调用时获得用户的已售数跟对应版本号,然后更新时将已售数跟版本号同时更新。因为  MySQL在更新时会自带乐观加速机制,如果更新成功则表示抢购成功,更新失败则表示抢购失败,此时你会发现不是手速越快就一定能抢到的哦,但起码保证了不会超卖,

6 第5版- 细节优化

有了乐观锁跟限流,接下来再思考写细节问题。

  1. 秒杀要有时间范围限制的,不能再任意时刻都可以接受秒杀请求,要实行限时抢购

  2. 如果有懂IT人员通过抓包获取了秒杀接口地址,在秒杀开始时,不通过按钮,直接通过脚本秒杀咋办?要实行秒杀接口隐藏

  3. 每个用户单位时间内访问次数要做频率限制

6.1 限时抢购

很简单,将秒杀商品放入Redis并设置超时,比如我们以kill + 商品id作为key,以商品id作为value,设置180秒超时。

加入时间校验:

6.2 秒杀接口隐藏
  1. 用户秒杀前先通过getMd5方法获得一个请求秒杀URL的MD5值。

  2. 请求getMd5算法,Key = 商品id + 用户id,value = 商品id + 用户id + 盐 。将KV存入redis并且设置过期时间,最终返回value作为md5值。

  3. 用户请求秒杀URL的时候需携带MD5值,然后Service层会根据商品id + 用户id从redis中获取下对应的value,看这个value跟MD5值是否一致,绝对下一步操作。

此时如果用户直接请求秒杀接口就会被限制了,但如果黑客技术升级,将请求MD5跟请求秒杀接口写到一起,还是无法防止被薅羊毛!咋办呢?再限制下用户访问频率

6.3 访问频率限制
  1. 通过前面请求后根据用户id生成个redis中的key,value为访问次数,默认为0,并且设置好该KV的过期时间。

  2. 用户在验证是否通过秒杀隐藏接口验证前,先看下他的单位时间内访问次数是多少,如果超过阈值则直接拒绝,没超过再进行隐藏接口的验证。

  3. 这里只是举例为用户访问次数限制,IP访问次数限制类似。

  4. 秒杀源码公众号回复秒杀获取。



本文分享自微信公众号 - sowhat1412(sowhat9094)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

03-24 20:50