1.使用redis訊息列隊釋出資訊
在一些使用者創造使用者的應用中(如SNS,微博),可能出現1秒有上千萬個使用者同時釋出訊息的情況,此時如果使用mysql可能出現‘too many connections’ 錯誤,當然,把mysql的max_connections 引數設定為更大數,不過這是一個治標不治本的方法,這是可以考慮使用redis。
使用redis的list型別作為訊息列隊,把使用者釋出的訊息暫時儲存在訊息列隊中,接著使用一個cron程式把訊息列隊中的訊息插入mysql,這樣有效率降低mysql的併發量。
例如釋出一條微博使用一下介面:
<?php $uid = get_uid(); $content = get_content(); $timestamp = time(); $weibo = new weibo(); $weibo->pot($uid,$contnet,$timestamp); ?>
weibo物件的post方法就是釋出微博的介面,它直接把微博寫入mysql。引數$uid是使用者的UID,$content是微博的內容,$timestamp是釋出的時間戳。
為了降低mysql的併發數,先把使用者釋出的微博存在redis中,程式碼如下:
<?php $redis = new redis('127.0.0.1',6379); $redis -> connect(); $weibo_info = [ 'uid' => get_uid(), 'content' => get_cntent(), 'timestamp' => time() ]; $redis->lpush('weibo_list',json_encode($weibo_info)); $redis -> close(); ?>
先把微博資訊使用json_encode編碼成JSON格式。然後使用redis物件的lpush方法把微博資訊插入到weibo_list列隊。
把微博存到redis以後,編寫一個cron程式吧redis中的微博資訊插入到mysql中程式碼如下:
<?php $redis = new redis('127.0.0.1',6379); $redis->connect(); $weibo = new weibo(); while(true){ if($redis->lsize('weibo_list') > 0){ $info = $redis->rpop('weibo_list'); $info = json_decode($info); $weibo -> post($info->uid,$info->content,$info->timestamp); }else{ sleep(1); } } $redis ->close(); ?>
在cron程式中,先使用redis物件的rpop()方法從weibo_list列表中取得一條微博資訊,然後使用json_decode ()函式解碼,最後呼叫weibo物件的post方法把微博資訊插入mysql。
使用訊息列隊有一個缺點,就是‘延時’。為了把延時降低,執行多個cron程式同時把訊息列隊中的資料插入mysql。使用訊息列隊的好處是擴充套件性好,當一臺redis伺服器不能應付大量併發時,使用‘一致性Hash演算法’把併發分發到redis伺服器。
2.使用redis代替檔案儲存session
PHP預設使用檔案儲存session,如果併發量大,效率非常低。而redis對高併發的支援非常好,所以,可以使用redis替代檔案儲存session。
在講解例項之前,先了解php的session_set_save_handler函式的作用和使用方法。該函式定義設定使用者自定義會話儲存函式(如開啟、關閉、寫入等)。原型如下:
bool session_set_save_handler(callback open,callback close,callback read,callback write,callback destroy,callback gc,callable create_sid)
sesson_set_save_handler 函式各引數作用如下:
open 回撥函式類似於類的建構函式, 在會話開啟的時候會被呼叫。 這是自動開始會話或者通過呼叫 session_start() 手動開始會話 之後第一個被呼叫的回撥函式。 此回撥函式操作成功返回 TRUE
,反之返回 FALSE
。
close 回撥函式類似於類的解構函式。 在 write 回撥函式呼叫之後呼叫。 當呼叫 session_write_close() 函式之後,也會呼叫 close 回撥函式。 此回撥函式操作成功返回 TRUE
,反之返回 FALSE
。
read
如果會話中有資料,read 回撥函式必須返回將會話資料編碼(序列化)後的字串。 如果會話中沒有資料,read 回撥函式返回空字串。
在自動開始會話或者通過呼叫 session_start() 函式手動開始會話之後,PHP 內部呼叫 read 回撥函式來獲取會話資料。 在呼叫 read 之前,PHP 會呼叫 open 回撥函式。
read 回撥返回的序列化之後的字串格式必須與 write
回撥函式儲存資料時的格式完全一致。 PHP 會自動反序列化返回的字串並填充 $_SESSION 超級全域性變數。 雖然資料看起來和 serialize() 函式很相似, 但是需要提醒的是,它們是不同的。 請參考: session.serialize_handler。
write
在會話儲存資料時會呼叫 write
回撥函式。 此回撥函式接收當前會話 ID 以及 $_SESSION 中資料序列化之後的字串作為引數。 序列化會話資料的過程由 PHP 根據 session.serialize_handler 設定值來完成。
序列化後的資料將和會話 ID 關聯在一起進行儲存。 當呼叫 read
回撥函式獲取資料時,所返回的資料必須要和 傳入 write
回撥函式的資料完全保持一致。
PHP 會在指令碼執行完畢或呼叫 session_write_close() 函式之後呼叫此回撥函式。 注意,在呼叫完此回撥函式之後,PHP 內部會呼叫 close
回撥函式。
destroy
當呼叫 session_destroy() 函式, 或者呼叫 session_regenerate_id() 函式並且設定 destroy 引數為 TRUE
時, 會呼叫此回撥函式。此回撥函式操作成功返回 TRUE
,反之返回 FALSE
。
gc
為了清理會話中的舊資料,PHP 會不時的呼叫垃圾收集回撥函式。 呼叫週期由 session.gc_probability 和 session.gc_divisor 引數控制。 傳入到此回撥函式的 lifetime 引數由 session.gc_maxlifetime 設定。 此回撥函式操作成功返回 TRUE
,反之返回 FALSE
。
create_sid
當需要新的會話 ID 時被呼叫的回撥函式。 回撥函式被呼叫時無傳入引數, 其返回值應該是一個字串格式的、有效的會話 ID。
在使用該函式前,先把php.ini配置檔案的session.save_handler選項設定為user,否則session_set_save_handler 不會生效。
編寫一個session管理sessionManager,程式碼如下:
<?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); $retval = session_set_save_handler ( [$this,'open'], [$this,'close'], [$this,'read'], [$this,'write'], [$this,'destroy'], [$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); return true; } return false; } public function destroy($id){ if($this->redis->delete($id)){ return false; } return false; } public function gc($maxlifetime){ return true; } public function __destruct(){ session_write_close(); } }
SessionManager建構函式主要用來連線redis伺服器,使用session_set_save_handler函式設定session函式回撥,並呼叫session_start函式開始session功能。因為本例中open、close和gc回撥的作用不大,所以直接返回true。
在write回撥函式中,以SessionID作為key,把session的資料作為value儲存到redis伺服器,設定session的過期時間為30秒。在read回撥函式中,以SessionID作為key從redis伺服器中讀取資料,並返回資料。而在destroy回撥函式中,則以sessionID 作為key從redis伺服器中刪除對應的session資料。
使用時只需要包含SessionManager類,然後例項化一個SessionManager物件。下面例子使用SessionManager來管理session,首先建立一個session_set.php,輸入程式碼如下:
include('SessionManager.php'); new SessionManager(); $_SESSION['username'] = 'newsession';
然後再建立一個sesion_get.php 檔案程式碼如下:
include('SessionManager.php'); new SessionManager(); echo $_SESSION['username'];
測試時先訪問 session_set.php 然後再訪問 session_get.php