search

Home  >  Q&A  >  body text

php - 怎样理解mysql innodb的行级锁?

因为最近在做公司的一个秒杀项目,就是现在一些购物网站最常见的那种。但是考虑到并发的一些问题(也许并发不是主要的,主要的是现在有秒杀就会出现秒杀工具之类的)导致被秒出去的商品比实际库存还要多,所以就上网看了一下mysql的锁,公司用的是innodb引擎,thinkphp3.2框架,根据网上的相关资料,应该用的是行级锁(对于mysql,本人菜鸟,懂的不是很深入)。

 public function test_sql(){
        set_time_limit(0);
        $model = D('Liren/GroupPurchase');
        $row = $model->lock(true)->where(array('id'=>1))->find();
        if($row){
            dump($row);
            sleep(10);
        }
    }

test_sql 方法是带锁查询了一条数据,之后延时了十秒,在这同时,我开了另一个进程:

 public function test_lock(){
        $model = D('Liren/GroupPurchase');
        $info = $model->find(1);
        if($info){
            dump($info);
        }
        
    }

在test_sql没有结束之前test_lock一直在等待。而且就算我在test_lock方法中查询的不是主键(id)为1的数据,也同样要等到test_sql结束之后才能执行。这样的话是不是整个表都锁住了,是不是需要继承thinkphp的AdvModel才能真正实现行级锁?

还有一个问题。事务和锁之间存在关系吗?

public function test_sql(){
        set_time_limit(0);
        $model = D('Liren/GroupPurchase');
        $model->startTrans();
        $row = $model->lock(true)->where(array('id'=>1))->find();
       // echo $model->getLastSql();
        if($row){
            $ret = $model->lock(true)->save(array('id'=>1,'is_show'=>0));
            echo $model->getLastSql();
        }
        if($ret){
            $model->commit();
            sleep(10);
            echo 'success';
        }
    }
    

虽然事务是提交了,数据库的状态也一早就改变了,但还是必须等到test_sql进程结束之后 test_lock 方法才能输出数据,如果能在事务结束的时候表锁就能结束,这样是不是好一点。

小弟愚钝,望指点!

阿神阿神2896 days ago361

reply all(3)I'll reply

  • 伊谢尔伦

    伊谢尔伦2017-04-10 17:41:26

    既然是秒杀功能为什么还要用MySQL呢?为什么不考虑redis等内存缓存数据库呢?
    毕竟内存的IO效率和磁盘的IO效率之间大概相差了中美之间经济实力那么多吧

    那么谈谈锁的问题:
    相比题主现在对概念应该还有些模糊,我先明确概念:
    读锁->共享锁 (S)
    写锁 -> 排它锁 (X)

    兼容性:

        X           S
    X    不兼容    不兼容
    S    不兼容    兼容
    

    还有一种叫乐观锁/悲观锁

    这份回答很好,我直接拿来了,总结来说就是:

    • △乐观锁是通过逻辑实现,本质上并没有给数据库加锁

    • 悲观锁是通过真实的数据库锁机制来完成的。

    最后回到你的问题:

    • test_sql()函数: 首先要确认,在对表获取行锁的时候,要尽量的使用索引检索纪录,如果没有使用索引访问,那么即便你只是要更新其中的一行纪录,也是全表锁定的。要确保sql是使用索引来访问纪录的,必要的时候,请使用explain检查sql的执行计划,判断是否按照预期使用了索引。
      由于mysql的行锁是针对索引加的锁,不是针对纪录加的锁,所以虽然是访问不同行的纪录,但是如果是相同的索引键,是会被加锁的。

    • 事务和锁没什么关系,锁的机制与存储引擎才有关

    reply
    0
  • 怪我咯

    怪我咯2017-04-10 17:41:26

    InnoDB引擎下,执行的SQL使用行级锁,还是全表锁。跟mysql采用的隔离级别、sql会使用到的索引、mysql自身针对这个sql的执行优化都有关系。
    所以怎么理解mysql的锁,需要根据你在用的mysql的具体配置有关。有一篇博客讲的特别透彻,希望对你有用。http://blog.sae.sina.com.cn/archives/2127

    reply
    0
  • 巴扎黑

    巴扎黑2017-04-10 17:41:26

    1.innodb 引擎支持行锁,但是不指定唯一索引键就会锁表
    test_sql 中,使用了悲观锁,也就是 select where for update
    当 where 条件指定了唯一索引键时---行锁
    当非唯一索引键时---表锁

    2.在事务中
    select 操作共享锁
    update,delete,insert 排它锁
    commit 会把锁给取消

    reply
    0
  • Cancelreply