Administrator
Published on 2025-09-01 / 11 Visits
0

基于Redis的分布式锁实现

  1. 准备条件 作者只有一台电脑 通过nginx负载均衡进行测试

	  upstream backend {
        server 127.0.0.1:8011 weight=1;
        server 127.0.0.1:8012 weight=1;
      }

    server {
        listen       8099;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

       # 代理所有请求到上游服务器组
       location / {
          proxy_pass http://backend;
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
      }
   }
  • 创建一个Sprinngboot 测试demo 通过Jemter进行压测

package com.example.demo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/tickets")
@Slf4j
public class TicketController {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @RequestMapping("/")
    public String getTickets() {
        String randomId = UUID.randomUUID().toString();
        String ticketKey = "apple";
        String lockKey = ticketKey + ":lock"; // 使用单独的锁key

        log.info("开始抢购商品:{}", ticketKey);
        try {
            Boolean getLock = redisTemplate.opsForValue().setIfAbsent(lockKey, randomId, Duration.ofSeconds(1));
            if (!getLock) {
                log.info("获取锁失败:{}", ticketKey);
                return "系统繁忙,请重试";
            }
            Integer ticketStore = (Integer) redisTemplate.opsForValue().get(ticketKey);
            if (ticketStore == null) {
                log.info("抢购失败:{},库存不存在", ticketKey);
                return "抢购失败";
            }
            if (ticketStore <= 0) {
                log.info("抢购失败:{},库存不足", ticketKey);
                return "抢购失败";
            }
            int remaining = ticketStore - 1;
            log.info("抢购成功:{},剩余库存数:{}", ticketKey, remaining);
            redisTemplate.opsForValue().set(ticketKey, remaining);
            return "抢购成功";

        } finally {
            String luaScript =
                    "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                            "return redis.call('del', KEYS[1]) " +
                            "else " +
                            "return 0 " +
                            "end";

            redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
                    Collections.singletonList(lockKey),
                    randomId);
        }
    }
}

简单测试 能实现相关的分布式锁 但是仍存在许多问题 如:无法进行 锁过期但是线程挂了 锁续期 可重入锁 redis如果挂了 怎么处理