PHP Session存储优化:服务器端与数据库的比较与代码实例
这篇文章给大家介绍的是 PHP 中把 session 存入数据库并进行使用的相关内容(还附带了代码),具有一定的参考价值,有需求的朋友可以参考,希望能对你起到帮助作用。

实例说明:
将 SESSION 数据变量存储在服务器端是比较安全的。然而设想一下,像校内网这样日访问量过亿、拥有用户几千万的大型网站,如果把所有用户的 SESSION 数据都存储在服务器端,会消耗巨大的服务器资源。所以,程序员在制作大型网站时,将 SESSION 存储在服务器端虽然安全,但并非是最好的选择。将 SESSION 数据存储在数据库中,这样能减轻服务器的压力。并且数据也会比较安全。
有关于 php session 的相关专题进行推荐,此专题包含图文方面的内容,也包含视频方面的内容,还包含案例方面的内容。
设计过程
首先在Mysql数据库创建存储SESSION的表:
表名为t_session
表结构为
说明:session_key:是用来存会话ID的
那么什么是序列化呢?
序列化是把对象的状态信息转变为能够存储或传输的形式的过程。在序列化这个过程中,对象会把它当下的状态写入到临时或者持久性的存储区里。之后,能够通过从存储区里读取或者进行反序列化对象的状态,再次创建出这个对象。
比如说
$_SESSION[“user”]=”张三” $_SESSION[“pwd”]=”zhangsan”
登录后复制
序列话后成为一个字符串
张三的用户名为"张三",其密码为"zhangsan"。
登录后复制
其中有一种类型为 string,其数字表示字符串的长度,通过这种方式就能够对这个字符串进行操作了。
接下来就是正文部分了
session.save_handler 用于定义存储和获取与会话相关联的数据的处理器的名称。它的默认值为 files。如果将其设定为 files(session.save_handler = files),那么采用的是 php 的内置机制。如果想要自定义存储方式,例如存储到数据库中,就需要使用 session_set_save_handler()来进行自定义设置。而我们这里所说的是第二种情况,即自定义存储方式的情况。
我们需要修改 php.ini 文件中的 session_set_save_handler 的值,把它改成 user ,就像图中所示的那样。
调用 session_set_save_handler 函数时,需要传入多个可调用的参数,包括 open、close、read、write、destroy、gc 等,还可以可选地传入 create_sid、validate_sid、update_timestamp 等可调用参数。
如果不进行修改,那么在使用 session 的时候可以不去理会它。然而,当你进行了修改,就不得不去面对它。这是一个非常特殊的函数,通常情况下函数的参数都是变量,但是这个函数的参数却是 6 个函数(后面的三个参数为可选参数,可予以忽略)。不用担忧,一个一个来处理就好。
第一个参数是 open(save_path,session_name),其中的两个参数是由 php 自动进行传递的。在 session.save_handler = files 的情况下,save_path 就是 session.save_path。session_name 是服务器用于识别客户端会话的 ID。然而,如果用户自定义,这两个参数都不会被使用,只会在其中连接数据库。open 回调函数类似于类的构造函数,在会话打开时会被调用。自动开始会话或者通过调用 session_start()手动开始会话之后,第一个被调用的是回调函数。此回调函数操作成功则返回 TRUE,操作失败则返回 FALSE。
第二个参数是 close(),此函数无需参数,其作用是关闭数据库。close 回调函数与类的析构函数类似。它在 write 回调函数调用之后被调用。当调用 session_write_close()函数时,也会调用 close 回调函数。若此回调函数操作成功,则返回 TRUE;若操作失败,则返回 FALSE。
第三个参数是 read($key),其中的参数为会话 ID,它是由 php 自动传递的。传递的条件是存在会话 ID,如果不存在会话 ID,那么这个参数就会返回空字符串。需要注意的是,倘若数据库中没有对应的数据,就一定要返回空字符串,否则就会报错。如果会话中存在数据,那么 read 回调函数必须返回一个字符串,这个字符串是将会话数据进行编码(序列化)后的结果,而此处的会话数据就是从表 t_session 里取出的 session_data。在自动开始会话或者通过调用 session_start()函数手动开始会话之后,PHP 内部会调用 read 回调函数来获取会话数据。PHP 在调用 read 之前会调用 open 回调函数。read 回调返回的是序列化之后的字符串格式,此格式必须与 write 回调函数保存数据时的格式完全一致。PHP 会自动对返回的字符串进行反序列化,并将其填充到 $_SESSION 超级全局变量中。
第四个参数是 write($key,$data),这两个参数是由 php 自动传递给该函数的。其中,$key 对应着会话 ID,$data 对应着当前脚本被序列化处理器处理的 session 变量,比如上文提到的 $_SESSION["user"]="张三",$_SESSION["pwd"]="zhangsan"。序列化会话数据的过程是由 PHP 根据 session.serialize_handler 的设定值来完成的。序列化后的数据会与会话 ID 相关联并进行保存。调用 read 回调函数去获取数据时,返回的数据必须和传入 write 回调函数的数据完全一样。PHP 会在脚本执行结束或者调用 session_write_close()函数之后调用这个回调函数。注意,PHP 内部在调用完此回调函数之后,会调用 close 回调函数。
PHP 会在输出流完成写入并且关闭之后才去调用 write 回调函数。因此,在 write 回调函数中的调试信息无法输出到浏览器中。如果想要在 write 回调函数中使用调试输出,建议把调试输出写入到文件中。
第五个参数是 destroy($key)。当执行 session_destroy()函数时,或者执行 session_regenerate_id()函数且将 destroy 参数设置为 TRUE 时,就会调用这个回调函数。用于注销 session 对应的 SESSION 键值,此回调函数若操作成功则返回 TRUE,若操作失败则返回 FALSE。它是在点击注销登录时经常会用到的函数。后面会提及这个小细节。
第六个参数是 gc(expire_time)。在默认机制下,这个函数的参数就是 session.gc_maxlifetime 所设置的 session 有效时间。然而,在 user 机制下,session 的过期时间是在表中的 session_time 里,所以在此处不需要传递参数。PHP 会不时调用垃圾收集回调函数,目的是清理会话中的旧数据。调用周期由 session.gc_probability 和 session.gc_pisor 参数来控制。如果此回调函数操作成功,就会返回 TRUE;如果操作失败,就会返回 FALSE。
至此六个函数已经介绍完了,但是其中有许多需要说明的:
在 open 函数中原本要传递 save__path,其目的是在该路径下找到与 session_name 相对应的文件,接着使用 read()函数读取文件中的数据,之后通过反序列化处理器把取到的字符串进行反序列化,最后通过 php 自动填充各个 $_session 超全局变量。可以使用 write 函数将序列化的数据存入该路径下的文件。那么在非默认机制下,这里的路径难道就不需要吗?答案是肯定的。在非默认机制下调试输出 session_save_path 时,结果为空值。若未设置存储路径,被填充的$_session 变量仅能在当前页面使用,无法在其他页面使用。可进行如下测试:在另一个页面使用 session_start()函数打开会话,接着输出 session_id 和 var_dump($_session),此时得到的是上一次浏览时服务器给客户端的 session_id,而$_session 输出的是空数组。(此为验证时的大致过程)其实我想说的是,在自定义会话存储机制时,不需要自定义路径。因为如果需要存入数据库,那就说明不需要自定义路径呀。
如何能够在其他页面读取到 $_session[] 里面的值呢?
引入这个函数,也就是把六个回调函数以及 session_set_save_handler 放置到一个文件当中,接着在 session_start()之前通过 include()来引入它。
他们的执行顺序是怎样的呢?有点让人晕乎呢。总结如下:首先,会执行 session_start()函数,此函数会打开 session 操作句柄;接着,执行 read 函数来读取数据;当脚本执行结束时,会执行 write 函数,然后执行 close 函数;如果有 session_destroy()函数,那么在执行完其他函数之后会执行它。
我之前提到过,PHP 会在输出流写入完毕并且关闭之后才调用 write 回调函数,这可把我给难住啦,我在上面花费了不少时间呢,不然我也不会在 write 函数里调试那么久啦!我因此了解了 register_shutdown_function 这个函数。下面为你附上这个函数的特点:它是在执行完所有 PHP 语句之后才会调用函数,不要将其理解为在客户端关闭流或浏览器页面时调用函数。
可以这样理解调用条件:
1、当页面被用户强制停止时
2、当程序代码运行超时时
3、当PHP代码执行完成时,代码执行存在异常和错误、警告
好了以上该说的都说完了,附上代码吧:
index.php用户登录界面
您刚来过不久;然后窗口的位置链接指向了 main.php。 } } ?>
登录后复制
账户:
密码:
index_ok.php表单提交处理文件
弹出“登录成功!”;窗口的地址跳转至“main.php”。 } } ?>
登录后复制
main.php主内容页
注销";
}else{
echo "您还没登录,请先登录!";
echo "登录";
}
?>
登录后复制
session_set_save_handler.php 是一个用于自定义 session 存储机制的函数文件。
$time";
如果执行 `mysqli_query($con,$sql)` 失败,就输出 "查询失败!";如果执行成功,则继续后续操作。
如果没有 $result ,这用于检查在数据库部分出现的错误,是很有用的。
printf 输出了一个错误信息,其中包含了字符串。这个字符串是通过 mysqli_error 函数从连接对象 $con 中获取到的。具体的错误信息格式为 "Error: %s\n",其中 %s 用于占位表示实际的字符串内容。这是在 C 语言编程环境中的一种常见操作方式。
exit();
}
使用 mysqli_fetch_array($result) 获取一行数据并赋值给 $row;如果使用 or die(),将会终止后面的程序。
if($row!=false){
return($row["session_data"]);
}else{
如果是空值,那么一定要返回"",而不是 false。
}
}
//存储session
function write($key,$data){
global $con;
随着时间的推移,时间加上 60 ;需要注意的是,time()表示的是时间戳,在 mysql 中,其数据类型不能用 date、datetime、timestamp 来进行存储。
从 t_session 表中选择 session_data,条件是 session_key 等于指定的 $key 。
$re=mysqli_query($con,$sql);
从 $re 中获取数组结果,结果赋值给 $result ,获取方式为 mysqli_fetch_array 。
如果$result为 false,也就是结果为空,这表明数据库中不存在相应的 session_id,此时就进行插入操作;如果结果不为空,即便存在未过期的 session_id,也应该进行更新。
if($result==false){
$sql 用于向 t_session 表中插入数据,插入的字段包括 session_key、session_data 和 session_time。其中 session_key 的值为 $key,session_data 的值为 $data,session_time 的值为 $over_time。当插入的是字符串时需要加单引号,而插入数字时则不用加。
使用 $con 和 $sql 进行查询操作,查询结果存储在 $result 中,通过 mysqli_query 函数来实现这一过程。
if (!$result) {//用来检查出现再数据库部分的错误,很有用
printf("Error: %s\n", mysqli_error($con));//%s表示的是字符串,这是c里面的
exit();
}
}else{
具体的 SQL 语句为:update t_session set session_key='$key',session_data='$data',session_time=$over_time where session_key='$key' 。
$result=mysqli_query($con,$sql);
}
return($result);
}
清楚相应的session数据
function destroy($key){
global $con;
从 t_session 表中删除满足 session_key 等于 '$key' 的记录,执行的 SQL 语句为:delete from t_session where session_key='$key';该语句用于在数据库中执行删除操作,指定了要删除的表为 t_session ,以及删除的条件是 session_key 的值为 '$key' 。
$result=mysqli_query($con,$sql);
return($result);
}
//执行垃圾回收
// 例如 1440s
global $con;
$time=time();
从 t_session 表中删除 session_time 满足一定条件的数据,具体条件未给出。
登录后复制