论坛首页 编程语言技术论坛

如何保证session的一致性(session.save_handler=sqlite时的一个bug)

浏览 9470 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-12-02  
PHP
php的在页面载入时或调用session_start()时从数据源中读取session数据到$_SESSION变量。
当页面执行完毕或调用session_write_close()时把$_SESSION变量写入数据源。

php默认的session.save_handler=files,可以通过文件锁来实现读写同步,保证session数据的一致性,不会产生问题。然而当使用sqlite作为session handler时,由于没有同步机制,会产生bug。如下代码:

test1.php
session_start();
$_SESSION['data1'] = 'data1';
sleep(10);
session_write_close();



test2.php
session_start();
$_SESSION['data2'] = 'data2';
session_write_close();


在同一浏览器进程里(保证使用相同的session_id),先访问test1.php,再访问test2.php,会发现data2根本没有写入sqlite数据库中。session.save_handler=memcache还没有测试,估计也有这个问题。如果采用默认的files handler会发现test2.php的请求会被挂起直至test1.php执行完毕,这就是文件锁同步机制造成的。

我想到了两种解决方案:
1、实现互斥锁,保证同一session_id访问session数据是同步的,这样会产生一个阻塞的问题(php默认的session实现也会有这个问题)。
2、写入session数据时进行数据合并,不过这样仍然不能完全保证数据一致性。

有没有人曾经碰到过这个问题?有什么解决方案吗?
   发表时间:2009-12-03   最后修改:2009-12-03
互斥锁不行的话就使用乐观锁喽。给每条 session 加个 version,取出来时的 version 如果与写回去的 version 不一致,说明发生了冲突,此时对数据进行 merge。
0 请登录后投票
   发表时间:2009-12-03   最后修改:2009-12-03
diogin 写道
互斥锁不行的话就使用乐观锁喽。给每条 session 加个 version,取出来时的 version 如果与写回去的 version 不一致,说明发生了冲突,此时对数据进行 merge。


异步读,写入时数据合并的方法我也考虑过,但会产生脏读等问题。

比如:
if (...condition...) {
  $_SESSION['a'] = 'a';
  unset($_SESSION['b']);
} else {
  $_SESSION['b'] = 'b';
  unset($_SESSION['a']);
}


如果是同步读写的话,session数据中b或c只能存在其中一个,如果采用数据合并的有可能会导致b和c同时存在。
0 请登录后投票
   发表时间:2009-12-03  
看到你又发这儿来了。。。

我觉得可以转个弯,把session存入db时使用事务,保证原子操作。
0 请登录后投票
   发表时间:2009-12-03  
hittyo 写道
看到你又发这儿来了。。。

我觉得可以转个弯,把session存入db时使用事务,保证原子操作。


这个开销实在太大了,只能不得已为之
0 请登录后投票
   发表时间:2009-12-03   最后修改:2009-12-03
折衷吧

或者使用
session_set_save_handler("open", "close", "read", "write", "destroy", "gc");

自己定义session读写等操作。

假设session记录
select data,time,... from tab where sid=$sid
同时取最后一次写入的时间 $time
写入时判断 update tab set data=合并后的数据 where sid=$sid and time=$time



如果更新不成功则重复上述过程。
0 请登录后投票
   发表时间:2009-12-03  
自定义session handler一样存在这个问题,除非引入锁机制。
据我所知,仅依靠php内置函数只能使用文件读写锁或者IPC信号量,而IPC信号量只能在*nix下使用。很头疼啊。

数据合并依然无法避免脏读等情况,也无法完全实现数据一致性。
0 请登录后投票
   发表时间:2009-12-04  
楼主的情况,应该是每用户的多个变量。 为该用户建个表。 每次session赋值替代了表insert或update。写个类操作起来应该也很方便。

memcache解决这个问题也很不错。

session不会满足楼主的要求。
0 请登录后投票
   发表时间:2009-12-04  
取了个折中的办法:

每次读写session都调用 session_start(),session_write_close(),前后加文件读写锁,降低并发的开销,防止页面阻塞
0 请登录后投票
   发表时间:2009-12-06  
仔细想了下,这确实是个问题,之间我还真没仔细考虑过。

用“session race condition”做关键字 google 了下,找到不少文章,找个时间做个整理。
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics