一、php连接sphinx的长连接事宜以及sphinx的排除查询
在使用php连接sphinx时,默认的sphinx连接非长连接,于是在想php连接sphinx能否进行一些优化 publish:January 9, 2018 -Tuesday:
方法:public bool SphinxClient::open ( void ) --- 建立到搜索服务端的持久连接。此函数没有参数。成功时返回 TRUE,或者在失败时返回FALSE。在网上了解到php调用sphinx api进行长连接时,并不是我们想象中的那样会在php-fpm的fastcgi状态下一直与sphinx的searchd进程保持长连接,sphinx的api接口中open()方法仅仅提供了在一次会话请求中保证多个sphinx调用在单个php进程中是共用一个sphinx tcp连接通道,当php解释运行完,与sphinx的连接也会自动断开,而不是保持连接状态。看到这里我觉得我的业务中使用这项那就没有什么意义了,因为我的业务中基本都是连接sphinx执行一次查询然后结束。不过我在线上业务时谨慎地在一台服务器上尝试查看这种情况.
在一台WEB上打开长连接,同时观察着sphinx的连接数据(我这里中间有haproxy作为中转),如果这个长连接能实现php与searchd的长连接,那么改了一台服务器后,因为线上一直有请求,则我的WEB上所有的PHP-FPM进程就应该都能保持一个与sphinx的连接(单台WEB上是60个php进程,理论上应该proxy上的sphinx请求能增加到60左右),而如果真是在php请求结束后就断开sphinx,则不会对proxy上的数据有任何影响。使用之后发现这个数据确实没有什么变化。如下图即是我依据的数据:
而如果sphinx的长连接真的只有这个意义的话,那我感觉它没有什么意义,因为在一个php请求中,除非我们自己去关闭,否则它不会关闭(当然超时除外,但这种情况正常情况基本不会出现)。查看spinx的api看的open方法,发现其就是判断当前是否connect,如果连接了就返回false,如果没有就执行一次连接。而实际在执行query查询的时候它本身也会去调用_Connect判断并重新连接了sphinx,即表示即使在php进程执行中sphinx连接超时中断,但随后你再进行query查询,依然会自动连接sphinx.不需要保存这个长连接。sphinx的api里的open方法如下:
php
#sphinx的api中的open方法
function Open()
{
if ( $this->_socket !== false )
{
$this->_error = 'already connected';
return false;
}
if ( !$fp = $this->_Connect() )
return false;
// command, command version = 0, body length = 4, body = 1
$req = pack ( "nnNN", SEARCHD_COMMAND_PERSIST, 0, 4, 1 );
if ( !$this->_Send ( $fp, $req, 12 ) )
return false;
$this->_socket = $fp;
return true;
}
#open方法里调用的_Send方法
function _Send ( $handle, $data, $length )
{
if ( feof($handle) || fwrite ( $handle, $data, $length ) !== $length )
{
$this->_error = 'connection unexpectedly closed (timed out?)';
$this->_connerror = true;
return false;
}
return true;
}
如果不能保持php-fpm进程与sphinx的长连接,依然是每次客户端向php发送请求(即使是同一个php-fpm进程处理),仍需要执行sphinx连接的话,确实未发现这个open方法有什么意义。
在使用sphinx时,大多数情况下是进行过滤,但有时有特殊需要进行一些排它查询,比如不需要某个ID的值出来。比如:在查询某篇文章相关联的文章时,不要把这篇文章本身查询出来。如果你的article_id字段在sphinx配置文件建的是字段xmlpipe_field_string,则可以使用:
php
$query.="!@article_id ".$id; #注意@号之前的感叹号
$Sphinx->query($query)
如果你的article_id字段在sphinx配置文件建的是属性xmlpipe_attr_uint,则可以使用:
php
$Sphinx->SetFilter('article_id', array($id), true); #注意最后一个参数,其就是排除的意义
二、关于sphinx里使用SetSelect进行复杂的条件过滤或复杂查询
sphinx提供了filter过滤以及通过关键词进行query查询,但简单的query一个关键词并不能满足我们的查询要求,filter并不能支持复杂的逻辑组合运算,过滤条件都只是多重的叠加,即AND操作,并不支持。比如我们有很多文章内容及其它属性已经建好索引,如果要在一次查询中实现查询今天发表的新闻和昨天发表的非新闻文章中带有关键词新闻的内容查询出来。如果使用filter的话就没法处理了,因为filter即不能用于过滤文章类别,也不能用于过滤发表时间。
可见sphinx的filter不能用于单条件多重过滤,如上即时间不能过滤今天同时又过滤昨天,类别不能用于过滤新闻又同时过滤非新闻。这个时间我们就要考虑使用sphinx的复杂查询了。
写过SQL的话我们一定对复杂查询不陌生,sphinx的复杂查询的实现方式有点类似于自定义的SQL查询。从而筛选出我们需要的数据。写sphinx的复杂查询就相当于写mysql的where条件。
SetSelect这个api,可以实现sphinx进行自定义复杂查询条件 publish:January 12, 2018 -Friday。
bash
#(PECL sphinx >= 1.0.1)
#SphinxClient::setSelect --- Set select clause
public bool SphinxClient::setSelect ( string $clause )
#Sets the select clause, listing specific attributes to fetch, and expressions to compute and fetch.
我目前使用过的sphinx里SetSelect的几个复杂SQL写法如下,当然肯定不只这些。
1:计算数据值
有时内容的权重并不是依靠某个字段,而是依靠一个计算值,比如把文章按总热度排序,假设我们的条件是把文章的评论数以阅读数的1%相加来得出,这时我们就可以使用下面的方法来计算:
php
#根据多个字段计算数值as成某个字段(像sql)一样,然后在后面再利用这个字段,比如SetSortMode使用weighta排序
$cl->SetSelect ( "*, (views * 0.01 + comments) AS weighta" );
2**:简单if判断**
比如要查询今天发表的新闻和昨天之后发表的非新闻文章。
php
#先设置生成myfilter字段,再用filter过滤这个字段为真即可
$cl->SetSelect ( "*, ( if( ((public_time>=16843434 , 1,0) AND type=1 ) AND
((public_time>=16813434 , 1,0) AND type>1 )
) AS myfilter" );
3**:IN查询**
比如要查询PHP和sphinx以及linux等类栏目中里今天发表的文章。
php
#如下1,2,3,4分别是各类的ID,使用逗号分开直接写在过滤字段后面,
$cl->SetSelect ( "*, IF( IN(type, 1,2,3,4) AND public_time>=16843434, 1, 0) AS myfilter" );
其它:
另外查询中还可以使用@调用sphinx的数据,比如 @weight可以调用sphinx系统中的权重值,可拿来进行进一步的计算。Starting with version 0.9.9-rc2, 复杂查询中可以支持aggregate functions (AVG(), MIN(), MAX(), SUM()) are supported when using GROUP BY.