`
y806839048
  • 浏览: 1118983 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

redis的进程锁(并发锁)(秒杀)

阅读更多

 

redis的进程锁(并发锁)(秒杀)

雷同zookeeper进程锁

 

 

虽然redis时单线程的这可以保证任务串行(可以是来自不同进程的多线程操作或同一个redis客户端的多个并发线程),

但是不能保证哪一个客户端的某个线程(某一个进程的某个线程)一直纯粹的操作redis。这样就会出现数据丢失修改,

脏数据的问题,用redis的进程锁可以保证一个进程的这个获得锁的线程一直操作直到自己删除释放锁,这样就解决了

并发的问题

这个可以用来做秒杀悲观锁的思路

 

 

不过秒杀也可用redis事物+watcher变量实现(类似乐观锁的观点)

 

 

 

 

 

使用Redis的 SETNX 命令可以实现分布式锁,下文介绍其实现方法。

SETNX命令简介

命令格式

SETNX key value

将 key 的值设为 value,当且仅当 key 不存在。 
若给定的 key 已经存在,则 SETNX 不做任何动作。 
SETNX 是SET if Not eXists的简写。

返回值

返回整数,具体为 
- 1,当 key 的值被设置 
- 0,当 key 的值没被设置

例子

redis> SETNX mykey “hello” 
(integer) 1 
redis> SETNX mykey “hello” 
(integer) 0 
redis> GET mykey 
“hello” 
redis>

使用SETNX实现分布式锁

多个进程执行以下Redis命令:

SETNX lock.foo <current Unix time + lock timeout + 1>

如果 SETNX 返回1,说明该进程获得锁,SETNX将键 lock.foo 的值设置为锁的超时时间(当前时间 + 锁的有效时间)。 
如果 SETNX 返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁。

解决死锁

考虑一种情况,如果进程获得锁后,断开了与 Redis 的连接(可能是进程挂掉,或者网络中断),如果没有有效的释放锁的机制,那么其他进程都会处于一直等待的状态,即出现“死锁”。

上面在使用 SETNX 获得锁时,我们将键 lock.foo 的值设置为锁的有效时间,进程获得锁后,其他进程还会不断的检测锁是否已超时,如果超时,那么等待的进程也将有机会获得锁。

然而,锁超时时,我们不能简单地使用 DEL 命令删除键 lock.foo 以释放锁。考虑以下情况,进程P1已经首先获得了锁 lock.foo,然后进程P1挂掉了。进程P2,P3正在不断地检测锁是否已释放或者已超时,执行流程如下:

  • P2和P3进程读取键 lock.foo 的值,检测锁是否已超时(通过比较当前时间和键 lock.foo 的值来判断是否超时)
  • P2和P3进程发现锁 lock.foo 已超时
  • P2执行 DEL lock.foo命令
  • P2执行 SETNX lock.foo命令,并返回1,即P2获得锁
  • P3执行 DEL lock.foo命令将P2刚刚设置的键 lock.foo 删除(这步是由于P3刚才已检测到锁已超时)
  • P3执行 SETNX lock.foo命令,并返回1,即P3获得锁
  • P2和P3同时获得了锁

从上面的情况可以得知,在检测到锁超时后,进程不能直接简单地执行 DEL 删除键的操作以获得锁。

为了解决上述算法可能出现的多个进程同时获得锁的问题,我们再来看以下的算法。 
我们同样假设进程P1已经首先获得了锁 lock.foo,然后进程P1挂掉了。接下来的情况:

  • 进程P4执行 SETNX lock.foo 以尝试获取锁
  • 由于进程P1已获得了锁,所以P4执行 SETNX lock.foo 返回0,即获取锁失败
  • P4执行 GET lock.foo 来检测锁是否已超时,如果没超时,则等待一段时间,再次检测
  • 如果P4检测到锁已超时,即当前的时间大于键 lock.foo 的值,P4会执行以下操作 
    GETSET lock.foo <current Unix timestamp + lock timeout + 1>
  • 由于 GETSET 操作在设置键的值的同时,还会返回键的旧值,通过比较键 lock.foo 的旧值是否小于当前时间,可以判断进程是否已获得锁
  • 假如另一个进程P5也检测到锁已超时,并在P4之前执行了 GETSET 操作,那么P4的 GETSET 操作返回的是一个大于当前时间的时间戳,这样P4就不会获得锁而继续等待。注意到,即使P4接下来将键 lock.foo 的值设置了比P5设置的更大的值也没影响。

另外,值得注意的是,在进程释放锁,即执行 DEL lock.foo 操作前,需要先判断锁是否已超时。如果锁已超时,那么锁可能已由其他进程获得,这时直接执行 DEL lock.foo 操作会导致把其他进程已获得的锁释放掉

程序代码

用以下python代码来实现上述的使用 SETNX 命令作分布式锁的算法。

LOCK_TIMEOUT = 3
lock = 0
lock_timeout = 0
lock_key = 'lock.foo'

# 获取锁
while lock != 1:
    now = int(time.time())
    lock_timeout = now + LOCK_TIMEOUT + 1
    lock = redis_client.setnx(lock_key, lock_timeout)
    if lock == 1 or (now > int(redis_client.get(lock_key))) and now > int(redis_client.getset(lock_key, lock_timeout)):
        break
    else:
        time.sleep(0.001)

# 已获得锁
do_job()

# 释放锁
now = int(time.time())
if now < lock_timeout:
    redis_client.delete(lock_key)

java代码:
public static boolean acquireLock(String lock) {
    // 1. 通过SETNX试图获取一个lock
    boolean success = false;
    Jedis jedis = pool.getResource();      
    long value = System.currentTimeMillis() + expired + 1;    
    System.out.println(value);    
    long acquired = jedis.setnx(lock, String.valueOf(value));
    //SETNX成功,则成功获取一个锁
    if (acquired == 1)      
        success = true;
    //SETNX失败,说明锁仍然被其他对象保持,检查其是否已经超时
    else {
        long oldValue = Long.valueOf(jedis.get(lock));
 
        //超时
        if (oldValue < System.currentTimeMillis()) {
            String getValue = jedis.getSet(lock, String.valueOf(value));              
            // 获取锁成功
            if (Long.valueOf(getValue) == oldValue)
                success = true;
            // 已被其他进程捷足先登了
            else
                success = false;
        }
        //未超时,则直接返回失败
        else            
            success = false;
    }        
    pool.returnResource(jedis);
    return success;      
}
 
 
参考:
http://www.cnblogs.com/jianwei-dai/p/6137896.html

 

 

 

分享到:
评论

相关推荐

    基于redis分布式锁实现“秒杀”

    ### 基于Redis分布式锁实现“秒杀” #### 一、引言 在现代互联网应用中,“秒杀”作为一种常见的促销手段,被广泛应用于电商领域。为了保证系统的稳定性和公平性,在高并发环境下实现秒杀功能时,合理地利用分布式...

    redis秒杀

    Redis秒杀系统是一种高效处理高并发请求的解决方案,常用于电商平台的限时抢购活动。它利用Redis的内存存储特性,可以快速读写数据,避免了传统数据库在高并发下的性能瓶颈。以下是对这个主题的详细解释: 1. **...

    java基于jedisLock—redis分布式锁实现示例代码

    "java基于jedisLock—redis分布式锁实现示例代码" java基于jedisLock—redis分布式锁实现示例代码主要介绍了jedisLock—redis分布式锁实现示例代码,以下是对标题和描述中所说的知识点的详细说明: 分布式锁是啥?...

    微服务 Spring Boot 整合Redis 秒杀 ,全局唯一ID,乐观锁解决库存超卖,Jmeter 测试 每秒千万级并发

    本项目基于微服务架构,使用Spring Boot整合Redis,实现了高效的秒杀系统,并通过全局唯一ID(UUID)和乐观锁技术来防止库存超卖问题。此外,还利用JMeter进行性能测试,确保系统能在每秒处理千万级别的并发请求。...

    基于Redis实现分布式锁以及任务队列

    在高并发场景下,如电商平台的秒杀活动,传统的数据库处理方式可能无法承受巨大压力,甚至导致服务崩溃。为了解决这些问题,我们可以利用分布式锁和任务队列来优化系统。分布式锁用于控制同一资源在同一时间仅允许一...

    PHP+Redis事务解决高并发下商品超卖问题(推荐)

    这个函数首先建立与Redis的连接,然后定义了秒杀商品库存的键`secKill:$couponId:stock`,并使用`watch`命令监控这个键,确保在接下来的操作中,如果该键的值被其他客户端修改,事务会被取消,从而保证了数据的一致...

    Redis从入门到精通(九)Redis实战(六)基于Redis队列实现异步秒杀下单 测试项目代码

    在高并发场景下,为了防止多个进程同时处理同一个请求,我们可以使用Redis的分布式锁,如`SETNX`或`REDIS-LOCK`库。一旦获取到锁,进程才能进行后续操作,处理完成后释放锁。 7. **消息通知与状态更新**: 秒杀...

    Java秒杀系统方案优化 高性能高并发实战

    ### Java秒杀系统方案优化与高性能高并发实战 在当今互联网快速发展的背景下,高并发、高性能成为了考验系统架构的关键指标之一。特别是在电商领域中的“秒杀”活动,它不仅能够吸引大量用户参与,还能有效提升商品...

    boot-redis-clusters.rar

    综合以上信息,这个压缩包可能包含了一个基于Spring Boot构建的微服务应用,该应用依赖于Nacos进行服务发现和配置管理,并使用Redis集群来提供缓存、分布式锁以及处理秒杀场景下的高并发问题。开发者可能需要了解...

    电商系统秒杀系统设计 营销活动-秒杀业务 小米网秒杀系统设计经验与问题 共28页.ppt

    原有的商城系统采用PHP + MySQL架构,下单流程复杂,包括查库存、锁表、减库存、创建订单等多个步骤,这在高并发环境下容易导致PHP进程卡死和带宽不足。 为了应对这一挑战,设计了资格抢购系统作为第一个关键设计...

    PHP并发场景的几种解决方案.docx

    以下将详细讨论两种常见的PHP并发解决方案:利用Redis事务特性和使用文件排他锁。 首先,我们可以利用Redis的事务特性来实现并发控制。Redis事务是原子性的,这意味着在事务中的所有操作都会在一个不可中断的序列中...

    第九讲:分布式锁的原理及应用&秒杀设计实现.pdf

    2. **分布式锁**:适用于分布式系统,防止多进程同时操作同一资源,解决进程间的并发问题。 3. **事务**:确保在一个会话内,所有对数据库表的操作要么全部成功,要么全部失败,保证数据一致性。 4. **分布式事务**...

    基于微服务SpringBoot的商城高并发抢单系统:商城高并发抢单系统-秒杀系统.zip

    商城的抢单或秒杀场景通常会面临极高并发,系统需要设计成能够处理大量用户同时请求。这涉及到负载均衡、线程池管理、数据库优化、缓存策略等。例如,使用Nginx进行反向代理和负载均衡,通过Redis缓存减轻数据库...

    第8讲:分布式锁的原理及应用&秒杀设计实现.pdf

    2. **分布式锁**:适用于多进程跨机器环境,解决不同进程间的并发问题。 3. **事务**:关注的是数据库层面,保证单个会话内的多条操作要么全部成功,要么全部回滚,确保数据一致性。 4. **分布式事务**:处理跨服务...

    Redis使用-缓存穿透,雪崩,击穿以及解决方案分析.docx

    缓存击穿是指一个 Key 非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个 Key 在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。 解决方案我们的...

    Java毕业设计-基于微服务的商城秒杀系统源码.zip

    该系统旨在模拟真实电商环境下的高并发秒杀场景,通过微服务的方式解耦各个功能模块,提高系统的可扩展性和容错性。 在该项目中,我们可以学习到以下关键知识点: 1. **微服务架构**:微服务是一种将大型应用拆分...

    商城秒杀-京东秒杀 Java微服务实现

    seckill-master"可能是一个开源项目的名字,暗示了该系统可能采用了树形结构来组织秒杀活动,可能是为了更有效地管理不同级别的秒杀商品,如分类、子分类等,同时可能利用了分布式锁或队列等技术来控制并发访问,...

    前后端分离的微服务、高并发、高可用客户端秒杀系统和后台管理系统说明资源来源网络以及部分开源社区、仅供参考与学习、项目不可商用

    为了实现高并发,开发者通常会采用负载均衡、缓存策略(如Redis)、数据库优化(如分库分表)、异步处理(如消息队列)等技术。同时,使用无状态服务和会话持久化也是提高系统并发能力的有效手段。 4. 高可用: 高...

    基于GO语言大型企业级电商秒杀系统实战教程

    利用缓存对写请求:缓存也是可以应对写请求,比如我们可以把数据库中库存数据迁移到Redis缓存中,所有减库存操作都在Redis中进行,然后通过后台进程把Redis中的用户秒杀请求同步到数据库中数据库层数据库层是最脆弱...

Global site tag (gtag.js) - Google Analytics