悲觀鎖和樂觀鎖
業(yè)務(wù)邏輯的實(shí)現(xiàn)過程中,往往需要保證數(shù)據(jù)訪問的排他性。如在金融系統(tǒng)的日終結(jié)算處理中,我們希望針對(duì)某個(gè)時(shí)間點(diǎn)的數(shù)據(jù)進(jìn)行處理,而不希望在結(jié)算進(jìn)行過程中(可能是幾秒種,也可能是幾個(gè)小時(shí)),數(shù)據(jù)再發(fā)生變化。此時(shí),我們就需要通過一些機(jī)制來保證這些數(shù)據(jù)在某個(gè)操作過程中不會(huì)被外界修改,這樣的機(jī)制,在這里,也就是所謂的 “ 鎖 ” ,即給我們選定的目標(biāo)數(shù)據(jù)上鎖,使其無法被其他程序修改。 ThinkPHP支持兩種鎖機(jī)制:即通常所說的 “ 悲觀鎖( Pessimistic Locking ) ”和 “ 樂觀鎖( Optimistic Locking ) ” --摘自tp官網(wǎng)
悲觀鎖( Pessimistic Locking ) 適用于查詢和更新 配合回滾事務(wù)
悲觀鎖,正如其名,它指的是對(duì)數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度,因此,在整個(gè)數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機(jī)制(也只有數(shù)據(jù)庫層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在本系統(tǒng)中實(shí)現(xiàn)了加鎖機(jī)制,也無法保證外部系統(tǒng)不會(huì)修改數(shù)據(jù))。
通常是使用for update子句來實(shí)現(xiàn)悲觀鎖機(jī)制。
例子:SELECT `id`,`wuser_id`,`out_trade_no`,`total`,`is_benefit` FROM `bj_injuryorder` WHERE `id` = 1 LIMIT 1 FOR UPDATE
ThinkPHP支持悲觀鎖機(jī)制,默認(rèn)情況下,是關(guān)閉悲觀鎖功能的,要在查詢和更新的時(shí)候啟用悲觀鎖功能,可以通過使用之前提到的查詢鎖定方法,例如:
$User->lock(true)->save($data);// 使用悲觀鎖功能
樂觀鎖( Optimistic Locking )
使用樂觀鎖呢有幾個(gè)條件:
1.繼承Think\Model\AdvModel類 高級(jí)模型
namespace Home\Model;
use Think\Model\AdvModel;
class UserModel extends AdvModel{
}
2.定義模型的optimLock屬性
默認(rèn)的optimLock屬性是 lock_version,也就是說如果要在User表里面啟用樂觀鎖機(jī)制,只需要在User表里面增加lock_version字段,如果有已經(jīng)存在的其它字段作為樂觀鎖用途,可以修改模型類的optimLock屬性即可。如果存在optimLock屬性對(duì)應(yīng)的字段,但是需要臨時(shí)關(guān)閉樂觀鎖機(jī)制,把optimLock屬性設(shè)置為false就可以了
3.數(shù)據(jù)表字段里面增加相應(yīng)的字段 只需要在User表里面增加lock_version字段
實(shí)例:
悲觀鎖:http://www.itdecent.cn/p/d200452b2877
樂觀鎖:
我自己總結(jié)的呢 就是加鎖執(zhí)行 必須先執(zhí)行完第一個(gè)操作才會(huì)執(zhí)行下一個(gè)操作 即測(cè)試時(shí)的sleep 或者 事務(wù)操作完成后或者其他的操作完成之后

mysql:
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for bj_ceshi
-- ----------------------------
DROP TABLE IF EXISTS `bj_ceshi`;
CREATE TABLE `bj_ceshi` (
`member_no` varchar(255) NOT NULL DEFAULT '',
`money` decimal(10,0) DEFAULT NULL,
`lock_version` int(255) DEFAULT NULL,
`source` varchar(255) DEFAULT NULL,
PRIMARY KEY (`member_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DemoController.class.php
/**
* demo
*/
namespace WxShop\Controller;
use Think\Controller;
class DemoController extends Controller{
public function demo()
{
$Test = D('Demo'); //測(cè)試樂觀鎖,已繼承AdvModel
$data = $Test->find('hxl');
$update = array(
'member_no' => $data['member_no'], //主鍵
'money' => $data['money'] + 1, //金額
'source' => $data['source'] + 1 ); //來源, 開兩個(gè)瀏覽器5秒內(nèi)分別點(diǎn)擊add鏈接方法,1為第一次,2為第二次
sleep(30); //延時(shí)測(cè)試
$flag = $Test->save($update);
echo date('Y-m-d H:i:s');
echo '<br/>';
echo 'lock_version:' . $data['lock_version']; //輸出版本號(hào)
echo '<br/>';
echo 'result: '. $flag; //輸出結(jié)果標(biāo)記
if($flag){
}
else{
echo $Test->getError();
echo $Test->getDbError();
}
}
}
DemoModel.class.php
<?php
namespace WxShop\Model;
use Think\Model\AdvModel;
class DemoModel extends AdvModel{
protected $tableName = 'ceshi';//如果表名不是Model的名 可以在這里設(shè)置
Protected $pk = 'member_no';//繼承的普通Model會(huì)自動(dòng)獲取主鍵 Adv的話如果主鍵不是id需要設(shè)置主鍵
}