原子文件file_get_contents + file_put_contents

时间:2015-07-19 00:49:13

标签: php file-io atomic

我有一段代码将CSV日志文件截断到指定的尾随时间段。每个CSV条目中的第一个字段是时间戳。

下面正确清除,但没有自动截断日志,导致file_get_contents&之间丢失日志条目丢失的可能性。 file_put_contents。由于新条目位于文件的底部,因此在此之前不存在破坏日志的风险。

我考虑过手动执行file_get_contents& file_put_contents,但PHP文档声称这些操作做了所有类型的超级有趣的巫毒优化,并且是我想要做的推荐方法(将所有文件内容作为字符串并使用字符串填充文件),所以我很好奇有一种方法可以使用这些功能而不会不安全。

$time = time();
$fp = @fopen( $file, 'r' );
if ( $fp !== false ) {
    $truncate = false;
    $offset   = 0;

    // find the first non-expired entry
    while ( ( $fields = fgetcsv( $fp ) ) !== false ) {
        if ( ! is_null( $fields ) && $time > ( $fields[0] + $purge_interval ) ) {
            // we've reached the recent entries -- nothing beyond here will be removed
            break;
        }

        $offset   = @ftell( $fp );
        if ( false === $offset ) {
            break;
        }

        $truncate = true;
    }

    @fclose( $fp );

    if ( $truncate ) {
        // need the next two lines atomically performed...
        $data = file_get_contents( $file, false, null, $offset );
        file_put_contents( $file, $data, LOCK_EX );
    }
}

2 个答案:

答案 0 :(得分:2)

没有像这样的就地并发修改的防弹方法。该过程必须删除其中一个属性才能实现。

由于您还控制了日志编写器,因此一个简单而好的解决方案是删除绝对并发并使用flock同步对日志的访问。日志编写者会定期打开要附加到它的日志,并且它们和截断过程也会在操作期间锁定日志文件。

例如,截断实用程序会执行

if (flock($fp, LOCK_EX)) {
    $data = file_get_contents( $file, false, null, $offset );
    file_put_contents( $file, $data, LOCK_EX );
    flock($fp, LOCK_UN);
}

日志编写者也会在写入文件之前获取锁。一个兴趣点是编写者可能更喜欢尝试非阻塞锁,如果忙,则继续将日志存储在内存中,以免在未知的时间内阻塞进程;在这种情况下,将定期再次尝试该过程。

答案 1 :(得分:0)

我认为日志文件遵循"追加,只写和#34;原因是:很难同时使它们具有高性能和可编辑性。这就是为什么通常的日志文件通过cron作业在文件系统中原子旋转,以允许切断旧部件,可能压缩或最终删除它,同时允许新数据存储在新文件中。

因此,我尝试通过使用单独的文件来分隔日志条目的创建和处理它们。每天或每小时创建一个新的日志文件。新文件启动后处理旧文件。