关于 PHP 中巨型数据对象的内存开销问题的研究 1、是的,都要重新在内存中加载2、既然是 所有的程序文件都包含 那么应该被缓存,因为他也是 opcode 的一部分3、那就是一件很无聊的事情了,还不如优化数据结构和算法 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 1、 老大说了2、 是opcode的一部分,当然要优化3、 如果编译的时间大于按索引获取数据的时间,那么为什么要编译呢?你编译也仅仅是为了按照key去获取相应的数据,当你有办法解决按key获取数据,为什么一定要用php的array数据的结构呢?这个问题有点像,是从一个txt文件中获取数据,还是从一个包含的php来获取数据?例如 1.txt 的内容如下1111222233334444……1.php 的内容如下<?php$a = array( 1111, 2222, 3333, 4444, ……);如果是1.php的每个进程都要初始化$a,然后获取$a[3]如果是1.txt的话,那么只要优化下怎么快速获取第4(3+1)行的内容就可以了,所有的进程都可以共享,但是这里存在一个问题就是:并发得问题,怎么合理优化并发的问题?没有一个通用的方式来解决这样的问题,要么是空间换时间(400M没有并发等待时间),要么是时间换空间(4M,但是要获取数据时有等待时间) 我觉得共享内存处理大数据是有必要的,否则每次http进程都会因为你这个大数据而增加内存压力。Xcache和apc都是opcode缓存吧?也就是即使php每次不用词法解析,直接到opcode执行阶段,依然要为大数组分配内存,重点是一个http请求就分配一次内存,倘若你把这个大数组存到memcache去,那就是多个http请求共用一块内存,既然无写操作,100个读并发memcache还是没啥问题的吧,即使涉及到读写并发,memcached已经支持乐观锁了。 首先,把这个玩意儿放memcache吧需要的地方去取,就这样,就够了 你可以把这个巨型的数据块放到客户端来.用json的方法来做服务端用File_Put_Contents("?.js",$value,LOCK_EX);输出.js文件,文件格式如下:var Class=[{"_p":[1,26,"會議\/影藝展示"],"_l":["20|超大圓桌(11人以上)","21|12人大圓桌","22|16人座大圓桌","23|18人座大圓桌","24|20人座大圓桌","143|婚\/宴\/會廳","144|會議廳","145|展示廳","146|表演廳","147|影劇院","148|舞廳","149|夜總會","150|Disco Pub","151|MTV室","152|卡拉OK室","153|電視間",]},{"_p":[2,17,"新的分類1"],"_l":[""]},{"_p":[2,18,"新的分類2"],"_l":[""]},{"_p":[4,15,"交通工具"],"_l":[""]},];当客户端需要用到这些数据的时候你就使用jquery的$.getScript("?.js",function(){});读这些json数据,就行了,这样做只会消耗网络带宽,而不会占用服务器的任何内存资源. 我也是这么想滴!那个包含大量 int 值的 array 初始值,作为常量数据,肯定是 opcode 的一部分。只是我还不太确定,当在程序中用它给一个变量赋值的时候,会不会先要分配一块内存来装载它。如果不分配的话,那就是要让这个变量直接指向存储 opcode 的那段地址,那么问题又来了,存储 opcode 的块内存是否是进程间共享的呢?我也是这么想滴!直接操作缓存引擎,说到底无非也是给一个变量做赋值,貌似不会比前面“间接通过缓存 opcode 而以共享方式使用内存”的机会大多少。 没太看清楚,你的意思是,使用了 XCache 之后,内存的使用就已经被优化了?还是说,我仍然要自己考虑优化工作?你说得对,这的确是一种优化的思路。不过因为 key/value 的数量巨大,如果都拆散了,每个 key/value 都作为 cache 的一个 entry,似乎并不符合 cache 的典型用法(倒是很像 NoSQL,hehe),anyway,这已经不是本贴的讨论目标了。如果原方案实在无果,也只有考虑类似这种优化方案了。 这正是问题所在!肯定不需要“写”操作,“只读”就够了。但问题是,把这个巨型数据对象放到 memcache 去之后,固然在缓存引擎里它只会占用一份内存,可是你如何使用它呢?总要给一个变量做赋值吧?memcache 是“网络缓存”,所以一定要把数据传输到 PHP 进程中,所以也一定要新分配内存,从“期望能以共享内存的方式访问这个巨型数据对象”的角度出发,memcache 的胜算恐怕还不如 APC、XCache 这种 in-process 的缓存引擎 :) 这个就不要想了,opcode仅仅是将语法编译的步骤给省略了,例如:$a = 1 + 1;替换成opcode以后就是ZEND_ADD ~0 1 1只不过~0代替了$a, 如果真正运行的话~0还是要单独分配空间的,否则,php怎么去获取数据呢?如果另外一个进程修改这个值,是否还要影响到另外一个进程呢?是不是就不能保证每个进程的逻辑正确性呢?因为每个进程都是相同的代码,我不知道我的$a还是否是刚初始化的状态?所以opcode的缓存不会缓存数据的,仅仅是缓存另外一种代码(脚本)格式!具体的数据还是要在运行的时候重新分配内存的!关于opcode更多可以点我另外我说的那个例子(当然1.txt可以用任何工具替代,memcache、sql等),仅仅是你可以开发接口,例如读一个分配内存空间,如果要下一个内容还可以使用这点内存空间存取下一个内容,这样才能保证每个进程的内存空间的减少伪代码示例:while($data = get()) { // dosamething}来起到foreach($datas as $data){}通过加大获取一条数据的时间,来减小内存的消耗 很想加入这个话题,可惜个人水平有限,缓存什么的都不太懂,说不上什么我从另一个角度去说说个人看法——web编程是一定要考虑并发的,包括服务器连接,而不是单纯为了解决问题这也是很多搞桌面开发的人没搞懂的地方,从而引发“php很差劲”之类的说法php的优势就是快速解决简单问题,完成并扔给客户端,结束一个连接,腾空给下一个请求复杂计算其实应该用更核心的语言配合更高质量的服务器去完成一个复杂的计算消耗的资源对核心语言和高性能服务器来说都是值得的,因为主要的目的就在于计算得出结果例如nasa的服务器和上面实用的程序,都要精益求精,可能计算错小数点亿万位都会造成“火星撞地球”的结果但对信息传播网络来说损失却是巨大的,因为每消耗多一秒钟,你的信息就可能少传给一个甚至几十个人消耗内存和消耗时间都是一样,内存使用太大,也会造成tcp连接的不稳定所以,大的多的参数可能对程序的灵活度很好,适应更多的使用者,但对一般网络访问却是失败的选择应该把这些参数切割,把用户群分类,每用户可选择更少的参数达到目的就够了例如一个面向全球的网站,不是把所有语言都放在一起做参数程序自适应,而是由用户选择语言,只针对这种语言写代码如果一些复杂的计算,应该选用其他语言或控件完成某些既定过程,缩减web程序的响应过程#6说的是其中一种情况,当然这样未必合适你的需要,但他的提法是符合传播网站的业务逻辑的说了那么多,估计也没能解决你的需求,能看完我都感到荣幸…… 4M,咔咔,不算太大把它简化成一个值, 然后测测你目前的运行的php的内存峰值比比看.... 看看这段代码对你有没有帮助,无需载入文件到内存<?php/*111222333444555*/$file = new SplFileObject(__FILE__);$file->seek(3);//这里是行数,从0开始echo $file->current().'<br>';?>测试一个50M左右的xml,需时不到0.01秒——行数越大,需时越多 抱歉耽搁了这么久才来跟进这个帖子,这期间在工作之余补习了一下 opcode 相关的知识。感谢 hnxxwyq 在 #10 楼的回帖,关于 opcode 的解释很有启发性,促使我去找了个用于分析 opcode 的小工具(请参考VLD是个好东西),顺便把 build PHP under windows 也操练了一遍,hehe。实践的结果是,对于这段程序:<?php$huge_array = array(1,2,3);$elem = $huge_array['2'];?>编译出来的 opcode 是:number of ops: 10compiled vars: !0 = $huge_array, !1 = $elemline # * op fetch ext return operands--------------------------------------------------------------------------------- 2 0 > EXT_STMT 1 INIT_ARRAY ~0 1 2 ADD_ARRAY_ELEMENT ~0 2 3 ADD_ARRAY_ELEMENT ~0 3 4 ASSIGN !0, ~0 3 5 EXT_STMT 6 FETCH_DIM_R $2 !0, '2' 7 ASSIGN !1, $2 4 8 > RETURN 1 9* > ZEND_HANDLE_EXCEPTION这就很容易看清楚了,正如 hnxxwyq 所说,那个“巨型”数据对象,是由一条一条的程序码执行堆砌起来的,也就是说,缓存的只是 opcode 程序码,运行时的数据对象还是要占用新的内存空间。看来 opcode cache 肯定是指望不上了,只能寻求其它的优化方案了。————————————————————————————————基于CSDN论坛提供的插件扩展功能,自己做了个签名档工具,分享给大家,欢迎技术交流 :) to snmr_com: 感谢你的热情回帖,我认真地看了 :)很多时候务虚的思考是必要的,我基本认同你所提的大部分观点。回到我这个具体的问题,既然寄希望于缓存机制来解决内存使用瓶颈的目标落空了,接下来恐怕也只能考虑其它的优化方案了。你在 #13 楼给出的方法挺有创意,hehe。其实如果采用数据文件存储的方案,倒也不一定要把数据保存在 PHP 程序文件本身里面了,而且“按行定位”的访问方式估计也不如采用二进制存储的方式效率高。我个人更喜欢 shmop 之类的共享内存方案,出于一个成见(也许是偏见),我总觉得内存访问怎么说也比文件访问来得快。这其实是一个很开放的问题了,具体的优化方案肯定是要针对具体的需求的,我在原帖中给出的只是“简化了的问题背景”,完全不足以设计具体的优化方案,anyway,非常感谢你的热情帮助 ^_^ 1.上面的程序写在一起只是方便你测试,没看我说测试用了xml,就是说外部文件了2.快速载入数据我也倾向二进制,但二进制的问题是不能明文搜索,要搜索就要整体载入了,但SplFileObject全文搜索是不需要整体载入的——跟其他例如sscan等函数结合使用3.不是我的创意,我是学习spl过程中见到洋人讨论如何paser一个1G的SQL文件所用到的方案,借花献佛 CSDN 对连续回帖有限制,这里对前面几位朋友的回帖一并回应,见谅 :)这个可以在设计优化方案的时候考虑,多谢!memcache 的编程接口很简洁,作为一种数据存储方案,这是它的优势。但用在我说的这个问题背景下,似乎并没有解决主要矛盾。这个方法会有更适合它的场景,具体到我这个问题来说,并不合适。因为我要用到这个“巨型”数据对象的地方是“服务端业务逻辑”,而且,10M 的数据作为网页的一部分传给浏览器也不合适 :)单独一个 4M 是不大,问题我现在考虑的是在 100 并发下,如果不能“共享”的话,就将是 400M,而且每次对这些内存区块的构建和销毁也是个不容忽略的开销。是一个思路,貌似 nosql 方案也可以解决类似的问题。 如果apc缓存不了用memcache应是比较好的方案了 想不一下子都放在内存里,就排序后放到文件里。需要搜索时,用二分法进行搜索。SPL的那个方法应该大概也是如此。 感谢大家的参与,我想我最初的问题基本已经有答案了。对 #6 楼说声抱歉,CSDN 的结贴界面不知是什么逻辑,你那个回帖不让给分 :( php是干什么的 在php中插入swf的文件 为什么无法播放 想用PHP写个表单发邮件到邮箱里 代码求助 用户提交非法的JS代码叫什么攻击? 理解$val = !empty($_GET[$str]) ? $_GET[$str] : null; 看书怎么收益大?代码看一行敲一行?还是盲敲? php运行环境配置 php版的朋友,能帮这个忙吗? 关于js前端调用php类中不同方法并获取返回值的实现问题 如何检索本地文件内容? php怎么伪造表单的提交来路?
2、 是opcode的一部分,当然要优化
3、 如果编译的时间大于按索引获取数据的时间,那么为什么要编译呢?你编译也仅仅是为了按照key去获取相应的数据,当你有办法解决按key获取数据,为什么一定要用php的array数据的结构呢?这个问题有点像,是从一个txt文件中获取数据,还是从一个包含的php来获取数据?例如 1.txt 的内容如下
1111
2222
3333
4444
……1.php 的内容如下
<?php
$a = array(
1111,
2222,
3333,
4444,
……
);如果是1.php的每个进程都要初始化$a,然后获取$a[3]
如果是1.txt的话,那么只要优化下怎么快速获取第4(3+1)行的内容就可以了,所有的进程都可以共享,但是这里存在一个问题就是:并发得问题,怎么合理优化并发的问题?没有一个通用的方式来解决这样的问题,要么是空间换时间(400M没有并发等待时间),要么是时间换空间(4M,但是要获取数据时有等待时间)
用json的方法来做
服务端用File_Put_Contents("?.js",$value,LOCK_EX);输出.js文件,文件格式如下:var Class=[
{"_p":[1,26,"會議\/影藝展示"],"_l":["20|超大圓桌(11人以上)","21|12人大圓桌","22|16人座大圓桌","23|18人座大圓桌","24|20人座大圓桌","143|婚\/宴\/會廳","144|會議廳","145|展示廳","146|表演廳","147|影劇院","148|舞廳","149|夜總會","150|Disco Pub","151|MTV室","152|卡拉OK室","153|電視間",]},
{"_p":[2,17,"新的分類1"],"_l":[""]},
{"_p":[2,18,"新的分類2"],"_l":[""]},
{"_p":[4,15,"交通工具"],"_l":[""]},
];当客户端需要用到这些数据的时候你就使用jquery的
$.getScript("?.js",function(){});
读这些json数据,就行了,这样做只会消耗网络带宽,而不会占用服务器的任何内存资源.
这个就不要想了,opcode仅仅是将语法编译的步骤给省略了,例如:
$a = 1 + 1;
替换成opcode以后就是
ZEND_ADD ~0 1 1
只不过~0代替了$a, 如果真正运行的话~0还是要单独分配空间的,否则,php怎么去获取数据呢?如果另外一个进程修改这个值,是否还要影响到另外一个进程呢?是不是就不能保证每个进程的逻辑正确性呢?因为每个进程都是相同的代码,我不知道我的$a还是否是刚初始化的状态?所以opcode的缓存不会缓存数据的,仅仅是缓存另外一种代码(脚本)格式!具体的数据还是要在运行的时候重新分配内存的!关于opcode更多可以点我另外我说的那个例子(当然1.txt可以用任何工具替代,memcache、sql等),仅仅是你可以开发接口,例如读一个分配内存空间,如果要下一个内容还可以使用这点内存空间存取下一个内容,这样才能保证每个进程的内存空间的减少伪代码示例:
while($data = get()) {
// dosamething
}来起到
foreach($datas as $data){
}通过加大获取一条数据的时间,来减小内存的消耗
我从另一个角度去说说个人看法——web编程是一定要考虑并发的,包括服务器连接,而不是单纯为了解决问题
这也是很多搞桌面开发的人没搞懂的地方,从而引发“php很差劲”之类的说法php的优势就是快速解决简单问题,完成并扔给客户端,结束一个连接,腾空给下一个请求
复杂计算其实应该用更核心的语言配合更高质量的服务器去完成一个复杂的计算消耗的资源对核心语言和高性能服务器来说都是值得的,因为主要的目的就在于计算得出结果
例如nasa的服务器和上面实用的程序,都要精益求精,可能计算错小数点亿万位都会造成“火星撞地球”的结果但对信息传播网络来说损失却是巨大的,因为每消耗多一秒钟,你的信息就可能少传给一个甚至几十个人
消耗内存和消耗时间都是一样,内存使用太大,也会造成tcp连接的不稳定
所以,大的多的参数可能对程序的灵活度很好,适应更多的使用者,但对一般网络访问却是失败的选择
应该把这些参数切割,把用户群分类,每用户可选择更少的参数达到目的就够了
例如一个面向全球的网站,不是把所有语言都放在一起做参数程序自适应,而是由用户选择语言,只针对这种语言写代码如果一些复杂的计算,应该选用其他语言或控件完成某些既定过程,缩减web程序的响应过程
#6说的是其中一种情况,当然这样未必合适你的需要,但他的提法是符合传播网站的业务逻辑的
说了那么多,估计也没能解决你的需求,能看完我都感到荣幸……
/*
111
222
333
444
555*/
$file = new SplFileObject(__FILE__);
$file->seek(3);//这里是行数,从0开始
echo $file->current().'<br>';
?>测试一个50M左右的xml,需时不到0.01秒——行数越大,需时越多
<?php
$huge_array = array(1,2,3);
$elem = $huge_array['2'];
?>编译出来的 opcode 是:
number of ops: 10
compiled vars: !0 = $huge_array, !1 = $elem
line # * op fetch ext return operands
---------------------------------------------------------------------------------
2 0 > EXT_STMT
1 INIT_ARRAY ~0 1
2 ADD_ARRAY_ELEMENT ~0 2
3 ADD_ARRAY_ELEMENT ~0 3
4 ASSIGN !0, ~0
3 5 EXT_STMT
6 FETCH_DIM_R $2 !0, '2'
7 ASSIGN !1, $2
4 8 > RETURN 1
9* > ZEND_HANDLE_EXCEPTION这就很容易看清楚了,正如 hnxxwyq 所说,那个“巨型”数据对象,是由一条一条的程序码执行堆砌起来的,也就是说,缓存的只是 opcode 程序码,运行时的数据对象还是要占用新的内存空间。看来 opcode cache 肯定是指望不上了,只能寻求其它的优化方案了。
————————————————————————————————
基于CSDN论坛提供的插件扩展功能,自己做了个签名档工具,分享给大家,欢迎技术交流 :)
用memcache应是比较好的方案了