• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    如何利用Redis锁解决高并发问题详解

    redis技术的使用:

    redis真的是一个很好的技术,它可以很好的在一定程度上解决网站一瞬间的并发量,例如商品抢购秒杀等活动。。。

    redis之所以能解决高并发的原因是它可以直接访问内存,而以往我们用的是数据库(硬盘),提高了访问效率,解决了数据库服务器压力。

    为什么redis的地位越来越高,我们为何不选择memcache,这是因为memcache只能存储字符串,而redis存储类型很丰富(例如有字符串、LIST、SET等),memcache每个值最大只能存储1M,存储资源非常有限,十分消耗内存资源,而redis可以存储1G,最重要的是memcache它不如redis安全,当服务器发生故障或者意外关机等情况时,redsi会把内存中的数据备份到硬盘中,而memcache所存储的东西全部丢失;这也说明了memcache不适合做数据库来用,可以用来做缓存。

    引言

    这里我们主要利用Redis的setnx的命令来处理高并发。

    setnx 有两个参数。第一个参数表示键。第二个参数表示值。如果当前键不存在,那么会插入当前键,将第二个参数做为值。返回 1。如果当前键存在,那么会返回0。

    创建库存表

    CREATE TABLE `storage` (
     `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
     `number` int(11) DEFAULT NULL,
     PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

    设置初始库存为10

    创建订单表

    CREATE TABLE `order` (
     `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
     `number` int(11) DEFAULT NULL,
     PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

    测试不用锁的时候

    $pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'root');
    $sql="select `number` from storage where id=1 limit 1";
    $res = $pdo->query($sql)->fetch();
    $number = $res['number'];
    if($number>0)
    {
     $sql ="insert into `order` VALUES (null,$number)";
    
     $order_id = $pdo->query($sql);
     if($order_id)
     {
    
     $sql="update storage set `number`=`number`-1 WHERE id=1";
     $pdo->query($sql);
     }
    }

    ab测试模拟并发,发现库存是正确的。

    mysql> select * from storage;
    +----+--------+
    | id | number |
    +----+--------+
    | 1 | 0 |
    +----+--------+
    1 row in set (0.00 sec)

    在来看订单表

    mysql> select * from `order`;
    +----+--------+
    | id | number |
    +----+--------+
    | 1 | 10 |
    | 2 | 10 |
    | 3 | 9 |
    | 4 | 7 |
    | 5 | 6 |
    | 6 | 5 |
    | 7 | 5 |
    | 8 | 5 |
    | 9 | 4 |
    | 10 | 1 |
    +----+--------+
    10 rows in set (0.00 sec)

    发现存在几个订单都是操作的同一个库存数据,这样就可能引起超卖的情况。

    修改代码加入redis锁进行数据控制

    ?php
    /**
     * Created by PhpStorm.
     * User: daisc
     * Date: 2018/7/23
     * Time: 14:45
     */
    class Lock
    {
     private static $_instance ;
     private $_redis;
     private function __construct()
     {
     $this->_redis = new Redis();
     $this->_redis ->connect('127.0.0.1');
     }
     public static function getInstance()
     {
     if(self::$_instance instanceof self)
     {
      return self::$_instance;
     }
     return self::$_instance = new self();
     }
    
     /**
     * @function 加锁
     * @param $key 锁名称
     * @param $expTime 过期时间
     */
     public function set($key,$expTime)
     {
     //初步加锁
     $isLock = $this->_redis->setnx($key,time()+$expTime);
     if($isLock)
     {
      return true;
     }
     else
     {
      //加锁失败的情况下。判断锁是否已经存在,如果锁存在切已经过期,那么删除锁。进行重新加锁
      $val = $this->_redis->get($key);
      if($val$valtime())
      {
      $this->del($key);
      }
      return $this->_redis->setnx($key,time()+$expTime);
     }
     }
    
    
     /**
     * @param $key 解锁
     */
     public function del($key)
     {
     $this->_redis->del($key);
     }
    
    }
    
    
    
    $pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'root');
    $lockObj = Lock::getInstance();
    //判断是能加锁成功
    if($lock = $lockObj->set('storage',10))
    {
     $sql="select `number` from storage where id=1 limit 1";
     $res = $pdo->query($sql)->fetch();
     $number = $res['number'];
     if($number>0)
     {
     $sql ="insert into `order` VALUES (null,$number)";
    
     $order_id = $pdo->query($sql);
     if($order_id)
     {
    
      $sql="update storage set `number`=`number`-1 WHERE id=1";
      $pdo->query($sql);
     }
     }
     //解锁
     $lockObj->del('storage');
    
    }
    else
    {
     //加锁不成功执行其他操作。
    }

    再次进行ab测试,查看测试结果

    mysql> select * from `order`;
    +----+--------+
    | id | number |
    +----+--------+
    | 1 | 10 |
    | 2 | 9 |
    | 3 | 8 |
    | 4 | 7 |
    | 5 | 6 |
    | 6 | 5 |
    | 7 | 4 |
    | 8 | 3 |
    | 9 | 2 |
    | 10 | 1 |
    +----+--------+
    10 rows in set (0.00 sec)

    发现订单表没有操作同一个库存数据的情况。所以利用redis锁是可以有效的处理高并发的。

    这里在加锁的时候其实是可以不需要判断过期时间的,这里我们为了避免造成死锁,所以加一个过期时间的判断。当过期的时候主动删除该锁。

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

    您可能感兴趣的文章:
    • 详解利用redis + lua解决抢红包高并发的问题
    • Nginx+Lua+Redis构建高并发Web应用
    • Redis实现高并发计数器
    • Redis瞬时高并发秒杀方案总结
    • PHP实现Redis单据锁以及防止并发重复写入
    • jedispool连redis高并发卡死的问题
    • 使用lua+redis解决发多张券的并发问题
    上一篇:Linux下Redis安装教程详解
    下一篇:Redis中一些最常见的面试问题总结
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    如何利用Redis锁解决高并发问题详解 如何,利用,Redis,锁,解决,