Yii CDBHttpSession数据库存储session性能优化实战

上一篇 关于 Yii CHttpSession性能优化篇之源码流程分析 有详细分析CHttpSession执行流程,在了解CHttpSession的详细执行流程之后,我们就可以非常轻松的扩展Yii Session,来优化和符合自己的业务流程。

首先我们来看CDBHttpSession的数据库表结构, 可以看到,Yii是基于大众化的考虑,Yii_Session表实际没有作详细的性能优化,这是非常合理的。

CREATE TABLE $tableName
    (
    id CHAR(32) PRIMARY KEY,
    expire INTEGER,
    data TEXT
    )"; 

当每次打开一个session时,都会调用readSession和writeSession方法,也就是说来一个用户相当于需要执行一次数据库的读和写操作,我们看表结构为 id CHAR(32),data 这字段为TEXT,存储引擎为MyISAM,当你在线访问量并发高时,使用CDBHttpSession一个很大的性能瓶颈,但是我们可以进一步优化.

首先从表结构入手, 当每来一次请求都需要读写session, 超时后将自动清除,所以将MySQL表类型设为TYPE=HEAP(内存表), session 为六个字符, data将保存session中的数据,这里我将设为255个字符,因为我本身session里面放的数据很少,如果你放的数据很多,更改data字符大小, ip地址实际都是为数字组成,我们将切分为四个tinyint字段。

CREATE TABLE $tableName (
    sid char(6) NOT NULL DEFAULT '',   //session id
    ip1 tinyint(3) unsigned NOT NULL DEFAULT '0',
    ip2 tinyint(3) unsigned NOT NULL DEFAULT '0',
    ip3 tinyint(3) unsigned NOT NULL DEFAULT '0',
    ip4 tinyint(3) unsigned NOT NULL DEFAULT '0',
    lastactivity int(10) unsigned NOT NULL DEFAULT '0',  //最后缓存时间作为过期时间清理条件
    data varchar(255) NOT NULL DEFAULT '', //保存session数据
    UNIQUE KEY sid (sid),
    KEY uid (uid)
    ) TYPE=HEAP;"; 

完成表设计之后就只需要重写openSession,readSession,writeSession,destroySession,gcSession 即可, 我们将使用6位session id 和 ip 作为当前请求用户的唯一ID.

下面看完整代码. 我们继承自CHttpSession,扩展一个类叫HttpSession.

首先我们重写createSessionTable方法:

/**
     * Creates the session DB table.
     * @param CDbConnection $db the database connection
     * @param string $tableName the name of the table to be created
     */
     protected function createSessionTable($db,$tableName) {
          $sql=" CREATE TABLE $tableName (
          sid char(6) NOT NULL DEFAULT '',
          ip1 tinyint(3) unsigned NOT NULL DEFAULT '0',
          ip2 tinyint(3) unsigned NOT NULL DEFAULT '0',
          ip3 tinyint(3) unsigned NOT NULL DEFAULT '0',
          ip4 tinyint(3) unsigned NOT NULL DEFAULT '0',
          lastactivity int(10) unsigned NOT NULL DEFAULT '0',  //最后缓存时间作为过期时间清理条件
          data varchar(255) NOT NULL DEFAULT '', //保存session数据
          UNIQUE KEY sid (sid),
          KEY uid (uid)
          ) TYPE=HEAP;";
          $db->createCommand($sql)->execute();
   } 

init方法:

/**
      * Initializes the application component.
      * This method is required by IApplicationComponent and is invoked by application.
      */
      public function init() {
          $sid  = $this->getCookie('sid');  //从cookie中取得session id
          if (!isset($sid)) {
               $sid = $this->random(6);  //如果不存在,重新生成一个新的session id
               $this->dsetcookie('sid',$sid);  //将session id存入cookie, 24小时过期
          }
          $this->sessionID = $sid;  
          parent::init();
     } 

readSession 方法, 以6位sid和当前用户ip地址为条件作为唯的session id

/**
      * Do not call this method directly.
      * @param string $id session ID
      * @return string the session data, 特别注意,必须返回$_SESSION可以识别的字符串
      */
       public function readSession($id) {              
         $ip = $this->getIp();
          $sql="SELECT data FROM {$this->sessionTableName} WHERE sid='$id' AND CONCAT_WS('.', ip1,ip2,ip3,ip4)='$ip'";
          $data=$this->getDbConnection()->createCommand($sql)->queryScalar();
          if($data !== false) {
               return $data; //注意,此处必须返回$_SESSION特定格式的字符串.
          } else {
                return '';
          }
      } 

writeSession 方法:

$cookieSid = $this->getCookie('sid');
                    if($id != $cookieSid) {
                            $newSid = $id;
                    }
                    $sid = isset($newSid) ? $newSid : $id;
    try {
                $db=$this->getDbConnection();
                $ip = $this->getIp();
                $ips = explode('.', $ip);
                //如果已经登陆,设置超时时间,如果是游客,设为60秒.
                $onlinehold = $data['id'] ? time() + $this->timeout : time() + 60 ;
                $sql="SELECT * FROM {$this->sessionTableName} WHERE sid=:sid AND CONCAT_WS('.', ip1,ip2,ip3,ip4)=:ip";
               //.......
                if($row!==false) {   //如果已经存在,更新session id以及session数据
                      
                } else {  //插入新的记录
                   
               }
    } catch(Exception $e) {
                if(YII_DEBUG)
                    echo $e->getMessage();
                // it is too late to log an error message here
                return false;
            }
            return true; 

destroySession

public function destroySession($id) {
            $sql="DELETE FROM {$this->sessionTableName} WHERE sid=:sid AND CONCAT_WS('.', ip1,ip2,ip3,ip4)=:ip";
            $this->getDbConnection()->createCommand($sql)
                    ->bindParam(':sid',$id,PDO:"%5C%22static/image/smiley/default/tongue.gif%5C%22" smilieid="\"7\"" alt="\"\"" border="\"0\"">ARAM_STR)
                    ->bindParam(':ip',$this->getIp(),PDO:"%5C%22static/image/smiley/default/tongue.gif%5C%22" smilieid="\"7\"" alt="\"\"" border="\"0\"">ARAM_STR)
                    ->execute();
            return true;
        } 

gcSession方法

public function gcSession($maxLifetime) {
        $now = time();
        $sql="DELETE FROM {$this->sessionTableName} WHERE lastactivity < $now";
        $this->getDbConnection()->createCommand($sql)->execute();
        return true;
    } 

注:本文只作CDBHttpSession探讨,Yii 本身已经提供了 CCacheHttpSession 类,如果你的服务器支持Memcached,可以优先考虑使用Cache管理Session, 尤其在分布式集群环境。

更多详细查看HttpSession类附件.

原文出自: IT快讯网 连接: Yii CDBHttpSession数据库存储session性能优化实战

1 1
1 follower
Viewed: 14 397 times
Version: 1.1
Category: Tips
Written by: Darwin Wen
Last updated by: Darwin Wen
Created on: Aug 17, 2011
Last updated: 13 years ago
Update Article

Revisions

View all history

Related Articles