zoukankan      html  css  js  c++  java
  • php xattr操作文件扩展属性

    观摩了这篇文章后https://www.cnblogs.com/zyblog-coder/p/15013804.html 学到了php还有操作文件扩展属性的扩展

    快速安装了一下

    sudo apt-get install xattr
    sudo pecl install xattr

    然后编辑php.ini加一下扩展开启

    extension=xattr

    然后查看了一下手册学到了这几个函数

    xattr_get($file)                      获取文件存储单个key的值
    xattr_list($file)                     获取文件存储的key列表
    xattr_remove($file, $key)             移除文件存储的某个key
    xattr_set($file, $key, $value)        设置文件存储的某个key的值
    xattr_supported($file)                查看文件是否支持xattr

    然后迅速实践了一下,发现在我这个ubuntu系统里面,单个文件,如果只设置一个key,最大长度能写入4040个字符

    <?php
    $file = "test.log";
    is_file($file) || touch($file);
    
    for ($i=0; $i < 5000; $i++) { 
      $value = str_repeat("1", $i);
      $result = xattr_set($file, '0', $value);
      if(!$result){
        var_dump($i);//打印出来的是4041 说明只能存储到4040
        exit;
        break;
      }
    }

    随着你设置的key越多,那单个key能存储的上限也就逐渐降低下来。

    如果设置2个key,平均每个key最大长度能写入2008个字符

    如果设置10个key,平均每个key最大长度能写入384个字符

    如果设置26个key,平均每个key最大长度能写入136个字符

    由此可见,如果你想尽可能多的存储数据,可以只存一个key,然后把数据放入序列化存储,如果你的数据不大想快速的获取数据,你可以把每个key都存储起来,比如一张表的所有字段(当然要求这些字段不是那种很长的text blob类型)

    这个扩展属性的优势在于其写入速度很快基本上写入单个key ,value存储4040个字符的时候是0.03-0.04毫秒,读取的时候也基本上是0.04毫秒。

    读取使用代码

    <?php
    $file = "test.log";
    is_file($file) || touch($file);
    $attr_key_list = xattr_list($file);
    foreach ($attr_key_list as $attr_key) {
      $new_data[$attr_key] = xattr_get($file, $attr_key);
    }

    但是随着你的key越来越多比如你存储50个key,每个key存储很短的20个字符的小数据,你会发现写入这50个字段耗时0.2-0.4毫秒左右,读取也是0.2-0.3毫秒左右

    同等情况下50个key,每个key存储20个字符,json_encode 序列化然后file_put_contents 耗时 0.16 毫秒,file_get_contents 读取内容然后json_decode耗时0.046毫秒左右

    同等情况下50个key,每个key存储20个字符,serialize 序列化然后file_put_contents 耗时 0.15毫秒,file_get_contents 读取内容然后unserialize耗时0.031毫秒左右

    同等情况下50个key,每个key存储20个字符,serialize 序列化然后file_put_contents 耗时 0.18毫秒,file_get_contents 读取内容然后unserialize耗时0.027毫秒左右

    这里可以做一个表格

    存储方式

    1个key序列化并写入(毫秒)

    1个key读取并反序列化(毫秒) 10个key序列化并写入(毫秒) 10个key读取并反序列化(毫秒) 50个key序列化并写入(毫秒) 50个key读取并反序列化(毫秒)
    xattr key存储 0.03 0.02 0.07 0.23 0.2-0.4 0.2-0.3
    json+file_put_contents 0.15 0.03 0.16 0.028 0.16 0.046
    serialize+file_put_contents 0.14 0.021 0.11 0.017 0.15 0.031
    igbinary_serialize+file_put_contents 0.165 0.04 0.14 0.018 0.18 0.027

    我们发现多key的时候 xattr并没有什么优势,只有在少量的几个key的时候它的优势才是非常明显的,而且我发现一个奇怪的现象file_put_contents对已有文件写入反而慢,如果之前没有文件使用file_put_contents反而快。

    而xattr的操作是已有文件比较快,没有文件的话,还得自己手动新建一个文件就会比较慢。

    这说明了这种xattr存储非常适合少量key数据的写入,因为总体来看写入速度是很快的,其读取速度随着key的增加而变慢,毕竟这里用了循环读取key,而序列化的方式还是读取一次文件+序列化一次文件,其成本没有太大开销,所以在key增多的时候反而更加有优势。

    比较经典的案例场景是

    场景1:有一个总列表数据做了文件缓存,但是有时候业务场景下列表页面只想知道其数量count,这种情况下,去file_get_contents unserialize/json_decode 再count 是很不划算的,因为那样拿到了全部的数据但是又只用到count,此时可以预先在该列表数据文件扩展属性上面增加 total_count 属性,单个key读取其属性返回,根本不用操作文件本身内容,大大提高了速度,可以把一个1ms左右(打开读取并序列化取count一般这样的操作一个文件要1ms)的操作提速50倍。

    场景2:还可以用于少量信息及时存储场景,比如有数据需要及时存储投递,但是不需要后续处理,后续处理可以交给定时器异步,那么就可以用这样的方式生成一个临时文件并存储少量4000个(左右)字符内的数据,及时返回写入结果,让后续的处理交给另外的程序来解决,可以将写操作省下大量时间,毕竟写单个小文件是要0.15ms的。(更别提redis连接connect set等一系列操作了)

    让我们更加极端一些,就只写入非常简单的字符串。

    写入

    1. attr写入

    <?php
    $file = "test.log";
    $start_time = microtime(true);
    xattr_set($file, 'test_key', 'test_data');
    $end_time = microtime(true);
    echo ($end_time - $start_time) * 1000 . "  ms 
    ";//耗费时间0.03ms

    2. file_put_contents 文件写入

    <?php
    $file = "test.log";
    $start_time = microtime(true);
    file_put_contents($file, 'test_data');
    $end_time = microtime(true);
    echo ($end_time - $start_time) * 1000 . "  ms 
    ";//耗费时间0.15ms 即使是这么小的文件写入也要0.15ms

    3. redis 写入 (为了排除连接connect耗费的时间,我特意把它拿出计算时间的区间外了,只记录一次set时间)

    <?php
    $file = "test.log";
    $redis = new Redis();
    $redis->connect("127.0.0.1", 6379);
    $start_time = microtime(true);
    $redis->set($file, "test_data");
    $end_time = microtime(true);
    echo ($end_time - $start_time) * 1000 . "  ms 
    ";
    //首次set耗费时间7.5ms 后面有了key之后再set耗费时间0.1299ms

    看到了吗,写入速度是file写入的5倍!

    如果你沉迷与内存数据库写入,认为redis非常快,那你错了,redis是很快,但是再快也是有上限的,毕竟虽然大家都说每秒50000次写入,这意味着每次写入要0.02ms,可是我们在本机测试的时候并不会达到这个速度的,我本地写入redis 每秒也就30000次写入撑死了,其速度仍然低于xattr写入速度。

    由此可见,作为小数据内容存储并写入其速度是很快的,很适合高速写入。

    读取

    1. attr读取

    <?php
    $file = "test.log";
    $start_time = microtime(true);
    $attr = xattr_get($file, 'test_key');
    $end_time = microtime(true);
    echo ($end_time - $start_time) * 1000 . "  ms 
    ";//耗费时间0.026ms

    2. file_get_contents 文件读取

    <?php
    $file = "test.log";
    $start_time = microtime(true);
    $attr = file_get_contents($file);
    $end_time = microtime(true);
    echo ($end_time - $start_time) * 1000 . "  ms 
    ";//耗费时间0.034ms

    3. redis读取 (同样为了排除连接connect耗费的时间,我特意把它拿出计算时间的区间外了,只记录一次get时间)

    <?php
    $file = "test.log";
    $redis = new Redis();
    $redis->connect("127.0.0.1", 6379);
    $start_time = microtime(true);
    $attr = $redis->get($file);
    $end_time = microtime(true);
    echo ($end_time - $start_time) * 1000 . "  ms 
    ";//get耗费时间0.12ms

    读取速度快于file读取23%!而且如果是刚刚描述的场景1下该文件有大量数据,但是只想获得一个count,那就更快了,毕竟文件越大,file_get_contents 越慢,但是获取attr的速度几乎不变。

    我们看到redis的读取速度并不是那么理想,也许是我电脑给redis的配置不是最优,但是根据大家所记录的redis get 每秒钟10万次请求来看那就是单次读取0.01ms,我认为这个10万次应该不是单进程读取而是能支撑的多线程多进程读取总量,可是我们往往无法在本机达到那样的速度,这是确实让人遗憾。

    总之,综合来看,xattr速度是非常的高啊,非常适合特殊场景进行使用,推荐大家尝试!

    另外给这个https://www.cnblogs.com/zyblog-coder/p/15013804.html作者点赞,是他给我提供了一个这样的知识点和灵感

  • 相关阅读:
    vc++ 编译器工具
    lesson 17 进程间通信 孙鑫 VC++
    VC++ msdn
    VC++ 相关问题
    MySQL
    Git
    Angular
    Linux
    阿里云服务器
    Git
  • 原文地址:https://www.cnblogs.com/lizhaoyao/p/15027896.html
Copyright © 2011-2022 走看看