zoukankan      html  css  js  c++  java
  • php结合redis高并发下,悲观锁解决数据二次写入

    悲观锁

    在悲观锁的情况下,为了保证事务的隔离性,就须要一致性锁定读。
    读取数据时给加锁,其他事务无法改动这些数据
    改动删除数据时也要加锁,其他事务无法读取这些数据。

    在做数据缓存的时候,通常都是把数据从数据库读取出来,然后放入缓存,接下来在缓存的有效期内都是从缓存读取数据减少数据库压力。但是在高并发环境下,就有可能出现问题,比如根据指定格式从redis下拿数据,但是当下key是不存在的,那么就需要往里面写数据,如果多个进程同时请求,会造成数据的二次写入,如果逻辑不复杂还不会出现大的问题,问题是假如这个key的数据会变化呢?那么这时候就需要加一个锁机制了,就是获取了锁权限的进程才有资格对数据操作。

    (意思是,加入悲观锁,让拿到锁的进程,进行判断key操作,如果有就读取,如果没有就写入,读取的时候别的进程无法改动这些数据,如果要是写数据的时候别的事务不能读取这条数据)

    提到悲观锁,先通过网上给出的一个比较形象的比喻

    拿健身房比喻,门口挂着把钥匙(只有一把),想进去的人必须拿到这把钥匙才行,拿到钥匙的人可以进入,不管是热身、喝水还是跑步都可以,直到他出来把钥匙挂回墙上,下一个才能去争取,拿到的才可以再进去。

    听着好像有点不人性化,所以悲观锁比较适合强一致性的场景,但效率比较低,特别是读的并发低。乐观锁则适用于读多写少,并发冲突少的场景。

    实现要点和思路

    1、一个任务在同一时间段内只能被一个用户所持有; 
    2、避免出现死任务,即避免任务被用户长时间占有,无法释放。(使用redis的)

    设置一个锁的key,setnx是原子操作,只能一个进程写入成功,写入成功返回true(表示获取锁权限),然后写入内容立即释放锁即删除锁key。

    如果只用SETNX命令设置锁的话,如果当持有锁的进程崩溃或删除锁失败时,其他进程将无法获取到锁,问题就大了。

    获取不到锁的进程去判断锁的剩余有效时间,如果为-1,那么表示没有设置过期时间,则设置锁的有效时间为5秒(预留5秒给拿到锁的进程处理时间,足够多了),返回true,等待锁删除。

    <?php
    $lock_key = 'LOCK_PREFIX' . $redis_key;
    $is_lock = $redis->setnx($lock_key, 1); // 加锁-》将上面变量当做key,判断如果有key值,不做操作,如果没有,将lockkey的值设置为1
    if($is_lock == true){ // 获取锁权限  
        $redis->setex($redis_key, $expire, $data); // 写入内容
        // 释放锁
        $redis->del($lock_key);
    }else{
        // 防止死锁
        if($redis->ttl($lock_key) == -1){
            $redis->expire($lock_key, 5);
        }
        return true; // 获取不到锁权限,直接返回
    }

    setnx :

    只在键 key 不存在的情况下, 将键 key 的值设置为 value 。
    若键 key 已经存在, 则 SETNX 命令不做任何动作。

    setex:

    将键 key 的值设置为 value , 并将键 key 的生存时间设置为 seconds 秒钟。

    如果键 key 已经存在, 那么 SETEX 命令将覆盖已有的值。

  • 相关阅读:
    【转】进程间通信方式总结(windows 和linux)
    Python-Analysis-Malware
    现场取证之流量分析总结
    木马通信与防护墙穿透
    【PE结构】恶意代码数字签名验证
    32位与64位架构上的区别
    【API】网络编程模型、多线程
    【API】遍历进程的几种方式
    【病毒取样】取证分析之逆向服务器提权开启3389远程连接工具
    【CTF WEB】XSS-https://alf.nu/alert1
  • 原文地址:https://www.cnblogs.com/yszr/p/10580026.html
Copyright © 2011-2022 走看看