事務不能解決並發,只能保證在一個事務內所有操作的一致性
常見的並發處理如下:
1.悲觀鎖
為什么叫悲觀鎖?
默認每次的執行都會發生並發
表必須是innodb類型,必須在事務中執行,加上for update 查詢的表id=10數據是,這條數據就被鎖定了,第一個人獲得鎖,后面的人只能等待第一個人完成事務提交后才能獲得鎖進行操作
$this->db->begin(); // 開啟事務 try{ $info = $this->db->findOne("select * from table where id=1 FOR UPDATE "); sleep(5); $this->db->commit() // 只有提交后后面的請求才可以執行 } catch (\Exception $e) { $this->db->rollback() }
2.樂觀鎖
為什么叫樂觀鎖?
默認每次的執行都不會發生並發,只有到真正執行變更的時候檢測並發
原理是利用mysql update 原子性,也就是不論多少次並發,mysql update 只會一條一條的更新
例:
$this->db->begin(); //開啟事務 try{ $info = $this->db->findOne("select id,name,version from table where id=1"); sleep(5); // 其他邏輯 $version = $info['version'] + 1; // 主要防止並發在這里 $this->db->excute("update set name='123', version=$version where version=$info['version'] and id=1 "); $this->db->commit() } catch (\Exception $e) { $this->db->rollback() }
3.redis鎖
// 單個用戶重復提交 場景 $hasLock = $this->redis->set($key . '用戶唯一標識', 1, 'nx', 'ex', $expire); if (!$hasLock) { // 沒有獲得鎖 throw new Exception('排隊中請稍后...'); } // 並發場景 $hasLock = $this->redis->set($key, 1, 'nx', 'ex', $expire); if (!$hasLock) { // 沒有獲得鎖 throw new Exception(''排隊中請稍后...'); } sleep(5); // 處理完業務 解鎖 $this->redis->del($key);
4.redis隊列
以上方法都不是真正意義上的並發,都是強制用戶排隊一個一個的來,而redis 隊列可以實現真正的並發
根據具體業務的提前使用$this->redis->lpush(); 將需要並發的紅包或者商品lpush入列,並發的時候lpop出列,因為lpop是原子性操作所以不會發生超賣或超領情況
5.其他
redis lua腳本、redis事務、中間件 等