thinkphp6使用mysql悲观锁解决商品超卖问题的实现
发布:smiling 来源: PHP粉丝网 添加日期:2022-05-18 08:59:49 浏览: 评论:0
这篇文章主要介绍了thinkphp6使用mysql悲观锁解决商品超卖问题的实现
悲观锁介绍(百科):
悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
使用场景举例:以MySQL InnoDB为例
商品goods表,假设商品的id为1,购买数量为1,status为1表示上架中,2表示下架。现在用户购买此商品,在不是高并发的情况下处理逻辑是:
查找此商品的信息;
检查商品库存是否大于购买数量;
修改商品库存和销量;
上面这种场景在高并发访问的情况下很可能会出现问题,如果商品库存是100个,高并发的情况下可能会有1000个同时访问,在到达第2步的时候,都会检测通过,这样会出现商品库存是-900个的情况。显然着不满足需求!!!
商品表结构:
- CREATE TABLE `goods` (
- `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
- `name` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
- `status` tinyint(1) NOT NULL DEFAULT '1',
- `total` int(11) NOT NULL DEFAULT '0',
- `sell` int(11) NOT NULL DEFAULT '100',
- `price` decimal(10,2) NOT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
- INSERT INTO `test`.`goods`(`id`, `name`, `status`, `total`, `sell`, `price`) VALUES (1, '商品', 1, 0, 100, 15.00);
订单表结构:
- CREATE TABLE `orders` (
- `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
- `uid` int(11) NOT NULL DEFAULT '0',
- `create_time` datetime NOT NULL,
- `status` tinyint(1) NOT NULL DEFAULT '1',
- `goods_id` int(11) NOT NULL DEFAULT '0',
- `order_no` varchar(200) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
使用悲观锁处理。
当我们在查询出goods信息后就把当前的数据锁定,直到我们修改完毕后再解锁。那么在这个过程中,因为goods被锁定了,就不会出现有第三者来对其进行修改了。
注:要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。thinkphp6中使用事务,手动进行提交回滚。
- <?php
- namespace app\controller;
- use app\BaseController;
- use think\facade\Db;
- class Test extends BaseController
- {
- /**
- * 不加锁
- * @return string|void
- */
- public function test_1()
- {
- $num = 1;
- $goods_id = 1;
- Db::startTrans();
- try {
- $where = [];
- $where['id'] = $goods_id;
- $where['status'] = 1;
- $goods_info = Db::table('goods')->where($where)->find();
- if (emptyempty($goods_info)) {
- return '商品不存在';
- }
- $total = $goods_info['total'];
- $sell = $goods_info['sell'];
- if ($total < $num) {
- return '库存不足';
- }
- $data['total'] = $total-$num;
- $data['sell'] = $sell+$num;
- $res = Db::table('goods')->where(['id'=>$goods_id])->update($data);
- $order_data = [];
- $order_data['uid'] = rand(1000,9999);
- $order_data['status'] = 1;
- $order_data['create_time'] = date('Y-m-d H:i:s');
- $order_data['goods_id'] = $goods_id;
- $order_data['order_no'] = date('YmdHis').rand(1000,10000);
- $order_res = Db::table('orders')->insert($order_data);
- Db::commit();
- } catch (\Exception $e) {
- // 回滚事务
- Db::rollback();
- echo $e->getMessage();
- exit('rollback');
- }
- echo '请求成功';
- }
- /**
- * 加锁--悲观锁
- * @return string|void
- */
- public function test_2()
- {
- $num = 1;
- $goods_id = 1;
- Db::startTrans();
- try {
- $where = [];
- $where['id'] = $goods_id;
- $where['status'] = 1;
- $goods_info = Db::table('goods')->lock(true)->where($where)->find();
- if (emptyempty($goods_info)) {
- return '商品不存在';
- }
- $total = $goods_info['total'];
- $sell = $goods_info['sell'];
- if ($total < $num) {
- return '库存不足';
- }
- $data['total'] = $total-$num;
- $data['sell'] = $sell+$num;
- $res = Db::table('goods')->where(['id'=>$goods_id])->update($data);
- $order_data = [];
- $order_data['uid'] = rand(1000,9999);
- $order_data['status'] = 1;
- $order_data['goods_id'] = $goods_id;
- $order_data['order_no'] = date('YmdHis').rand(1000,10000);
- $order_data['create_time'] = date('Y-m-d H:i:s');
- $order_res = Db::table('orders')->insert($order_data);
- Db::commit();
- } catch (\Exception $e) {
- // 回滚事务
- Db::rollback();
- echo $e->getMessage();
- exit('rollback');
- }
- echo '请求成功';
- }
- }
使用jmeter工具测试,创建线程测试组:
关于使用jmeter创建测试高并发例子,可查看:使用JMeter进行高并发测试_左右..的博客-CSDN博客_jmeter高并发测试
这里模拟1s内1000个用户同时访问
创建http请求:
添加察看结果树:
测试开始:
100个商品不加锁的结果:
100个商品库存,生成订单187个,超卖87个商品,这在项目开发中是绝对不允许的。
100个商品,加锁结果:
加锁,得到解决。
Tags: thinkphp6 mysql悲观锁
- 上一篇:tp5使用layui实现多个图片上传(带附件选择)的方法实例
- 下一篇:最后一页
相关文章
- ·使用composer安装使用thinkphp6.0框架问题【视频教程】(2021-12-26)
- ·基于thinkphp6.0的success、error实现方法(2022-01-21)
- ·让whoops帮我们告别ThinkPHP6的异常页面(2022-02-17)
- ·ThinkPHP6.0如何利用自定义验证规则规范的实现登陆(2022-04-06)
- ·ThinkPHP6通过Ucenter实现注册登录的示例代码(2022-04-07)
- ·ThinkPHP6.0 重写URL去掉Index.php的解决方法(2022-04-08)
推荐文章
热门文章
最新评论文章
- 写给考虑创业的年轻程序员(10)
- PHP新手上路(一)(7)
- 惹恼程序员的十件事(5)
- PHP邮件发送例子,已测试成功(5)
- 致初学者:PHP比ASP优秀的七个理由(4)
- PHP会被淘汰吗?(4)
- PHP新手上路(四)(4)
- 如何去学习PHP?(2)
- 简单入门级php分页代码(2)
- php中邮箱email 电话等格式的验证(2)