V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tabris17
V2EX  ›  PHP

大多数PHP框架的缓存和Session实现都没有考虑并发同步的问题

  •  
  •   tabris17 · 2013-10-10 10:52:23 +08:00 · 10682 次点击
    这是一个创建于 3822 天前的主题,其中的信息可能已经有所发展或是发生改变。
    连Yii这种比较有名的框架也是如此,到底是作者根本没考虑到这些,还是他们觉得PHP也就用在普通web开发上不用很严谨
    60 条回复    1970-01-01 08:00:00 +08:00
    yangqi
        1
    yangqi  
       2013-10-10 11:04:32 +08:00
    这些本身不属于框架里的,一般框架都是给出最简单的一些功能, 还是需要进行二次开发的
    Dlad
        2
    Dlad  
       2013-10-10 11:39:42 +08:00
    缓存的并发同步应由缓存考虑,Session的并发同步php提供了函数,需要自己做,代码量不大。
    可能框架设计者觉得没有必要吧。
    civoic
        3
    civoic  
       2013-10-10 11:56:39 +08:00
    并发同步的什么问题?是指多用户并发时同时写同一个缓存,出现冲突的问题吗?
    mahone3297
        4
    mahone3297  
       2013-10-10 12:00:34 +08:00
    没有明白lz 的意思,说说?你是想说大并发下,数据加锁?
    raincious
        5
    raincious  
       2013-10-10 12:02:18 +08:00
    Session什么的,可以用PHP自己的session函数组,然后在php.ini里面将session储存方式修改为memcache服务器。冲突什么的交给memcache自己解决。
    tabris17
        6
    tabris17  
    OP
       2013-10-10 12:03:17 +08:00
    @yangqi 组件防止并发冲突当然是基本的要求
    @Dlad Session同步PHP没有相关内置函数的,PHP的默认的File类型Session handle是有加文件锁同步的,但是PHP内置的memcached session handle是没有同步的
    @civoic 是的。比如Yii的CFileCache实现,是不加锁的,缓存lifetime靠touch函数修改filemtime来实现,如果并发访问会有冲突
    tabris17
        7
    tabris17  
    OP
       2013-10-10 12:05:31 +08:00
    @raincious 我测试过PHP内置的memcache session handle,是没有同步的,并发时会产生冲突,只是大多数浏览器对同一站点使用一个HTTP连接同步访问的,所以问题不明显。
    luikore
        8
    luikore  
       2013-10-10 12:07:17 +08:00
    前面 nginx 再加个 http_limit 限制单 ip 并发数, 消灭并发就没并发问题了...
    yangqi
        9
    yangqi  
       2013-10-10 12:07:20 +08:00
    @tabris17 session应该算基本的组件, cache算不上基本的要求, 一般框架都只是做简单的开发, 肯定需要二次开发. session没考虑并发? 不知道LZ具体指的是什么, 给点代码会比较好
    tabris17
        10
    tabris17  
    OP
       2013-10-10 12:07:29 +08:00
    @mahone3297 是的
    tabris17
        11
    tabris17  
    OP
       2013-10-10 12:09:43 +08:00
    @luikore 这样只能解决session的同步问题
    Ever
        12
    Ever  
       2013-10-10 12:11:48 +08:00
    https://code.google.com/p/memcached/wiki/NewProgrammingFAQ#Is_memcached_atomic?

    Is memcached atomic?
    Aside from any bugs you may come across, yes all commands are internally atomic. Issuing multiple sets at the same time has no ill effect, aside from the last one in being the one that sticks.
    tabris17
        13
    tabris17  
    OP
       2013-10-10 12:17:06 +08:00
    tabris17
        14
    tabris17  
    OP
       2013-10-10 12:20:44 +08:00
    @Ever PHP session同步问题和session handle的存储类型无关,而是和PHP的session实现机制有关
    civoic
        15
    civoic  
       2013-10-10 12:24:52 +08:00
    @tabris17 filecache什么的,可以打开同一个文件同时写入的么?
    另外,你上面发的这个例子,是否要考虑服务器对于单ip的http请求是并发处理还是同步处理的情况。
    yangqi
        16
    yangqi  
       2013-10-10 12:27:43 +08:00   ❤️ 1
    @tabris17 这个和框架有什么关系, 看下面这个. 你那个例子里面应该是那个sleep(10)导致session file一直被lock, 10秒, 第二个session当然无法写入了

    http://konrness.com/php5/how-to-prevent-blocking-php-requests/
    tabris17
        17
    tabris17  
    OP
       2013-10-10 12:41:48 +08:00
    @civoic 打开同一个文件同时写当然是可以的,否则file_put_contents也不用提供LOCK_EX标志做参数了。问题不在于同时写入同一文件,而在于访问缓存必须是原子操作。
    tabris17
        18
    tabris17  
    OP
       2013-10-10 12:42:42 +08:00
    @yangqi 我已经说过了,默认file类型的session handle是用文件锁实现同步的,而memcache和sqlite作为session handle是没有同步措施的
    tabris17
        19
    tabris17  
    OP
       2013-10-10 12:44:03 +08:00
    @yangqi 而大多数框架实现的session handle也都是没有考虑同步的
    raincious
        20
    raincious  
       2013-10-10 12:46:24 +08:00
    @tabris17 嗯……四不像兄,我觉得这是你的需求太复杂了。一般框架都不会这样封装甚至是这样处理。因为这涉及到业务层的东西。我自己设计框架的时候直接将Session部分忽略了,框架用户必须自己设计适合自己的Session。

    另外如果自己封装的话,一切问题就都很好解决了。比如用直接用数据库或者直接调用memcache。

    此外个人觉得php的session并不适用各种环境,只是一种简单的解决方案。我宁愿自己写个Session管理器来处理这些东西。
    est
        21
    est  
       2013-10-10 12:48:13 +08:00
    同一个用户的session,前者还没来得及往session里写入 1,后一个请求就马上要求写入2了,是这种并发同步吗?
    tabris17
        22
    tabris17  
    OP
       2013-10-10 12:50:03 +08:00
    @est 对,导致的问题就是后一个请求写入的数据丢失,被前一个请求写入的数据覆盖了
    raincious
        23
    raincious  
       2013-10-10 12:50:54 +08:00
    @tabris17 我愚钝,但,除了这样,还能怎么办?写入之后就不给改么?
    est
        24
    est  
       2013-10-10 12:57:04 +08:00
    @tabris17 一般web framework都不会给session加锁。因为很有可能就做成deadlock了。这个时候你要反思下你的设计是不是有问题。可否避免往session里频繁写数据?

    不要啥东西都往session里边放。
    tabris17
        25
    tabris17  
    OP
       2013-10-10 12:57:06 +08:00
    @raincious 加锁啊。就像PHP默认的File类型session handle一样
    tabris17
        26
    tabris17  
    OP
       2013-10-10 12:59:59 +08:00
    @est session是不会死锁的,因为两个session是不会相互访问对方数据的。
    iodragon
        27
    iodragon  
       2013-10-10 13:07:26 +08:00
    @tabris17 我记得memcached是有锁的,memcache 3.0.4版本开始也支持加锁了,session这个本来就不是框架应该考虑的问题吧
    Dlad
        28
    Dlad  
       2013-10-10 13:10:27 +08:00
    @tabris17 不理解“session并发同步“是什么意思。
    具体到 配置session_set_save_handler 存进redis或者memcached里,会出现什么样的并发问题呢?
    raincious
        29
    raincious  
       2013-10-10 13:34:10 +08:00
    @tabris17 如果是为了防止在本次过程中的Session写入于读取不一致的问题,可以自行封装一个方法,先将Session将要写入memcache的数据存在某个变量里,读取的时候直接读取这个变量。写入方面用register_shutdown方法Late write到服务器上。

    比如这样:
    <script src="https://gist.github.com/raincious/3e5fac8b775697924f6b.js"></script>

    用法
    <script src="https://gist.github.com/raincious/6662bc0f3cf24ede7fe9.js"></script>
    raincious
        30
    raincious  
       2013-10-10 13:34:57 +08:00
    andyhuax
        32
    andyhuax  
       2013-10-10 17:25:51 +08:00
    有些框架的session类里有session_write_close,用来处理这种session并发的问题,将session资源在请求结束前提前释放。
    pubby
        33
    pubby  
       2013-10-10 18:03:18 +08:00
    用户正常浏览,加上服务器响应正常时,貌似session互斥锁没啥意义啊

    要用到同步,说明对写入数据有顺序要求,但是http协议本身就不保证这一点,

    只能说这个需求太特殊了,自己实现下吧。
    breeswish
        34
    breeswish  
       2013-10-10 18:06:01 +08:00
    首先。。优秀的代码都是在操作完SESSION之后立即session_write_close()的。。
    其次。。PHP自己的SESSION是带文件锁的。。
    Jex
        35
    Jex  
       2013-10-10 19:16:52 +08:00   ❤️ 1
    这问题怎么提出来的?
    session生成的每个ID都是唯一的,怎么可能出现其它用户覆盖你的数据的可能?
    LZ你这是在?
    Jex
        36
    Jex  
       2013-10-10 19:19:39 +08:00   ❤️ 1
    缓存本来就没有一致性、持久性等保证,怎么可能还需要考虑并发读写同一条缓存?

    LZ你没理解,你说的两个场景都是根本不需要考虑并发读写的问题
    yangqi
        37
    yangqi  
       2013-10-10 23:12:42 +08:00
    @tabris17 基本上大部分框架默认肯定是用Php自己的session, 用户要改用sqlite或者别的当然要自己二次开发了,框架不可能把所有需求都开发了,否则也不叫框架了。。。
    tabris17
        38
    tabris17  
    OP
       2013-10-11 08:57:18 +08:00
    @pubby 用户同时点开两个链接,并发请求这很常见,有些浏览器对于同一IP或站点限制的访问连接数可不止一个
    tabris17
        39
    tabris17  
    OP
       2013-10-11 09:00:33 +08:00
    @raincious 不是复杂,而是严谨。虽然并发导致的问题在web应用中发生几率很低,而即便发生了也不太会导致致命错误。所以这个问题从来不被引起重视。至于Cache的一致性问题,由于Cache使用方式的局限性,所以也不会引起致命错误。但实际上错误还是会发生的。
    tabris17
        40
    tabris17  
    OP
       2013-10-11 09:01:53 +08:00
    @breeswish 不。PHP的Sesssion handler只有内置的file handler是带同步的,而内置的sqlite handler和memcache handler都是不带同步的
    tabris17
        41
    tabris17  
    OP
       2013-10-11 09:04:27 +08:00
    @Jex 是你没理解才对。我那个链接里把问题说的很清楚了
    breeswish
        42
    breeswish  
       2013-10-11 09:11:43 +08:00
    tabris17
        43
    tabris17  
    OP
       2013-10-11 09:14:48 +08:00
    @breeswish 嗯,这个贴也是我发的
    tabris17
        44
    tabris17  
    OP
       2013-10-11 09:19:01 +08:00
    @yangqi 问题在于这些框架都没把这些组件存在的问题说清楚
    raincious
        45
    raincious  
       2013-10-11 09:41:49 +08:00
    @tabris17 框架没必要将PHP自身的问题体现出来吧。如果PHP有这样的问题,造成了框架出现这种情况,那么跟框架没有关系。

    PHP SESSION这种东西给我的第一映像就是不适合频繁读写。还是自己实现一个妥当。但,还是不能完全解决这种问题。事实上只有有状态链接的应用程序才能完全解决这个问题。
    tabris17
        46
    tabris17  
    OP
       2013-10-11 09:48:11 +08:00
    @raincious 不光是session handler实现的问题,还有大多数框架实现的文件型缓存的问题,读写均不是原子操作。比如Yii的CFileCache实现。以写入缓存操作为例:
    步骤1、file_put_contents($keyFile, $serilizedValue);
    步骤2、touch($keyFile, $lifetime); //设置文件modified time作为缓存过期时间

    如果对同一key进行并发写入,实际执行次序可能如下:

    file_put_contents($keyFile, $serilizedValue1);
    file_put_contents($keyFile, $serilizedValue2);
    touch($keyFile, $lifetime2);
    touch($keyFile, $lifetime1);

    数据1被数据2写入覆盖,但是缓存过期时间确是数据1的
    raincious
        47
    raincious  
       2013-10-11 09:58:07 +08:00
    @tabris17 说真的,我的框架也是这样的。缓存部分不实现原子读写。因为缓存本来的设定就是少写多读。所以“危害性”比Session小很多个数量级,大多数框架都“忽略了”这个问题。

    其实也不只PHP的框架,很多其他框架也都是这样的。程序原理的限制,需要花费很多逻辑来修正,关键是这些逻辑是不是值得,显然很多框架选择了不值得,于是。。。。
    est
        48
    est  
       2013-10-11 09:58:21 +08:00
    @tabris17 你说说你的使用场景?这种业务压根就不应该往session里放啊。直接做成db操作。user作为外键不是更好么。
    breeswish
        49
    breeswish  
       2013-10-11 11:28:59 +08:00
    @tabris17 噢~

    在我的项目中,本来想做一个自己的SESSION,使用类似于这样的逻辑:

    use_session(function($_SESSION)
    {

    // session operation here

    });

    包装一个session,在使用之前读取,在使用之后写入。

    后来还是没这么做,主要还是考虑到 首先并发写SESSION情况太少了,这种情况仅仅发生在用户同时开多个页面 然后每个页面都执行了很长时间以至于他们是并发的,并且每个页面都是仅仅在最后才写入SESSION从而导致了冲突;其次就是,SESSION中本来就不应该保存关键数据,保存的数据可以说都是“用户数据缓存”,所以最后还是用了Redis的无锁SESSION。不过为了减少潜在的影响,我还是将session_write_close()这种语句尽可能提前了(比如说,至少提前到了模板引擎渲染页面之前)。
    tabris17
        50
    tabris17  
    OP
       2013-10-11 11:33:37 +08:00
    @raincious
    @breeswish 好吧,看来web应用中数据的完整性和一致性确实不重要,性能和效率才是关键
    pubby
        51
    pubby  
       2013-10-11 11:34:30 +08:00
    @tabris17 "@pubby 用户同时点开两个链接,并发请求这很常见,有些浏览器对于同一IP或站点限制的访问连接数可不止一个"

    “同时”总有先后的,哪怕用户先0.000001秒点击第一个链接,但是服务器可能会先处理第二个链接。因为第二个请求头更短,服务器完全有可能先接收完先处理。既然如此,session同步还有啥意思?

    session写入应该都是原子的,保证最后一次结果正确不就可以了?
    tabris17
        52
    tabris17  
    OP
       2013-10-11 11:36:23 +08:00
    pubby
        53
    pubby  
       2013-10-11 11:52:22 +08:00
    @tabris17 "@pubby http://bbs.phpchina.com/thread-166580-1-1.html"

    虽然我没测试过上面的代码,但是我想这不是你所说的问题。

    如果浏览器都是第一次访问这个站,那么test.php会生成PHPSESSID ,test2.php也会生成PHPSESSID,这两个必然是不同的。

    但问题是test.php 有sleep(10) 所以应该是后于test2.php返回给浏览器,那么在浏览器看来最终的SESSION应该是test.php返回的PHPSESSID对应的数据
    raincious
        54
    raincious  
       2013-10-11 11:58:12 +08:00
    @breeswish 我之前也写过一个Session管理器,但我的Session写入是在程序结束过程时执行的。

    但:

    我从来不用Session存储什么重要数据,里面只有这几样:Session的Key,用户ID,发给用户的随机Key用来让服务器能够判断Session合法性。然后服务器上存好Session Key和用户ID+用户Key的对应值来做效验。

    而且Session会在用户登录的时候写入,然后每次访问读出来用这个做判断以便识别这个用户。同时,唯一频繁更新的也只是用来验证用户提交的Token。

    这样一来,所有关键数据都是可以缺失和重建的。

    至于Session所产生的数据,我在数据库里有一个内存表(tempDatas,Fields: userID, dataKey, dataVal, updatedTime),使用用户ID+Key来绑定用户站内操作所产生的数据(比如未保存的文章什么的),不过这个基本跟Session没任何关系了。
    denghongcai
        55
    denghongcai  
       2013-10-11 13:06:56 +08:00
    其他的语言就没有这种问题么?
    donnior
        56
    donnior  
       2013-10-11 13:46:18 +08:00
    @pubby @tabris17

    Session的同步,我用过的web框架里面都没有同步功能。

    session的并发是存在的,也不仅仅是一个用户同时操作了两次引起的,别忘了所谓的web2.0,还有那么多javascript脚本(诸如定时操作啥的),还有现在基本上每个开发人员都会用的Ajax请求,加上一个用户自己的操作;这样一个用户产生两个并发的请求并不是不可能,很多时候并发产生不一定非得高负载,偶然因素也颇高,所以这时候如果有需要确实要处理session同步问题。

    像Java的Servlet Specification中就明确的提到了session的线程安全问题,只不过都是让用户自己去保证。

    至于Session中到底应该放什么那就属于设计问题了。
    BOYPT
        57
    BOYPT  
       2013-10-11 13:55:07 +08:00   ❤️ 1
    1. 楼主的业务要求的严格顺序不适合使用PHP Session
    2. 因为除了file session,memcache/sqlite session都原生保证了原子操作,故不需要锁。即便是file session,也只是保证了单个写入的原子性,不保证请求发生的先后性。
    tabris17
        58
    tabris17  
    OP
       2013-10-11 14:42:43 +08:00
    @BOYPT PHP默认file handler是加文件锁的,你可以测试下
    tabris17
        59
    tabris17  
    OP
       2013-10-11 14:48:14 +08:00
    @BOYPT PHP默认file save handler从session_start()执行开始flock加锁,直到session_write_close()才释放文件锁
    ksc010
        60
    ksc010  
       2013-10-11 22:27:31 +08:00   ❤️ 1
    @tabris17 我记得 php好像对同一个会话(session)是有加锁的
    原来数据采集的时候,不能同时打开多个请求,每个请求都花费很长时间,并不是同步进行的
    不知道跟我用的框架有没有关系(session数据是存在数据库中的,肯定有锁)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1236 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 18:01 · PVG 02:01 · LAX 11:01 · JFK 14:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.