<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 為什么說樂觀鎖是安全的 來看一個具體案例: ```php /** * 檢測用戶VIP狀態,并且當VIP過期時更新VIP狀態 * 使用樂觀鎖,確保僅在不持有舊數據才可以進行操作,因此是安全的 * 返回1說明當前調用的地方持有的已經是舊數據了,要注意樂觀鎖安全是指它本身,而不是其他地方也是一樣,其他地方還是需要自己去控制的 * 2017-6-30 09:32:51 * @param int $uid * @return int 此操作鎖影響的行數 */ function checkVip($uid) { $request = Request::instance(); // 這里必須使用樂觀鎖控制,不然并發會出現嚴重BUG,這樣的話支付通知那里也需要使用樂觀鎖和行鎖啊,不然還是會出現BUG的(事物外的修改對事物內是不可見的) // 這里如果開啟事物,可能又會產生事物嵌套的問題,總之要看你能不能承受并發帶來的后果了(需要仔細分析一下各種并發情況下有可能會產生的各種問題) // 通過仔細分析發現使用樂觀鎖,不管什么情況下和支付通知鎖那里都是不會產出任何問題的(如果不用樂觀鎖,那問題就大了) $userInfo = Db::name('user')->find($uid); if (($userInfo['vip_status'] = 2) && ($userInfo['vip_expires_in'] < $request->time())) { $data = [ 'vip_status' => 1, 'update_time' => $request->time(), ]; // ……并發時這中間可能有很多其他的過程,就會出現是有舊數據 // 而樂觀鎖在更新是的更新條件就是檢測數據是不是舊了,所以確保了如果持有舊數據時就什么都做不了,也就安全了 // 返回操作影響的行數,如果有成功的操作,那么會返回1,也說明了沒有持有舊數據哦 return Db::name('user')->where(['vip_status' => $userInfo['vip_status'], 'vip_expires_in' => $userInfo['vip_expires_in'], 'update_time' => $userInfo['update_time']])->update($data); } // 沒過期什么都不做,返回0 return 0; } // use: /** * 判斷是否登錄 * @return int 返回uid,沒有登錄返回0 */ function isLogin() { // (int) 很重要,否則很嚴重,將會出現重大BUG,永遠自動登錄第一個用戶 $user_id = (int) session('user_id'); $user = Db::name('user')->field('password', true)->find($user_id); if ($user) { // 使用樂觀鎖檢測/刷新VIP狀態信息 // 返回1說明當前這里就是持有舊數據的了,要注意樂觀鎖安全是指它本身,而不是其他地方也是一樣,其他地方還是需要自己去控制的 if (checkVip($user_id)) { $user = Db::name('user')->field('password', true)->find($user_id); } session('user_id', $user['id']); session('user', $user); $auth = [ 'id' => $user['id'], // 'nickname' => $user['nickname'], // 如果用戶名修改則需要重新設置簽名認證,不然會出現登陸失效 'auth_sign_key' => config('auth_sign_key'), ]; // TODO: 做得完善點這里認證不通過應該被系統退出,并且有退出原因,這個做登陸記錄時需要考慮的 if (session('user_auth_sign') == dataAuthSign($auth)) { Session::pause(); return $user['id']; } else { Session::pause(); // 退出說明 } } session('user_id', null); session('user', null); session('user_auth_sign', null); Session::pause(); return 0; } ``` 并發不安全的原因就在于,一個進程持有了舊數據而進行操作而發生的并發問題,產生不符合業務邏輯預期的后果,也就是BUG,這種BUG根據不同的場景有不同的級別,最嚴重的就是錢變少了,所以任何時候,都要考慮你當前會不會持有舊數據,持有就數據之后你做的操作會帶來那些問題,任何時候都要考慮并發問題。**而樂觀鎖的sql決定了它不會持有舊數據再去做什么(在更新的同時檢測是否持有舊數據了,舊數據為更新的條件,如果真舊了也就不滿足更新條件而不能更新了),所以它本身是安全的,注意是它自己本身,如上面的checkVip內部的操作是安全的的,但是其他地方還需要自己去進行控制。** >[danger] **你永遠無法不知道自己哪一刻會持有舊數據(除非使用悲觀鎖)**,而樂觀鎖的聰明之處就是在自己做更新操作的時候問自己,最后確定一遍自己是不是安全的,是不是沒有持有舊數據,確保只有當前沒有持有舊數據才可以進行更新操作,所以它是安全的,也是聰明的。 * * * * * ### 相關閱讀 [并發問題帶來的后果](http://www.hmoore.net/xiak/php-node/347803) [查看這個重要的提交 - 用樂觀鎖刷新會員狀態在用戶列表的使用](https://coding.net/u/xiasf/p/zb/git/commit/3732ba2cb0c50b500578469c093a6b39eb1e2b6c) [Mysql樂觀鎖悲觀鎖行鎖表鎖是從哪幾個方面來分類的?比如說表鎖也可是樂觀鎖,也可以是悲觀鎖嗎?](https://segmentfault.com/q/1010000003917591) ~~~ 加鎖對并發訪問的影響體現在鎖的粒度上,可見行鎖粒度最小,并發訪問最好,頁鎖粒度最大,表鎖介于2者之間。 鎖有兩種:悲觀鎖和樂觀鎖。悲觀鎖假定其他用戶企圖訪問或者改變你正在訪問、更改的對象的概率是很高的,因此在悲觀鎖的環境中,在你開始改變此對象之前就將該對象鎖住,并且直到你提交了所作的更改之后才釋放鎖。悲觀的缺陷是不論是頁鎖還是行鎖,加鎖的時間可能會很長,這樣可能會長時間的限制其他用戶的訪問,也就是說悲觀鎖的并發訪問性不好。 與悲觀鎖相反,樂觀鎖則認為其他用戶企圖改變你正在更改的對象的概率是很小的,因此樂觀鎖直到你準備提交所作的更改時才將對象鎖住,當你讀取以及改變該對象時并不加鎖。可見樂觀鎖加鎖的時間要比悲觀鎖短,樂觀鎖可以用較大的鎖粒度獲得較好的并發訪問性能。但是如果第二個用戶恰好在第一個用戶提交更改之前讀取了該對象,那么當他完成了自己的更改進行提交時,數據庫就會發現該對象已經變化了,這樣,第二個用戶不得不重新讀取該對象并作出更改。這說明在樂觀鎖環境中,會增加并發用戶讀取對象的次數。 ~~~ [樂觀鎖與悲觀鎖各自適用場景是什么?](https://segmentfault.com/q/1010000002660301) [搜索 悲觀鎖 - SegmentFault](https://segmentfault.com/search?q=悲觀鎖) * * * * * [消息推送 - 樂觀鎖](https://coding.net/u/xiasf/p/zb/git/commit/6ffe08eef85564dece1af7c012ff7e2c412d3514) >[danger] 樂觀鎖能夠成立的原因就是 **因為數據庫對同一數據進行更新操作時,不是并行的,而是串行阻塞的。自動對符合WHERE條件的行加的排他鎖)** 并且查詢是查的最新的,所以能保證更新語句中的where能查詢到最新的、確定的、穩定的、安全的數據。不然如果兩條update能夠同時執行的話,沒有對共享數據上鎖,那么where就會持有舊數據,就不會有樂觀鎖的效果了,樂觀鎖做的事就是在最后更新的那一步,攔截,檢測是否持有舊數據了,如果持有舊數據則不滿足更新條件。 > 這其實又繞到了即使樂觀鎖,其實也要上鎖的問題上面去了,只不過這種方式利用了數據庫的隱式鎖——對數據進行更新時會自動對數據上鎖(**這是編程語言底層約定俗成的規定吧,不然更新數據,更改變量不會發生數據錯亂的情況嗎?**)。JAVA CAS內部原理估計也是如此。 [JAVA CAS原理深度分析 - CSDN博客](http://blog.csdn.net/hsuxu/article/details/9467651) > 確保對內存的讀-改-寫操作原子執行。在Pentium及Pentium之前的處理器中,帶有lock前綴的指令在執行期間會鎖住總線,使得其他處理器暫時無法通過總線訪問內存。 >[danger] 可見,內部還是用的鎖,那怕是到cpu層面,也是用的鎖(CPU的鎖),不然對共享變量讀寫時怎么保證數據的正確性,雖然我們平時寫代碼不用考慮這個問題,編程語言對于我們來說總是安全的,但是計算機內部其實也做了安全考量和優化,以保證我們認為的那些再正常和普通的操作得以正確的執行,只不過我們忽略掉了操作系統/編程語言內部為此所做的努力而已。 [小白科普:悲觀鎖和樂觀鎖 - 碼農翻身](http://mp.weixin.qq.com/s/gWR1-511SAwVAHrtGrsZ8g) [愛炫耀的數據庫老頭兒](https://mp.weixin.qq.com/s/Tdh5fYu-2cPVd6PkpiyeBA) [select語句for update---轉載 - 一天不進步,就是退步 - 博客園](https://www.cnblogs.com/davidwang456/archive/2013/08/20/3270101.html) > **FOR UPDATE [NOWAIT]** > nowait的含義很多人都會誤解為“不用等待,立即執行”。但實際上該關鍵字的含義是“不用等待,立即返回” 如果當前請求的資源被其他會話鎖定時,會發生阻塞,nowait可以避免這一阻塞,因為 If another user is in the process of modifying that row, we will get an ORA‐00054 Resource Busy error. We are blocked and must wait for the other user to finish with it. * * * * * ### for update 細節注意!!! 并不是查詢(查詢行數據)時被卡住阻塞,而是阻塞它查詢,必須先持有鎖(先持有where 范圍內的行的鎖),才能夠進行查詢。查詢行數據前先獲取了 where 行范圍,檢查鎖的情況。 獲得鎖之前還沒有執行查詢(獲取行數據)且被阻塞著,不讓返回,要先獲得鎖(寫鎖/排它鎖),才能查詢。而如果當前鎖被別人持有,那么就只能被阻塞著,直到別人釋放鎖。 如果當前沒有被其他人占有鎖,那么取得鎖,進行查詢,返回結果。同樣的,在鎖釋放之前(事務提交/事務完成),其他人要想獲得鎖時也會被阻塞。 `select ... for update`:**獲得范圍內行的排它鎖。** 所以這里關鍵點就是 **持有 where 范圍內的鎖**,如果where 范圍內記錄存在,申請獲取獲取這些行的鎖,如果這些行還不存在,**申請間隙鎖或next鎖**,開發時尤其要注意這點,不然很容易出現死鎖。 ```` // a select id from orders where id = 1 and status = 1 for update // b select id from orders where id = 1 and status = 2 for update 他們都要鎖同一條記錄,但遺憾的是,他們申請的是不同的 next 鎖,這樣導致都能同時獲得鎖,顯然這并不符合我們想要的結果,并且 next 鎖 還極有可能造成死鎖。 所以業務中使用鎖時,一定要明確鎖定的行記錄,只使用 where id,想要獲得某種狀態,下面自己再判斷就是了,不要寫在 where 中。 驗證了,不會出現這個情況,應該是有主鍵的情況。果然是的,如果沒有主鍵(id 換成 sn),就出現了我們擔心的情況了。 如果主鍵不靠左呢?測試:無關乎左右。 所以 where 中一定要有主鍵。 ```` READ COMMITTED 隔離級別下:沒有間隙鎖 ~~~ 當數據不存在時,另一個事務中 FOR UPDATE 不會被阻塞(不論順序和時機)。 START TRANSACTION; SELECT * FROM `sp_test` WHERE `user_id` = 2 LIMIT 1 FOR UPDATE; INSERT INTO `u_user_smartpark` (`user_id` , `name`) VALUES (1, 'test'); COMMIT; SELECT @@global.tx_isolation; READ-COMMITTED ~~~ * * * * * [程序員進階樂觀鎖與悲觀鎖](https://www.toutiao.com/a6491604587517051406/?tt_from=weixin&utm_campaign=client_share&timestamp=1516012360&app=news_article&utm_source=weixin&iid=22069500288&utm_medium=toutiao_android&wxshare_count=1) ```sql update goods set num= newnum, version = oldversion+1 where version = oldversion; ``` > 樂觀鎖可實現是因為更新時是對數據上鎖的,即在數據庫里面,同一時刻,對同一行數據的更新,是阻塞串行的。不可能兩個會話同時對同一數據進行更新。 > > 正因為如此,所以樂觀鎖where查詢時數據是可靠的,最新的,能保證此時不會被別人更改了,所以才能保證樂觀鎖是可靠的。 >[tip] 上面說的不準確,樂觀鎖能成立的保障就是當前讀啊。 [為什么開發人員必須要了解數據庫鎖?](https://www.toutiao.com/a6587983050582262279/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&timestamp=1536342441&app=news_article_lite&utm_source=weixin&iid=33124962994&utm_medium=toutiao_android&group_id=6587983050582262279) * * * * * >[danger] 要想保證數據正確,那么必須要保證同一時刻,只能有一個會話能對這行數據進行操作。 > >[danger] 如果要防止并發問題(下單查庫存問題),則必須要保證,一個會話在更改這行數據時必須獨占,必須阻塞其它會話對這行數據的訪問,查詢也不行,也要被阻塞著。因為當數據處于被更改的狀態時,我們無法確定它到底是什么,它是不可知的,而“一致性非鎖定讀”是快照讀,不能保證查出來的數據是最新可靠真實的,相當于從緩存中讀出來的數據,你說緩存中的數據靠譜嗎,所以用這個數據去下單,當然會有問題了。 ***** > 樂觀鎖成立是因為,讀和寫是不可分割的,這是在同一條sql語句中,中間沒有空隙,是獨占的。 * * * * * [請問 5.1.29 LTS 怎么實現樂觀鎖的功能? · Issue #1548 · top-think/framework](https://github.com/top-think/framework/issues/1548) ***** last update:2018-1-15 22:31:01
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看