mysql lock table/unlock table 表锁/解锁

在某个地方看到有个例子,具体描述类似如下:商店现在某商品只有1件库存,然后A与B在网上进行下订,A与B几乎同时(或许也就差几毫秒,A比B快那么一点点)进行。

很明显是只有A才能成功下单的,B则会收到库存不足的提示,但是作为放置在服务端的那个页面(或者称为脚本程序)我们得怎样去处理这个问题呢?或者我先放出一段代码吧。

 代码如下 复制代码

    $sql = "select number from goods where id=1";
    $number = intval( $db->result( $db->query( $sql ), 0 ) );
    if ( $number > 0 ) {
    sleep( 2 );
    $sql = "update goods set number=number-1 where id = 1";
    if ( $db->query( $sql ) ) {
    echo 'Ok!Here you are!';
    } else {
    echo 'Sorry!Something Go wrong!Try it again.';
    }
    } else {
    echo 'No more!you are so late!';
    }

这部分代码除了缺少一定注释外都写得没错,当然$db是一个操作数据库的类,我只是将大部分方法封装了,这里的逻辑也是很明显了。

先获取id为1这个东东的库存数,看看是否为0,如果为0就订购不成功了,如果大于0则将库存减1然后提示ok。这确实没有任何错误,逻辑也对。如果请求是一个接一个地产生的,那么什么问题都没有,但当一些并发情况出现时就可能出现一些无厘头的问题了。你想啊,是不是可能存在一种情况,A刚发出请求,脚本处理到update之前B又发出请求,那么现在库存依然还有1,因为A的update还没有执行呢,所以$number不少于0,这次完了,B也下单了,于是库存变成-1了(假设原来只有1件),确实是一个荒谬而且比较搞笑的结果。

出现问题的原因很明显,就是忽略了这种并发情况的考虑,处理下订应该是种队列方式,也就是先来先得,就是说在执行这个下订动作是要排队的,前面的那个先下订然后后者才能下订,当然当后者下订前才再判断库存的数量。那么怎样解决这个问题呢,在程序层面上貌似真的没有方法去解决这个问题,所以在此才提到锁表的概念,上面出现这个问题的归根于没有控制一个select number的先后顺序(或者可以这么说吧),因为在A执行update之前你又允许B去查询库存,当然结果还是1,至少要等待A更新库存后才允许其他人的任何操作,也就是对goods表进行一个排队操作,对goods表进行锁定。

说到这里,请不要以为锁表有多么高深,其实它就是一条sql

    LOCK TABLE `table` [READ|WRITE]

解锁

    UNLOCK TABLES;

引用专业的描述是

LOCK TABLES为当前线程锁定表。 UNLOCK TABLES释放被当前线程持有的任何锁。当线程发出另外一个LOCK TABLES时,或当服务器的连接被关闭时,当前线程锁定的所有表会自动被解锁。 

如果一个线程获得在一个表上的一个READ锁,该线程和所有其他线程只能从表中读。 如果一个线程获得一个表上的一个WRITE锁,那么只有持锁的线程READ或WRITE表,其他线程被阻止。

已经是有种队列的味道,对不,所以解决方案很简单嘛,在select前加锁,执行完后面逻辑代码后解锁。或许有没有人会有一个疑问,就是如果万一锁表后线程就断掉了那么是不是就一直锁表了,这个确实是可能存在但是既然你想到了那么数据库的设计人员也一定考虑到了,可以告诉你关于unlock的一些资料:当线程发出另一个 LOCK TABLES,或当与服务器的连接被关闭时,被当前线程锁定的所有表将被自动地解锁。这下放心了吧。

好,看下改进后的代码。

   

 代码如下 复制代码
$db->lock( 'goods', 2 );
    $sql = "select number from goods where id=1";
    $number = intval( $db->result( $db->query( $sql ), 0 ) );
    if ( $number > 0 ) {
    sleep( 2 );
    $sql = "update goods set number=number-1 where id = 1";
    if ( $db->query( $sql ) ) {
    echo 'Ok!Here you are!';
    } else {
    echo 'Sorry!Something go wrong!Try it again.';
    }
    } else {
    echo 'No more!you are so late!';
    }
    $db->unlock();

只加了两行代码,不过也不能这么说,因为paperen我修改了自己那个操作数据库的类,加了两个方法lock与unlock,其实这两个方法也很简单。

 

 代码如下 复制代码
   /**
    * 锁表
    * @param string $table 表名
    * @param int $type 读锁1还是写锁2
    */
    public function lock( $table, $type = 1 ) {
    $type = ( $type == 1 ) ? 'READ' : 'WRITE';
    $this->query( "LOCK TABLE `$table` $type" );
    }
     
    /**
    * 解锁
    */
    public function unlock() {
    $this->query( "UNLOCK TABLES" );
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值