php+中文分词scws+sphinx+mysql打造千万级数据全文搜索

Posted cn三少nginx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了php+中文分词scws+sphinx+mysql打造千万级数据全文搜索相关的知识,希望对你有一定的参考价值。

Sphinx是由俄罗斯人Andrew Aksyonoff开发的一个全文检索引擎。意图为其他应用提供高速、低空间占用、高结果 相关度的全文搜索功能。Sphinx可以非常容易的与SQL数据库和脚本语言集成。当前系统内置mysql和PostgreSQL 数据库数据源的支持,也支持从标准输入读取特定格式 的XML数据。
Sphinx创建索引的速度为:创建100万条记录的索引只需3~4分钟,创建1000万条记录的索引可以在50分钟内完成,而只包含最新10万条记录的增量索引,重建一次只需几十秒。
Sphinx的特性如下:
a)  高速的建立索引(在当代CPU上,峰值性能可达到10 MB/秒);
b)  高性能的搜索(在2 – 4GB 的文本数据上,平均每次检索响应时间小于0.1秒);
c)  可处理海量数据(目前已知可以处理超过100 GB的文本数据, 在单一CPU的系统上可处理100 M 文档);
d)  提供了优秀的相关度算法,基于短语相似度和统计(BM25)的复合Ranking方法;
e)  支持分布式搜索;
f)  支持短语搜索
g)  提供文档摘要生成
h)  可作为MySQL的存储引擎提供搜索服务;
i)  支持布尔、短语、词语相似度等多种检索模式;
j)  文档支持多个全文检索字段(最大不超过32个);
k)  文档支持多个额外的属性信息(例如:分组信息,时间戳等);
l)  支持断词;
虽然mysql的MYISAM提供全文索引,但是性能却不敢让人恭维


开始搭建

系统环境:centos6.5+php5.6+apache+MySQL

1、安装依赖包

[php] view plain copy
 
  1. yum -y install make gcc g++ gcc-c++ libtool autoconf automake imake php-devel mysql-devel libxml2-devel expat-devel  


2、安装Sphinx

[php] view plain copy
 
  1. yum install expat expat-devel  
  2. wget -c http://sphinxsearch.com/files/sphinx-2.0.7-release.tar.gz  
  3. tar zxvf sphinx-2.0.7-release.tar.gz  
  4. cd sphinx-2.0.7-release  
  5. ./configure --prefix=/usr/local/sphinx  --with-mysql --with-libexpat --enable-id64  
  6. make && make install  

 

3、安装libsphinxclient,PHP扩展用到

 

[php] view plain copy
 
  1. cd api/libsphinxclient  
  2. ./configure --prefix=/usr/local/sphinx/libsphinxclient  
  3. make && make install  


4、安装Sphinx的PHP扩展:我的是5.6需装sphinx-1.3.3.tgz,如果是php5.4以下可sphinx-1.3.0.tgz

 

 

[php] view plain copy
 
  1. wget -c http://pecl.php.net/get/sphinx-1.3.3.tgz  
  2. tar zxvf sphinx-1.3.3.tgz  
  3. cd sphinx-1.3.3  
  4. phpize  
  5. ./configure --with-sphinx=/usr/local/sphinx/libsphinxclient/ --with-php-config=/usr/bin/php-config  
  6. make && make install  
  7. 成功后会提示:  
  8. Installing shared extensions:     /usr/lib64/php/modules/  
  9.   
  10.   
  11. echo "[Sphinx]" >> /etc/php.ini  
  12. echo "extension = sphinx.so" >> /etc/php.ini  
  13. #重启apache  
  14. service httpd restart  


5、创建测试数据

 

 

[php] view plain copy
 
  1. CREATE TABLE IF NOT EXISTS `items` (  
  2.   `id` int(11) NOT NULL AUTO_INCREMENT,  
  3.   `title` varchar(255) NOT NULL,  
  4.   `content` text NOT NULL,  
  5.   `created` datetime NOT NULL,  
  6.   PRIMARY KEY (`id`)  
  7. ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT=‘全文检索测试的数据表‘ AUTO_INCREMENT=11 ;  
  8.   
  9. INSERT INTO `items` (`id`, `title`, `content`, `created`) VALUES  
  10. (1, ‘linux mysql集群安装‘, ‘MySQL Cluster 是MySQL 适合于分布式计算环境的高实用、可拓展、高性能、高冗余版本‘, ‘2016-09-07 00:00:00‘),  
  11. (2, ‘mysql主从复制‘, ‘mysql主从备份(复制)的基本原理 mysql支持单向、异步复制,复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器‘, ‘2016-09-06 00:00:00‘),  
  12. (3, ‘hello‘, ‘can you search me?‘, ‘2016-09-05 00:00:00‘),  
  13. (4, ‘mysql‘, ‘mysql is the best database?‘, ‘2016-09-03 00:00:00‘),  
  14. (5, ‘mysql索引‘, ‘关于MySQL索引的好处,如果正确合理设计并且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车‘, ‘2016-09-01 00:00:00‘),  
  15. (6, ‘集群‘, ‘关于MySQL索引的好处,如果正确合理设计并且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车‘, ‘0000-00-00 00:00:00‘),  
  16. (9, ‘复制原理‘, ‘redis也有复制‘, ‘0000-00-00 00:00:00‘),  
  17. (10, ‘redis集群‘, ‘集群技术是构建高性能网站架构的重要手段,试想在网站承受高并发访问压力的同时,还需要从海量数据中查询出满足条件的数据,并快速响应,我们必然想到的是将数据进行切片,把数据根据某种规则放入多个不同的服务器节点,来降低单节点服务器的压力‘, ‘0000-00-00 00:00:00‘);  
  18.   
  19. CREATE TABLE IF NOT EXISTS `sph_counter` (  
  20.   `counter_id` int(11) NOT NULL,  
  21.   `max_doc_id` int(11) NOT NULL,  
  22.   PRIMARY KEY (`counter_id`)  
  23. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT=‘增量索引标示的计数表‘;  

 

 

以下采用"Main + Delta" ("主索引"+"增量索引")的索引策略,使用Sphinx自带的一元分词。

6、Sphinx配置:注意修改数据源配置信息

 

[php] view plain copy
 
  1. vi /usr/local/sphinx/etc/sphinx.conf  
  2. source items {  
  3.     type = mysql  
  4.     sql_host = localhost  
  5.     sql_user = root  
  6.     sql_pass = 123456  
  7.     sql_db = sphinx_items  
  8.       
  9.     sql_query_pre = SET NAMES utf8  
  10.     sql_query_pre = SET SESSION query_cache_type = OFF  
  11.     sql_query_pre = REPLACE INTO sph_counter SELECT 1, MAX(id) FROM items  
  12.       
  13.     sql_query_range = SELECT MIN(id), MAX(id) FROM items \  
  14.                             WHERE id<=(SELECT max_doc_id FROM sph_counter WHERE counter_id=1)  
  15.     sql_range_step = 1000  
  16.     sql_ranged_throttle = 1000  
  17.   
  18.     sql_query = SELECT id, title, content, created, 0 as deleted FROM items \  
  19.                     WHERE id<=(SELECT max_doc_id FROM sph_counter WHERE counter_id=1) \  
  20.                                 AND id >= $start AND id <= $end    
  21.       
  22.     sql_attr_timestamp = created  
  23.     sql_attr_bool = deleted  
  24. }  
  25.   
  26. source items_delta : items {  
  27.     sql_query_pre = SET NAMES utf8  
  28.     sql_query_range = SELECT MIN(id), MAX(id) FROM items \  
  29.                             WHERE id > (SELECT max_doc_id FROM sph_counter WHERE counter_id=1)  
  30.     sql_query = SELECT id, title, content, created, 0 as deleted FROM items \  
  31.                     WHERE id>( SELECT max_doc_id FROM sph_counter WHERE counter_id=1 ) \  
  32.                         AND id >= $start AND id <= $end  
  33.     sql_query_post_index = set @max_doc_id :=(SELECT max_doc_id FROM sph_counter WHERE counter_id=1)  
  34.     sql_query_post_index = REPLACE INTO sph_counter SELECT 2, IF($maxid, $maxid, @max_doc_id)  
  35. }  
  36. #主索引  
  37. index items {  
  38.     source = items  
  39.     path = /usr/local/sphinx/var/data/items  
  40.     docinfo = extern  
  41.     morphology = none  
  42.     min_word_len = 1  
  43.     min_prefix_len = 0  
  44.     html_strip = 1  
  45.     html_remove_elements = style, script  
  46.     ngram_len = 1  
  47.     ngram_chars = U+3000..U+2FA1F  
  48.     charset_type = utf-8  
  49.     charset_table = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F  
  50.     preopen = 1     
  51.     min_infix_len = 1  
  52. }  
  53.   
  54. #增量索引  
  55. index items_delta : items {  
  56.     source = items_delta  
  57.     path = /usr/local/sphinx/var/data/items-delta  
  58. }  
  59.   
  60. #分布式索引  
  61. index master {  
  62.     type = distributed  
  63.     local = items  
  64.     local = items_delta  
  65. }  
  66.   
  67. indexer {  
  68.     mem_limit = 256M  
  69. }  
  70.   
  71. searchd {  
  72.     listen                   = 9312  
  73.     listen                   = 9306:mysql41 #Used for SphinxQL  
  74.     log                      = /usr/local/sphinx/var/log/searchd.log  
  75.     query_log          = /usr/local/sphinx/var/log/query.log  
  76.     compat_sphinxql_magics   = 0  
  77.     attr_flush_period                = 600  
  78.     mva_updates_pool   = 16M  
  79.     read_timeout           = 5  
  80.     max_children           = 0  
  81.     dist_threads             = 2   
  82.     pid_file                    = /usr/local/sphinx/var/log/searchd.pid  
  83.     max_matches          = 1000  
  84.     seamless_rotate       = 1  
  85.     preopen_indexes     = 1  
  86.     unlink_old               = 1  
  87.     workers                  = threads # for RT to work  
  88.     binlog_path            = /usr/local/sphinx/var/data  
  89.       
  90. }  

保存退出

 

7、Sphinx创建索引

 

[php] view plain copy
 
  1. #第一次需重建索引:  
  2. [[email protected] bin]# ./indexer -c /usr/local/sphinx/etc/sphinx.conf --all  
  3. Sphinx 2.0.7-id64-release (r3759)  
  4. Copyright (c) 2001-2012, Andrew Aksyonoff  
  5. Copyright (c) 2008-2012, Sphinx Technologies Inc (http://sphinxsearch.com)  
  6.   
  7. using config file ‘/usr/local/sphinx/etc/sphinx.conf‘...  
  8. indexing index ‘items‘...  
  9. collected 8 docs, 0.0 MB  
  10. sorted 0.0 Mhits, 100.0% done  
  11. total 8 docs, 1121 bytes  
  12. total 1.017 sec, 1101 bytes/sec, 7.86 docs/sec  
  13. indexing index ‘items_delta‘...  
  14. collected 0 docs, 0.0 MB  
  15. total 0 docs, 0 bytes  
  16. total 1.007 sec, 0 bytes/sec, 0.00 docs/sec  
  17. skipping non-plain index ‘master‘...  
  18. total 4 reads, 0.000 sec, 0.7 kb/call avg, 0.0 msec/call avg  
  19. total 14 writes, 0.001 sec, 0.5 kb/call avg, 0.1 msec/call avg  
  20. #启动sphinx  
  21. [[email protected] bin]# ./searchd -c /usr/local/sphinx/etc/sphinx.conf  
  22. Sphinx 2.0.7-id64-release (r3759)  
  23. Copyright (c) 2001-2012, Andrew Aksyonoff  
  24. Copyright (c) 2008-2012, Sphinx Technologies Inc (http://sphinxsearch.com)  
  25.   
  26. using config file ‘/usr/local/sphinx/etc/sphinx.conf‘...  
  27. listening on all interfaces, port=9312  
  28. listening on all interfaces, port=9306  
  29. precaching index ‘items‘  
  30. precaching index ‘items_delta‘  
  31. rotating index ‘items_delta‘: success  
  32. precached 2 indexes in 0.012 sec  
  33. #查看进程  
  34. [[email protected] bin]# ps -ef | grep searchd  
  35. root     30431     1  0 23:59 ?        00:00:00 ./searchd -c /usr/local/sphinx/etc/sphinx.conf  
  36. root     30432 30431  0 23:59 ?        00:00:00 ./searchd -c /usr/local/sphinx/etc/sphinx.conf  
  37. root     30437  1490  0 23:59 pts/0    00:00:00 grep searchd  
  38. #停止Searchd:  
  39. ./searchd -c /usr/local/sphinx/etc/sphinx.conf --stop  
  40. #查看Searchd状态:  
  41. ./searchd -c /usr/local/sphinx/etc/sphinx.conf --status  

 

索引更新及使用说明
"增量索引"每N分钟更新一次.通常在每天晚上低负载的时进行一次索引合并,同时重新建立"增量索引"。当然"主索引"数据不多的话,也可以直接重新建立"主索引"。
API搜索的时,同时使用"主索引"和"增量索引",这样可以获得准实时的搜索数据.本文的Sphinx配置将"主索引"和"增量索引"放到分布式索引master中,因此只需查询分布式索引"master"即可获得全部匹配数据(包括最新数据)。

索引的更新与合并的操作可以放到cron job完成:

[php] view plain copy
 
  1. crontab -e  
  2. */1 * * * *  /usr/local/sphinx/shell/delta_index_update.sh  
  3. 0 3 * * *    /usr/local/sphinx/shell/merge_daily_index.sh  
  4. crontab -l  

 

cron job所用的shell脚本例子:

delta_index_update.sh:

[php] view plain copy
 
  1. #!/bin/bash  
  2. /usr/local/sphinx/bin/indexer -c /usr/local/sphinx/etc/sphinx.conf --rotate items_delta > /dev/null 2>&1  

merge_daily_index.sh:

[php] view plain copy
 
  1. #!/bin/bash  
  2. indexer=`which indexer`  
  3. mysql=`which mysql`  
  4.   
  5. QUERY="use sphinx_items;select max_doc_id from sph_counter where counter_id = 2 limit 1;"  
  6. index_counter=$($mysql -h192.168.1.198 -uroot -p123456 -sN -e "$QUERY")  
  7.   
  8. #merge "main + delta" indexes  
  9. $indexer -c /usr/local/sphinx/etc/sphinx.conf --rotate --merge items items_delta --merge-dst-range deleted 0 0 >> /usr/local/sphinx/var/index_merge.log 2>&1  
  10.   
  11. if [ "$?" -eq 0 ]; then  
  12.     ##update sphinx counter  
  13.     if [ ! -z $index_counter ]; then  
  14.         $mysql -h192.168.1.198 -uroot -p123456 -Dsphinx_items -e "REPLACE INTO sph_counter VALUES (1, ‘$index_counter‘)"  
  15.     fi  
  16.     ##rebuild delta index to avoid confusion with main index  
  17.     $indexer -c /usr/local/sphinx/etc/sphinx.conf --rotate items_delta >> /usr/local/sphinx/var/rebuild_deltaindex.log 2>&1  
  18. fi  


8、php中文分词scws安装:注意扩展的版本和php的版本

 

[php] view plain copy
 
  1. wget -c http://www.xunsearch.com/scws/down/scws-1.2.3.tar.bz2  
  2. tar jxvf scws-1.2.3.tar.bz2  
  3. cd scws-1.2.3  
  4. ./configure --prefix=/usr/local/scws  
  5. make && make install  


9、scws的PHP扩展安装:

 

[php] view plain copy
 
  1. cd ./phpext  
  2. phpize   
  3. ./configure  
  4. make && make install  
  5. echo "[scws]" >> /etc/php.ini  
  6. echo "extension = scws.so" >> /etc/php.ini  
  7. echo "scws.default.charset = utf-8" >> /etc/php.ini  
  8. echo "scws.default.fpath = /usr/local/scws/etc/" >> /etc/php.ini  


10、词库安装:

[php] view plain copy
 
  1. wget http://www.xunsearch.com/scws/down/scws-dict-chs-utf8.tar.bz2  
  2. tar jxvf scws-dict-chs-utf8.tar.bz2 -C /usr/local/scws/etc/  
  3. chown www:www /usr/local/scws/etc/dict.utf8.xdb  

 

 

11、php使用Sphinx+scws测试例子
在Sphinx源码API中,有好几种语言的API调用.其中有一个是sphinxapi.php。
不过以下的测试使用的是Sphinx的PHP扩展.具体安装见本文开头的Sphinx安装部分。
测试用的搜索类Search.php:注意修改getDBConnection()信息为自己的

 

[php] view plain copy
 
  1. <?php  
  2. class Search {  
  3.     /** 
  4.      * @var SphinxClient  
  5.     **/  
  6.     protected $client;  
  7.     /** 
  8.      * @var string 
  9.     **/  
  10.     protected $keywords;  
  11.     /** 
  12.      * @var resource 
  13.     **/  
  14.     private static $dbconnection = null;  
  15.       
  16.     /** 
  17.      * Constructor 
  18.      **/  
  19.     public function __construct($options = array()) {  
  20.         $defaults = array(  
  21.             ‘query_mode‘ => SPH_MATCH_EXTENDED2,  
  22.             ‘sort_mode‘ => SPH_SORT_EXTENDED,  
  23.             ‘ranking_mode‘ => SPH_RANK_PROXIMITY_BM25,  
  24.             ‘field_weights‘ => array(),  
  25.             ‘max_matches‘ => 1000,  
  26.             ‘snippet_enabled‘ => true,  
  27.             ‘snippet_index‘ => ‘items‘,  
  28.             ‘snippet_fields‘ => array(),   
  29.         );  
  30.         $this->options = array_merge($defaults, $options);  
  31.         $this->client = new SphinxClient();  
  32.         //$this->client->setServer("192.168.1.198", 9312);  
  33.         $this->client->setMatchMode($this->options[‘query_mode‘]);  
  34.         if ($this->options[‘field_weights‘] !== array()) {  
  35.             $this->client->setFieldWeights($this->options[‘field_weights‘]);  
  36.         }  
  37.         /* 
  38.         if ( in_array($this->options[‘query_mode‘], [SPH_MATCH_EXTENDED2,SPH_MATCH_EXTENDED]) ) { 
  39.             $this->client->setRankingMode($this->options[‘ranking_mode‘]); 
  40.         } 
  41.         */  
  42.     }  
  43.      
  44.     /** 
  45.      * Query 
  46.      * 
  47.      * @param string  $keywords 
  48.      * @param integer $offset 
  49.      * @param integer $limit 
  50.      * @param string  $index 
  51.      * @return array 
  52.      **/  
  53.     public function query($keywords, $offset = 0, $limit = 10, $index = ‘*‘) {  
  54.         $this->keywords = $keywords;  
  55.         $max_matches = $limit > $this->options[‘max_matches‘] ? $limit : $this->options[‘max_matches‘];  
  56.         $this->client->setLimits($offset, $limit, $max_matches);  
  57.         $query_results = $this->client->query($keywords, $index);  
  58.   
  59.         if ($query_results === false) {  
  60.             $this->log(‘error:‘ . $this->client->getLastError());  
  61.         }  
  62.                  
  63.         $res = [];  
  64.         if ( empty($query_results[‘matches‘]) ) {  
  65.             return $res;  
  66.         }  
  67.         $res[‘total‘] = $query_results[‘total‘];  
  68.         $res[‘total_found‘] = $query_results[‘total_found‘];  
  69.         $res[‘time‘] = $query_results[‘time‘];  
  70.         $doc_ids = array_keys($query_results[‘matches‘]);  
  71.         unset($query_results);  
  72.         $res[‘data‘] = $this->fetch_data($doc_ids);  
  73.         if ($this->options[‘snippet_enabled‘]) {  
  74.             $this->buildExcerptRows($res[‘data‘]);  
  75.         }  
  76.           
  77.         return $res;  
  78.     }  
  79.       
  80.     /** 
  81.      * custom sorting  
  82.      *  
  83.      * @param string $sortBy 
  84.      * @param int $mode 
  85.      * @return bool 
  86.      **/  
  87.     public function setSortBy($sortBy = ‘‘, $mode = 0) {  
  88.         if ($sortBy) {  
  89.             $mode = $mode ?: $this->options[‘sort_mode‘];  
  90.             $this->client->setSortMode($mode, $sortBy);  
  91.         } else {  
  92.             $this->client->setSortMode(SPH_SORT_RELEVANCE);  
  93.         }  
  94.     }  
  95.       
  96.     /** 
  97.      * fetch data based on matched doc_ids 
  98.      *  
  99.      * @param array $doc_ids 
  100.      * @return array 
  101.      **/      
  102.     protected function fetch_data($doc_ids) {  
  103.         $ids = implode(‘,‘, $doc_ids);  
  104.         $queries = self::getDBConnection()->query("SELECT * FROM items WHERE id in ($ids)", PDO::FETCH_ASSOC);  
  105.         return iterator_to_array($queries);  
  106.     }  
  107.       
  108.     /** 
  109.      * build excerpts for data 
  110.      *  
  111.      * @param array $rows 
  112.      * @return array 
  113.      **/   
  114.     protected function buildExcerptRows(&$rows) {  
  115.         $options = array(  
  116.             ‘before_match‘ => ‘<b style="color:red">‘,  
  117.             ‘after_match‘  => ‘</b>‘,  
  118.             ‘chunk_separator‘ => ‘...‘,  
  119.             ‘limit‘    => 256,  
  120.             ‘around‘   => 3,  
  121.             ‘exact_phrase‘ => false,  
  122.             ‘single_passage‘ => true,  
  123.             ‘limit_words‘ => 5,  
  124.         );  
  125.         $scount = count($this->options[‘snippet_fields‘]);  
  126.         foreach ($rows as &$row) {  
  127.             foreach ($row as $fk => $item) {  
  128.                 if (!is_string($item) || ($scount && !in_array($fk, $this->options[‘snippet_fields‘])) ) continue;  
  129.                 $item = preg_replace(‘/[\r\t\n]+/‘, ‘‘, strip_tags($item));  
  130.                 $res = $this->client->buildExcerpts(array($item), $this->options[‘snippet_index‘], $this->keywords, $options);  
  131.                 $row[$fk] = $res === false ? $item : $res[0];  
  132.             }  
  133.         }          
  134.         return $rows;  
  135.     }  
  136.       
  137.     /** 
  138.      * database connection 
  139.      * 
  140.      * @return resource 
  141.      **/   
  142.     private static function getDBConnection() {  
  143.         $dsn = ‘mysql:host=192.168.1.198;dbname=sphinx_items‘;  
  144.         $user = ‘root‘;  
  145.         $pass = ‘123456‘;  
  146.         if (!self::$dbconnection) {  
  147.             try {  
  148.                 self::$dbconnection = new PDO($dsn, $user, $pass);  
  149.             } catch (PDOException $e) {  
  150.                 die(‘Connection failed: ‘ . $e->getMessage());  
  151.             }  
  152.         }  
  153.         return self::$dbconnection;  
  154.     }  
  155.   
  156.     /** 
  157.      * Chinese words segmentation 
  158.      * 
  159.      **/  
  160.     public function wordSplit($keywords) {  
  161.         $fpath = ini_get(‘scws.default.fpath‘);  
  162.         $so = scws_new();  
  163.         $so->set_charset(‘utf-8‘);  
  164.         $so->add_dict($fpath . ‘/dict.utf8.xdb‘);  
  165.         //$so->add_dict($fpath .‘/custom_dict.txt‘, SCWS_XDICT_TXT);  
  166.         $so->set_rule($fpath . ‘/rules.utf8.ini‘);  
  167.         $so->set_ignore(true);  
  168.         $so->set_multi(false);  
  169.         $so->set_duality(false);  
  170.         $so->send_text($keywords);  
  171.         $words = [];  
  172.         $results =  $so->get_result();  
  173.         foreach ($results as $res) {  
  174.             $words[] = ‘(‘ . $res[‘word‘] . ‘)‘;  
  175.         }  
  176.         $words[] = ‘(‘ . $keywords . ‘)‘;  
  177.         return join(‘|‘, $words);  
  178.     }  
  179.       
  180.     /** 
  181.      * get current sphinx client 
  182.      * 
  183.      * @return resource 
  184.      **/  
  185.     public function getClient() {  
  186.         return $this->client;  
  187.     }  
  188.     /** 
  189.      * log error 
  190.      **/  
  191.     public function log($msg) {  
  192.         // log errors here  
  193.         //echo $msg;  
  194.     }      
  195.     /** 
  196.      * magic methods 
  197.      **/  
  198.     public function __call($method, $args) {  
  199.         $rc = new ReflectionClass(‘SphinxClient‘);  
  200.         if ( !$rc->hasMethod($method) ) {  
  201.             throw new Exception(‘invalid method :‘ . $method);  
  202.         }  
  203.         return call_user_func_array(array($this->client, $method), $args);  
  204.     }  
  205. }  


测试文件test.php:

 

[php] view plain copy
 
  1. <?php  
  2. require(‘Search.php‘);  
  3. $s = new Search([  
  4.         ‘snippet_fields‘ => [‘title‘, ‘content‘],  
  5.         ‘field_weights‘ => [‘title‘ => 20, ‘content‘ => 10],  
  6.     ]);  
  7. $s->setSortMode(SPH_SORT_EXTENDED, ‘created desc,@weight desc‘);  
  8. //$s->setSortBy(‘created desc,@weight desc‘);  
  9. $words = $s->wordSplit("mysql集群");//先分词 结果:(mysql)|(mysql集群)  
  10. //print_r($words);exit;  
  11. $res = $s->query($words, 0, 10, ‘master‘);  
  12. echo ‘<pre/>‘;print_r($res);  

 

测试结果:

技术分享

 

 

12、SphinxQL测试

要使用SphinxQL需要在Searchd的配置里面增加相应的监听端口(参考上文配置)。

 

[php] view plain copy
 
  1. [[email protected] bin]# mysql -h127.0.0.1 -P9306 -uroot -p  
  2. Enter password:  
  3. Welcome to the MySQL monitor.  Commands end with ; or \g.  
  4. Your MySQL connection id is 1  
  5. Server version: 2.0.7-id64-release (r3759)  
  6.   
  7. Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.  
  8.   
  9. Oracle is a registered trademark of Oracle Corporation and/or its  
  10. affiliates. Other names may be trademarks of their respective  
  11. owners.  
  12.   
  13. Type ‘help;‘ or ‘\h‘ for help. Type ‘\c‘ to clear the current input statement.  
  14.   
  15. mysql> show global variables;  
  16. +----------------------+---------+  
  17. | Variable_name        | Value   |  
  18. +----------------------+---------+  
  19. | autocommit           | 1       |  
  20. | collation_connection | libc_ci |  
  21. | query_log_format     | plain   |  
  22. | log_level            | info    |  
  23. +----------------------+---------+  
  24. 4 rows in set (0.00 sec)  
  25.   
  26. mysql> desc items;  
  27. +---------+-----------+  
  28. | Field   | Type      |  
  29. +---------+-----------+  
  30. | id      | bigint    |  
  31. | title   | field     |  
  32. | content | field     |  
  33. | created | timestamp |  
  34. | deleted | bool      |  
  35. +---------+-----------+  
  36. 5 rows in set (0.00 sec)  
  37.   
  38. mysql> select * from master where match (‘mysql集群‘) limit 10;  
  39. +------+---------+---------+  
  40. | id   | created | deleted |  
  41. +------+---------+---------+  
  42. |    1 |    2016 |       0 |  
  43. |    6 |       0 |       0 |  
  44. +------+---------+---------+  
  45. 2 rows in set (0.00 sec)  
  46.   
  47. mysql> show meta;  
  48. +---------------+-------+  
  49. | Variable_name | Value |  
  50. +---------------+-------+  
  51. | total         | 2     |  
  52. | total_found   | 2     |  
  53. | time          | 0.006 |  
  54. | keyword[0]    | mysql |  
  55. | docs[0]       | 5     |  
  56. | hits[0]       | 15    |  
  57. | keyword[1]    | 集    |  
  58. | docs[1]       | 3     |  
  59. | hits[1]       | 4     |  
  60. | keyword[2]    | 群    |  
  61. | docs[2]       | 3     |  
  62. | hits[2]       | 4     |  
  63. +---------------+-------+  
  64. 12 rows in set (0.00 sec)  
  65.   
  66. mysql>  

 

以上是关于php+中文分词scws+sphinx+mysql打造千万级数据全文搜索的主要内容,如果未能解决你的问题,请参考以下文章

PHP中文分词扩展 SCWS

php mysql 分词 模糊查询 并根据分词匹配度排序

SCWS中文分词PHP扩展详细安装说明

PHP中使用Sphinx中文全文检索/搜索

php分词的了解与使用

PHP实现关键词全文搜索Sphinx及中文分词Coreseek的安装配置