Rumah  >  Artikel  >  pangkalan data  >  Apakah prinsip kunci yang diedarkan Redis dan bagaimana untuk melaksanakannya

Apakah prinsip kunci yang diedarkan Redis dan bagaimana untuk melaksanakannya

王林
王林ke hadapan
2023-05-27 16:16:283353semak imbas

Isu keselamatan satu orang, satu pesanan serentak

Kunci pesimis yang digunakan dalam satu orang sebelumnya, perniagaan satu pesanan tidak boleh berkuat kuasa dalam sistem yang diedarkan.

Dalam senario terbaik, jika satu utas berjaya memperoleh mutex dan pertanyaan serta membuat pesanan, urutan lain tidak akan dapat mengganggu. Prinsipnya akan ada pemantau kunci untuk memantau siapa yang telah mendapat kunci tersebut.

Apakah prinsip kunci yang diedarkan Redis dan bagaimana untuk melaksanakannya

Tetapi masalahnya timbul:

Dalam sistem teragih, terdapat berbilang JVM yang berbeza Dalam persekitaran JVM yang berbeza, pendengar kunci adalah Jika terdapat berbilang benang, sesetengah benang masih boleh mendapatkan kunci walaupun benang lain telah memperoleh kunci.

Apakah prinsip kunci yang diedarkan Redis dan bagaimana untuk melaksanakannya

Pada masa ini, kunci dalam JVM biasa tidak lagi berfungsi, jadi kita perlu menggunakan kunci yang diedarkan.

2 Prinsip dan pelaksanaan kunci teragih

2.1 Apakah kunci teragih

Ia adalah kunci yang boleh memenuhi keperluan keterlihatan dan pengecualian bersama pelbagai proses dalam sistem teragih atau mod kluster .

Prinsip pelaksanaannya ialah persekitaran JVM yang berbeza berkongsi monitor kunci. Ini tidak akan membawa kepada berbilang benang menggunakan berbilang kunci.

Apakah prinsip kunci yang diedarkan Redis dan bagaimana untuk melaksanakannya

Ciri-ciri:

Apakah prinsip kunci yang diedarkan Redis dan bagaimana untuk melaksanakannya

2.2 Pelaksanaan kunci teragih

Terdapat tiga kaedah pelaksanaan utama, Kita semua boleh membuat perbandingan.

Seperti yang ditunjukkan di bawah:

Apakah prinsip kunci yang diedarkan Redis dan bagaimana untuk melaksanakannya

Di sini kita bercakap tentang pelaksanaan kunci teragih berdasarkan Redis.

Kaedah melaksanakan kunci teragih Redis terutamanya melibatkan dua langkah berikut:

1 Dapatkan kunci

Gunakan kaedah setnx jenis String dalam Redis (menjamin eksklusiviti bersama. ) Ia sudah menjadi cara yang terkenal untuk mendapatkan kunci. Untuk mengelakkan pelayan redis daripada ranap, kita perlu menetapkan tamat masa untuk kunci untuk mengelakkan kebuntuan. (Tidak menyekat)

Oleh itu, kod berikut boleh digunakan untuk mendapatkan kunci

SET kunci benang1 nx ex 10

kunci ialah kunci kunci, thread1 ialah nilai, nx ialah kaedah setnx, ex ialah untuk menetapkan tamat masa

2. Lepaskan kunci

Melepaskan kunci adalah mudah, hanya padamkannya.

del lock

Pelaksanaan kod:

Keperluan: Tentukan antara muka dan gunakan Redis untuk melaksanakan fungsi kunci yang diedarkan.

Apakah prinsip kunci yang diedarkan Redis dan bagaimana untuk melaksanakannya

Kod adalah seperti berikut:

Kod antara muka:

package com.hmdp.utils;
public interface ILock {
    /**
     * 尝试获取锁
     * @param timeoutSec 锁的持有时间,过期自动释放
     * @return true代表获取锁成功,false代表获取锁失败。
     */
    boolean tryLock(long timeoutSec);
    /**
     * 释放锁
     */
    void unlock();
}

Kelas pelaksanaan antara muka:

package com.hmdp.utils;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;
/**
 * @Version 1.0
 */
public class SimpleRedisLock implements ILock {
    //Redis
    private StringRedisTemplate stringRedisTemplate;
    //业务名称,也就是锁的名称
    private String name;
    public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.name = name;
    }
    //key的前缀
    private static final String KEY_PREFIX = "lock:";
    @Override
    public boolean tryLock(long timeoutSec) {
        //获取线程id,当作set的value
        long threadId = Thread.currentThread().getId();
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId+"", timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }
    //释放锁
    @Override
    public void unlock() {
        //删除key
        stringRedisTemplate.delete(KEY_PREFIX+name);
    }
}

Perniagaan lapisan memperoleh kunci dan kunci Lepaskan (pengubahsuaian perniagaan jualan kilat kupon)

package com.hmdp.service.impl;
import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.SimpleRedisLock;
import com.hmdp.utils.UserHolder;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
 * <p>
 *  服务实现类
 * </p>
 *
 */
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
 
    @Resource
    private ISeckillVoucherService iSeckillVoucherService;
    @Resource
    private RedisIdWorker redisIdWorker;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public Result seckillVoucher(Long voucherId) {
        //1.获取优惠券信息
        SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
        //2.判断是否已经开始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())){
            Result.fail("秒杀尚未开始!");
        }
        //3.判断是否已经结束
        if (voucher.getEndTime().isBefore(LocalDateTime.now())){
            Result.fail("秒杀已经结束了!");
        }
        //4.判断库存是否充足
        if (voucher.getStock() < 1) {
            Result.fail("库存不充足!");
        }
        //5.扣减库存
        boolean success = iSeckillVoucherService.update()
                .setSql("stock = stock-1").eq("voucher_id",voucherId).gt("stock",0)
                .update();
        if (!success){
            Result.fail("库存不充足!");
        }
        Long userId = UserHolder.getUser().getId();
        //1.创建锁对象
        SimpleRedisLock lock = new SimpleRedisLock(stringRedisTemplate, "order:" + userId);
        //2.尝试获取锁
        boolean isLock = lock.tryLock(1200);
        if (!isLock){
            //获取锁失败
            return Result.fail("一个用户只能下一单!");
        }
        try {
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.createVoucherOrder(voucherId);
        } finally {
            //释放锁
            lock.unlock();
        }
    }
    @Transactional
    public Result createVoucherOrder(Long voucherId) {
        Long userId = UserHolder.getUser().getId();
        //6.根据优惠券id和用户id判断订单是否已经存在
        //如果存在,则返回错误信息
        int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
        if (count > 0) {
            return Result.fail("用户已经购买!");
        }
        //7. 创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //7.1添加订单id
        Long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        //7.2添加用户id
        voucherOrder.setUserId(userId);
        //7.3添加优惠券id
        voucherOrder.setVoucherId(voucherId);
        save(voucherOrder);
        //8.返回订单id
        return Result.ok(orderId);
    }
}

Atas ialah kandungan terperinci Apakah prinsip kunci yang diedarkan Redis dan bagaimana untuk melaksanakannya. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam