最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MySQL中SELECT+UPDATE處理并發(fā)更新問題解決方案分享

 更新時間:2014年05月14日 09:52:24   作者:  
這篇文章主要介紹了MySQL中SELECT+UPDATE處理并發(fā)更新問題解決方案分享,需要的朋友可以參考下

問題背景:

假設MySQL數據庫有一張會員表vip_member(InnoDB表),結構如下:

 

當一個會員想續(xù)買會員(只能續(xù)買1個月、3個月或6個月)時,必須滿足以下業(yè)務要求:

•如果end_at早于當前時間,則設置start_at為當前時間,end_at為當前時間加上續(xù)買的月數

•如果end_at等于或晚于當前時間,則設置end_at=end_at+續(xù)買的月數

•續(xù)買后active_status必須為1(即被激活)

問題分析:

對于上面這種情況,我們一般會先SELECT查出這條記錄,然后根據查出記錄的end_at再UPDATE start_at和end_at,偽代碼如下(為uid是1001的會員續(xù)1個月):

復制代碼 代碼如下:

vipMember = SELECT * FROM vip_member WHERE uid=1001 LIMIT 1 # 查uid為1001的會員
if vipMember.end_at < NOW():
   UPDATE vip_member SET start_at=NOW(), end_at=DATE_ADD(NOW(), INTERVAL 1 MONTH), active_status=1, updated_at=NOW() WHERE uid=1001
else:
   UPDATE vip_member SET end_at=DATE_ADD(end_at, INTERVAL 1 MONTH), active_status=1, updated_at=NOW() WHERE uid=1001

假如同時有兩個線程執(zhí)行上面的代碼,很顯然存在“數據覆蓋”問題(即一個是續(xù)1個月,一個續(xù)2個月,但最終可能只續(xù)了2個月,而不是加起來的3個月)。

解決方案:

A、我想到的第一種方案是把SELECT和UPDATE合成一條SQL,如下:

復制代碼 代碼如下:

UPDATE vip_member
SET
   start_at = CASE
              WHEN end_at < NOW()
                 THEN NOW()
              ELSE start_at
              END,
   end_at = CASE
            WHEN end_at < NOW()
               THEN DATE_ADD(NOW(), INTERVAL #duration:INTEGER# MONTH)
            ELSE DATE_ADD(end_at, INTERVAL #duration:INTEGER# MONTH)
            END,
   active_status=1,
   updated_at=NOW()
WHERE uid=#uid:BIGINT#
LIMIT 1;

    So easy!

B、第二種方案:事務,即用一個事務來包裹上面的SELECT+UPDATE操作。

    那么是否包上事務就萬事大吉了呢?

    顯然不是。因為如果同時有兩個事務都分別SELECT到相同的vip_member記錄,那么一樣的會發(fā)生數據覆蓋問題。那有什么辦法可以解決呢?難道要設置事務隔離級別為SERIALIZABLE,考慮到性能不現(xiàn)實。

    我們知道InnoDB支持行鎖。查看MySQL官方文檔(innodb locking reads)了解到InnoDB在讀取行數據時可以加兩種鎖:讀共享鎖和寫獨占鎖。

    讀共享鎖是通過下面這樣的SQL獲得的:

復制代碼 代碼如下:

SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;

    如果事務A獲得了先獲得了讀共享鎖,那么事務B之后仍然可以讀取加了讀共享鎖的行數據,但必須等事務A commit或者roll back之后才可以更新或者刪除加了讀共享鎖的行數據。

復制代碼 代碼如下:

SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;

   如果事務A先獲得了某行的寫共享鎖,那么事務B就必須等待事務A commit或者roll back之后才可以訪問行數據。

   顯然要解決會員狀態(tài)更新問題,不能加讀共享鎖,只能加寫共享鎖,即將前面的SQL改寫成如下:

復制代碼 代碼如下:

vipMember = SELECT * FROM vip_member WHERE uid=1001 LIMIT 1 FOR UPDATE # 查uid為1001的會員
if vipMember.end_at < NOW():
   UPDATE vip_member SET start_at=NOW(), end_at=DATE_ADD(NOW(), INTERVAL 1 MONTH), active_status=1, updated_at=NOW() WHERE uid=1001
else:
   UPDATE vip_member SET end_at=DATE_ADD(end_at, INTERVAL 1 MONTH), active_status=1, updated_at=NOW() WHERE uid=1001

    另外這里特別提醒下:UPDATE/DELETE SQL盡量帶上WHERE條件并在WHERE條件中設定索引過濾條件,否則會鎖表,性能可想而知有多差了。

C、第三種方案:樂觀鎖,類CAS機制

    第二種加鎖方案是一種悲觀鎖機制。而且SELECT...FOR UPDATE方式也不太常用,聯(lián)想到CAS實現(xiàn)的樂觀鎖機制,于是我想到了第三種解決方案:樂觀鎖。

    具體來說也挺簡單,首先SELECT SQL不作任何修改,然后在UPDATE SQL的WHERE條件中加上SELECT出來的vip_memer的end_at條件。如下:

復制代碼 代碼如下:

vipMember = SELECT * FROM vip_member WHERE uid=1001 LIMIT 1 # 查uid為1001的會員
cur_end_at = vipMember.end_at
if vipMember.end_at < NOW():
   UPDATE vip_member SET start_at=NOW(), end_at=DATE_ADD(NOW(), INTERVAL 1 MONTH), active_status=1, updated_at=NOW() WHERE uid=1001 AND end_at=cur_end_at
else:
   UPDATE vip_member SET end_at=DATE_ADD(end_at, INTERVAL 1 MONTH), active_status=1, updated_at=NOW() WHERE uid=1001 AND end_at=cur_end_at

    這樣可以根據UPDATE返回值來判斷是否更新成功,如果返回值是0則表明存在并發(fā)更新,那么只需要重試一下就好了。

方案比較:

三種方案各自優(yōu)劣也許眾說紛紜,只說說我自己的看法:

•第一種方案利用一條比較復雜的SQL解決問題,不利于維護,因為把具體業(yè)務糅在SQL里了,以后修改業(yè)務時不但需要讀懂這條SQL,還很有可能會修改成更復雜的SQL

•第二種方案寫獨占鎖,可以解決問題,但不常用

•第三種方案應該是比較中庸的解決方案,并且甚至可以不加事務,也是我個人推薦的方案


此外,樂觀鎖和悲觀鎖的選擇一般是這樣的(參考了文末第二篇資料):

•如果對讀的響應度要求非常高,比如證券交易系統(tǒng),那么適合用樂觀鎖,因為悲觀鎖會阻塞讀

•如果讀遠多于寫,那么也適合用樂觀鎖,因為用悲觀鎖會導致大量讀被少量的寫阻塞

•如果寫操作頻繁并且沖突比例很高,那么適合用悲觀寫獨占鎖

相關文章

  • MySQL中的insert set 和 insert values用法

    MySQL中的insert set 和 insert values用法

    這篇文章主要介紹了MySQL中的insert set 和 insert values用法說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • Mysql8.0不存在mysql.proc表的解決

    Mysql8.0不存在mysql.proc表的解決

    MySQL 8.0中官方移除了proc表,MySQL 5.7版本中還是存在proc表的,本文就介紹MySQL 8.0的替代方案,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • MySQL數據庫表約束講解

    MySQL數據庫表約束講解

    這篇文章主要介紹了MySQL數據庫表約束講解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • mysql誤刪數據后快速恢復的辦法推薦

    mysql誤刪數據后快速恢復的辦法推薦

    手抖不小心把表里的數據刪除或修改錯誤怎么辦?該如何快速恢復呢?遇到這樣的問題怎么辦?下面這篇文章主要給大家介紹了關于mysql誤刪數據后快速恢復的相關資料,需要的朋友可以參考下
    2023-02-02
  • 詳解MySQL主從復制及讀寫分離

    詳解MySQL主從復制及讀寫分離

    這篇文章主要介紹了詳解MySQL主從復制及讀寫分離,文中有非常詳細的代碼示例,對正在學習mysql的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05
  • Mysql查詢語句優(yōu)化技巧

    Mysql查詢語句優(yōu)化技巧

    這篇文章主要介紹了Mysql查詢語句優(yōu)化技巧的相關資料,需要的朋友可以參考下
    2016-04-04
  • Windows中Mysql啟動失敗的完美解決方案

    Windows中Mysql啟動失敗的完美解決方案

    這篇文章主要介紹了Windows中Mysql啟動失敗解決方案,mysql服務啟動失敗分為2種情況給大家詳細介紹,針對每一種給大家詳細解決,需要的朋友可以參考下
    2022-10-10
  • MySQL復合查詢操作實戰(zhàn)案例

    MySQL復合查詢操作實戰(zhàn)案例

    這篇文章主要給大家介紹了關于MySQL復合查詢操作的相關資料,MySQL復合查詢是指在一個SQL語句中使用多個查詢條件,以過濾和檢索數據,需要的朋友可以參考下
    2023-08-08
  • 實現(xiàn)mysql級聯(lián)復制的方法示例

    實現(xiàn)mysql級聯(lián)復制的方法示例

    這篇文章主要介紹了實現(xiàn)mysql級聯(lián)復制的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-05-05
  • MySQL快速插入一億測試數據

    MySQL快速插入一億測試數據

    本文主要介紹了MySQL快速插入一億測試數據,有時候測試需要大量的數據,需要的朋友們下面隨著小編來一起學習學習吧
    2021-06-06

最新評論

靖州| 福鼎市| 将乐县| 安新县| 舒兰市| 库伦旗| 自贡市| 龙南县| 北海市| 乌拉特前旗| 瑞丽市| 崇明县| 潞西市| 张家界市| 阳原县| 宣汉县| 桓台县| 建瓯市| 花莲县| 高清| 凤山县| 黔西县| 兴仁县| 平江县| 五常市| 百色市| 恩施市| 共和县| 巴东县| 嘉禾县| 宝清县| 耒阳市| 简阳市| 彭州市| 东乌| 丰都县| 青河县| 长春市| 涟源市| 安化县| 博兴县|