Yii框架實(shí)現(xiàn)樂(lè)觀鎖與悲觀鎖流程詳解
目錄
- 一、在Yii中實(shí)現(xiàn)樂(lè)觀鎖
- 1、在yii中實(shí)現(xiàn)樂(lè)觀鎖步驟
- 2、Yii中實(shí)現(xiàn)樂(lè)觀鎖
- 3、實(shí)現(xiàn)樂(lè)觀鎖
- 二、在Yii中實(shí)現(xiàn)悲觀鎖
- 1、在yii中實(shí)現(xiàn)悲觀鎖的步驟
- 2、yii中悲觀鎖實(shí)現(xiàn)
一、在Yii中實(shí)現(xiàn)樂(lè)觀鎖
樂(lè)觀鎖(optimistic locking)表現(xiàn)出大膽、務(wù)實(shí)的態(tài)度。使用樂(lè)觀鎖的前提是, 實(shí)際應(yīng)用當(dāng)中,發(fā)生沖突的概率比較低。他的設(shè)計(jì)和實(shí)現(xiàn)直接而簡(jiǎn)潔。 目前Web應(yīng)用中,樂(lè)觀鎖的使用占有絕對(duì)優(yōu)勢(shì)。因此在Yii為ActiveReocrd樂(lè)觀鎖支持
1、在yii中實(shí)現(xiàn)樂(lè)觀鎖步驟
1、給需要加鎖的表添加一個(gè)字段,用于表示版本號(hào),這里我一般選手version字段作為版本號(hào)字段,注意,如果你需要加鎖的表已經(jīng)生成Model了,那么對(duì)應(yīng)表的Model要將你添加的版本號(hào)字段(version)信息加入Model
2、在更新表中字段時(shí),使用 try ... catch 看看是否能捕獲一個(gè) yii\db\StaleObjectException 異常,如果捕捉到y(tǒng)ii\db\StaleObjectException 異常,說(shuō)明在本次修改這個(gè)記錄的過(guò)程中, 該記錄已經(jīng)被修改過(guò)了,作出相應(yīng)提示
2、Yii中實(shí)現(xiàn)樂(lè)觀鎖
1、在yii中聲明指定字段為版本號(hào)
版本號(hào)是實(shí)現(xiàn)樂(lè)觀鎖的根本所在。所以第一步,我們要告訴Yii,哪個(gè)字段是版本號(hào)字段,聲明版本號(hào)的方法由yii\db\BaseActiveRecord(vendor/yiisoft/yii2/db/BaseActiveRecord)中的optimisticLock方法負(fù)責(zé)
public function optimisticLock() { return null; }
這個(gè)方法返回 null ,表示不使用樂(lè)觀鎖,如果我們需要使用樂(lè)觀鎖的話,我們需要在我們的需要加鎖的表的Model中重載optimisticLock方法
public function optimisticLock() { return "version"; }
如上說(shuō)明當(dāng)前的ActiveRecord中,有一個(gè) version 字段,可以為樂(lè)觀鎖所用
3、實(shí)現(xiàn)樂(lè)觀鎖
我們?cè)贛odel中設(shè)置了版本號(hào)后,這時(shí)候我們的更新和刪除都是樂(lè)觀鎖操作了,與正常操作數(shù)據(jù)庫(kù)的方式一致
try { $crowd = Crowd::findOne(["crowd_id" => 12]); $crowd->status = 1; $crowd->save(); } catch (\Exception $e) { return false; }
在更新過(guò)程中,我們會(huì)調(diào)用到 yii\db\BaseActiveRecord::updateInternal()方法,此方法里面就具有處理樂(lè)觀鎖的代碼
protected function updateInternal($attributes = null) { if (!$this->beforeSave(false)) { return false; } // 獲取等下要更新的字段及新的字段值 $values = $this->getDirtyAttributes($attributes); if (empty($values)) { $this->afterSave(false, $values); return 0; } // 把原來(lái)ActiveRecord的主鍵作為等下更新記錄的條件,也就是說(shuō),等下更新的,最多只有1個(gè)記錄。 $condition = $this->getOldPrimaryKey(true); // 獲取版本號(hào)字段的字段名,比如 version $lock = $this->optimisticLock(); // 如果 optimisticLock() 返回的是 null,那么,不啟用樂(lè)觀鎖。 if ($lock !== null) { // 這里的 $this->$lock ,就是 $this->version 的意思; 這里把 version+1 作為要更新的字段之一。 $values[$lock] = $this->$lock + 1; // 這里把舊的版本號(hào)作為更新的另一個(gè)條件 $condition[$lock] = $this->$lock; } $rows = static::updateAll($values, $condition); // 如果已經(jīng)啟用了樂(lè)觀鎖,但是卻沒(méi)有完成更新,或者更新的記錄數(shù)為0; // 那就說(shuō)明是由于 version 不匹配,記錄被修改過(guò)了,于是拋出異常。 if ($lock !== null && !$rows) { throw new StaleObjectException("The object being updated is outdated."); } if (isset($values[$lock])) { $this->$lock = $values[$lock]; } $changedAttributes = []; foreach ($values as $name => $value) { $changedAttributes[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null; $this->_oldAttributes[$name] = $value; } $this->afterSave(false, $changedAttributes); return $rows; }
在刪除過(guò)程中,我們會(huì)調(diào)用到 yii\db\BaseActiveRecord::delete()方法,此方法里面就具有處理樂(lè)觀鎖的代碼
public function delete() { $result = false; if ($this->beforeDelete()) { // 刪除的SQL語(yǔ)句中,WHERE部分是主鍵 $condition = $this->getOldPrimaryKey(true); // 獲取版本號(hào)字段的字段名,比如 version $lock = $this->optimisticLock(); // 如果啟用樂(lè)觀鎖,那么WHERE部分再加一個(gè)條件,版本號(hào) if ($lock !== null) { $condition[$lock] = $this->$lock; } $result = static::deleteAll($condition); if ($lock !== null && !$result) { throw new StaleObjectException("The object being deleted is outdated."); } $this->_oldAttributes = null; $this->afterDelete(); } return $result; }
如上我們就知道了,在yii中已經(jīng)有了樂(lè)觀鎖相關(guān)的代碼了,我們只需要在Model中設(shè)置一個(gè)版本號(hào)字段即可
二、在Yii中實(shí)現(xiàn)悲觀鎖
正如其名字,悲觀鎖(pessimistic locking)體現(xiàn)了一種謹(jǐn)慎的處事態(tài)度
1、在yii中實(shí)現(xiàn)悲觀鎖的步驟
1、在對(duì)任意記錄進(jìn)行修改前,先嘗試為該記錄加上鎖
2、如果加鎖失敗,說(shuō)明該記錄正在被修改,那么當(dāng)前查詢可能要等待或者拋出異常
3、如果成功加鎖,那么就可以對(duì)記錄做修改,事務(wù)完成后就會(huì)解鎖了
2、yii中悲觀鎖實(shí)現(xiàn)
使用select.....for update實(shí)現(xiàn)悲觀鎖,簡(jiǎn)單示例如下:
$transaction = Yii::$app->db->beginTransaction(); try{ //查詢id為12的這條數(shù)據(jù)并且鎖定 $sql = "select * from ubo_crowd where crowd_id = 12 for update"; $crowd = Yii::$app->db->createCommand($sql)->queryOne(); //更新數(shù)據(jù) $crowd1 = Crowd::findOne(["crowd_id" => $crowd["crowd_id"]]); $crowd1->sort += 1; if($crowd1->save()){ $transaction->commit(); } }catch(Exception $e){ $transaction->rollBack(); }
到此這篇關(guān)于Yii框架實(shí)現(xiàn)樂(lè)觀鎖與悲觀鎖流程詳解的文章就介紹到這了,更多相關(guān)Yii樂(lè)觀鎖與悲觀鎖內(nèi)容請(qǐng)搜索以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持!
