大家都知道,預設的session是儲存在檔案裡的,一般情況下這是沒什麼問題的,然而一旦訪問很多,session的使用就會頻繁讀寫檔案,必然會影響應用的效能。另外,假如是多機部署,session的共享也是個問題。
既然我們知道有Redis這個利器,而PHP也是支援session自定義的,那麼為何不要效能更好又能實現共享session的Redis呢?
Redis是什麼?
Redis 是完全開源免費的,遵守 BSD 協議,是一個高效能的 key - value 資料庫
Redis資料結構
- string
- hash
- set
- sorted set
- pub/sub
- list
另外還有HyperLogLog,geo….
Redis的優勢
- 效能極佳,官網顯示QPS能達到100k/s
- 資料結構豐富,不止於字串,hash
- 穩定性不錯,持久化
- 支援叢集
- 社群不錯,使用率高,對於PHP程式設計師,除了LNMP/LAMP,然後應該就是Redis了
Redis應用
- 快取
- 分散式鎖
- 計數器
- 佇列
- geo
- ……
Redis命令
使用redis儲存session
我介紹兩種方法給大家:
- session_start帶參
- session_set_save_handler託管session
下面我們裡一一說明:
session_start
session_start ([ array $options = array() ] ) : bool —— 啟動新會話或者重用現有會話(點選檢視更多引數)
來看demo:
session_start([
'save_path' => 'tcp://127.0.0.1:6379',
'save_handler' => 'redis',
]);
$_SESSION['user_id'] = 10001;
$_SESSION['userInfo'] = ['name' => 'sai'];
p($_SESSION['user_id']);
p($_SESSION['userInfo']);
輸出如下:
10001
Array ( [name] => sai )
我們可以使用redis-cli連線檢視:
redis-cli -h 127.0.0.1 -p 6379
// 如設定密碼再輸入(auth 你設定的密碼)即可
我們可以看到redis裡有了PHPREDIS_SESSION:nmo65igogqnq8ur2gia94jt15u,裡面儲存了我們的session資訊。
session_set_save_handler
建議session.serialize_handler = php_serialize,預設php寫入和讀取略微繁瑣。
這裡說明我們成功了將session資訊通過Redis進行了讀寫。下面我們使用session_set_save_handler來實現:
<?php
namespace Library\Sessions;
use SessionHandler;
class RedisSession extends SessionHandler
{
private $redis;
private $lifeTime = 7200;
private $config;
private $prefix = 'PHPREDIS_SESSION:';
public function __construct($config)
{
$this->config = $config;
}
private function getRedisInstance()
{
if (empty($this->redis)) {
$redis = new \Redis();
$redis->connect($this->config['host'], $this->config['port'], $this->config['timeout']);
if (!$this->config['auth']) {
$redis->auth($this->config['auth']);
}
$this->redis = $redis;
}
return $this->redis;
}
public function read($id)
{
return $this->getRedisInstance()->get($this->prefix.$id);
}
public function write($id, $data)
{
if ($this->getRedisInstance()->setex($this->prefix.$id, $this->lifeTime, $data)) {
return true;
}
return false;
}
public function destroy($id)
{
if($this->getRedisInstance()->delete($id)){//刪除redis中的指定記錄
return true;
}
return false;
}
public function gc($maxlifetime)
{
return true;
}
public function __destruct()
{
session_write_close();
}
}
$handler = new RedisSession([
'host' => '127.0.0.1',
'port' => 6379,
'auth' => null,
'timeout' => 5,
]);
session_set_save_handler($handler, true);
session_start();
$_SESSION['user_id'] = 10001;
$_SESSION['userInfo'] = ['name' => 'sai'];
p($_SESSION['user_id']);
p($_SESSION['userInfo']);
知識點:
- 這裡需要注意下read方法,裡面需要加一下serialize,以便於我們儲存複雜的session結構。如果不加會報錯(Warning: session_start(): Failed to read session data: user (path: ))這是因為Redis無法直接儲存array結構,需要轉化為string型別儲存。
我們也來看看Redis客戶端儲存情況:
通用我們也看到redis儲存了session,與前面略有不同的只是儲存的key不一樣。但是我們可以定義一個私有屬性:
private $prefix = ‘PHPREDIS_SESSION:’;
然後做一下調整即可:
public function read($id)
{
return serialize($this->getRedisInstance()->get($this->prefix.$id));
}
public function write($id,$data)
{
if ($this->getRedisInstance()->setex($this->prefix.$id, $this->lifeTime, $data)) {
return true;
}
return false;
}
執行後會發現把之前第一種設定的session覆蓋掉。
當然我比較建議使用第二種方法,便於我們定製化編碼。
總結
總的來說,兩種方法配置都比較簡單,個人建議使用第二種方式實現,這樣也比較適合整合到框架,後期我們可以在進行擴充套件。
本作品採用《CC 協議》,轉載必須註明作者和本文連結