分布式分段锁 解决高并发情况锁导致请求处理慢的问题

使用redisson 分布式锁 会导致 线程并行执行,在秒杀系统中导致并发下降,此时可以对库存进行分段 如 stockCount = 40 分成四十段,分段的段数和总库存 相关, 用户请求时,随机分配一个段,若获取失败再重试几次,以保证尽可能购买到,详细代码如下
book 不使用锁 本机测试 20000/分钟
bookWithLock:使用分布式锁 但是没有分段 测试结果 1000/分钟
bookWithLock2 使用分布式锁 分段20时测试结果12000/分钟 分段40时测试结果 16000/分钟


  1. @Slf4j
  2. @Service
  3. public class OrderServiceImpl implements OrderService {
  4. private static String key = "count";
  5. private static int stockCount = 40;
  6. @Autowired
  7. private RedissonClient redissonClient;
  8. @Autowired
  9. private StringRedisTemplate stringRedisTemplate;
  10. @Override
  11. public String book() {
  12. return this.decrementOrder();
  13. }
  14. @Override
  15. public String bookWithLock() {
  16. RLock lock = this.redissonClient.getLock("lock");
  17. Boolean result = false;
  18. try {
  19. result = lock.tryLock(10, TimeUnit.SECONDS);
  20. if (result) {
  21. return this.decrementOrder();
  22. } else {
  23. log.info("==================获取锁失败================");
  24. }
  25. // Boolean result = lock.tryLock(10, TimeUnit.SECONDS);
  26. // if (result) {
  27. // return this.decrementOrder();
  28. // } else {
  29. // log.info("获取锁失败");
  30. // return "获取锁失败";
  31. // }
  32. } catch (Exception e) {
  33. log.error(e.getMessage());
  34. return "预定失败";
  35. } finally {
  36. if (result) {
  37. lock.unlock();
  38. }
  39. }
  40. return "0";
  41. }
  42. private String decrementOrder() {
  43. Long count = Long.valueOf(this.stringRedisTemplate.opsForValue().get(key));
  44. if (count > 0) {
  45. count = this.stringRedisTemplate.opsForValue().decrement(key);
  46. log.info("==================库存还剩{}个================", count);
  47. return String.valueOf(count);
  48. } else {
  49. log.info("库存不足");
  50. }
  51. return "库存不足";
  52. }
  53. @Override
  54. public Long initCount(Long count) {
  55. count = count == null ? 100000 : count;
  56. if (count < stockCount) {
  57. stockCount = 1;
  58. this.stringRedisTemplate.opsForValue().set(key + "_0", count.toString());
  59. } else {
  60. for (int i = 0; i < stockCount; i++) {
  61. this.stringRedisTemplate.opsForValue().set(key + "_" + i, String.valueOf(count / stockCount));
  62. }
  63. if (count % stockCount != 0) {
  64. this.stringRedisTemplate.opsForValue().set(key + "_" + 0, String.valueOf(count / stockCount + count % stockCount));
  65. }
  66. }
  67. this.stringRedisTemplate.opsForValue().set(key, count.toString());
  68. return Long.valueOf(this.stringRedisTemplate.opsForValue().get(key));
  69. }
  70. @Override
  71. public String bookWithLock2() {
  72. RLock lock = null;
  73. Boolean result = false;
  74. //尝试三次
  75. for (int i = 0; i < 3; i++) {
  76. try {
  77. int index = getRandom(stockCount);
  78. lock = this.redissonClient.getLock("lock" + index);
  79. result = lock.tryLock(10, TimeUnit.SECONDS);
  80. if (result) {
  81. String count = decrementOrder2(index);
  82. //如果没获取到(某个段的库存不足)则释放锁
  83. if ("-1".equals(count)) {
  84. lock.unlock();
  85. } else {
  86. return count;
  87. }
  88. }
  89. } catch (Exception ex) {
  90. log.error("执行失败", ex);
  91. } finally {
  92. if (result && lock != null) {
  93. try {
  94. lock.unlock();
  95. } catch (IllegalMonitorStateException e) {
  96. }
  97. }
  98. }
  99. }
  100. log.info("预定失败");
  101. return "0";
  102. }
  103. private String decrementOrder2(Integer index) {
  104. index = index == null ? 0 : index;
  105. //尝试获取次数
  106. String keyTemp = key + "_" + index;
  107. Long count = Long.valueOf(this.stringRedisTemplate.opsForValue().get(keyTemp));
  108. if (count > 0) {
  109. count = this.stringRedisTemplate.opsForValue().decrement(keyTemp);
  110. log.info("==================库存还剩{}个================", count);
  111. return String.valueOf(count);
  112. }
  113. log.info("获取是失败.......");
  114. return "-1";
  115. }
  116. private int getRandom(int count) {
  117. return new Random().nextInt(count);
  118. }
  119. }
上一篇:Apache 配置与应用


下一篇:Jmeter压力测试工具安装及使用教程