mysql怎麼用鎖 MySQL的鎖應該怎麼用 一

2021-10-17 15:06:14 字數 4217 閱讀 3782

先說重點:

鎖是為了固化資源狀態的,加鎖之後一定會有狀態判斷(或加鎖語句的條件裡含有這個),只加鎖不判斷狀態,那這個鎖就沒啥用。

如果你加了鎖,但是後邊並沒有回滾機制,那你就要考慮下你**的邏輯是不是有問題了。

**要有自檢的功能,第一不能相信使用者,第二不能相信呼叫你函式的其它程式設計師。盡量保證你的**在被重複執行時不會出現問題(要不你就保證你這段**絕對不會遇到併發)。

寫完任何乙個函式或者指令碼,都要想乙個問題「如果這段**被併發執行兩次,會有問題麼?」。

咱們從兩個簡單的問題開始:

事務的作用是什麼?

mysql裡怎麼簡單的鎖一行(排他鎖)?

這兩個很基礎的問題大家不妨先自己嘗試回答一下。

2參***:第一,須是innodb引擎;第二,開事務;第三 select * from t where id = 111 for update(行鎖的條件必須為主鍵或者索引列,如果不是索引列,那將產生表鎖。主鍵和唯一索引那就是鎖一行)。

上邊的問題很多人都清楚,或者查一查也能查出來,但是,在日常使用的時候,我總發現大家會犯各種各樣的錯誤,綜合原因,那就是「不能從邏輯上明白事務和鎖到底要做哪些事情,資源競爭到底是應該怎麼做」。

舉個例子:

電商相關的專案有個最基本的問題,一件商品有一定的庫存量,**怎麼寫才能保證這個商品不超售。

比如product表結構是這樣的:id, product_name, count,其中count為庫存量。

當有人購買之後,需要更新這個表裡的庫存量,都執行這個sql:

$sql = "select * from product where id = ***";

$row = $connection->createcommand($sql)->queryrow();

if ($row[count] <= 0) {

return false;

$sql = "update product set count = count - 1 where id = ***";

$connection->createcommand($sql)->execute();

return true;

大家都知道,在沒有鎖的情況下,如果出現併發請求(別問我併發是怎麼出現的),更新結果就會有問題,大家都知道加鎖,但是我發現有人這這麼加的

// 錯誤用法,請勿模仿

$sql = "select * from product where id = ***";

$row = $connection->createcommand($sql)->queryrow();

if ($row[count] <= 0) {

return false;

$transaction = $connection->begintransaction(); // 開事務

$sql = "select * from product where id = *** for update";

$row = $connection->createcommand($sql)->queryrow();

$sql = "update product set count = count - 1 where id = ***";

$connection->createcommand($sql)->execute();

$transaction->commit(); // 提交事務

return true;

上邊**這個鎖加的有什麼意義麼?並沒有!!!

請記住:

狀態判斷一定要在加鎖之後

所以正確的寫法應該是:

// 1. 開事務

$transaction = $connection->begintransaction();

// 2. 給要更新的資源加鎖

$sql = "select * from product where id = *** for update";

$row = $connection->createcommand($sql)->queryrow();

// 3. 判斷資源狀態

if ($row['count'] <= 0) {

// 事務回滾

$transaction->rollback();

return false;

// 4. 更新資料

$sql = "update product set count = count - 1 where id = ***";

$connection->createcommand($sql)->execute();

// 5. 提交事務

$transaction->commit(); // 提交事務

return true;

鎖的存在是為了解決併發的,所謂併發就是多個請求要同時操作同乙個資源,鎖就是在這種資源競爭的環境中產生的,先給資源加上鎖的請求,就優先完成自己的操作,在鎖被釋放之前,其它的請求都得等著,卡在加鎖那一步。找準你要鎖的資源是重中之重。

再舉乙個例子:在支付場景中,你建立了一條訂單,在訂單狀態更新為成功之後,你同時要更新使用者的餘額,有時候有人犯錯就會這麼寫:

// 錯誤示範,請勿模仿

// 開事務

$transaction = $connection->begintransaction();

$sql = "select * from order where id = ***";

$order = $connection->createcommand($sql)->queryrow();

// 判斷訂單狀態

if ($order['status'] != 0) {

$transaction->rollback();

return false;

// 為要更新的 account 記錄加鎖

$sql = "select * from account where id = ***xx for update";

$account = $connection->createcommand($sql)->queryrow();

// 更新account記錄

$sql = "update account set balance = balance - 50 where id = ***xx";

$connection->createcommand($sql)->execute();

// 更新訂單狀態

$sql = "update order set status = 1 where id = ***";

$connection->createcommand($sql)->execute();

// 提交事務

$transaction->commit();

上邊**的問題在哪兒?沒有搞清楚你要競爭的資源是什麼!雖然你要更新account表,但是你要競爭的資源其實是order表中的那一行。所以正確的做法應該是:

// 開事務

$transaction = $connection->begintransaction();

// 為order記錄加鎖

$sql = "select * from order where id = *** for update";

$order = $connection->createcommand($sql)->queryrow();

// 判斷訂單狀態

if ($order['status'] != 0) {

$transaction->rollback();

return false;

// 更新訂單狀態

$sql = "update order set status = 1 where id = ***";

$connection->createcommand($sql)->execute();

$sql = "select * from account where id = ***xx for update";

$account = $connection->createcommand($sql)->queryrow();

// 更新account記錄

$sql = "update account set balance = balance - 50 where id = ***xx";

$connection->createcommand($sql)->execute();

// 提交事務

$transaction->commit();

綜上,其實用好鎖,就是要搞清楚整個資源競爭和狀態判斷的邏輯。

mysql 鎖怎麼使用 MySQL鎖的用法之行級鎖

行級鎖是mysql中粒度最小的一種鎖,他能大大減少資料庫操作的衝突。但是粒度越小,實現的成本也越高。myisam引擎只支援表級鎖,而innodb引擎能夠支援行級鎖,下面的內容也是針對innodb行級鎖展開的。innodb的行級鎖有共享鎖 s lock 和排他鎖 x lock 兩種。共享鎖允許事物讀一...

mysql索引怎麼用 mysql怎麼使用索引?

在排序操作中如果能使用到索引來排序,那麼可以極大的提高排序的速度,要使用索引來排序需要滿足以下兩點即可。1 order by子句後的列順序要與組合索引的列順序一致,且所有排序列的排序方向 正序 倒序 需一致 2 所查詢的字段值需要包含在索引列中,及滿足覆蓋索引。通過例子來具體分析 在user tes...

mysql行鎖爭用 Mysql行鎖機制引發的血案

最近在開發中使用到了多執行緒對同個表進行讀寫操作,由於資料庫基礎渣渣,寫完 後程式跑起來出現了死鎖。於是對日誌進行分析跟蹤,發現在執行以下sql時出現死鎖 update linkgoo message queue set state 3 where state 2 很明顯在執行上述sql時,由於某個...