昨天花了一下午的时间测试一个程序的执行效率,该程序是一个后台日志分析的程序,犯懒用php写的,毕竟php对文本处理效率很高(编写&执行),一开始担心php的数组按照键值查询效率低,所以使用memcache作为内存缓存来用。
程序编写好后执行正常,当处理数据量上来以后,发现执行很慢。其它的项目中我也发现在压力测试时memcache会是瓶颈并导致很高的cpu占用,因此怀疑是否自己编译的php+memcached有问题,或者是其它问题。于是做了下面的测试。
Trace for /logprocess/process_sessiontime_memcached.php
Total Elapsed Time = 767.74
Total System Time = 19.15
Total User Time = 742.67
Real User System secs/ cumm
%Time (excl/cumm) (excl/cumm) (excl/cumm) Calls call s/call Memory Usage Name
--------------------------------------------------------------------------------------
69.2 531.12 531.12 520.74 520.74 6.94 6.94 92646 0.0057 0.0057 0 Memcached->set
21.4 164.18 164.18 157.25 157.25 4.85 4.85 92646 0.0018 0.0018 0 Memcached->get
从上面的数据能看出,这个程序php执行的瓶颈在memcache,而set操作更加有问题,接近70%的时间都在做set操作,难道真是使用的模块有问题嘛?我查过php的memcached模块,代码基本上就是一个连接,实质使用的还是libmemcached这个库的内容,所以如果要是有问题的话也肯定在libmemcached这个上面。但网上搜索后,无果,没有人说它有问题。
后来细想,按照上面的数据折算,读写平均的话122.22次/秒,读单独是564.30次/秒,写单独是174.47次/秒,数据表现也算不错了。写慢是memcache以及被测试的程序逻辑有矛盾,我了解了一下,memcache会使用item(key+value)的长度做前期的定位,试想如果程序使得value长度不断变化,是否set会不断移动item所在的slab位置呢(将旧的变成过期,再按照新写入数据的流程写入新的item)?没有深度考究,但觉得值得怀疑;如果写操作所占用的cpu释放出来的话,读的效率应该可以更高,回头可以测试一下,估计能超过1千次/秒。
总结上述,memcache使用要被仔细的规划,取长补短,如果随意使用的话未必能有好的效果。上面的程序其实就不应该用memcache,频繁的写不是memcache的强项,而分布式、多点频繁读取才是强项。
说道缓存,所以我顺便又测试了Alternative PHP Cache(APC)的模式,还是刚才的程序,出来的结果是读写平均392.57次/秒,比memcache快3倍多,我没有出读写分离的数据,估计如果单读的话,应该还能再快一些,毕竟APC是本地缓存。因此,如果你的应用不需要分布式的话,APC是一个很好的选择,它能帮你在一台机器的情况下,保持并缓存每次php请求之间的数据。其次,它还是一个类似eAccelerator的代码opcode缓存模块,可以提高php代码执行速度。
最后,回到我一开始没有考虑的解决方案,因为我不需要跟其它进程或者服务器共享我的数据,所以直接用php的数组,处理速度是多少呢?15,441次/秒!当然啦,内部就是一堆指针指来指去而已,我没有想到的是数组在很多无序的key中找value还是很快的,所以以后大家可以放心这么使用,只是注意配置文件中允许的php运行时占用的内存再调大点就好了。
总结,任何设计要根据具体的应用需求来选择解决方案,选择时要考虑所选技术本身的特性,取其优点避其缺点。Memcache不适合做数据频繁变化的内存共享,一次写入多次读取的模式会更适合一些。Tokyo Tyrant / Tokyo Cabinet / Redis也许能用于频繁读写的情况,我还没有时间去评测;BTW,以前还使用Mysql内存表做这类事情多一点,希望能构建一个数据写入缓存机制,但是实际使用时还是有一些问题,所以有空再找找新的解决方案吧。
最后说一句,就一句:在Memcache盛行的当今,Memcache不可滥用。












