天天看点

原生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 执行机制