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