自从 2007年07月 ↓
2007年07月30日 | 技术一般般 | 1 Comment
你是不是也在寻找LAMP结构下的PHP连接池?如果没找到更好的,建议你试试SQL Relay.
之前我曾将写过一篇关于SQL Relay的blog,介绍了一下怎么把SQL Realy用在PEAR DB中。事实证明,这样使用并不能有效的缓解MySQL中产生大量连接的问题,因为PHP从SQL Relay得到的还是一个真实的数据库连接,也就是说PHP连接一次,SQL Relay就要提供一个,至于这个连接什么时候还给SQL Relay,还是PHP说了算,SQL Relay没有管理权,所以运行了一段时间之后SQL Relay就会一下子增加N个连接,然后崩溃。。。。。。希望你没碰到这种状况。
最近实验了一下用另外一种方式使用SQL Relay,首先建立一个SQL Relay服务器,直接连接数据库,然后PHP端只建立到SQL Relay服务器的TCP连接,并不参与对数据库连接的使用和管理,基本的原理就是PHP连接到SQL Relay服务器,传递要运行的SQL语句,SQL Relay负责把语句通过一个空闲的连接送到MySQL服务器去执行得到结果后立即断开,然后返回给PHP,这样大量的PHP请求传递给SQL Relay,后者负责去维护一个MySQL连接池,完全拥有对数据库连接的管理权。经过实验,效果比较理想。
安装和配置运行这里就不赘述了,有问题可以mail我,说一下怎么实现PHP到SQL Relay的连接。
安装成功之后,在php.ini里设置sqlrelay.so的extension信息,然后可以按照下面的方法写一个class,你可以按照getRow方法的样子去实现其他的查询方法。
class SQLR_DB
{
private $conn;
private $cur;
private function __contruct(){}//构造函数
private function _conn($server,$port,$user,$password,$retrytime,$tries)//建立连接的私有方法(server:SQL Relay的服务器
,port:端口,user:用户名,password:密码,retrytime:隔多长时间重新连接,tries:重新连接的次数)
{
$this -> conn = sqlrcon_alloc($server,$port,"",$user,$password,$retrytime,$tries);
$this -> cur = sqlrcur_alloc($this->conn);
}
private function retrieve($sql) //传递SQL语句到SQL Relay服务器,获得返回结果的方法
{
if( !$sql) return false;
$this -> _conn("server" , "port" , "user" , "password" , "retrytime", "tries"); if(!sqlrcur_sendQuery($this->cur,$sql))
{
echo sqlrcur_errorMessage($this->cur);
sqlrcur_free($this->cur); sqlrcon_free($this->conn);
return null; }
//sqlrcon_endSession($con); for($i=0; $i < sqlrcur_rowCount($this->cur); $i++)
{ $res[] = sqlrcur_getRowAssoc($this->cur,$i);
} sqlrcur_free($this->cur);
sqlrcon_free($this->conn); return $res; }
然后在数据层通过这个class来取得数据。例如:
function getOneRecord($sql)
{
if(!$sql) return false;
$sdb = new SQLR_DB();
return $sdb -> getRow($sql);
}
public function getRow($sql)//取一行的共有方法
{
if(!$sql) return false;
$res = $this -> retrieve($sql);
if("null" == $res) return null;
return $res[0];
}
}
现在的问题是,用这种方法来对付所有的读操作是可以的(用来管理所有的对Slave服务器的读操作),但是用来管理写操作的时候却遇到了问题,在向数据库中写入一条记录,并返回插入ID的时候,以前可以使用mysql_insert_id方法,可是用SQL Relay来管理数据库连接之后,每次查询都有可能使用不同的连接,因此执行完一条insert语句,再去执行mysql_insert_id的时候,使用的很可能已经不是之前的连接了,因此也就不能返回正确的结果。不知道有哪位大侠知道解决方法的,请不吝赐教,在下感激不尽:)
2007年07月24日 | 技术一般般 | 1 Comment
Solr是基于Lucene Java搜索库的企业级全文搜索引擎,目前是apache的一个项目。它的特点包括支持XML/HTTP和JSON API,支持高亮显示,支持缓存机制以及支持分布式等等。
下载和安装自己去看文档好了。我们主要研究一下它的配置和使用以及如何实现对中文分词的支持。
假设你现在已经安装好了一个带有Jetty的Solr服务器,那么它的目录结构应该是这样的:
$mySolr
|– etc
| |– LICENSE.javax.servlet.txt
| |– LICENSE.javax.xml.html
| |– LICENSE.jsse.txt
| |– admin.xml
| |– jetty-jmx.xml
| |– jetty.xml
| `– webdefault.xml
|– ext
| |– ant.jar
| |– commons-el.jar
| |– commons-logging.jar
| |– jasper-compiler.jar
| |– jasper-runtime.jar
| |– mx4j-remote.jar
| |– mx4j-tools.jar
| `– mx4j.jar
|– lib
| |– javax.servlet.jar
| |– jsp
| |– org.mortbay.jetty.jar
| `– org.mortbay.jmx.jar
|– solr
| |– README.txt
| `– conf
| |– admin-extra.html
| |– protwords.txt
| |– schema.xml
| |– scripts.conf
| |– solrconfig.xml
| |– stopwords.txt
| |– synonyms.txt
| `– xslt
| `– example.xsl
|– start.jar
`– webapps
`– mySolr
|– META-INF
| |– LICENSE.txt
| |– MANIFEST.MF
| `– NOTICE.txt
|– WEB-INF
| |– lib
| | |– apache-solr-1.2-dev-incubating.jar
| | |– commons-fileupload-1.1.1.jar
| | |– commons-io-1.2.jar
| | |– lucene-core-nightly.jar
| | |– lucene-highlighter-nightly.jar
| | |– lucene-snowball-nightly.jar
| | `– xpp3-1.1.3.4.O.jar
| `– web.xml
`– index.html
Solr是用xml文件进行配置的,其中最主要的配置文件在solr/conf/下面。
首先是schema.xml,它是用来设置你要加入索引的数据的数据类型的,首先你要定义一个FieldType,包括name,class,positionIncrementGap等等一些参数,name就是这个FieldType的名称,class指向org.apache.solr.analysis包里面对应的class名称,用来定义这个类型的行为。在FieldType定义的时候最重要的就是定义这个类型的数据在建立索引和进行查询的时候要使用的分析器analyzer,包括分词和过滤。你会看到在例子中text这个FieldType在定义的时候,在index的analyzer中使用solr.WhitespaceTokenizerFactory这个分词包,就是空格分词,然后使用solr.StopFilterFactory,solr.WordDelimiterFilterFactory,solr.LowerCaseFilterFactory,solr.EnglishPorterFilterFactory,solr.RemoveDuplicatesTokenFilterFactory这几个过滤器。在向索引库中添加text类型的索引的时候,Solr会首先用空格进行分词,然后把分词结果依次使用指定的过滤器进行过滤,最后剩下的结果才会加入到索引库中以备查询。定义完FieldType以后就是定义具体的字段(类似数据库中的字段),就是filed,filed定义包括name,type(就是之前定义过的各种FieldType),indexed(是否被索引),stored(是否被储存),multiValued(是否有多个值)等等。除此之外,你还可以定义动态字段,所谓动态字段就是不用指定具体的名称,只要定义字段名称的规则,例如定义一个dynamicField,name为*_i,定义它的type为text,那么在使用这个字段的时候,任何以_i结尾的字段都被认为是符合这个定义的,例如name_i,gender_i,school_i等。
还有solrconfig.xml,这个文件用来定义Solr的一些基本特性,最重要的一个就是可以通过更改其中的dataDir属性来指定索引文件的存放位置。
这些都定义好之后,你可以重新启动你的服务器,然后向其中添加数据。
添加数据是通过向服务器的update服务POSTxml格式的数据来实现的,xml结构是这样的add中间有很多个doc,每个doc中有很多个field。添加到索引库中的每条记录都必须指定唯一的数字id来唯一标识这条索引。建立好xml文件(例如a.xml)之后,在exampledocs目录下执行:java -jar post.jar a.xml来添加索引数据。根据实验,每个xml文件最好不要超过30M,不然会添加失败。
添加成功之后就可以通过web服务来进行查询了,查询方法是把“字段名称:要查询的值”这样的字符串传递给服务器,服务器返回xml格式的结果。例如要在一个名为user_name的字段上查询值为”test”的数据,那么传递给服务器的就是”user_name:test”.多个查询之间用”&&”连接。
返回的结果包括查询所用时间,找到的符合条件的记录数,以及根据指定的start和limit确定的部分记录(这个可以用来实现分页)。可以根据自己的需要对结果xml进行解析。查询速度还不错(当然这个和运行环境和网络环境等有关)。在同一个查询进行第二次的时候,Solr会从缓存中取得数据,只要几毫秒。
下面说说索引的删除和更新,更新很容易,只要把新的数据按照对应的唯一id重新添加一次就可以了,删除有几种情况,一是删除指定id的一条记录,那么就建立一个简单的xml片段:《delete》id:21《/delete》,如果删除某一类字段,例如删除所有user_name是john的记录,那么可以建立这样的xml片段:《delete》《query》user_name:john《/query》《/delete》。因此,如果要删除所有的索引的话,你可以把其中的user_name:john替换成*.*。然后把这个xml用添加索引同样的办法POST到服务器就可以了。当然如果想删除所有的索引,你也可以先停止服务,然后删除index目录,然后再重新启动服务:)
好了,现在可以自由的管理索引,也可以方便的进行查询了,接下来说说怎么实现对中文分词的支持。
根据前面说的,分词是在添加索引和进行查询的时候实现的,就是在定义FieldType的时候进行对应分词方法的定义。Solr默认是不支持中文分词的,但是lucene中提供了用来实现中文分词的包,按照Solr的方法包装成ChineseFilterFactory.java和ChineseTokenizerFactory.java两个类,然后在定义FieldType的时候指定使用这两个类来进行分词和过滤就可以了(这里只是思路,具体方法自己琢磨,嘿嘿)。
好了,现在可以用它来代替以前的weblucene了。hoho。
2007年07月18日 | 闲扯一篇篇 | 4 Comments
如果打平就能出线,那必然是被淘汰。
解说必然是乌鸦嘴。
2007年07月13日 | 闲扯一篇篇 | 5 Comments
什么都不用想。多好。
可是有时间的时候我却没有钱。
神阿,剥夺我的一切吧,只要让我去西藏。