千家信息网

如何使用 Laravel sharedLock 与 lockForUpdate 进行数据表行锁

发表于:2025-02-06 作者:千家信息网编辑
千家信息网最后更新 2025年02月06日,本篇文章为大家展示了如何使用 Laravel sharedLock 与 lockForUpdate 进行数据表行锁,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获
千家信息网最后更新 2025年02月06日如何使用 Laravel sharedLock 与 lockForUpdate 进行数据表行锁

本篇文章为大家展示了如何使用 Laravel sharedLock 与 lockForUpdate 进行数据表行锁,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

场景

拼团功能,当 A 客户开团之后(两人团),如果 B 和 C 同时支付,如何规避两人同时将拼团人数增加。

Laravel 中 sharedLock 与 lockForUpdate 的区别

  • sharedLock 对应的是 LOCK IN SHARE MODE

  • lockForUpdate 对应的是 FOR UPDATE

sharedLock 与 lockForUpdate 相同的地方是,都能避免同一行数据被其他 transaction 进行 update。

不同的地方是:

  • sharedLock 不会阻止其他 transaction 读取同一行

  • lockForUpdate 会阻止其他 transaction 读取同一行 (需要特别注意的是,普通的非锁定读取读取依然可以读取到该行,只有 sharedLock 和 lockForUpdate 的读取会被阻止。)

即 sharedLock locks only for write, lockForUpdate also prevents them from being selected

这样做是有意义的,例如,两个 transaction 要更新同一个计数器,如果不使用 lockForUpdate, 会导致两个 transaction 同时读到同一个初始值,然后在应用层逻辑中增加计数之后,提交到数据库中,后者的操作会覆盖掉前者的操作。

如何测试

在 MySQL 命令行终端操作一个表

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from users for update;
+----+------+
| id | name |
+----+------+
| 1 | tom |
| 2 | bob |
+----+------+

这时再开一个命令行终端

mysql> select * from users for update;
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted
mysql> select * from users lock in share mode;
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted

你会发现,无论是 for update 还是 lock in share mode 都无法读取到数据,更加确切地说是,查询被阻塞了。

只有在第一个终端执行

commit;

第二个终端才能得到数据返回。

需要注意的是,发起者必须在 transaction 里上锁才有效,如果不是在 transaction 中,上锁是无效的。但是,第二个人无论是不是在 transaction 里,都会被锁。

我依然有几个疑问

  • Laravel 如何设置数据库操作超时时间

  • 什么场景下适合使用 sharedLock 呢?

  • sharedLock,lockForUpdate 与 Pessimistic Locking 是什么关系

  • Pessimistic locking(悲观锁) 与 Optimistic locking(乐观锁)的区别

如何测试 Laravel

A 用户,在浏览器里访问接口 (模拟支付回调),此时对数据表中某一行锁住,进行 30s 操作,然后提交事务。

B 用户,在浏览器里访问同一接口 (模拟支付回调),其无法修改该行。对应的返回是什么?

会一直 wait 到数据库操作超时。

那么问题来了,Laravel 如何设置数据库操作超时时间?

简单的测试方法,是在命令行中开两个 artisan tinker 窗口,分别执行

DB::transaction(function () {
echo 1;
User::where('id', 33)->lockForUpdate()->get();
echo 2;
sleep(10);
});

你会发现第二个 tinker 窗口中的 get 操作,需要等到第一个 transaction 执行完毕之后,才能得到查询结果。

需要注意的是,不在 transaction 中的 lockForUpdate 操作,是没有锁效果的。

真实场景,防止用户重复提现

DB::transaction(function () use ($user, &$user_award) {
$user_award = UserAward::where([
['user_id', $user->id],
['status', 0],
])
->lockForUpdate()
->first();
if ($user_award) {
$user_award->status = 1; // 提现中状态
$user_award->save();
}
});

if (!is_null($user_award)) {
$amount = $user_award->money * 100;
}

事务与锁的关系

事务中涉及的操作都会加上锁?

如果默认会加上锁,那么默认会加上什么锁呢?

事务中涉及的操作,不需要显式加锁?

要理清其中关系,就需要了解事务的四种隔离级别:

  • 未提交读(Read uncommitted)

  • 已提交读(Read committed)

  • 可重复读(Repeatable read)

  • 可串行化(Serializable )

MySQL 默认的是:可重复读(Repeatable read)

上述内容就是如何使用 Laravel sharedLock 与 lockForUpdate 进行数据表行锁,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注行业资讯频道。

数据 事务 一行 数据库 终端 数据表 两个 同时 命令 场景 用户 支付 测试 内容 只有 地方 技能 接口 时间 是在 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 长春华方网络技术有限公司 两个类共享数据库 中国四大服务器域名 柳州工务段软件开发 一个数据库能跑多个实例 网络安全概论精品课 未来教育三级网络技术精讲 软件开发制作的流程范文 公务员为什么会有5年服务器 数据库工具压缩 测股软件开发资质 金山区高科技软件开发应用范围 浙江信息化软件开发价格标准 松江区服务器设备回收公司 北京通信软件开发设施标准 软件开发风险识别思路 幻塔不同服务器的好可以买吗 设计web服务器可以申请专利吗 软件开发 思维 面试题 合肥医疗软件开发需要多少钱 西安哪个学校学软件开发 微软软件开发 证书 井陉应用软件开发服务供应 软件开发企业两免三减半 sdp软件开发平台破解版 主流的syslog服务器 计算机网络技术类有哪些专业 j教育局网络安全管理 惠普服务器修改时间 广州品高软件开发公司总经理
0