PHP中的fopen文件锁定(读写器类型的情况)

时间:2010-09-21 10:21:49

标签: php locking delay fopen

我有一个场景,其中一个PHP进程每秒写入一次文件大约3次,然后有几个PHP进程正在读取此文件。

这个文件实际上是一个缓存。我们的网站有一个非常坚持的轮询,对于不断变化的数据,我们不希望每个访问者在每次轮询时都访问数据库,因此我们有一个cron进程,每秒读取数据库3次,处理数据,并将其转储到轮询客户端可以读取的文件中。

我遇到的问题是,有时,打开文件写入它需要很长时间,有时甚至长达2-3秒。我假设发生了这种情况,因为它被读取(或通过某些东西)锁定,但我没有任何确凿的方法证明这一点,而且,根据我从文档中理解的, PHP不应该锁定任何东西。 这种情况每2-5分钟发生一次,所以这很常见。

在代码中,我没有做任何类型的锁定,而且我几乎不关心该文件的信息是否被破坏,如果读取失败,或者数据是否发生了变化中间读。 但是,如果写入它需要花费2秒钟,我会非常谨慎,因为这个过程必须发生三次,现在跳过几次节拍。

我正在使用以下代码编写文件:

$handle = fopen(DIR_PUBLIC . 'filename.txt', "w");
fwrite($handle, $data);
fclose($handle);

我正在直接阅读:

file_get_contents('filename.txt')

(它不是作为静态文件直接提供给客户端,我正在获取一个常规的PHP请求来读取文件并使用它做一些基本的东西)

该文件大约为11kb,因此不需要花费大量时间来读/写。差不多1ms。

这是问题发生时的典型日志条目:

  Open File:    2657.27 ms
  Write:    0.05984 ms
  Close:    0.03886 ms

不确定它是否相关,但是读取是通过apache在常规Web请求中发生的,但是写入是由Linux的cron执行的常规“命令行”PHP执行,而不是通过Apache。

有什么想法可能会导致打开文件出现这么大的延迟? 我可以看到哪些指针帮助我查明实际原因?

或者,你能想到我能做些什么来避免这种情况吗?例如,我希望能够设置一个50ms的超时来fopen,如果它没有打开文件,它只是向前跳过,并让cron的下一次运行来处理它。

同样,我的首要任务是让cron一秒钟跳三次,其他都是次要的,所以任何想法,建议,任何事情都是非常受欢迎的。

谢谢! 丹尼尔

2 个答案:

答案 0 :(得分:3)

我可以想到3个可能的问题:

  • 文件在通过较低的php系统调用读取/写入时被锁定而不知道它。这应该阻止文件最多1 / 3s。你得到的时间更长。
  • fs cache启动fsync(),整个系统阻止对磁盘的读/写操作,直到完成为止。作为修复,您可以尝试安装更多RAM或升级内核或使用更快的硬盘
  • 你的“缓存”解决方案不是分布式的,并且它会在整个系统中每秒多次击中最糟糕的硬件......这意味着你无法通过简单地添加更多机器来进一步扩展它,只需增加硬盘速度。你应该看看memcache或APC,甚至是共享内存http://www.php.net/manual/en/function.shm-put-var.php

我能想到的解决方案:

  • 将该文件放入ramdisk http://www.cyberciti.biz/faq/howto-create-linux-ram-disk-filesystem/。这应该是避免频繁访问磁盘的最简单方法,而不需要进行其他重大更改。
  • 使用memcache。它在本地使用时非常快,它可以很好地扩展,被“大”玩家使用。 http://www.php.net/manual/en/book.memcache.php
  • 使用共享内存。它是为你在这里尝试做的而设计的......拥有一个“共享内存区”......
  • 将cron调度程序更改为更少,可能实现某种事件系统,因此它只会在必要时更新缓存,而不是按时更新
  • 使“写入”脚本写入不同的3个文件,并随机地从其中一个文件中读取“读者”。这可能允许在更多文件中“分配”loking,并且可能减少写入某个文件时锁定的可能性......但我怀疑它会带来任何显着的好处。

答案 1 :(得分:0)

如果您想保证持续的低开放时间,您应该使用真正的快速解决方案。也许您的操作系统正在进行磁盘同步,数据库文件提交或其他无法解决的问题。

我建议使用memcached,redis甚至mongoDB来完成这些任务。您甚至可以编写自己的缓存守护进程,即使在php中也是如此(但这完全没必要,而且可能很棘手)。

如果您是绝对的,请确定您只能通过此文件缓存解决此任务,并且您在Linux下,尝试使用不同的磁盘I / O调度程序,如截止日期,或者(cfq并将PHP进程优先级降低到-3 / -4)。