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

