<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>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ### 2018 年 12 月 29 日 發布 ## 悲觀鎖和樂觀鎖 業務邏輯的實現過程中,往往需要保證數據訪問的排他性。如在金融系統的日終結算處理中,我們希望針對某個時間點的數據進行處理,而不希望在結算進行過程中(可能是幾秒種,也可能是幾個小時),數據再發生變化。此時,我們就需要通過一些機制來保證這些數據在某個操作過程中不會被外界修改,這樣的機制,在這里,也就是所謂的 “ 鎖 ” ,即給我們選定的目標數據上鎖,使其無法被其他程序修改。 通常有兩種鎖機制:即通常所說的 “ 悲觀鎖( Pessimistic Locking ) ”和 “ 樂觀鎖( Optimistic Locking ) ” 。 ### 悲觀鎖( Pessimistic Locking ) 悲觀鎖,正如其名,它指的是對數據被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個數據處理過程中,將數據處于鎖定狀態。悲觀鎖的實現,往往依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據)。 通常是使用`for update`子句來實現悲觀鎖機制。 ThinkPHP5支持悲觀鎖機制,要啟用悲觀鎖功能,可以通過使用`lock`鎖定方法,例如: ~~~ // 使用悲觀鎖功能 Db::name('user')->lock(true)->find(1); ~~~ 就會自動在生成的SQL語句最后加上`FOR UPDATE`或者`FOR UPDATE NOWAIT`(Oracle數據庫)。 `lock`方法還支持傳入字符串,以實現特殊的鎖機制。 ``` Db::name('user')->lock('LOCK IN SHARE MODE')->find(1); ``` ### 樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖機制采取了更加寬松的加鎖機制。悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大程度的獨占性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。 如一個金融系統,當某個操作員讀取用戶的數據,并在讀出的用戶數據的基礎上進行修改時(如更改用戶帳戶余額),如果采用悲觀鎖機制,也就意味著整個操作過程中(從操作員讀出數據、開始修改直至提交修改結果的全過程,甚至還包括操作員中途去煮咖啡的時間),數據庫記錄始終處于加鎖狀態,可以想見,如果面對幾百上千個并發,這樣的情況將導致怎樣的后果。樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基于數據版本( `Version `)記錄機制實現。何謂數據版本?即為數據增加一個版本標識,在基于數據庫表的版本解決方案中,一般是通過為數據庫表增加一個`version`字段來實現。 ThinkPHP`5.1`版本中并沒有內置樂觀鎖功能,因此需要自己實現,本文就來利用`Trait`特性實現樂觀鎖的功能。 ## 樂觀鎖的實現 要實現樂觀鎖功能,主要涉及三個地方: **記錄樂觀鎖**:第一次寫入數據的時候自動記錄`version`字段,當然也可以使用數據庫默認值功能。 **讀取樂觀鎖**:每次讀取數據的時候都要單獨記錄下當前的`version`數據值。 **檢測樂觀鎖**:每次更新數據的時候要重新檢測下最新數據的`version`數據值,如果記錄的版本號和最新的不一致,表示數據需要更新,否則把當前記錄的版本號加1后更新到數據庫。 而ThinkPHP`5.1`的模型`save`方法會統一調用`checkBeforeSave`方法,因此我們可以通過重寫該方法來實現樂觀鎖的檢測樂觀鎖功能。而每次查詢后都會調用模型的`newInstance`方法,因此可以重寫該方法添加讀取樂觀鎖功能。 ### 繼承方式實現 我們可以創建一個公共的模型繼承系統的`think\Model`類,當你的模型需要使用樂觀鎖功能的話就單獨繼承。 ``` <?php namespace app\common\model; use think\Exception; use think\Model; class OptimLock extends Model { protected $optimLock = 'version'; /** * 創建新的模型實例 * @access public * @param array $data 數據 * @param bool $isUpdate 是否為更新 * @param mixed $where 更新條件 * @return Model */ public function newInstance($data = [], $isUpdate = false, $where = null) { // 緩存樂觀鎖 $this->cacheLockVersion($data); return (new static($data))->isUpdate($isUpdate, $where); } /** * 寫入之前檢查數據 * @access protected * @param array $data 數據 * @param array $where 保存條件 * @return bool */ protected function checkBeforeSave($data, $where) { if (!empty($data)) { // 數據對象賦值 foreach ($data as $key => $value) { $this->setAttr($key, $value, $data); } if (!empty($where)) { $this->isUpdate(true, $where); } } // 數據自動完成 $this->autoCompleteData($this->auto); // 事件回調 if (false === $this->trigger('before_write')) { return false; } if ($this->isExists() && !$this->checkLockVersion()) { throw new Exception('record has update'); } return true; } /** * 緩存樂觀鎖 * @access protected * @param array $data 數據 * @return void */ protected function cacheLockVersion($data): void { $pk = $this->getPk(); if ($this->optimLock && isset($data[$this->optimLock]) && is_string($pk) && isset($data[$pk])) { $key = $this->name . '_' . $data[$pk] . '_lock_version'; $_SESSION[$key] = $data[$this->optimLock]; } } /** * 檢查樂觀鎖 * @access protected * @param array $data 數據 * @return bool */ protected function checkLockVersion() { // 檢查樂觀鎖 $id = $this->getKey(); if (empty($id)) { return true; } $key = $this->name . '_' . $id . '_lock_version'; if ($this->optimLock && isset($_SESSION[$key])) { $lockVer = $_SESSION[$key]; $vo = $this->field($this->optimLock)->find($id); $_SESSION[$key] = $lockVer; $currVer = $vo[$optimLock]; if (isset($currVer)) { if ($currVer > 0 && $lockVer != $currVer) { // 記錄已經更新 return false; } // 更新樂觀鎖 $lockVer++; if ($this->data[$this->optimLock] != $lockVer) { $this->data[$this->optimLock] = $lockVer; } $_SESSION[$key] = $lockVer; } } return true; } } ``` 對需要使用樂觀鎖的模型,可以使用 ``` namespace app\index\model; use app\common\model\OptimLock; class User extends OptimLock { } ``` ### 利用`Trait`特性實現 但由于PHP不支持多繼承,因此并不建議使用模型繼承功能來擴展功能。我們可以利用`Trait`特性來更方便的引入`OptimLock`后開啟樂觀鎖功能。 因為`Trait`機制的問題,我們對上面的代碼進行了一些必要的調整。 ``` <?php namespace app\common\traits; use think\Exception; use think\Model; trait OptimLock { protected function getOptimLockField() { return property_exists($this, 'optimLock') && isset($this->optimLock) ? $this->optimLock : 'version'; } /** * 創建新的模型實例 * @access public * @param array $data 數據 * @param bool $isUpdate 是否為更新 * @param mixed $where 更新條件 * @return Model */ public function newInstance($data = [], $isUpdate = false, $where = null) { // 緩存樂觀鎖 $this->cacheLockVersion($data); return (new static($data))->isUpdate($isUpdate, $where); } /** * 寫入之前檢查數據 * @access protected * @param array $data 數據 * @param array $where 保存條件 * @return bool */ protected function checkBeforeSave($data, $where) { if (!empty($data)) { // 數據對象賦值 foreach ($data as $key => $value) { $this->setAttr($key, $value, $data); } if (!empty($where)) { $this->isUpdate(true, $where); } } // 數據自動完成 $this->autoCompleteData($this->auto); // 事件回調 if (false === $this->trigger('before_write')) { return false; } if ($this->isExists()) { if (!$this->checkLockVersion()) { throw new Exception('record has update'); } } else { $this->recordLockVersion(); } return true; } /** * 緩存樂觀鎖 * @access protected * @param array $data 數據 * @return void */ protected function cacheLockVersion($data): void { $optimLock = $this->getOptimLockField(); $pk = $this->getPk(); if ($optimLock && isset($data[$optimLock]) && is_string($pk) && isset($data[$pk])) { $key = $this->getName() . '_' . $data[$pk] . '_lock_version'; $_SESSION[$key] = $data[$optimLock]; } } /** * 檢查樂觀鎖 * @access protected * @param array $data 數據 * @return bool */ protected function checkLockVersion() { // 檢查樂觀鎖 $id = $this->getKey(); if (empty($id)) { return true; } $key = $this->getName() . '_' . $id . '_lock_version'; $optimLock = $this->getOptimLockField(); if ($optimLock && isset($_SESSION[$key])) { $lockVer = $_SESSION[$key]; $vo = $this->field($optimLock)->find($id); $_SESSION[$key] = $lockVer; $currVer = $vo[$optimLock]; if (isset($currVer)) { if ($currVer > 0 && $lockVer != $currVer) { // 記錄已經更新 return false; } // 更新樂觀鎖 $lockVer++; $data = $this->getData(); if ($data[$optimLock] != $lockVer) { $this->data($optimLock, $lockVer); } $_SESSION[$key] = $lockVer; } } return true; } } ``` 對需要使用樂觀鎖的模型,可以使用 ``` namespace app\index\model; use app\common\traits\OptimLock; use think\Model; class User extends Model { use OptimLock; } ``` >[info] 值得注意的是,`5.2`版本目前已經內置了一個`OptimLock`的`Trait`實現。
                  <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>

                              哎呀哎呀视频在线观看