新聞中心
php怎么將數(shù)據(jù)導(dǎo)入redis
對(duì)于大訪問量的站點(diǎn)使用默認(rèn)的Session 并不合適,我們可以將其存入數(shù)據(jù)庫、或者使用Redis KEY-VALUE數(shù)據(jù)存儲(chǔ)方案
創(chuàng)新互聯(lián)是一家專業(yè)提供建鄴企業(yè)網(wǎng)站建設(shè),專注與成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、H5響應(yīng)式網(wǎng)站、小程序制作等業(yè)務(wù)。10年已為建鄴眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站制作公司優(yōu)惠進(jìn)行中。
首先新建一個(gè)session表
CREATE TABLE `sessions` (
`sid` char(40) NOT NULL,
`updatetime` int(20) NOT NULL,
`data` varchar(200) NOT NULL,
UNIQUE KEY `sid` (`sid`) USING HASH
) ENGINE=MEMORY DEFAULT CHARSET=utf8;
Mysql 的memory引擎采用內(nèi)存表,所有數(shù)據(jù)存儲(chǔ)在內(nèi)存,操作速度快
復(fù)制代碼
?php
//引入數(shù)據(jù)庫文件
include "db.php";
class MySessionHandler implements SessionHandlerInterface
{
private $savePath;
private $sessData;
public $expiretime; //設(shè)置過期時(shí)間
public $db; //數(shù)據(jù)庫
public function __construct($hanlder =''){
$this-db = Database::getInstance();
//獲取數(shù)據(jù)庫實(shí)力
///var_dump($this-db);
}
public function open($savePath, $sessionName)
{
return true;
}
public function close()
{
return true;
}
public function read($id)
{
$sql ="select * from sessions where sid ='$id'";
$result = $this-db-execute($sql);
if(!empty($result)){
return $this-sessData = $result;
}
}
//函數(shù)的參數(shù) $id - 當(dāng)前會(huì)話ID
//數(shù)據(jù)DATA - 序列化之后的字符串
public function write($id, $data)
{
// echo $id;
// echo $data;
$now = time();
$newExp = $now+$this-expiretime; //總時(shí)間=當(dāng)前時(shí)間 + 期限時(shí)間
$sql = "select * from sessions where sid ='$id'";
$result = $this-db-getOne($sql);
//var_dump($result);
if($data==''||isset($data)){
$data = $this-sessData;
}
if($result){
//如果存在則更新
$sql ="update sessions set updatetime = '$newExp',data ='$data' where sid = '$id'";
//echo $sql;
$update_data =$this-db-execute($sql);
if($update_data){
return true;
}
}else{
//不存在則生成生成
$sql = "insert into sessions(sid,updatetime,data) values('$id','$now','$data')";
$insert_data = $this-db-execute($sql);
if($insert_data){
return true;
}
}
return false;
}
public function destroy($id)
{ //銷毀
$sql = "delete from sessions where sid="."$id";
$destory = $this-db-execute($sql);
if($destory){
return true;
}else{
return false;
}
}
public function gc($sessMaxLifeTime)
{
$t = time();
$sql ="delete from sessions where $t - 'updatetime'${sessMaxLifeTime}";
$data = $this-db-execute($this-tosql);
if($data){
return true;
}else{
return false;
}
return true;
}
}
復(fù)制代碼
實(shí)例化
此處 PHP 手冊(cè)可以有兩種方法
1,實(shí)現(xiàn)了SessionHandlerInterface借口的對(duì)象,自PHP5.4可以使用
2 ,直接使用 session_set_save_handler
復(fù)制代碼
//判斷PHP版本
if(version_compare(PHP_VERSION,5.4)==1){
session_set_save_handler($handler, true);
session_start();
}else{
ini_set('session.use_trans_sid',0);
ini_set('session.use_cookies',1);
ini_set('session.cookie_path','/');
ini_set('session.save_handler','user');
session_module_name('user');
session_set_save_handler(array($session,"open"),array($session,"close"),array($session,"read"),array($session,"write"),array($session,"destory"),array($session,"gc"));
session_start();
}
$_SESSION['QQ']="QQ";
echo $_SESSION['QQ'];
復(fù)制代碼
數(shù)據(jù)庫代碼 db.php
復(fù)制代碼
?php
class Database{
static $instance;
static $db;
static function getInstance(){
if(self::$instance){
return self::$instance;
}else{
return new Database();
}
}
public function __construct(){
self::$db = new PDO('mysql:host=localhost;dbname=session', 'root','');
}
public function getOne($sql){
$rs =self::$db-query($sql);
@$rs-setFetchMode(PDO::FETCH_ASSOC);//返回關(guān)聯(lián)數(shù)組
$result = $rs - fetch();
return $result;
}
public function execute($sql){
$rs = self::$db-exec($sql);
return $rs;
}
}
//$data = Database::getInstance();
//var_dump($data);
復(fù)制代碼
使用REDIS 存儲(chǔ)SESSION
復(fù)制代碼
?php
class SessionManager{
private $redis;
private $sessionSavePath;
private $sessionName;
private $sessionExpireTime = 30;
public function __construct(){
$this-redis = new Redis();
$this-redis-connect('127.0.0.1',6379); //連接redis
$retval = session_set_save_handler(
array($this,"open"),
array($this,"close"),
array($this,"read"),
array($this,"write"),
array($this,"destory"),
array($this,"gc")
);
session_start();
}
public function open($path,$name){
return true;
}
public function close(){
return true;
}
public function read($id){
$value = $this-redis-get($id);
if($value){
return $value;
}else{
return "";
}
}
public function write($id,$data){
if($this-redis-set($id,$data)){
$this-redis-expire($id,$this-sessionExpireTime);
//設(shè)置過期時(shí)間
return true;
}
return false;
}
public function destory($id){
if($this-redis-delete($id)){
return true;
}
return false;
}
public function gc($maxlifetime){
return true;
}
//析構(gòu)函數(shù)
public function __destruct(){
session_write_close();
}
}
$re = new SessionManager();
$_SESSION['name'] = "qq";
echo $_SESSION['name'];
php中redis和memcached區(qū)別和應(yīng)用場(chǎng)景
redis比memcached功能更多更強(qiáng)大,現(xiàn)在基本都是只用redis了.
利用redis的原子性可以給數(shù)據(jù)加鎖
可以保存臨時(shí)數(shù)據(jù),比如短信驗(yàn)證碼和session
可以用于實(shí)現(xiàn)簡單的隊(duì)列任務(wù)
可以實(shí)現(xiàn)排行榜功能
可以實(shí)現(xiàn)經(jīng)緯度距離計(jì)算
........
當(dāng)然不止這些,redis是一種key-value數(shù)據(jù)庫,他的業(yè)務(wù)場(chǎng)景還可以自己擴(kuò)展.
php 怎么給redis加查詢鎖
能不能加鎖這個(gè)不知道,但是可以用監(jiān)控watch 和事務(wù)結(jié)合起來用。因?yàn)閣atch的功能就是當(dāng)它監(jiān)控一個(gè)鍵的時(shí)候,如果這個(gè)鍵被修改了,那么它后面的事務(wù)就不會(huì)執(zhí)行。
比如:
set key 1;
watch key
set key 2
mulit
set key 3
exec
get key ='2' //key在watch后被修改了,所以后面的事務(wù)沒有執(zhí)行
PHP中SESSION的問題
Session 和 Cookie 有什么關(guān)系
Cookie 也是由于 HTTP 無狀態(tài)的特點(diǎn)而產(chǎn)生的技術(shù)。也被用于保存訪問者的身份標(biāo)示和一些數(shù)據(jù)。每次客戶端發(fā)起 HTTP 請(qǐng)求時(shí),會(huì)將 Cookie 數(shù)據(jù)加到 HTTP header 中,提交給服務(wù)端。這樣服務(wù)端就可以根據(jù) Cookie 的內(nèi)容知道訪問者的信息了。 可以說,Session 和 Cookie 做著相似的事情,只是 Session 是將數(shù)據(jù)保存在服務(wù)端,通過客戶端提交來的 session_id 來獲取對(duì)應(yīng)的數(shù)據(jù);而 Cookie 是將數(shù)據(jù)保存在客戶端,每次發(fā)起請(qǐng)求時(shí)將數(shù)據(jù)提交給服務(wù)端的。
上面提到,session_id 可以通過 URL 或 cookie 來傳遞,由于 URL 的方式比 cookie 的方式更加不安全且使用不方便,所以一般是采用 cookie 來傳遞 session_id。
服務(wù)端生成 session_id,通過 HTTP 報(bào)文發(fā)送給客戶端(比如瀏覽器),客戶端收到后按指示創(chuàng)建保存著 session_id 的 cookie。cookie 是以 key/value 形式保存的,看上去大概就這個(gè)樣子的:PHPSESSID=e4tqo2ajfbqqia9prm8t83b1f2。在 PHP 中,保存 session_id 的 cookie 名稱默認(rèn)叫作 PHPSESSID,這個(gè)名稱可以通過 php.ini 中 session.name 來修改,也可以通過函數(shù) session_name() 來修改。
為什么不推薦使用 PHP 自帶的 files 型 Session 處理器
在 PHP 中,默認(rèn)的 Session 處理器是 files,處理器可以用戶自己實(shí)現(xiàn)(參見:自定義會(huì)話管理器)。我知道的成熟的 Session 處理器還有很多:Redis、Memcached、MongoDB……
為什么不推薦使用 PHP 自帶的 files 類型處理器,PHP 官方手冊(cè)中給出過這樣一段 Note:
無論是通過調(diào)用函數(shù) session_start() 手動(dòng)開啟會(huì)話, 還是使用配置項(xiàng) session.auto_start 自動(dòng)開啟會(huì)話, 對(duì)于基于文件的會(huì)話數(shù)據(jù)保存(PHP 的默認(rèn)行為)而言, 在會(huì)話開始的時(shí)候都會(huì)給會(huì)話數(shù)據(jù)文件加鎖, 直到 PHP 腳本執(zhí)行完畢或者顯式調(diào)用 session_write_close() 來保存會(huì)話數(shù)據(jù)。 在此期間,其他腳本不可以訪問同一個(gè)會(huì)話數(shù)據(jù)文件。
上述引用參見:Session 的基本用法
為了證明這段話,我們創(chuàng)建一下 2 個(gè)文件: 文件:session1.php
?php
session_start();
sleep(5);
var_dump($_SESSION);
?
文件:session2.php
?php
session_start();
var_dump($_SESSION);
?
在同一個(gè)瀏覽器中,先訪問 ,然后在當(dāng)前瀏覽器新的標(biāo)簽頁立刻訪問 。實(shí)驗(yàn)發(fā)現(xiàn),session1.php 等了 5 秒鐘才有輸出,而 session2.php 也等到了將近 5 秒才有輸出。而單獨(dú)訪問 session2.php 是秒開的。在一個(gè)瀏覽器中訪問 session1.php,然后立刻在另外一個(gè)瀏覽器中訪問 session2.php。結(jié)果是 session1.php 等待 5 秒鐘有輸出,而 session2.php 是秒開的。
分析一下造成這個(gè)現(xiàn)象的原因:上面例子中,默認(rèn)使用 Cookie 來傳遞 session_id,而且 Cookie 的作用域是相同。這樣,在同一個(gè)瀏覽器中訪問這 2 個(gè)地址,提交給服務(wù)器的 session_id 就是相同的(這樣才能標(biāo)記訪問者,這是我們期望的效果)。當(dāng)訪問 session1.php 時(shí),PHP 根據(jù)提交的 session_id,在服務(wù)器保存 Session 文件的路徑(默認(rèn)為 /tmp,通過 php.ini 中的 session.save_path 或者函數(shù) session_save_path() 來修改)中找到了對(duì)應(yīng)的 Session 文件,并對(duì)其加鎖。如果不顯式調(diào)用 session_write_close(),那么直到當(dāng)前 PHP 腳本執(zhí)行完畢才會(huì)釋放文件鎖。如果在腳本中有比較耗時(shí)的操作(比如例子中的 sleep(5)),那么另一個(gè)持有相同 session_id 的請(qǐng)求由于文件被鎖,所以只能被迫等待,于是就發(fā)生了請(qǐng)求阻塞的情況。
既然如此,在使用完 Session 后,立刻顯示調(diào)用 session_write_close() 是不是就解決問題了哩?比如上面例子中,在 sleep(5) 前面調(diào)用 session_write_close()。
確實(shí),這樣 session2.php 就不會(huì)被 session1.php 所阻塞。但是,顯示調(diào)用了 session_write_close() 就意味著將數(shù)據(jù)寫到文件中并結(jié)束當(dāng)前會(huì)話。那么,在后面代碼中要使用 Session 時(shí),必須重新調(diào)用 session_start()。
例如:
?php
session_start();
$_SESSION['name'] = 'Jing';
var_dump($_SESSION);
session_write_close();
sleep(5);
session_start();
$_SESSION['name'] = 'Mr.Jing';
var_dump($_SESSION);
?
官方給出的方案:
對(duì)于大量使用 Ajax 或者并發(fā)請(qǐng)求的網(wǎng)站而言,這可能是一個(gè)嚴(yán)重的問題。 解決這個(gè)問題最簡單的做法是如果修改了會(huì)話中的變量, 那么應(yīng)該盡快調(diào)用 session_write_close() 來保存會(huì)話數(shù)據(jù)并釋放文件鎖。 還有一種選擇就是使用支持并發(fā)操作的會(huì)話保存管理器來替代文件會(huì)話保存管理器。
我推薦的方式是使用 Redis 作為 Session 的處理器。
拓展閱讀:
為什么不能用 memcached 存儲(chǔ) Session
如何使用 Redis 作為 PHP Session handler
Session 數(shù)據(jù)是什么時(shí)候被刪除的
這是一道經(jīng)常被面試官問起的問題。
先看看官方手冊(cè)中的說明:
session.gc_maxlifetime 指定過了多少秒之后數(shù)據(jù)就會(huì)被視為"垃圾"并被清除。 垃圾搜集可能會(huì)在 session 啟動(dòng)的時(shí)候開始( 取決于 session.gc_probability 和 session.gc_divisor)。 session.gc_probability 與 session.gc_divisor 合起來用來管理 gc(garbage collection 垃圾回收)進(jìn)程啟動(dòng)的概率。此概率用 gc_probability/gc_divisor 計(jì)算得來。例如 1/100 意味著在每個(gè)請(qǐng)求中有 1% 的概率啟動(dòng) gc 進(jìn)程。session.gc_probability 默認(rèn)為 1,session.gc_divisor 默認(rèn)為 100。
繼續(xù)用我上面那個(gè)不太恰當(dāng)?shù)谋确桨桑喝绻覀儼盐锲贩旁诔械膬?chǔ)物箱中而不取走,過了很久(比如一個(gè)月),那么保安就要清理這些儲(chǔ)物箱中的物品了。當(dāng)然并不是超過期限了保安就一定會(huì)來清理,也許他懶,又或者他壓根就沒有想起來這件事情。
再看看兩段手冊(cè)的引用:
如果使用默認(rèn)的基于文件的會(huì)話處理器,則文件系統(tǒng)必須保持跟蹤訪問時(shí)間(atime)。Windows FAT 文件系統(tǒng)不行,因此如果必須使用 FAT 文件系統(tǒng)或者其他不能跟蹤 atime 的文件系統(tǒng),那就不得不想別的辦法來處理會(huì)話數(shù)據(jù)的垃圾回收。自 PHP 4.2.3 起用 mtime(修改時(shí)間)來代替了 atime。因此對(duì)于不能跟蹤 atime 的文件系統(tǒng)也沒問題了。
GC 的運(yùn)行時(shí)機(jī)并不是精準(zhǔn)的,帶有一定的或然性,所以這個(gè)設(shè)置項(xiàng)并不能確保舊的會(huì)話數(shù)據(jù)被刪除。某些會(huì)話存儲(chǔ)處理模塊不使用此設(shè)置項(xiàng)。
對(duì)于這種刪除機(jī)制,我是存疑的。
比如 gc_probability/gc_divisor 設(shè)置得比較大,或者網(wǎng)站的請(qǐng)求量比較大,那么 GC 進(jìn)程啟動(dòng)就會(huì)比較頻繁。
還有,GC 進(jìn)程啟動(dòng)后都需要遍歷 Session 文件列表,對(duì)比文件的修改時(shí)間和服務(wù)端的當(dāng)前時(shí)間,判斷文件是否過期而決定是否刪除文件。
這也是我覺得不應(yīng)該使用 PHP 自帶的 files 型 Session 處理器的原因。而 Redis 或 Memcached 天生就支持 key/value 過期機(jī)制的,用于作為會(huì)話處理器很合適。或者自己實(shí)現(xiàn)一個(gè)基于文件的處理器,當(dāng)根據(jù) session_id 獲取對(duì)應(yīng)的單個(gè) Session 文件時(shí)判斷文件是否過期。
為什么重啟瀏覽器后 Session 數(shù)據(jù)就取不到了
session.cookie_lifetime 以秒數(shù)指定了發(fā)送到瀏覽器的 cookie 的生命周期。值為 0 表示"直到關(guān)閉瀏覽器"。默認(rèn)為 0。
其實(shí),并不是 Session 數(shù)據(jù)被刪除(也有可能是,概率比較小,參見上一節(jié))。只是關(guān)閉瀏覽器時(shí),保存 session_id 的 Cookie 沒有了。也就是你弄丟了打開超市儲(chǔ)物箱的鑰匙(session_id)。
同理,瀏覽器 Cookie 被手動(dòng)清除或者其他軟件清除也會(huì)造成這個(gè)結(jié)果。
為什么瀏覽器開著,我很久沒有操作就被登出了
這個(gè)是稱為“防呆”,為了保護(hù)用戶賬戶安全的。
這個(gè)小節(jié)放進(jìn)來,是因?yàn)檫@個(gè)功能的實(shí)現(xiàn)可能和 Session 的刪除機(jī)制有關(guān)(之所以說是可能,是因?yàn)檫@個(gè)功能不一定要借住 Session 實(shí)現(xiàn),用 Cookie 也同樣可以實(shí)現(xiàn))。 說簡單一點(diǎn),就是長時(shí)間沒有操作,服務(wù)端的 Session 文件過期被刪除了。
一個(gè)有意思的事情
在我試驗(yàn)的過程中,發(fā)現(xiàn)了小有意思的事情:我把 GC 啟動(dòng)的概率設(shè)置為 100%。如果只有一個(gè)訪問者請(qǐng)求,該訪問者即使過了很久(超過了過期時(shí)間)后才發(fā)起第二次請(qǐng)求,那么 Session 數(shù)據(jù)也還是存在的('session.save_path' 目錄下面的 Session 文件存在)。是的,明明就超過了過期時(shí)間,卻沒有被 GC 刪除。這時(shí),我用另外一個(gè)瀏覽器訪問時(shí)(相對(duì)于另一個(gè)訪問者),這次請(qǐng)求生成了新的 Session 文件,而上一個(gè)瀏覽器請(qǐng)求生成的那個(gè) Session 文件終于沒有了(之前那個(gè) Session 文件在 'session.save_path' 目錄下面的消失了)。
還有,發(fā)現(xiàn) Session 文件被刪除后,再次請(qǐng)求,還是會(huì)生成和之前文件名相同的 Session 文件(因?yàn)闉g覽器并沒有關(guān)閉,再次請(qǐng)求發(fā)送的 session_id 是相同的,所以重新生成的 Session 文件的文件名還是一樣的)。但是,我不理解的是:這個(gè)重新出現(xiàn)的文件的創(chuàng)建時(shí)間竟然是第一次的那個(gè)創(chuàng)建時(shí)間,難道它是從回收站中回來的?(確實(shí),我做這個(gè)試驗(yàn)時(shí)是在 window 下進(jìn)行的)
我猜測(cè)的原因是這樣:當(dāng)啟動(dòng)會(huì)話后,PHP 根據(jù) session_id 找到并打開了對(duì)應(yīng)的 Session 文件,然后才啟動(dòng) GC 進(jìn)程。GC 進(jìn)程就只檢查除了當(dāng)前這個(gè) Session 文件外的其他文件,發(fā)現(xiàn)過期的就干掉。所有,即使當(dāng)前這個(gè) Session 文件已經(jīng)過期了,GC 也沒有刪除它。
我認(rèn)為這個(gè)不合理的。
由于發(fā)生這種情況影響也不大(畢竟線上請(qǐng)求很多,當(dāng)前請(qǐng)求的過期文件被其他請(qǐng)求喚起的 GC 干掉的可能性是比較大的) + 我沒有信心去看 PHP 源代碼 + 我并不在線上使用 PHP 自帶的 files 型 Session 處理器。所以,這個(gè)問題我就沒有深入研究了。請(qǐng)諒解。
?php
// 過期時(shí)間設(shè)置為 30 秒
ini_set('session.gc_maxlifetime', '30');
// GC 啟動(dòng)概率設(shè)置為 100%
ini_set('session.gc_probability', '100');
ini_set('session.gc_divisor', '100');
session_start();
$_SESSION['name'] = 'Jing';
var_dump($_SESSION);
?
php session 存redis 會(huì)存在鎖的問題嗎
方法:
修改 php.ini 的設(shè)置
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379"
修改完之后,重啟一下 php-fpm。
如何使用redis實(shí)現(xiàn)分布式鎖功能?
由于redis是單線程的且性能很快,所以比較適合做全局分布式鎖。
基本流程就是在操作可能某個(gè)全局沖突資源的時(shí)候,使用一個(gè)全局唯一key來判斷是否有其他線程占用了資源,如果有其他線程占用,則報(bào)錯(cuò)退出或者循環(huán)等待。如果沒有其他線程占用,則就可以通過添加分布式鎖來占用這個(gè)資源,然后再執(zhí)行后續(xù)的任務(wù),在任務(wù)執(zhí)行完成之后,再釋放分布式鎖,其他線程就可以繼續(xù)使用這個(gè)資源了。
那么通過redis加鎖的動(dòng)作是什么呢?
簡單加鎖命令:
命令是:setnx
內(nèi)部的實(shí)現(xiàn)機(jī)制就是判斷這個(gè)key位置是不是有數(shù)據(jù),沒有數(shù)據(jù)就設(shè)置成value返回,有數(shù)據(jù)就返回一個(gè)特殊數(shù)值。
但是這里有一個(gè)問題是,如果占用資源的線程錯(cuò)誤退出了,沒有來得及釋放分布式鎖,這個(gè)鎖就被永遠(yuǎn)的占用了
改進(jìn)版的加鎖:
命令是:1. setnx 2. expire
添加分布式鎖的同時(shí),添加一個(gè)鎖鎖過期的時(shí)間。這樣,當(dāng)加鎖線程退出之后,至少等一段時(shí)間之后,鎖是有機(jī)會(huì)釋放掉的。
這里有一個(gè)小問題是,這兩個(gè)命令是分開執(zhí)行的,不是原子操作。那么就存在理論上來說,第一個(gè)命令執(zhí)行完之后,就出現(xiàn)錯(cuò)誤,來不及執(zhí)行expire命令的可能,一種辦法是自己寫lua腳本,可以實(shí)現(xiàn)多條命令的原子化執(zhí)行。一種辦法是引用一些開源庫。在2.8版本之后,redis為了解決這個(gè)問題,提供了官方版的解法,就是命令:set key value nx expireTimeNum ex,將上述兩個(gè)命令合并成了一個(gè)命令。
有了過期時(shí)間之后解決了一部分問題,但是也有可能出現(xiàn)鎖都過期了,但是中間執(zhí)行的任務(wù)還沒有結(jié)束,第一個(gè)線程還在執(zhí)行了,第二個(gè)線程已經(jīng)拿到鎖開始執(zhí)行了,那么這時(shí)候第一個(gè)線程如果執(zhí)行完成之后,那么就會(huì)將第二個(gè)線程的鎖釋放掉了。第二個(gè)線程釋放鎖的時(shí)候,要不然出錯(cuò),要不然是釋放的其他線程的鎖,這樣也會(huì)和預(yù)期不符。
如果單純地要解決這個(gè)問題的話,可以在設(shè)置value的時(shí)候使用一個(gè)隨機(jī)數(shù),釋放鎖的時(shí)候,先判斷這個(gè)隨機(jī)數(shù)是否一致,如果一致再刪除鎖,否則就退出。但是判斷value和刪除key也不是一個(gè)原子操作,這時(shí)候就需要使用lua腳本了。
上面的方案依然不能解決超時(shí)釋放的問題,依然違背分布式鎖的初衷。怎么辦了?
解題思路是另外啟動(dòng)一個(gè)線程,它的任務(wù)就是每隔一段時(shí)間判斷一下如果發(fā)現(xiàn)當(dāng)前線程的任務(wù)快過期了還沒有完成,則定期給當(dāng)前線程的鎖續(xù)個(gè)期。
有個(gè)開源庫解決了這個(gè)問題,它大概率會(huì)比你實(shí)現(xiàn)得更好一些。這個(gè)庫就是redisson,非常好記,就是redis的兒子son,連起來就是reidsson,雖然可能不是親的,但是也足夠了。
這個(gè)庫里面有一個(gè)組件是watchdog,直譯過來就是看門狗,它的作用就是每隔一段時(shí)間判斷的。
再繼續(xù)思考,還有一個(gè)更極端的問題是,redis如果是單節(jié)點(diǎn)的,它宕機(jī)了;或者是主備節(jié)點(diǎn)的,但是備份節(jié)點(diǎn)還沒有來得及同步主節(jié)點(diǎn)的數(shù)據(jù),主節(jié)點(diǎn)拿到鎖之后,在同步數(shù)據(jù)之前就馬上宕機(jī)了,則也有可能出現(xiàn)鎖不住的問題。如果認(rèn)為這是一個(gè)問題,想要解決這個(gè)問題,這個(gè)問題怎么解決了?
思路是在加鎖的時(shí)候多加鎖幾臺(tái)redis服務(wù)器,通常情況下redis部署的時(shí)候是2n+1臺(tái),那么在加鎖的時(shí)候需要保證過半數(shù)服務(wù)器加鎖成功了,也就是說n+1臺(tái)服務(wù)器。這時(shí)候除非整個(gè)集群都不可用了,則這個(gè)安全性將大幅度提升。
這個(gè)問題也有開源庫解決了,就是redis紅鎖。
下一個(gè)問題是分布式鎖可以重入么?
如果想要實(shí)現(xiàn)可重入的分布式鎖的話,需要在設(shè)置value的時(shí)候加上線程信息和加鎖次數(shù)的信息。但是這是簡單的思路,如果加上過期時(shí)間等問題之后,可重入鎖就可能比較復(fù)雜了。
本文標(biāo)題:php數(shù)據(jù)加鎖redis php 鎖
鏈接分享:http://www.dlmjj.cn/article/docdeoj.html