天天看點

原生session與session in redis對比

剛畢業不久的時候讀過一份面試文章,文章連結記不清了,提到session在伺服器上的存儲方式,重點講到原生session在伺服器上是以檔案的形式存儲的,是以其有一些磁盤io上的缺點。然後文章裡又特别提到,不建議将session放在memcached等緩存中,但是沒有講原因。

後來一直對此心存疑惑,在實際的工作項目中也既接觸過原生session的項目,也接觸過使用redis來存儲會話資訊的項目,今天特意去查了一下大家在使用中的一些見解。

下面是從stackoverflow上找到的一些觀點:

Using something like Redis for storing sessions is a great way to get more performance out of load balanced servers. Here is a case in point:

On Amazon Web Services, the load balances have what’s called ‘sticky sessions’. What this means is that when a user first connects to your web app, e.g. when logging in to it, the load balance will choose one of your app. servers and this user will continue to be served from this server until they exit your application. This is because the sessions used by PHP, for example, will be stored on the app. server that they first start using. Now, if you use Redis on a separate server, then configure your PHP on each of your app. servers to store it’s sessions in Redis, you can turn this ‘sticky sessions’ off. This would mean that any of your servers can access the sessions and, therefore, the user be served from a different server with every request to your app. This ultimately makes for more efficient use of your load balancing set-up.

上面這段提到了一個優點:在叢集環境中,使用redis可以更靈活地實作負載均衡。

You want the session save handler to be fast. This is due to the fact that a PHP session will block all other concurrent requests from the same user until the first request is finished.

There are a variety of handlers you could use for PHP sessions across multiple servers: File w/ NFS, MySQL Database, Memcache, and Redis.

The database method (using InnoDB) was the slowest in my experience followed by File w/ NFS. Locking and write contention are the main factors. Memcache and Redis provide similar performance and are by far the better alternatives since all operations are in RAM. Redis is my choice because you can enable disk persistence, and Memcache is only memory based.

這裡提到了幾種用來存儲會話資料的方式,并把原生的session歸類的使用檔案的存儲。顯然是Redis在效率上要更快些,而與memcached相比,因為有持久化,也更安全一些。

由大家的使用經驗可以看出,說“原生的session要比使用redis來存儲session更好”的說話是沒有道理的。而且session還存在以下問題:

  • 由于session回收的問題,使用session還會帶來一些像登入會話不能準時過期等問題。
  • 在使用swoole做websocket伺服器的時候,在嘗試使用session_id來擷取原生session的會話資訊的時候,由于原生session總是需要配合session_start()使用,在嘗試在處理請求session_start()的時候會報“header already sent”的問題;嘗試使用sessionHandler類的方法時,也會報告一些奇怪的問題。

是以沒必要守着原生session這老古董,應該積極擁抱redis存儲會話的方式。

最簡單的通過redis管理session的配置方式

在php.ini裡實作如下配置

[Session]
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?database=3"
           

這樣就可以在不對原有的業務代碼做任何修改的情況下,完成原生session向redis session的遷移。在分布式環境中,通過配置redis叢集或主庫,也可以友善地解決一緻性或同步的問題。

使用redis來管理session還有一個優勢,可以更快更友善地直接處理使用者的session資料。使用原生session的時候,想要在非使用者請求的程序中擷取指定phpsessionid使用者的session資料是比較麻煩的,使用php自帶的一些session操作方式都需要session_start,因為不是使用者的請求程序,執行session_start總是會遇到各種奇怪的問題。

将session交給redis管理之後,問題就簡單多了,可以直接通過get key為

PHPREDIS_SESSION:PHPSESSIONID

的redis資料,就可以取到session中的内容。

因為session裡的資料有一些自己的特殊處理,直接使用unserialize是不能解析出資料來下,下附解析出session中資料的方法:

static function unserialize_php($session_data) {
    $return_data = array();
    $offset = ;
    while ($offset < strlen($session_data)) {
        if (!strstr(substr($session_data, $offset), "|")) {
            throw new Exception("invalid data, remaining: " . substr($session_data, $offset));
        }
        $pos = strpos($session_data, "|", $offset);
        $num = $pos - $offset;
        $varname = substr($session_data, $offset, $num);
        $offset += $num + ;
        $data = unserialize(substr($session_data, $offset));
        $return_data[$varname] = $data;
        $offset += strlen(serialize($data));
    }
    return $return_data;
}
           

使用這個方法的前提是在php.ini中配置的

session.serialize_handler=php

如果是别的handler,比如binary,得使用不同的解析方式,這裡就不列出了,可以自行google。

擴充閱讀:自定義session管理方法

php支援在各個層面上自行操作session,隻要定義好一系列的接口方法即可。詳見下面的參考文章。

參考文章

  1. 讓php Session 存入 redis 配置方法
  2. php Session存儲到Redis的方法
  3. PHP的session_set_save_handler 執行機制