nerou/large-array-buffer 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

nerou/large-array-buffer

Composer 安装命令:

composer require nerou/large-array-buffer

包简介

Handle arrays with huge cardinality but small items with ease

README 文档

README

License PHP Version Require Version Psalm Type Coverage

This is for you, if...

...you are working with pretty large arrays that conflict with close to every memory limit you can set. The array elements on the other hand should not be that big, such that you can keep a single element in memory quite easily. If the array gets too big for memory, it will be moved to disk temporarily and transparently. You can still iterate over it as if it was a normal array.

One typical use case would be to load a lot of datasets from a database at once. (There are reasons to prefer this over running multiple queries.) See Usage below for an example for this use case using this library.

In general, you might want to try the following PHP-builtin solutions first:

  • SplFixedArray: By being of fixed length, this saves some memory compared to traditional arrays. It behaves pretty much like arrays do. Although the savings are not that huge compared to other solutions, it is probably the easiest to adopt.
  • Generator: Instead of returning an array all at once, this allows to return one item after the other. Thereby a "consumer" is able to process one item after the other accordingly. One can also terminate the "generation" of further items. One of the downsides is, that a Generator can only be iterated over once. The memory consumption can be as good as constant, but this approach is quite different and therefore rather hard to adopt and not appropriate in every scenario.

It should be noted, that a LargeArrayBuffer can be converted to both of these using toFixedArray() and toGenerator() respectively.

Install

Note: This library requires PHP 8.0+!

Use composer to install this library:

composer require nerou/large-array-buffer

There are pretty much no dependencies with some exceptions:

  • If you want to use the toJSONFile() method, you need to install ext-json (PHP's PECL JSON extension) as well.
  • If you want to use the igbinary serializer, ext-igbinary is required. See php-ext-igbinary.
  • If you want to use the msgpack serializer, ext-msgpack is required. See php-ext-msgpack.
  • If you want to use LZ4 compression, ext-lz4 is required. See php-ext-lz4.

Usage

$pdo = new PDO(/* your database credentials */);
$stmt = $pdo->query('SELECT * FROM SomeDatabaseTable', PDO::FETCH_ASSOC);

$buffer = new LargeArrayBuffer();       // explicit use of LargeArrayBuffer
$buffer = new ArrayBuffer(1000);        // wrapper using `array` until given threshold (item count) is reached,
                                        // then switching to LargeArrayBuffer

while(($dataset = $stmt->fetch()) !== false){  // load one dataset at a time
    $buffer->push($dataset);    // push this dataset to the buffer
}

// ...

foreach($buffer as $item){
    // work with your data here
}

Options

The constructor of LargeArrayBuffer provides some options:

  1. You can set the threshold when to move the data to disk. When pushing data to the buffer, it is stored in memory until it gets too large. E.g.: new LargeArrayBuffer(512); to set a 512 MiB threshold.
  2. You can choose either the PHP serializer, the igbinary serializer or the msgpack serializer (PHP serializer is default). E.g.: new LargeArrayBuffer(serializer: LargeArrayBuffer::SERIALIZER_IGBINARY);
  3. You can enable GZIP or LZ4 compression for the serialized items. Although this is recommended only if your items are pretty big like > 1 KiB each. E.g.: new LargeArrayBuffer(compression: LargeArrayBuffer::COMPRESSION_GZIP);. Note, that LZ4 compression requires ext-lz4 to be loaded.

Read from the buffer

There are several options to read the data:

  1. Iterate: As you might have seen in the example above, you can iterate over the buffer just like over an array with foreach. foreach($buffer as $item){ /* work with your data here */ }
  2. toArray(): If you have a set of buffers which do not fit in memory all together but one at a time, you can use $buffer->toArray() to get an array to work with.
  3. toJSONFile(): If you want to write the array in JSON format to some file or resource, use this method. It supports all the json_encode() options and flags.

Buffer stats

There are some stats you can obtain:

  1. count(): The number of items in the buffer.
  2. getSize(): The number of bytes of the serialized (!) data.

How it works

To put it in one sentence: This library uses php://temp as well as PHP's serialize/unserialize functions to store an array on disk if it gets too large.

Limitations and concerns

  • associative arrays are not supported
  • the item type needs to be compatible with PHP's serialize/unserialize functions
  • since storage drives (even PCIe SSDs) are a lot slower than memory and de-/serialization needs to be done, you trade hard memory overflows for performance losses

Benchmark

A benchmark with 1 million measurements (consisting of DateTimeImmutable, int and float) using PHP 8.1 with 10 iterations comparing a normal array with the LargeArrayBuffer gave the following results (LargeArrayBuffer was configured with a memory limit of 128 MiB):

Action Serializer Compression Consumed time Consumed memory Buffer size
Fill array none none 1.57 s 490.0 MiB NA
Iterate over array none none 0.27 s 492.0 MiB NA
Fill buffer PHP none 4.27 s 0 B 378.7 MiB
Iterate over buffer PHP none 2.85 s 0 B 378.7 MiB
Fill buffer PHP GZIP 24.76 s 0 B 192.5 MiB
Iterate over buffer PHP GZIP 6.79 s 0 B 192.5 MiB
Fill buffer PHP LZ4 4.89 s 0 B 241.0 MiB
Iterate over buffer PHP LZ4 2.93 s 0 B 241.0 MiB
Fill buffer igbinary none 4.26 s 0 B 319.1 MiB
Iterate over buffer igbinary none 3.41 s 0 B 319.1 MiB
Fill buffer igbinary GZIP 21.50 s 0 B 173.2 MiB
Iterate over buffer igbinary GZIP 4.80 s 0 B 173.2 MiB
Fill buffer igbinary LZ4 4.38 s 0 B 195.1 MiB
Iterate over buffer igbinary LZ4 3.17 s 0 B 195.1 MiB

Note:

  • The peak memory usage using the buffer is about its memory limit. The table shows the memory usage after the specified action.
  • PHP seems to cache the array once it is created for the first time, although unset is used. That is why I have not put the average value in the table for this specific value but the maximum (first run).
  • The serialized data is smaller than the binary data in memory. I have absolutly no idea why.

To reproduce call bench/benchmark.php.

License

This library is licensed under the MIT License. Please see LICENSE for more information.

nerou/large-array-buffer 适用场景与选型建议

nerou/large-array-buffer 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 2.26k 次下载、GitHub Stars 达 1, 最近一次更新时间为 2023 年 11 月 20 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 nerou/large-array-buffer 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 nerou/large-array-buffer 我们能提供哪些服务?
定制开发 / 二次开发

基于 nerou/large-array-buffer 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 2.26k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 19
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2023-11-20