PHP的Session过期问题深入分析
PHP开发过程中,可能有朋友经常会遇到Session所产生的文件无法自动清除的问题,其实并非真的无法清除,而是有一个概率问题,只要你的站点访问量足够大,那些文件就可以自动被清除掉。如果访问量比较少,又看那些文件不顺眼的话,只要在php.ini里的配置一下即可实现Session文件自动清除的功能,具体配置如下:
找到
session.gc_probability = 1
session.gc_divisor = 1000
上面这两项参数其实就是这个概率,默认情况下是1/1000
将 session.gc_divisor = 1000 改为 session.gc_divisor = 100 即可
如果想达到完全的实时,那么可以把这个参数改为1,这样概率就是100%了
看看session如何工作的
概述:每一次php请求,会有1/100的概率(默认值)触发“session回收”。如果“session回收”发生,那就会检查/tmp/sess_*的文件,如果最后的修改时间到现在超过了1440秒(gc_maxlifetime的值),就将其删除,意味着这些session过期失效。
1. session在server端(一般是Apache with PHP module)如何存在的?
默认的,php会将session保存在/tmp目录下,文件名为这个样子:sess_01aab840166fd1dc253e3b4a3f0b8381。每一个文件对应了一个session(会话)。
more /tmp/sess_01aab840166fd1dc253e3b4a3f0b8381
username|s:9:”jiangfeng”;admin|s:1:”0〃;
#变量名|类型:长度:值
删除这里的session文件,就表示对应的session失效了。
2. session在client端(一般是浏览器)如何存在的?
session在浏览器端,只需要保存session ID(由server端生成的唯一ID)就可以了。有两种保存方式:在cookie中、在url里面。如果cookie中保存session ID,就可以看到浏览器的cookie中有一个PHPSESID变量。如果是URL传递的,就可以看到形如:
index.php?PHPSESID=01aab840166fd1dc253e3b4a3f0b8381的URL。(在server端通过session.use_cookies来控制使用哪一种方式)
3. 在server端,php如何判断session文件是否过期?
如果”最后的修改时间”到”现在”超过了gc_maxlifetime(默认是1440)秒,这个session文件就被认为是过期了,在下一次session回收的时候,如果这个文件仍然没有被更改过,这个session文件就会被删除(session就过期了)。
简单的说,如果我登录到某网站,如果在1440秒(默认值)内没有操作过,那么对应的session就认为是过期了。
所以,修改php.ini文件中的gc_maxlifetime变量就可以延长session的过期时间了:(例如,我们把过期时间修改为86400秒)
session.gc_maxlifetime = 86400
然后,重启你的web服务(一般是apache)就可以了。
注意:php5里面session过期使用了回收机制。这里设置时间为86400秒,如果session在86400秒内没有被修改过,那么在下一次“回收”时才真的被删除。
3. session“回收”何时发生?
默认情况下,每一次php请求,就会有1/100的概率发生回收,所以可能简单的理解为“每100次php请求就有一次回收发生”。这个概率是通过以下参数控制的
#概率是gc_probability/gc_divisor
session.gc_probability = 1
session.gc_divisor = 100
注意1:假设这种情况gc_maxlifetime=120,如果某个session文件最后修改时间是120秒之前,那么在下一次回收(1/100的概率)发生前,这个session仍然是有效的。
注意2:如果你的session使用session.save_path中使用别的地方保存session,session回收机制有可能不会自动处理过期session文件。这时需要定时手动(或者crontab)的删除过期的session:cd /path/to/sessions; find -cmin +24 | xargs rm
4. 一些特殊情况
因为回收机制会检查文件的“最后修改时间”,所以如果某个会话是活跃的,但是session的内容没有改变过,那么对应的session文件也就没有改变过,回收机制会认为这是一个长时间没有活跃的session而将其删除。这是我们不愿看到的,可以通过增加如下的简单代码解决这个问题:
<?php if(!isset($_SESSION['last_access'])||(time()-$_SESSION['last_access'])>60) $_SESSION['last_access'] = time(); ?>
代码会每隔60秒,尝试修改修改一次session。
总结:如果想修改session过期时间,修改变量gc_maxlifetime就可以了。php5的session采用被动的回收机制(garbage collection)。过期的session文件不会自己消失,而是通过触发“回收”来处理过期的session。
我们下面来详细看看一些其它的设置session时间的问题
Session 过期时间参数
设定过期时间参数, 主要是设定 session.gc_maxlifetime 的参数即可, 再保险一点的设定, 就设定下面这两个参数.
ini_set('session.cookie_lifetime', 0); // 可用 print_r(session_get_cookie_params()); 观察
ini_set('session.gc_maxlifetime', 3600); // 可用 echo ini_get("session.gc_maxlifetime"); 观察
session_cookie_lifetime 设为 0 的话, 代表等到 browser 才把此 cookie 清掉.(session 和 browser cookie 是有相关的)
如果懒得想这些, 直接用下面的 function 就可以了~ :P
Session 过期时间程式
下述程式转载自: Cross-Browser Session Starter
<?php
function start_session($expire = 0)
{
if ($expire == 0) {
$expire = ini_get('session.gc_maxlifetime');
} else {
ini_set('session.gc_maxlifetime', $expire);
}
if (empty($_COOKIE['PHPSESSID'])) {
session_set_cookie_params($expire);
session_start();
} else {
session_start();
setcookie('PHPSESSID', session_id(), time() + $expire);
}
}
?>
使用方式
于程式最上方加入: start_session(600); // 代表 600 秒后会过期 (取代原本 session_start())
如果要再延长过期时间, 只要再做修改即可.
但是有个问题要注意, 就是 PHP 的 session 预设是存成 file, 所以 /tmp 可能会因这样设定而爆掉(档案太多), 通常解法是把 session 存进 DB/memcache 中.
file_get_contents函数是一个可以读取本地与远程服务器文件的函数了,下面我们就来介绍利用file_get_contents做一个小的采集功能。这个倒不难,查看网页源代码的方法太多了,不知道的可以网上找找吧。查看到了,源代码,发现没找页面中的内容未在源代码中显示。接着拿出httpwatch抓包分析,在其中的另外一个链接里找到页面源代码,不过源代码是加密过的。如下:
[root@web20 php]# base64 --helpUsage: base64 [OPTION] [FILE]Base64 encode or decode FILE, or standard input, to standard output.-w, --wrap=COLS Wrap encoded lines after COLS character (default 76).Use 0 to disable line wrapping.-d, --decode Decode data.-i, --ignore-garbage When decoding, ignore non-alphabet characters.--help Display this help and exit.--version Output version information and exit.如果[文件]缺省,或者[文件]为 - ,则读取标准输入。The data are encoded as described for the base64 alphabet in RFC 3548.Decoding require compliant input by default, use --ignore-garbage to attempt to recover from non-alphabet characters (such as newlines) in the encoded stream.
%20%20%5B%E8%AF%86%E8%AE%B0%5D%E4%BC%9A%E8%AE%A1%E7%9A%84%E6%B6%B5%E4%B9%89%E6%98%AF%E4%BB%80%E4%B9%88%EF%20%20
http://www.baidu.com/s?word=%B2%E2%CA%D4&tn=sitehao123
<?php for ($i=18291; $i<=18788 ;$i++ ){ $content = file_get_contents("http://www.XXX.com/test.php?wiki_id=".$i);//echo $content; $spwt1=explode("问题:",$content); $spwt2=explode('));<',$spwt1[1]); $spdn=explode("答案:",$spwt2[1]);//echo $spwt2[0];//echo $spdn[1]; preg_match('/base64decode("(.*?)"/',$spwt2[0],$matchesw); $wen=urldecode(base64_decode($matchesw[1])); echo $wen; echo "n"; echo "n"; preg_match('/base64decode("(.*?)"/',$spdn[1],$matchesd); $da=urldecode(base64_decode($matchesd[1])); echo $da;}?>
array_search和in_array都可以用来查找值是否存在与数组中,顾名思义,一个是搜寻数组,一个是判断是否在数组,array_search和in_array有什么区别呢?以及性能上有什么差异?
in_array用法
参数 | 描述 |
---|---|
value | 必需。规定要在数组搜索的值。 |
array | 必需。规定要搜索的数组。 |
type | 可选。如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。 |
array_search示例:
$array = array(0 => 'blue', 1 => 'red', 2 => 'green', 3 => 'red');
$key = array_search('green', $array); // $key = 2;
$key = array_search('red', $array); // $key = 1;
第一个参数是要查找的值,第二个参数是数组,它返回的是查找到的值的“键”。而in_array只能判断是否存在,返回布尔值。在效率上in_array与array_search效率几乎相同,in_array仅返回真伪,array_search同时返回
array_search语法
参数 | 描述 |
---|---|
value | 必需。规定在数组中搜索的值。 |
array | 必需。被搜索的数组。 |
strict |
可选。可能的值:
如果值设置为 true,还将在数组中检查给定值的类型。(参见例子 2) |
再如:
public function add_rewrite_tag( $tag, $regex, $query ) {
$position = array_search( $tag, $this->rewritecode );
if ( false !== $position && null !== $position ) {
$this->rewritereplace[ $position ] = $regex;
$this->queryreplace[ $position ] = $query;
} else {
$this->rewritecode[] = $tag;
$this->rewritereplace[] = $regex;
$this->queryreplace[] = $query;
}
}
利用array_search返回位置,可以直接存放到需要标记位置的数组中。
Hash算法我们多少会了解一点了,下面来介绍利用php实现Hash表的一个例子,希望这些例子可以给各位带来帮助。 Hash表作为最重要的数据结构之一,也叫做散列表。使用PHP实现Hash表的功能。PHP可以模拟实现Hash表的增删改查。通过对key的映射到数组中的一个位置来访问。映射函数叫做Hash函数,存放记录的数组称为Hash表。
Hash函数把任意长度的和类型的key转换成固定长度输出。不同的key可能拥有相同的hash。
Hash表的时间复杂度为O(1)
代码如下 | 复制代码 |
<?php class HashTable{ /** /** /** public function getList(){ public function editSize($size){ |
下面对我们的HashTable进行测试。
代码如下 | 复制代码 |
<?php //测试2
|
改变了值之后可以存放更多的元素。但是仍然存在不同的key可能产生相同的hash值,那么赋值的时候后操作会覆盖前操作的问题。这种冲突的问题我们来用拉链法解决。
拉链法解决冲突。拉链法解决冲突的做法是将所有的相同Hash值的key放在一个链表中,比如key3和key14在hash之后都是0,那么在数组的键为0的地方存储这两个值,形式是链表。如果不能理解我的文字,请看下面的示例,看一下打印信息就明白了。拉链法是什么,就是链表。
创建一个HashNode类,用来存储key和value的值,并且存储相同hash的另一个元素。在同一条链上,查找越后的元素越费时。时间复杂度为O(n).
代码如下 | 复制代码 |
<?php
|
对我们新的HashTable进行测试
代码如下 | 复制代码 |
<?php //测试1 $newArr = new NewHashTable(); for($i=0; $i<30; $i++){ $newArr->set('key'.$i, 'value'.$i); } print_r($newArr->getList()); var_dump($newArr->get('key3')); //SplFixedArray Object //( // [0] => HashNode Object //( // [key] => key23 // [value] => value23 // [nextNode] => HashNode Object //( // [key] => key14 // [value] => value14 // [nextNode] => HashNode Object //( // [key] => key3 // [value] => value3 // [nextNode] => // ) // // ) // // ) // // [1] => HashNode Object //( // [key] => key24 // [value] => value24 // [nextNode] => HashNode Object //( // [key] => key15 // [value] => value15 // [nextNode] => HashNode Object //( // [key] => key4 // [value] => value4 // [nextNode] => // ) // // ) // // ) // // [2] => HashNode Object //( // [key] => key25 // [value] => value25 // [nextNode] => HashNode Object //( // [key] => key16 // [value] => value16 // [nextNode] => HashNode Object //( // [key] => key5 // [value] => value5 // [nextNode] => // ) // // ) // // ) // // [3] => HashNode Object //( // [key] => key26 // [value] => value26 // [nextNode] => HashNode Object //( // [key] => key17 // [value] => value17 // [nextNode] => HashNode Object //( // [key] => key6 // [value] => value6 // [nextNode] => // ) // // ) // // ) // // [4] => HashNode Object //( // [key] => key27 // [value] => value27 // [nextNode] => HashNode Object //( // [key] => key18 // [value] => value18 // [nextNode] => HashNode Object //( // [key] => key7 // [value] => value7 // [nextNode] => // ) // // ) // // ) // // [5] => HashNode Object //( // [key] => key28 // [value] => value28 // [nextNode] => HashNode Object //( // [key] => key19 // [value] => value19 // [nextNode] => HashNode Object //( // [key] => key8 // [value] => value8 // [nextNode] => // ) // // ) // // ) // // [6] => HashNode Object //( // [key] => key29 // [value] => value29 // [nextNode] => HashNode Object //( // [key] => key10 // [value] => value10 // [nextNode] => HashNode Object //( // [key] => key9 // [value] => value9 // [nextNode] => // ) // // ) // // ) // // [7] => HashNode Object //( // [key] => key20 // [value] => value20 // [nextNode] => HashNode Object //( // [key] => key11 // [value] => value11 // [nextNode] => HashNode Object //( // [key] => key0 // [value] => value0 // [nextNode] => // ) // // ) // // ) // // [8] => HashNode Object //( // [key] => key21 // [value] => value21 // [nextNode] => HashNode Object //( // [key] => key12 // [value] => value12 // [nextNode] => HashNode Object //( // [key] => key1 // [value] => value1 // [nextNode] => // ) // // ) // // ) // // [9] => HashNode Object //( // [key] => key22 // [value] => value22 // [nextNode] => HashNode Object //( // [key] => key13 // [value] => value13 // [nextNode] => HashNode Object //( // [key] => key2 // [value] => value2 // [nextNode] => // ) // // ) // // ) // //) //string(6) "value3" ?> |
用文件的方式读写,一个文件是索引文件,另一个文件是真实的数据文件。
索引文件分为2部分,第一部分是所有的指针,记录第二部分的位置;第二部分是索引记录。所有的索引指针:是记录所有相同Hash值的key的指针,它是一个链表结构,记录在数据文件的位置和同key的下一个值。
索引记录中:每条记录有四部分,第一部分4个字节,是下一条索引的偏移量;第二部分是该记录的key,128字节;第三部分是数据偏移量,4个字节;第四部分是数据记录长度,4个字节。
我们设定文件的存储上限为262144个。
查找流程如下:
1、根据key算出hash值,获取该hash值的链表在索引文件的第一部分(所有指针区)的位置。
2、根据步骤一的位置,获取值,时间复杂度O(1);
2、根据步骤一中的值,找到索引文件中第二部分(索引记录)的位置,也就是和key相同hash值的所有指针的链表。顺着链表查找该key,获取该key在链表中存放的数据,数据只包含该key在索引文件中的位置,时间复杂度为O(n);
3、根据步骤二所获取的key在索引文件位置,得到索引文件中存放该key的信息。信息包含在真实数据文件中存放真实数据的位置。
4、根据步骤三所获取的位置,在真实数据文件中获取数据,并返回给应用程序。
测试结果:插入10000条耗时:793ms。查找10000条耗时:149ms。虽然这效率只有Redis的十分之一。。。但是请不要在意这些细节。。。
代码做了注释,上述文字有些乱。代码只实现三个方法,一个插入(如果存在则跳过),一个是查找,一个是删除。
思路来源:《PHP核心技术与最佳实践》一书。尊重作者,转载请保留该书名。
代码如下 | 复制代码 |
<?php //成功-返回码 class DB{ /** return DB_SUCCESS; /** /** $idxoff = fstat($this->idx_fp); $datoff = fstat($this->dat_fp); $keylen = strlen($key); fseek($this->idx_fp, 0, SEEK_END); fseek($this->dat_fp, 0, SEEK_END); return DB_SUCCESS; if($found){ /** $found = false; if(!strncmp($key, $cpkey, strlen($key))){ $datalen = unpack('L', substr($block, DB_KEY_SIZE + 8, 4)); $found = true; /** $next = unpack('L', substr($block, 0, 4)); $cpkey = substr($block, 4, DB_KEY_SIZE); public function close(){ |
测试,测试添加一万条和查找一万条:
代码如下 | 复制代码 |
<?php $startTime = microtime(true); //插入测试...插入10000条:成功,耗时: 793.48206520081ms //查找测试...查找10000条:成功,耗时: 149.08313751221ms $endTime = microtime(true); |
相关文章
PHP session_start()很慢问题分析与解决办法
本文章来给各位同学介绍一下关于PHP session_start()很慢问题分析与解决办法,希望碰到此问题的同学可进入参考。 最近在做东西的时候发现一个问题 有一个接口挂...2016-11-25PHP分布式框架如何使用Memcache同步SESSION教程
本教程主要讲解PHP项目如何用实现memcache分布式,配置使用memcache存储session数据,以及memcache的SESSION数据如何同步。 至于Memcache的安装配置,我们就不讲了,以前...2016-11-25- 因此,正确的原子操作是真正被执行过的。是物理执行。在当前事务中确实能看到插入的记录。最后只不过删除了。但是AUTO_INCREMENT不会应删除而改变值。1、为什么auto_increament没有回滚?因为innodb的auto_increament的...2014-05-31
- 这篇文章主要介绍了C#中的session用法 ,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下...2020-06-25
Python3使用Selenium获取session和token方法详解
这篇文章主要介绍了Python3使用Selenium获取session和token方法详解,需要的朋友可以参考下...2021-02-17- 索引并不是时时都会生效的,比如以下几种情况,将导致索引失效: 1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因) 注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引 ...2014-06-07
- 本文主要讲述了利用Python网络爬虫对指定京东商城中指定商品下的用户评论进行爬取,对数据预处理操作后进行文本情感分析,感兴趣的朋友可以了解下...2021-05-28
- Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象。这篇文章主要介绍了underscore源码分析相关知识,感兴趣的朋友一起学习吧...2016-01-02
- 这篇文章主要介绍了MySQL8.0 密码过期策略的相关资料,帮助大家更好的理解和使用MySQL8.0的新功能,感兴趣的朋友可以了解下...2020-11-09
- session和cookie是网站浏览中较为常见的两个概念,也是比较难以辨析的两个概念,但它们在点击流及基于用户浏览行为的网站分析中却相当关键。基于网上一些文章和资料的参阅,及作者个人的应用体会,对这两个概念做一个简单的阐述...2013-09-11
- session在php中是一个非常重要的东西,像我们用户登录一般都使用到session这个东西,相对于cookie来说session 要安全很多,同时我们购物车经常使用session来做临时的记录保存哦。使用session保存页面登录信息1、数据库连接...2015-10-21
- Google是这样介绍PageRank的: Google 出类拔萃的地方在于专注开发“完美的搜索引擎”,联合创始人拉里·佩奇将这种搜索引擎定义为可“确解用户...2017-07-06
- 这篇文章主要介绍了Vue使用axios引起的后台session不同操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-14
Fatal error: Cannot redeclare class 原因分析与解决办法
我使用的都是php __autoload状态自动加载类的,今天好好的程序不知道怎么在运行时提示Fatal error: Cannot redeclare class 了,看是重复定义了类,下面我来分析一下解决办...2016-11-25- PHP的session功能,一直为许多的初学者为难。就连有些老手,有时都被搞得莫名其妙。本文,将这些问题,做一个简单的汇总,以便大家查阅。 1. 错误提示 引用 代...2016-11-25
- 要设置php生存有效时间我们可以利用session_set_cookie_params函数或修改php.ini文件哦,下面小编来介绍一下。 第一种方法:session_set_cookie_params 函数原型 voi...2016-11-25
jQuery ajax全局函数处理session过期后的ajax跳转问题
这篇文章主要介绍了基于jQuery的全局ajax函数处理session过期后的ajax操作的相关资料,需要的朋友可以参考下...2016-06-12jsp使用sessionScope获取session案例详解
这篇文章主要介绍了jsp使用sessionScope获取session案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...2021-08-29- 这篇文章主要介绍了C#实现简单获取及设置Session类,涉及C#针对session的设置及获取的技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
- 文章简单的分析了在php文件包含时inlcude的一个漏洞分析,下面希望对大家有点用处哦。 基本的文件包含漏洞: 代码如下 复制代码 <?php include...2016-11-25