1. 記一次線上問題排查與解決

        共 1874字,需瀏覽 4分鐘

         ·

        2020-12-24 02:42

        文 |?極光

        來源:Python 技術(shù)「ID: pythonall」

        最近開發(fā)中遇到個(gè)小問題,因?yàn)闃I(yè)務(wù)上的設(shè)計(jì)存在問題,導(dǎo)致數(shù)據(jù)庫表總是被鎖,而且是不定期的鎖定,導(dǎo)致服務(wù)器運(yùn)行異常,最后經(jīng)過排查原因是多線程同時(shí)更新同一表中同一條記錄導(dǎo)致問題。今天就來跟大家說說該如何避免這種問題。

        問題描述

        最近因?yàn)楣緲I(yè)務(wù)需要,產(chǎn)品設(shè)計(jì)了一套業(yè)務(wù)系統(tǒng),據(jù)說會(huì)有很多內(nèi)部和外部人員使用,拿到系統(tǒng)說明我們研發(fā)部門拼命加班趕時(shí)間,經(jīng)歷了兩個(gè)月的后終于把系統(tǒng)上線運(yùn)行。

        剛開始用的人少,并沒有出現(xiàn)什么問題,感覺系統(tǒng)還是很穩(wěn)定,隨著后來用的人越來越多,系統(tǒng)就開始出現(xiàn)一些莫名其妙的問題,其中就有某些業(yè)務(wù)信息在更新的時(shí)候總是報(bào)錯(cuò),查日志就發(fā)現(xiàn)是表記錄被鎖定導(dǎo)致更新失敗。

        找到錯(cuò)誤問題后我們就開始一遍遍的翻日志,各種分析查找到底是什么原因?qū)е铝吮碛涗洷绘i。最后發(fā)現(xiàn)這個(gè)表的狀態(tài)字段,存在多個(gè)接口方法同時(shí)更新的情況,而且經(jīng)常是同時(shí)操作的。也就是數(shù)據(jù)庫中存在多個(gè)會(huì)話同時(shí)操作同一表中同一行記錄,從而導(dǎo)致表記錄被鎖。

        問題分析

        那定位到問題,要如何解決呢?這里我們先了解兩個(gè)名詞:

        悲觀鎖(Pessimistic Lock):簡單解釋就是很悲觀,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改,所以每次在修改數(shù)據(jù)的時(shí)候都會(huì)上鎖,這樣別人想修改這個(gè)數(shù)據(jù)就會(huì)等待一直到它能拿到鎖。

        樂觀鎖(Optimistic Lock):這個(gè)正好相反,就是很樂觀,每次去修改數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖,但是在提交更新的時(shí)候會(huì)判斷一下在此期間別人有沒有去更新這個(gè)數(shù)據(jù)。樂觀鎖適用于讀多寫少的應(yīng)用場景,這樣可以提高吞吐量。

        通過這兩種方式就能解決問題,不過選哪種比較好,我們來簡單分析下:

        悲觀鎖 通過 “select …… for update” 實(shí)現(xiàn),就是在更新表前先對這條記錄進(jìn)行上鎖,然后下面再執(zhí)行更新語句。不過這種方式有些太重,畢竟加鎖還是需要很大時(shí)間成本的,不符合業(yè)務(wù)的需要,直接pass掉。

        樂觀鎖 相對就要輕量很多,它的主要實(shí)現(xiàn)就是通過在表中多增加一個(gè)記錄版本的字段,比如 version 。然后每次查詢記錄要更新時(shí),where 后面都要加上 version=? ,這樣當(dāng)你查詢拿到 version 后,如果有其他會(huì)話更新了這個(gè)字段,那這個(gè) version 就會(huì)和你現(xiàn)在拿的不一樣,從而會(huì)使你這次的更新失效,需要重新獲取最新 version 后再次執(zhí)行 update 語句。

        問題解決

        好了,經(jīng)過以上分析,已經(jīng)有了比較清晰的解決思路,剩下就是碼代碼了:

        ????/**
        ?????*?樂觀鎖更新
        ?????*?@param?id
        ?????*?@return
        ?????*/

        ????public?boolean?update(int?id){
        ????????int?cnt?=?0;
        ????????while?(cnt?==?0)?{
        ????????????USER?user?=?query("SELECT?*?FROM?table_user?WHERE?id?=?#{id}",?id);
        ????????????cnt?=?update("UPDATE?table_user?SET?version=version?+?1,?status?=?2?WHERE?id=#{id}?AND?version=#{version}",?id,?user.version());
        ????????????if(cnt?>?0){
        ???????????????//?返回更新成功
        ????????????????return?true;
        ????????????}
        ????????}
        ????????return?false;
        ????}

        總結(jié)

        這里只是基于 Mysql 自身特性解決這個(gè)問題,當(dāng)然還有很多其他的方式可以解決,例如通過 Redis 或者 MQ 消息隊(duì)列等,如果大家感興趣我們可以以后再介紹。OK,這次就聊這些,如果你喜歡記得點(diǎn) 在看。

        PS公號內(nèi)回復(fù)「Python」即可進(jìn)入Python 新手學(xué)習(xí)交流群,一起 100 天計(jì)劃!


        老規(guī)矩,兄弟們還記得么,右下角的 “在看” 點(diǎn)一下,如果感覺文章內(nèi)容不錯(cuò)的話,記得分享朋友圈讓更多的人知道!

        代碼獲取方式

        識(shí)別文末二維碼,回復(fù):1024

        瀏覽 30
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 久热这里只有 | 美女脱裤子让男人捅 | 搞逼网站视频 | 中文字幕无码一区二区三区一本久道不卡 | 一女多男3根一起进去图片 |