溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何使用 Laravel sharedLock 與 lockForUpdate 進(jìn)行數(shù)據(jù)表行鎖

發(fā)布時(shí)間:2021-11-09 18:25:12 來(lái)源:億速云 閱讀:251 作者:柒染 欄目:大數(shù)據(jù)

本篇文章為大家展示了如何使用 Laravel sharedLock 與 lockForUpdate 進(jìn)行數(shù)據(jù)表行鎖,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。

場(chǎng)景

拼團(tuán)功能,當(dāng) A 客戶開(kāi)團(tuán)之后(兩人團(tuán)),如果 B 和 C 同時(shí)支付,如何規(guī)避兩人同時(shí)將拼團(tuán)人數(shù)增加。

Laravel 中 sharedLock 與 lockForUpdate 的區(qū)別

  • sharedLock 對(duì)應(yīng)的是 LOCK IN SHARE MODE

  • lockForUpdate 對(duì)應(yīng)的是 FOR UPDATE

sharedLock 與 lockForUpdate 相同的地方是,都能避免同一行數(shù)據(jù)被其他 transaction 進(jìn)行 update。

不同的地方是:

  • sharedLock 不會(huì)阻止其他 transaction 讀取同一行

  • lockForUpdate 會(huì)阻止其他 transaction 讀取同一行 (需要特別注意的是,普通的非鎖定讀取讀取依然可以讀取到該行,只有 sharedLock 和 lockForUpdate 的讀取會(huì)被阻止。)

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

這樣做是有意義的,例如,兩個(gè) transaction 要更新同一個(gè)計(jì)數(shù)器,如果不使用 lockForUpdate, 會(huì)導(dǎo)致兩個(gè) transaction 同時(shí)讀到同一個(gè)初始值,然后在應(yīng)用層邏輯中增加計(jì)數(shù)之后,提交到數(shù)據(jù)庫(kù)中,后者的操作會(huì)覆蓋掉前者的操作。

如何測(cè)試

MySQL 命令行終端操作一個(gè)表

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

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

這時(shí)再開(kāi)一個(gè)命令行終端

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

你會(huì)發(fā)現(xiàn),無(wú)論是 for update 還是 lock in share mode 都無(wú)法讀取到數(shù)據(jù),更加確切地說(shuō)是,查詢被阻塞了。

只有在第一個(gè)終端執(zhí)行

commit;

第二個(gè)終端才能得到數(shù)據(jù)返回。

需要注意的是,發(fā)起者必須在 transaction 里上鎖才有效,如果不是在 transaction 中,上鎖是無(wú)效的。但是,第二個(gè)人無(wú)論是不是在 transaction 里,都會(huì)被鎖。

我依然有幾個(gè)疑問(wèn)

  • Laravel 如何設(shè)置數(shù)據(jù)庫(kù)操作超時(shí)時(shí)間

  • 什么場(chǎng)景下適合使用 sharedLock 呢?

  • sharedLock,lockForUpdate 與 Pessimistic Locking 是什么關(guān)系

  • Pessimistic locking(悲觀鎖) 與 Optimistic locking(樂(lè)觀鎖)的區(qū)別

如何測(cè)試 Laravel

A 用戶,在瀏覽器里訪問(wèn)接口 (模擬支付回調(diào)),此時(shí)對(duì)數(shù)據(jù)表中某一行鎖住,進(jìn)行 30s 操作,然后提交事務(wù)。

B 用戶,在瀏覽器里訪問(wèn)同一接口 (模擬支付回調(diào)),其無(wú)法修改該行。對(duì)應(yīng)的返回是什么?

會(huì)一直 wait 到數(shù)據(jù)庫(kù)操作超時(shí)。

那么問(wèn)題來(lái)了,Laravel 如何設(shè)置數(shù)據(jù)庫(kù)操作超時(shí)時(shí)間?

簡(jiǎn)單的測(cè)試方法,是在命令行中開(kāi)兩個(gè) artisan tinker 窗口,分別執(zhí)行

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

你會(huì)發(fā)現(xiàn)第二個(gè) tinker 窗口中的 get 操作,需要等到第一個(gè) transaction 執(zhí)行完畢之后,才能得到查詢結(jié)果。

需要注意的是,不在 transaction 中的 lockForUpdate 操作,是沒(méi)有鎖效果的。

真實(shí)場(chǎng)景,防止用戶重復(fù)提現(xiàn)

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;        // 提現(xiàn)中狀態(tài)
               $user_award->save();
           }
});

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

事務(wù)與鎖的關(guān)系

事務(wù)中涉及的操作都會(huì)加上鎖?

如果默認(rèn)會(huì)加上鎖,那么默認(rèn)會(huì)加上什么鎖呢?

事務(wù)中涉及的操作,不需要顯式加鎖?

要理清其中關(guān)系,就需要了解事務(wù)的四種隔離級(jí)別:

  • 未提交讀(Read uncommitted)

  • 已提交讀(Read committed)

  • 可重復(fù)讀(Repeatable read)

  • 可串行化(Serializable )

MySQL 默認(rèn)的是:可重復(fù)讀(Repeatable read)

上述內(nèi)容就是如何使用 Laravel sharedLock 與 lockForUpdate 進(jìn)行數(shù)據(jù)表行鎖,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI