问题来源于 https://segmentfault.com/q/10... 这里。看了@elarity 的回答,他使用了200000的元素插入到redis集合。于是乎我使用了1百万个元素数组来插入,在我这里是内存溢出的,所以我使用了生成器的方式
function xrange() {
for ($i=0; $i<1000000; $i++) {
yield $i;
}
}
$r = xrange();
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$key = 'jimu';
$redis->del($key);
$begin = microtime(true);
$redis->sadd($key, ...$r);
$end = microtime(true);
echo ($end - $begin) . "\n";
输出结果:
[vagrant@localhost ~]$ php redis.php
1.2786898612976
[vagrant@localhost ~]$
然后redis-cli中确实有了一百万个元素。那么当我把代码中的一百万修改为一千万的时候又报内存溢出
[vagrant@localhost ~]$ php redis.php
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in /home/vagrant/redis.php on line 6
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in /home/vagrant/redis.php on line 6
根据我理解的生成器知识不应该出现内存溢出的情况。因为自始至终生成器xrange只占用一个变量($i)
内存?
所以我猜测是不是$redis->sadd($key, ...$r);
这一步的时候...$r
依然会解析成大数组。 现在不知道如何证实。
补充:
我在sadd
之前使用var_dump(...$r);exit;
发现输出的都是
int(999775)
int(999776)
int(999777)
int(999778)
int(999779)
int(999780)
int(999781)
这样可以证明生成器确实是一个一个产出值的。那么为什么将...$r传入到sadd()的时候还报内存不足呢?不明白这个...
的原理,还请大佬们指点。
好久没看到想答的问题了,来一波
a. 这个问题和redis毫无关系
b. 上代码
c. 解释
27-29-33之间,几乎没有内存占用,这是所谓的"生成器节省内存”的现象,也就是各种相关文章里都会解释的,在30行迭代生成器的时候,每次循环都会进到生成器内部去yield一次,产生一个大字符串,下次循环的时候循环变量又重新被赋值,之前的字符串自然会被GC回收,所以无论循环多大多少次,占用的内存是稳定的(包括上面的
$gen=gen()
也是几乎不占内存的)33-35,无论被调用的函数如何,甚至noop函数,都一样会占用大量内存,占用内存的量明显和次数成正比,也就是说生成器的内容被合并到一起而占用了一整块内存。这其实很容易解释,几乎的所有语言“调用函数”的过程都是类似的
(当然省略了超级多的细节,比如实参形参的映射/copy啊,内存管理啊等等什么的,和本题无关)
...$args
这个操作符其实影响的就是第一个阶段,计算参数的时候,看到...操作符,就需要展开其中的参数来形成参数列表,那么用生成器的场合,这个阶段内存就从原有生成器的少量占用变成了完整的占用了,所以即使是空的noop
函数也会占用几乎一样多的内存,你的理解是正确的回到原题的那个redis问题的话,因为重复调用redis方法一定会占用大量的额外网络开销,而一次性批量插入又铁定逃不开内存占用(其实你想redis扩展要发送这个批量的指令给redis,那么这块内存肯定是要的),比较好的方式就是分组了,每1000个或者10000个合并成一次$redis调用,mysql也好其他场景也是类似的