1.分布式锁解决方案
常用的一般有Zookeeper,Redisson,数据库。
- Zookeeper方案使用的是CP(保证了一致性和分区容错性,牺牲了一点可用性),适合流量请求不是很大,一致性要求较高的业务场景。
- Redisson方案使用的是AP(保证了可用性和分区容错性,牺牲了一点一致性),适合高并发场景,对一致性要求不是很高的业务场景。一致性可以人工通过脚本弥补,也可以通过redlock去解决。
2.Redission分布式锁解决方案
- <dependency>
- <groupId>org.redisson</groupId>
- <artifactId>redisson</artifactId>
- <version>3.17.3</version>
- </dependency>
复制代码- @Bean
- public Redisson redisson(){
- //redis为单机模式
- Config config = new Config();
- config.useSingleServer().setAddress("redis://localhost:6379").setPassword("123456");
- return (Redisson) Redisson.create(config);
- }
复制代码- package com.fast.controller;
- import org.redisson.Redisson;
- import org.redisson.api.RLock;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
- import java.util.UUID;
- import java.util.concurrent.TimeUnit;
- /**
- * @author hjw
- * @since 2022年06月23日 11:42:00
- */
- @RestController
- @RequestMapping("stock")
- public class StockController {
- @Autowired
- private RedisTemplate redisTemplate;
- @Autowired
- private Redisson redisson;
- @GetMapping("init")
- public String init() {
- redisTemplate.opsForValue().set("stock", 100);
- return "库存新增成功";
- }
- @GetMapping("sale")
- public String saleGoods() {
- //这样写,单机部署不会有问题,多节点就会出问题,因为synchronized只能基于jvm做加锁,多个节点属于多个jvm了
- synchronized (this) {
- int stock = (int) redisTemplate.opsForValue().get("stock");
- if (stock > 0) {
- int i = stock - 1;
- redisTemplate.opsForValue().set("stock", i);
- System.out.println("库存剩余:" + i);
- } else {
- System.out.println("库存数量不足");
- return "库存数量不足";
- }
- }
- return "库存扣减成功";
- }
- @GetMapping("saleImprove")
- public String saleImprove() {
- //使用redisson实现分布式锁
- String lockKey = "product_001";
- // String clientId = UUID.randomUUID().toString();
- **RLock redissonLock = redisson.getLock(lockKey);**
- try {
- //使用redisTemplate还需要手写子线程每隔30s*1/3=10s,根据clientId给每个线程的lockKey进行续期,防止lockKey失效后,业务未执行完,结果下个线程进来了
- // Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey,clientId,30, TimeUnit.SECONDS);
- // if (!result){
- // return "error_code";
- // }
- **redissonLock.lock();**//相当于setIfAbsent(lockKey,clientId,30, TimeUnit.SECONDS);
- int stock = (int) redisTemplate.opsForValue().get("stock");
- if (stock > 0) {
- int i = stock - 1;
- redisTemplate.opsForValue().set("stock", i);
- System.out.println("库存剩余:" + i);
- } else {
- System.out.println("库存数量不足");
- return "库存数量不足";
- }
- }finally {
- **redissonLock.unlock();**
- // if (clientId.equals(redisTemplate.opsForValue().get(lockKey))){
- // redisTemplate.delete(lockKey);
- // }
- }
- return "库存扣减成功";
- }
- }
复制代码
- 若想进一步优化,则可以采用1.8中ConcurrentHashMap的设计思想,分段加锁
进一步把库存1000,进行拆分成stock1-200,stock2-200,stock3-200,stock4-200,stock5-200,然后再进行分配,第一个请求进来访问stock1,进行库存加减,第二个访问stock2进行库存加减...依此类推,分别加锁解锁,就可以大大提高并发量。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |