mysql 如果存在修改_mysql如存在并发修改可能,一定要注意保证数据一致性
近日,因人員調整接手了一個其他部門負責的項目。隨后發現其中的很多關鍵環節是沒有考慮mysql并發操作的,現列出存在的一例問題 并分享如何解決的。
問題描述:
用戶賬戶余額轉移贈送 (用戶A將自己的賬戶剩余金額贈送給用戶B),同一時刻還可能存在用戶A消費操作(例如贈送操作在app,消費操作在手機站)。
PHP 代碼:
$sql_select = 'select amount from user where id = 1';
/***
* 從mysql查詢出的用戶A余額,放入php 變量$amount
* 做一些邏輯處理,例如A用戶是否有權限操作,此處代碼省略
*
* 用戶A賬戶清零
* $sql_deduct = ' update user set amount = 0 where id = 1';
*/
//將 用戶A金額轉移給用戶B
$sql_transfer = sprintf (' update user set amount = amount+%d where id = 2', $amount );
此種寫法存在問題,因:
1.$sql_select執行查詢后,有可能存在并發操作,例如剛好此時 用戶A有其他消費,mysql ?賬戶余額amount 被扣減。
此時php 變量$amount 與 mysql amount 內容已經不一致。
2. $sql_transfer 使用了php變量:$amount(值已經不是最新的) 進行賬戶增加,會導致增加與扣除的金額不一致。
解決方案:
處理并發修改一般是要進行加鎖防止其他mysql session 修改同一條內容,mysql 也提供了獨占鎖機制解決并發更新問題。
方案1:
InnoDB 引擎可以使用select … for update , 對要修改的表中的某一行加鎖。另外:此方案對MyISAM引擎無效。MyISAM可以考慮使用方案2.
SET autocommit = 0;
$sql_select = 'select amount from user where id = 1 for update ';
//這里可以寫修改db 的業務邏輯
COMMIT;
1.執行$sql_select后,user id = 1 的這一行會被mysql 鎖定,其他mysql session 只能讀取鎖定前的數據,其他mysql session要加鎖或者修改涉及此行,都會被阻塞(例如修改,刪除此行,修改表結構等),直到鎖定釋放 (commit提交事務 或 mysql session 結束)
2.必須將mysql 自動提交關閉(SET autocommit = 0;),否則鎖定無效
3.特定的行進行加鎖僅針對“特定的索引 ” 有效,例如id = 1 的select查詢,是行鎖因id 是主鍵索引。
3.1 如果查詢條件是 id>1,那么mysql 會鎖定整個表.
3.2 如果查詢條件沒有使用索引,那么mysql 也會鎖定整個表.
建議大家盡可能使用行鎖,以提高mysql并發性能。
另外, 需要注意的是 ,此方案可能會造成死鎖。這個還要從業務方面盡可能避免,以后會繼續討論如何避免死鎖。
方案2:
2. 使用LOCK TABLES … READ 鎖表
LOCK TABLES user READ
//這里可以寫修改db 的業務邏輯
UNLOCK TABLES;
此方案比較粗暴,會鎖定整個表的寫操作,但如果在MyISAM 引擎下,也就只能選擇這種方式了。
結論:
mysql 加鎖本質上都是通過犧牲并發性能換取數據的一致性,所以在業務需求分析設計時,就要考慮哪些可能存在并發寫入,進行規劃盡可能減少鎖的次數、時間。
作者: 白金馬桶
總結
以上是生活随笔為你收集整理的mysql 如果存在修改_mysql如存在并发修改可能,一定要注意保证数据一致性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: git 提交文件_GIT不小心提交了大文
- 下一篇: python怎么测试程序_python如