基于redis实现分布式锁适用于秒杀场景

Redis秒杀

1.全局唯一ID

特性:

  1. 高可用

  2. 唯一性

  3. 高性能

  4. 安全性

  5. 递增性

全局唯一ID生成有很多方法例如:UUID、redis自增、snowflake算法、数据库自增等。

此我们失语redis自增的方式。

生成策略

具体代码实现

 

package com.hmdp.utils;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

/**
 * 全局唯一id生成器,利用Redis的自增功能保证每个ID的唯一性。
 */
@Component
public class RedisWorker {

    /**
     * 系统开始时间戳,用于计算时间差
     */
    private static final long BEGIN_TIMESTAMP = 1640995200L; // 2022-01-01 00:00:00 的UTC时间戳

    /**
     * 序列号占用的位数
     */
    private static final long COUNT_BITS = 32; // 序列号占用32位

    private StringRedisTemplate stringRedisTemplate;

    /**
     * 构造函数,注入StringRedisTemplate
     * @param stringRedisTemplate 用于操作Redis的模板类
     */
    public RedisWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 生成下一个唯一ID
     * @param keyPrefix ID前缀,用于区分不同业务或类别的ID
     * @return 生成的唯一ID
     */
    public long nextId(String keyPrefix) {
        // 1. 计算当前时间戳(秒)
        long nowSecond = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
        long time = nowSecond - BEGIN_TIMESTAMP; // 时间差,用于占据高位

        // 2. 获取当天序列号,基于Redis自增实现
        // 根据日期生成Redis键,保证每天序列号从1开始递增
        String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        Long increment = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
        // 3. 将时间差和序列号拼接成最终的ID
        return time << COUNT_BITS | increment; // 左移位数后与序列号进行位或操作
    }
}

 

2.库存超卖

在高并发下,去抢购商品优惠卷时会出现超卖,我们该如何避免呢?

使用锁

我们可以使用锁的机制去实现,说到锁我们脑海里通常会出现悲观锁和乐观锁,那么这两个分别是什么呢?

  1. 悲观锁:悲观锁是一种在并发控制中采用的策略,它假定多线程访问数据时发生冲突的可能性非常高,因此采取一种保守的机制来防止冲突。当一个事务使用悲观锁访问某个数据时,会先锁定这些数据,确保在整个事务处理过程中,没有任何其他事务能够修改这些数据。这种做法可以有效避免并发带来的数据不一致性问题,但可能会增加系统的锁开销,并可能引起其他的性能问题,如阻塞、死锁等。

  2. 乐观锁:一种在并发控制中采用的策略,与悲观锁相反,它假设多线程访问数据时发生冲突的可能性较低。在读取数据时,乐观锁不会立即锁定数据,而是先进行读取操作,然后在更新数据时检查在此期间是否有其他事务修改了数据。如果检测到有其他事务进行了修改,那么更新操作就会失败,否则更新成功。 乐观锁通常通过版本号(Version)或时间戳(Timestamp)来实现。在读取数据时,会记录当前的版本号或时间戳。当事务尝试更新数据时,会再次检查版本号或时间戳是否与之前读取时的一致。如果不一致,说明数据已被其他事务修改,更新操作就会失败;如果一致,说明没有发生冲突,更新操作可以继续。 相比于悲观锁,乐观锁的优点在于减少了锁的使用,从而降低了锁带来的开销和潜在的阻塞问题,提高了并发性能。然而,在高并发且冲突频繁的情况下,乐观锁可能导致大量的重试,这可能增加系统的计算负担。

案例

具体业务代码:

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result seckillVoucher(Long voucherId) {
//      1.获取优惠卷信息
        SeckillVoucher seckillVoucher = seckillVoucherMapper.selectById(voucherId);
//       2.判断秒杀是否开启
//       2.1获取当前时间
        LocalDateTime now = LocalDateTime.now();
        if (seckillVoucher.getBeginTime().isAfter(now) ) {
            return Result.fa
<think>好的,我现在需要帮用户比较ZooKeeper和Redis实现分布式锁时的优缺点。首先,我得回忆一下分布式锁的基本概念以及两种实现方式的不同点。用户可能对技术细节不太清楚,所以解释时要简明扼要。 首先,ZooKeeper和Redis都是常用的分布式锁实现工具,但它们的机制不同。Redis基于内存,操作快,而ZooKeeper基于ZAB协议,更强调一致性。用户提到的引用里提到Redis可能存在主从切换导致失效的问题,而ZooKeeper通过临时顺序节点和监听机制来保证的正确性。 接下来,我需要整理两者的优缺点。根据引用中的内容,Redis的缺点包括主从异步复制可能导致失效,而ZooKeeper虽然一致性更好,但性能可能不如Redis。同时,ZooKeeper的实现可能更复杂,需要维护会话,而Redis实现相对简单,但在高并发下可能有性能问题。 然后,我需要考虑用户的使用场景。比如高并发且允许偶尔失效的情况可能适合Redis,而需要强一致性的场景则适合ZooKeeper。用户可能想知道在不同业务需求下如何选择,所以需要分点列出优缺点,并给出选择建议。 另外,用户提供的引用中有几个关键点:Redis的AP特性可能导致数据不一致,ZooKeeper的CP特性确保一致性。Redlock算法虽然被提出,但仍有争议。ZooKeeper的临时节点在会话结束时会自动释放,避免死,而Redis需要设置超时时间,可能因超时不当导致问题。 最后,生成相关问题时要覆盖性能分析、具体实现方法、应用场景等,帮助用户进一步学习。需要确保回答结构清晰,分点明确,并且引用正确来源。</think>### ZooKeeper vs Redis 实现分布式锁的优劣势分析 #### 1. **ZooKeeper 分布式锁的优缺点** **优点**: - **强一致性**:基于 ZAB 协议(类 Paxos 协议),保证集群数据强一致性,状态变更对所有客户端可见[^3]。 - **自动释放**:通过临时节点(Ephemeral Node)特性,客户端会话断开时自动删除节点,避免死[^3]。 - **有序竞争**:通过临时顺序节点(Sequential Node)实现公平,客户端按申请顺序获取。 - **监听机制**:通过 Watcher 监听节点变化,减少客户端轮询开销,提升效率。 **缺点**: - **性能较低**:ZooKeeper 的写操作需集群半数以上节点确认,延迟较高,不适合高频竞争场景[^2]。 - **实现复杂**:需维护客户端会话,网络波动可能导致会话超时,需额外处理重连逻辑。 --- #### 2. **Redis 分布式锁的优缺点** **优点**: - **高性能**:基于内存操作,响应速度快,适合高并发场景[^2]。 - **简单易用**:通过 `SETNX`(或 Redlock 算法)即可实现机制,开发成本低[^1]。 - **灵活超时**:支持通过 `EXPIRE` 设置超时时间,避免死[^4]。 **缺点**: - **弱一致性风险**:主从异步复制可能导致状态不一致(如主节点宕机后从节点升级,信息丢失)。 - **续期问题**:需自行实现续期逻辑(如 Watch Dog 机制),否则可能因业务未完成而超时释放[^4]。 - **竞争重试开销**:若使用轮询获取,可能对 Redis 服务端造成压力[^2]。 --- #### 3. **对比总结** | **维度** | **ZooKeeper** | **Redis** | |----------------|---------------------------------------|---------------------------------------| | **一致性** | 强一致(CP 模型) | 最终一致(AP 模型) | | **性能** | 低吞吐,高延迟 | 高吞吐,低延迟 | | **死处理** | 自动释放(临时节点) | 依赖超时机制,需额外实现续期 | | **适用场景** | 强一致性需求(如金融交易) | 高并发且允许短暂不一致(如秒杀系统) | | **复杂度** | 实现复杂,需维护会话 | 实现简单,依赖原子命令 | --- #### 4. **选择建议** - **选 ZooKeeper**:若业务对一致性要求严格(如资金结算),且可接受性能损耗。 - **选 Redis**:若业务追求高并发性能,且允许极低概率的冲突(如库存扣减)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值