Thinkphp5 实现悲观锁


 

悲观锁介绍(百科):

悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。


 

使用场景举例:以MySQL InnoDB为例

商品goods表,假设商品的id为2,购买数量为1,status为1表示上架中,2表示下架。现在用户购买此商品,在不是高并发的情况下处理逻辑是:

  1. 查找此商品的信息;
  2. 检查商品库存是否大于购买数量;
  3. 修改商品库存和销量;

上面这种场景在高并发访问的情况下很可能会出现问题。如果商品库存是100个,高并发的情况下可能会有1000个同时访问,在到达第2步的时候,都会检测通过。这样会出现商品库存是-900个的情况。显然着不满足需求!!!

 

使用悲观锁在处理。

当我们在查询出goods信息后就把当前的数据锁定,直到我们修改完毕后再解锁。那么在这个过程中,因为goods被锁定了,就不会出现有第三者来对其进行修改了。

注:要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。

 

 

 1 /**
 2  * thinkphp使用悲观锁。悲观锁需要配合事务一起使用
 3  * 商品表。购买数量为1,先锁定该商品,不让其他操作减库存。
 4  */
 5 public function mysql_lock(){
 6     $num = 1;
 7     $goods_id = 2;
 8     $model = \db('goods');
 9     $model->execute('set autocommit=0');
10     $model->startTrans();
11     //使用主键或者其他索引字段时为行级锁,否则为表级锁
12     $where['id'] = $goods_id;
13     $where['status'] = 1;
14     $goods_info = $model->lock(true)->where($where)->find();
15     if(empty($goods_info)){
16         return '商品不存在';
17     }
18     $total = $goods_info['total'];  //商品库存
19     $sell = $goods_info['sell'];    //商品销量
20     if($total<$num){
21         return '库存不足';
22     }
23     $data['total'] = $total-$num;
24     $data['sell'] = $sell+$num;
25     $res = $model->where(array('id'=>$goods_id))->update($data);
26     if($res){
27         $order_model = \db('orders');
28         $order_data['uid'] = rand(1000,9999);
29         $order_data['status'] = 1;
30         $order_data['create_time'] = date('Y-m-d H:i:s');
31         $order_res = $order_model->insert($order_data);
32         if($order_res){
33             $model->commit();
34         }else{
35             $model->rollback();
36         }
37     }else{
38         $model->rollback();
39     }
40     exit;
41 
42 }
43 
44 
45 
46 /**
47  * 不加锁的情况
48  * @return string
49  */
50 public function mysql_unlock(){
51     $num = 1;
52     $goods_id = 2;
53     $model = \db('goods');
54     $model->execute('set autocommit=0');
55     $model->startTrans();
56     $where['id'] = $goods_id;
57     $where['status'] = 1;
58     $goods_info = $model->where($where)->find();
59     if(empty($goods_info)){
60         return '商品不存在';
61     }
62     $total = $goods_info['total'];  //商品库存
63     $sell = $goods_info['sell'];    //商品销量
64     if($total<$num){
65         return '库存不足';
66     }
67     $data['total'] = $total-$num;
68     $data['sell'] = $sell+$num;
69     $res = $model->where(array('id'=>$goods_id))->update($data);
70     if($res){
71         $order_model = \db('orders');
72         $order_data['uid'] = rand(1000,9999);
73         $order_data['status'] = 1;
74         $order_data['create_time'] = date('Y-m-d H:i:s');
75         $order_res = $order_model->insert($order_data);
76         if($order_res){
77             $model->commit();
78         }else{
79             $model->rollback();
80         }
81     }else{
82         $model->rollback();
83     }
84     exit;
85 
86 }

 

 

加锁和不加锁的测试请看下篇文章【Thinkphp5 用ab压力测试工具测试高并发请求】。

 

posted @ 2017-08-15 17:56  Edward1108  阅读(1221)  评论(0编辑  收藏  举报