秒杀系统 不用Redis 秒杀项目redis控制超卖_缓存


图1


以上是秒杀系统的大体思路

首先

我们可以思考如何在一台机器,单线程的情况下 如何完成这个这个系统


秒杀系统 不用Redis 秒杀项目redis控制超卖_秒杀系统 不用Redis_02


我们可以看到大多数请求都要访问数据 而mysql是与硬盘交互 IO速率慢 因此效率慢 我们可以使redis(在内存中进行操作 可以节省很多时间)但是redis中的数据从哪里来呢?

我们可以在用第一次查询该商品的相关信息时,把这些数据写入到redis 这样用户第二次访问或其他用户访问该数据时 直接走redis中 而不是mysql 效率就会大幅度提高。

这样我们就可以把图1中的 查询优惠卷 判断秒杀数据库存 查询订单的操作分别细化为:


秒杀系统 不用Redis 秒杀项目redis控制超卖_分布式_03


判断秒杀数据库存 查询订单的操作与查询优惠卷操作一致.。

这样效率提高了不少

单片机上的高并发问题

高并发可能会导致

库存超卖和

一人有多单问题

库存超卖问题解决方案:

我们在进行减少库存时可以利用MYSQL数据自带的update的互斥锁来解决高并发导致的超卖问题

seckillVoucherService.update()
                     .setSql("stock = stock - 1") // set stock = stock - 1
                     .eq("voucher_id", voucherId).gt("stock", 0) // where id = ? and stock > 0
                     .update();

通过where id = ? and stock(库存) >0 控制了库存不能为负数

一人多单问题解决方案:

一人多单问题的关键在于一个人同时拥有超过一条以上的线程处理。因此我们要对“这个人”进行加锁就可以解决一人多单问题

synchronized (userId.toString().intern()) {
            // 5.1.查询订单
            int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
            // 5.2.判断是否存在
            if (count > 0) {
                // 用户已经购买过了
                return Result.fail("用户已经购买过一次!");
            }

我们在用toString方法底层可能会调用 new String()方法 导致同一个id地址不同 从而锁不上 调用intern()锁上着id的字符串常量。

分布式环境下 高并发问题:

分布式的本质是多台JVM之间一些锁的相关数据不共享导致的高并发问题

那就意味着我们需要一个JVM们的一个共同第三方来管理他们需要共享的锁数据

例如 mysql redis

这里我们用redis来管理JVM之间共享的锁

redis获取锁:调用 redis的setnx(“锁”)方法 返回值为null则说明没人锁

自己上锁 不为null 则有人上锁

以下为使用redisson api的java代码

// 获取线程标示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        // 获取锁
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);

redis释放锁:调用redis的del(“锁”)方法 来释放锁

以下为使用redisson api的java代码

// 获取线程标示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        // 获取锁中的标示
        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
        // 判断标示是否一致
        if(threadId.equals(id)) {
            // 释放锁
            stringRedisTemplate.delete(KEY_PREFIX + name);
        }

这样就可以实现在分布式 多线程环境 实现一人一单 不能超卖的秒杀系统了 但是速度等方面还有待优化