PHP利用MySQL保存session的实现思路及示例代码

 实现环境:

PHP 5.4.24
MySQL 5.6.19
OS X 10.9.4/Apache 2.2.26

一、代码

1 CREATE TABLE `session` (
2 `skey` char(32) CHARACTER SET ascii NOT NULL,
3 `data` text COLLATE utf8mb4_bin,
4 `expire` int(11) NOT NULL,
5 PRIMARY KEY (`skey`),
6 KEY `index_session_expire` (`expire`) USING BTREE
7 ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

 

001 <?php
002 /*
003 * 连接数据库所需的DNS、用户名、密码等,一般情况不会在代码中进行更改,
004 * 所以使用常量的形式,可以避免在函数中引用而需要global。
005 */
006 define('SESSION_DNS', 'mysql:host=localhost;dbname=db;charset=utf8mb4');
007 define('SESSION_USR', 'usr');
008 define('SESSION_PWD', 'pwd');
009 define('SESSION_MAXLIFETIME', get_cfg_var('session.gc_maxlifetime'));
010   
011 //创建PDO连接
012 //持久化连接可以提供更好的效率
013 function getConnection() {
014 try {
015 $conn = new PDO(SESSION_DNS, SESSION_USR, SESSION_PWD, array(
016 PDO::ATTR_PERSISTENT => TRUE,
017 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
018 PDO::ATTR_EMULATE_PREPARES => FALSE
019 ));
020 return $conn;
021 } catch (Exception $ex) {
022   
023 }
024 }
025   
026 //自定义的session的open函数
027 function sessionMysqlOpen($savePath, $sessionName) {
028 return TRUE;
029 }
030   
031 //自定义的session的close函数
032 function sessionMysqlClose() {
033 return TRUE;
034 }
035 /*
036 * 由于一般不会把用户提交的数据直接保存到session,所以普通情况不存在注入问题。
037 * 且处理session数据的SQL语句也不会多次使用。因此预处理功能的效益无法体现。
038 * 所以,实际工程中可以不必教条的使用预处理功能。
039 */
040 /*
041 * sessionMysqlRead()函数中,首先通过SELECT count(*)来判断sessionID是否存在。
042 * 由于MySQL数据库提供SELECT对PDOStatement::rowCount()的支持,
043 * 因此,实际的工程中可以直接使用rowCount()进行判断。
044 */
045 //自定义的session的read函数
046 //SQL语句中增加了“expire > time()”判断,用以避免读取过期的session。
047 function sessionMysqlRead($sessionId) {
048 try {
049 $dbh = getConnection();
050 $time = time();
051   
052 $sql = 'SELECT count(*) AS `count` FROM session '
053 . 'WHERE skey = ? and expire > ?';
054 $stmt = $dbh->prepare($sql);
055 $stmt->execute(array($sessionId, $time));
056 $data = $stmt->fetch(PDO::FETCH_ASSOC)['count'];
057 if ($data = 0) {
058 return '';
059 }
060   
061 $sql = 'SELECT `data` FROM `session` '
062 . 'WHERE `skey` = ? and `expire` > ?';
063 $stmt = $dbh->prepare($sql);
064 $stmt->execute(array($sessionId, $time));
065 $data = $stmt->fetch(PDO::FETCH_ASSOC)['data'];
066 return $data;
067 } catch (Exception $e) {
068 return '';
069 }
070 }
071   
072 //自定义的session的write函数
073 //expire字段存储的数据为当前时间+session生命期,当这个值小于time()时表明session失效。
074 function sessionMysqlWrite($sessionId, $data) {
075 try {
076 $dbh = getConnection();
077 $expire = time() + SESSION_MAXLIFETIME;
078   
079 $sql = 'INSERT INTO `session` (`skey`, `data`, `expire`) '
080 . 'values (?, ?, ?) '
081 . 'ON DUPLICATE KEY UPDATE data = ?, expire = ?';
082 $stmt = $dbh->prepare($sql);
083 $stmt->execute(array($sessionId, $data, $expire, $data, $expire));
084 } catch (Exception $e) {
085 echo $e->getMessage();
086 }
087 }
088   
089 //自定义的session的destroy函数
090 function sessionMysqlDestroy($sessionId) {
091 try {
092 $dbh = getConnection();
093   
094 $sql = 'DELETE FROM `session` where skey = ?';
095 $stmt = $dbh->prepare($sql);
096 $stmt->execute(array($sessionId));
097 return TRUE;
098 } catch (Exception $e) {
099 return FALSE;
100 }
101 }
102   
103 //自定义的session的gc函数
104 function sessionMysqlGc($lifetime) {
105 try {
106 $dbh = getConnection();
107   
108 $sql = 'DELETE FROM `session` WHERE expire < ?';
109 $stmt = $dbh->prepare($sql);
110 $stmt->execute(array(time()));
111 $dbh = NULL;
112 return TRUE;
113 } catch (Exception $e) {
114 return FALSE;
115 }
116 }
117   
118 //自定义的session的session id设置函数
119 /*
120 * 由于在session_start()之前,SID和session_id()均无效,
121 * 故使用$_GET[session_name()]和$_COOKIE[session_name()]进行检测。
122 * 如果此两者均为空,则表明session尚未建立,需要为新session设置session id。
123 * 通过MySQL数据库获取uuid作为session id可以更好的避免session id碰撞。
124 */
125 function sessionMysqlId() {
126 if (filter_input(INPUT_GET, session_name()) == '' and
127 filter_input(INPUT_COOKIE, session_name()) == '') {
128 try {
129 $dbh = getConnection();
130 $stmt = $dbh->query('SELECT uuid() AS uuid');
131 $data = $stmt->fetch(PDO::FETCH_ASSOC)['uuid'];
132 $data = str_replace('-', '', $data);
133 session_id($data);
134 return TRUE;
135 } catch (Exception $ex) {
136 return FALSE;
137 }
138   
139 }
140 }
141   
142 //session启动函数,包括了session_start()及其之前的所有步骤。
143 function startSession() {
144 session_set_save_handler(
145 'sessionMysqlOpen',
146 'sessionMysqlClose',
147 'sessionMysqlRead',
148 'sessionMysqlWrite',
149 'sessionMysqlDestroy',
150 'sessionMysqlGc');
151 register_shutdown_function('session_write_close');
152 sessionMysqlId();
153 session_start();
154 }

二、简介

使用MySQL保存session,需要保存三个关键性的数据:session id、session数据、session生命期。
考虑到session的使用方式,没必要使用InnoDB引擎,MyISAM引擎可以获得更好的性能。如果环境允许,可以尝试使用MEMORY引擎。
保存session数据的列,有需要的话,可以使用utf8或utf8mb4字符集;保存session id的列则没有必要,一般情况使用ascii字符集就可以了,可以节约存储成本。
保存session生命期的列,可以根据工程需要进行设计。比如datetime类型、timestamp类型、int类型。对于datetime、int类型可以保存session生成时间或过期时间。
如果有必要可以扩展session表的列并修改读、写函数以支持(维护)相关列来保存诸如用户名等信息。
当前版本,只要通过session_set_save_handler注册自定义的会话维护函数就可以,不需要在其之前使用session_module_name('user')函数。
当read函数获取数据并返回,PHP会自动对其进行反序列化,一般情况请不要对数据进行更改。
PHP传递给write函数的date参数是序列化之后的session数据,直接保存即可,一般情况请不要对数据进行更改。
按照本段代码的逻辑,PHP配置选项关于会话生命期的设置已经不再有效,这个值可以自行维护,不一定需要通过get_cfg_var获取。
sessionMysqlId()函数是为了避免大用户量、多台Web服务器情况下的碰撞,一般情况PHP自动生成的session id是可以满足用户要求的。
没了

三、需求

当用户量非常大,需要多台服务器提供应用的时候,使用MySQL存储会话相对使用会话文件具有一定的优越性。比如具有最小的存储开销,比如可以避免文件共享带来的复杂性,比如可以更好的避免发生碰撞,比如相比会话文件共享具有更好的性能。总体上来说,当访问量剧增的时候,如果使用数据库保存会话带来的问题是线性增长的,那么使用会话文件带来的问题几乎是爆炸性的。好吧,换一个更直白的说法吧:如果您的应用用户量不大,其实让PHP自己处理session就好了,没必要考虑MySQL。

时间: 2024-12-25 15:01:33

PHP利用MySQL保存session的实现思路及示例代码的相关文章

PHP利用MySQL保存session的实现思路及示例代码_php技巧

实现环境: PHP 5.4.24 MySQL 5.6.19 OS X 10.9.4/Apache 2.2.26 一.代码 CREATE TABLE `session` ( `skey` char(32) CHARACTER SET ascii NOT NULL, `data` text COLLATE utf8mb4_bin, `expire` int(11) NOT NULL, PRIMARY KEY (`skey`), KEY `index_session_expire` (`expire`

PHP实现利用MySQL保存session的方法_php技巧

session是PHP程序设计中服务器端用来保存用户信息的一个变量,具有非常广泛的应用价值.本文实例讲述了PHP实现利用MySQL保存session的方法.分享给大家供大家参考之用.具体步骤如下: 本文实例的实现环境为: PHP 5.4.24 MySQL 5.6.19 OS X 10.9.4/Apache 2.2.26 一.代码部分 1.SQL语句: CREATE TABLE `session` ( `skey` char(32) CHARACTER SET ascii NOT NULL, `d

php使用MySQL保存session会话的方法_php技巧

本文实例讲述了php使用MySQL保存session会话的方法.分享给大家供大家参考.具体分析如下: 在很多大的系统中一般都有这个功能,但是要分离出来分析,网上的资料也不太多 这里我整理了一篇发出来与大家分享 使用MySQL保存session会话较files有很多优点: 1) 有利于分布式系统,files只能保存在一台机器上 2) 有利于大访问量的系统,使用files时每个session保存在一个文件中,目录会超级大,查找session文件会比较困难. 使用MySQL保存会话首先要创建sessi

利用js动态添加删除table行的示例代码

 本篇文章主要是对利用js动态添加删除table行的示例代码进行了介绍,需要的朋友可以过来参考下,希望对大家有所帮助 如下所示:    代码如下: //动态添加行 function addRow(){    var table = document.getElementById("tableID");    var newRow = table.insertRow(); //创建新行    var newCell1 = newRow.insertCell(); //创建新单元格    

利用php抓取蜘蛛爬虫痕迹的示例代码_php实例

前言 相信许多的站长.博主可能最关心的无非就是自己网站的收录情况,一般情况下我们可以通过查看空间服务器的日志文件来查看搜索引擎到底爬取了我们哪些个页面,不过,如果用php代码分析web日志中蜘蛛爬虫痕迹,是比较好又比较直观方便操作的!下面是示例代码,有需要的朋友们下面来一起看看吧. 示例代码 <?php //获取蜘蛛爬虫名或防采集 function isSpider(){ $bots = array( 'Google' => 'googlebot', 'Baidu' => 'baidus

利用php抓取蜘蛛爬虫痕迹的示例代码

前言 相信许多的站长.博主可能最关心的无非就是自己网站的收录情况,一般情况下我们可以通过查看空间服务器的日志文件来查看搜索引擎到底爬取了我们哪些个页面,不过,如果用php代码分析web日志中蜘蛛爬虫痕迹,是比较好又比较直观方便操作的!下面是示例代码,有需要的朋友们下面来一起看看吧. 示例代码 <?php //获取蜘蛛爬虫名或防采集 function isSpider(){ $bots = array( 'Google' => 'googlebot', 'Baidu' => 'baidus

利用MySQL函数实现判断视频扩展名的代码_Mysql

复制代码 代码如下: delimiter || DROP FUNCTION IF EXISTS IS_MOBILE|| CREATE FUNCTION IS_MOBILE( x VARCHAR(255)) RETURNS TINYINT(1) BEGIN DECLARE result TINYINT(1) DEFAULT 0; SET x = LCASE(x); IF RIGHT(x,4) = '.mp4' THEN SET result = 1; ELSEIF LEFT(x,9) = '[ct

利用curl抓取远程页面内容的示例代码

利用curl抓取远程页面内容的一个小示例,需要的朋友可以过来参考下   最基本的操作如下 复制代码 代码如下: $curlPost = 'a=1&b=2';//模拟POST数据 $ch = curl_init(); curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:0.0.0.0', 'CLIENT-IP:0.0.0.0'));  //构造IP curl_setopt($ch, CURLOPT_REFERER, "ht

用PHP代替JS玩转DOM的思路及示例代码

  事情的起源比较简单,我需要把一个导航页的数据整理好写入数据库.一个比较直观的方法是对html文件进行分析,通用的方法是用php的正则表达式来匹配.但是这样做开发和维护都很困难,代码可读性非常差. 导航页的数据都是规则的排列在DOM树当中的,用JS可以用几个循环轻松的对其进行操作,而且JS需要依赖浏览器,操作数据库很困难.其实PHP就有现成的类库对DOM树种的节点进行增删改查操作,在此做一些笔记. 这里涉及到2个类 DOMDocument 和 DOMXPath. 其实思路比较明确,就是通过DO