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 \r\n";//耗费时间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 \r\n";//耗费时间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 \r\n";
//首次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 \r\n";//耗费时间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 \r\n";//耗费时间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 \r\n";//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作者点赞,是他给我提供了一个这样的知识点和灵感

php xattr操作文件扩展属性

上一篇:阿里面试题——如何实现微信“附近的人”功能?


下一篇:fastweixin v1.3.0 发布,极速微信公众号开发框架