由php的call_user_func传reference引发的思考

搞技术一定要深入到群众当中

问题的提出 网友bercmisir在院内留言,针对php手册中的call_user_func函数的文档一事,大致如下:
http://php.net/manual/en/function.call-user-func.php
其中parameter下有这样一句话:
Note: Note that the parameters for call_user_func() are not passed by reference.
简单地翻译一下,是说这个函数的参数是不能依靠引用来传递的。
还有一个例子: error_reporting(E_ALL);
function increment(&$var)
$var++;
}

$a = 0;
call_user_func('increment', $a);
echo $a."\n";

call_user_func_array('increment', array(&$a)); // You can use this instead before PHP 5.3
echo $a."\n";
?> 输出是:
0 1
而网友bercmisir的问题在于:
call_user_func('increment', $a);输出是0,而call_user_func('increment', &$a);却输出是1,明明说不能依靠引用来传递。

寻根溯源

然后再进一步寻根溯源,这个Note的信息其实是http://bugs.php.net/bug.php?id=24931这个bug中最后处理的结果。
并且在call_user_func('increment', &$a);虽然输出了1的结果,但一般情况下,会有一个警告信息:Deprecated: Call-time pass-by-reference has been deprecated。

这是什么原因呢?
先看一个例子: error_reporting(E_ALL);
function increment(&$var)
{ $var++;
$x = 1;
increment($x);
echo $x;
?> 结果为2,并且没有类似expected to be a reference, value given的警告信息,相反地,如果将第8行代码修改为&$x,将得到一个废除警告。从而得以验证,其实PHP在传递过程中,变量会根据形参需要的到底是引用还是值来自行决定传输的是引用还是值,并不需要显式地传递(相反显式传递是即将被废除的)。

继续深入

http://www.php.net/manual/en/language.references.pass.php
在php手册中,介绍引用的传递一节,在中间位置有一个Note说到:在函数调用时是不需要传引用的(也就是上节所说的显式调用),在5.3中如果显式调用会出来一个废除警告。

分析源码

有人说:在php中写入,everything is a reference。
查阅php源码,在./Zend/zend_compile.c的1579行有函数定义zend_do_pass_param。(php5.2.13)
其中有这样一句判断:
if (original_op == ZEND_SEND_REF && !CG(allow_call_time_pass_reference)) {打印废除警告。}
大概意思就是说,在传递的是引用,并且php.ini的allow_call_time_pass_reference为否的话,打印警告。
再看zend_do_pass_param使用的地方,可以发现是在parser阶段时,根据参数ZVAL结构体中元素的定义,来传递到底是var还是value还是reference。(php5.2.13 ./Zend/zend_language_parser.y/c 451/3593)

结论

引用其实类似linux里的文件硬链接一样,但和C语言中的指针是不相同的,在parser阶段php会根据上下文环境自行判断是传引用还是值。而本文所提到的call_user_function并不会自行判断传的是引用还是值。所以前面的例子call_user_function在传值的时候不管用,而在传引用的时候得出了正确结果(但其实还有一个废除警告)。

用sphinx轻松搞定方便管理的多节点过亿级数据搜索

54chen

概述 来自俄罗斯的开源全文搜索引擎软件Sphinx,单一索引最大可包含1亿条记录,在1千万条记录情况下的查询速度为0.x秒(毫秒级),实测千万级数据在0.0X秒和0.00X秒占大多数。 Sphinx创建索引的速度为:创建100万条记录的索引只需3~4分钟,实测30W线上复杂的blog数据需要5分钟,创建1000万条记录的索引可以在50分钟内完成,实测时间比这个更长得多,而只包含最新10万条记录的增量索引,重建一次只需几十秒,实测十万条在一分钟不到的时间。 Sphinx 是一个基于 GPL 2 协议颁发的免费开源的全文搜索引擎.它是专门为更好的整合脚本语言和SQL数据库而设计的.当前内置的数据源支持直接从连接到的 MySQL 或 PostgreSQL 获取数据, 或者你可以使用 XML 通道结构(XML pipe mechanism , 一种基于 Sphinx 可识别的特殊xml格式的索引通道) 。

sphinx安装 安装见 用Sphinx快速搭建站内搜索功能

配置多节点协同工作 第一点,原理
在sphinx.conf中可以配置index段落里的local和agent两个参数,local = blog_1表示使用本地索引名为blog_1的索引,agent = 10.1.1.1:3312:blog_2表示使用10.1.1.1这个机器的3312端口上服务的blog_2索引。这两个参数均可在此段落中重复出现。
利用这两个参数,可进行节点与节点间的配置。如图1所示,一个searchd服务在接到请求时两种使用索引的示意图。 sphinx 54chen 图1 一个searchd服务在接到请求时两种使用索引的示意图

第二点,架构
利用agent参数,可以灵活配置去代理取满足搜索条数的超时时间等等。
如图2,只需要将索引灵活分布,各自除了自己的local索引以外,全部连成相互的agent,使得别的节点也可以得到自己的索引搜索结果,以此得到分布式的处理结果。 sphinx 54chen 图2 一个简单的分布式搜索的例子

第三点,性能
分布索引的情况下,实测千万数据量,大致在500qps左右,没有做容灾的方案,如果想容灾,可以考虑将索引做成在某些节点上重复。总得来说,性能还算可以,更具体的架构方案,可能要和具体的业务来分布效果会更好。

一条SQL引发的对order By的思考

热 ==========尽职的安静的分隔线===========
在实际工作中遇到下面一个问题:
有一个表,存有2000万数据。
主键为ID bigint(20) NOT NULL auto_increment
另有一字段time timestamp NOT NULL default CURRENT_TIMESTAMP

故事从这两个字段说起:
sql1需要从这个表中检索出来时间为2010-05-26 11:55:00之并且id号大于20000的前10条数据
sql2需要从这个表中检索出来时间为2010-05-26 11:55:00之并且id号大于20000的前10条数据

两条sql写出来大概是这样子的:
sql1:select * from table where time 20000 order by id limit 10;
sql2:select * from table where time >'2010-05-26 11:55:00' and id>20000 order by id limit 10;

并且已经知道表中的数据,在上面所示时间之前的数据要远远多于所示时间之后的数据。如图1所示: 54chen 图1 数据在时间线上的示意图

实测发现,sql1执行时间0.03s,sql2执行时间33s。

为何大于小于运行的速度相比如何巨大?下面来解答。

第一,用explain来观察两条sql的区别 结论:没什么区别

第二,研究order by 将sql2的order by id修改为order by id desc(排序方向颠倒)后,发现速度马上提到了0.03s的水平。
同样修改sql1的时候,速度马上降到了30s的水平。

进行多次测试,排除mysql本身的缓存干扰。

结论:
sql1的运行示意图如图2所示: 54chen 图2 第一条SQL语句快慢解释图

sql2的运行示意图如图3所示: 54chen 图2 第二条SQL语句快慢解释图

综合上面两个图,mySQL在where查询的时候,也许按照where的条件,按照主键的顺序,最后满足条件的,最后进到内存中去,再进行后面的order by时,asc如果在内存中比不在内存中的就要快得多。

未研究真正实现的代码,仅凭感觉验证。
一句话概括是:按照使用的索引,最后满足条件的数据将留在内存里供进一步排序。

近期分布式相关文章汇总

号外:http://url.cn/0ThU83 infoQ的线下技术活动Qclub本周六在知春路京仪大酒店举行,我给沙龙参会者分享来自人人网nuclear的开发经验以及如何运用在RIA上.

五四陈科学院总结

1.54chen解读NoSQLDynamo 这篇文章颇费了心思从构思到成文,把dynamo原理描述得还算是清楚。

2.4月24日QCon现场直播流水帖 这篇是在QCon大会上现场记录的一些不算是太完整的笔记

4.java线程控制器代码分享-根据cpu情况决定线程运行数量和情况 一个根据cpu来执行的线程控制器实现,评论中有高人改进方案。

5.jdbc socketRead0 locked 记一个Mysql的不明bug 在开发nuclear过程中遇到的一个jdbc的bug

6.LinkIn基于Dynamo设计的系统:伏地魔(voldemort)设计中文文档[我是陈科学院译]-完稿 54chen翻译的一个来自SNS网站的分布式存储系统文档

用Sphinx快速搭建站内搜索功能

Sphinx[英] [sfɪŋks] [美] [sfɪŋks]

出自俄罗斯的开源全文搜索引擎软件Sphinx,单一索引最大可包含1亿条记录,在1千万条记录情况下的查询速度为0.x秒(毫秒级)。Sphinx创建索引的速度为:创建100万条记录的索引只需3~4分钟,创建1000万条记录的索引可以在50分钟内完成,而只包含最新10万条记录的增量索引,重建一次只需几十秒。
Sphinx 是一个基于 GPL 2 协议颁发的免费开源的全文搜索引擎.它是专门为更好的整合脚本语言和SQL数据库而设计的.当前内置的数据源支持直接从连接到的 MySQL 或 PostgreSQL 获取数据, 或者你可以使用 XML 通道结构(XML pipe mechanism , 一种基于 Sphinx 可识别的特殊xml格式的索引通道)

下面发出作记录:

10106 wget http://www.coreseek.cn/uploads/csft/3.2/csft-3.2.12.tar.gz
10107 wget http://www.coreseek.cn/uploads/csft/3.2/mmseg-3.2.12.tar.gz

10109 tar -zxvf mmseg-3.2.12.tar.gz
10110 tar -zxvf csft-3.2.12.tar.gz

10111 cd mmseg-3.2.12
10112 yum -y install glibc-common libtool autoconf automake mysql-devel expat-devel
10113 aclocal
10114 libtoolize --force
10115 automake --add-missing
10116 autoconf
10117 autoheader
10118 ./configure --prefix=/usr/local/mmseg3
10119 make
10120 make install
10121 cp -f src/*/*.h /usr/local/mmseg3/include/mmseg/

10122 cd ..
10123 ls
10124 cd csft-3.2.12
10125 aclocal
10126 libtoolize --force
10127 automake --add-missing
10128 autoconf
10129 autoheader
10130 perl -pi -e 's/lpthread/lpthread -liconv/g' src/Makefile*
10131 ./configure --prefix=/usr/local/coreseek --enable-id64 --without-python --with-mysql --with-mmseg --with-mmseg-includes=/usr/local/mmseg3/include/mmseg/ --with-mmseg-libs=/usr/local/mmseg3/lib/
10132 make make后出现iconv错误,所以修改configure文件,查找到#define USE_LIBICONV 把1改为0
重新执行configure和make

10157 make install
10158 cd /usr/local/coreseek/etc/
10164 cp sphinx.conf.dist csft.conf
10165 vim csft.conf
10169 mysql -uroot test

10173 touch /data/exceptions.txt
10174 bin/indexer --all
10177 bin/search test 下期将推出中文搜索测试以及分布式方案。

---华丽的分隔线---

向本地喜爱看电影的哥推荐:

昨天哥去UME华星提前看了电影 80后 ,严格的说,这电影和80后没有太多关系,故事还是很感人的,适合带MM看。