近段时间的一个新项目,使用了分表之后,mysql 做一些搜索就只能使用联表做视图,对搜索很不友好,我们选择了,sphinx/coreseek,因为基本能满足我们的需求,当然还是其他的Xunsearch,Lucene,都是不错。这整个过程中,我们遇到了不少的问题,,希望以下内容能帮助到你。
环境版本
os:centos 6.6 64位,ceonts 7在编译coreseek时会报各种automake等错误
php5.4 + mysql5.6
安装过程略过
网上有很多安装之类的文章。
如果你想做搜索的表主键是bigint类型,请在编辑时加上--enable-id64
./configure --prefix=/usr/local/coreseek --without-unixodbc --with-mmseg --with-mmseg-includes=/usr/local/mmseg3/include/mmseg/ --with-mmseg-libs=/usr/local/mmseg3/lib/ --with-mysql --enable-id64
使用过程中碰到的问题
1、主键Id为bigint,长度为20位
我们将用户表分了50个单表,如user_0,user_1,...user_49,里面的用户ID,都是使用mysql的uuid_short()函数生成,如果使用多库的情况下请使用同一台服务器生成uuid,不然长度不会统一。如果在编译过程中没有加--enable-id64,那对不起,你搜索的结果不对,但你又看不出什么错误,需要重新编译安装
2、主键是字符串,怎么破
这是官方说的:文档ID必须是第一列,而且必须是唯一的正整数值(不能是0也不能是负数),既可以是32位的也可以是64位的。所以说没有办法,只能添加一列,然后使用uuid_short(),更新并添加索引。
3、怎么做模糊搜索
在中文分词时,如中国人,在搜索中,没有结果,这怎么能忍,发现搜索拆分的词为中国,人,国人,没有将词单个拆分,其实官方说明文档里有。
MMSEG分词配置选项 mmseg分词相关的配置选项,需要保存到文件mmseg.ini,并将该配置文件放置到charset_dictpath所设置的目录中。
基本配置:
[mmseg]
seperate_number_ascii=1;;就是将字母和数字打散;
这个地方请注意,开启之后生成索引的文件会比之前大很多
4、分表或多表联合查询时应该怎么建立索引
之前我们join或复杂的sql时,发现维护起来很麻烦,可以先在数据库中新建视图,以方便做搜索。
5、如何更方便的配置管理多库数据库
source server
{
type = mysql
sql_host = 127.0.0.1
sql_user = db_user
sql_pass = db_password
sql_port = 3306
sql_query_pre = SET NAMES utf8
}
#test_user为库名
#view_user为视图表名,继承server
source test_user_view_user : server
{
sql_db = test_user
sql_query = select uid,nick,source,ctime from test_view_user
sql_field_string = nick #可全文搜索,可返回原始文本信息
sql_attr_string = source
sql_attr_string = ctime
sql_query_info = select * from test_view_user where uid = $uid
}
#test_content为库名
#view_content为视图表名,继承server
source test_content_view_content : server
{
sql_db = test_content
sql_query = select content_id,ctime,content from taoo_view_content
sql_field_string = content
sql_attr_string = ctime
sql_query_info = select * from taoo_view_content where content_id = $content_id
}
这样配置数据,可以在server源中做更多的基础配置,如用户密码统一管理,还可以做增量数据索引更新
6、配置多个索引
index test_index_user_view_user
{
source = test_user_view_user
docinfo = extern
mlock = 0
morphology = none
min_word_len = 1
ngram_len = 1
min_infix_len = 0
#这里有重要
charset_type = zh_cn.utf-8
#这里有重要
charset_dictpath = /usr/local/mmseg3/etc/
html_strip = 0
path = /usr/local/coreseek/var/data/taoo_index_user_view_user
}
#这是继承前面的索引,没有找到更简洁的写法
index test_index_content_view_note_info : test_index_user_view_user
{
source = test_content_view_content
path = /usr/local/coreseek/var/data/test_index_content_view_note_info
}
7、基础数据源,官网贴的
indexer
{
#最大可能的限制是2047M,官方说的
mem_limit = 1048M
}
searchd
{
listen = 9312
listen = 9306:mysql41
log = /usr/local/coreseek/var/log/searchd.log
query_log = /usr/local/coreseek/var/log/query.log
read_timeout = 5
client_timeout = 300
max_children = 30
pid_file = /usr/local/coreseek/var/log/searchd.pid
max_matches = 1000
seamless_rotate = 1
preopen_indexes = 1
unlink_old = 1
mva_updates_pool = 1M
max_packet_size = 8M
max_filters = 256
max_filter_values = 4096
max_batch_queries = 32
workers = threads # for RT to work
}
php 简单操作类
/**
* Sphinx.php
*
* @author mingzhi.l
* @version 1.0
* @copyright 2015-2015
*/
namespace Search;
/**
* SPH_MATCH_ALL 匹配所有查询词(默认模式).
* SPH_MATCH_ANY 匹配查询词中的任意一个.
* SPH_MATCH_PHRASE 将整个查询看作一个词组,要求按顺序完整匹配.
* SPH_MATCH_BOOLEAN 将查询看作一个布尔表达式.
* SPH_MATCH_EXTENDED 将查询看作一个Sphinx内部查询语言的表达式.
* SPH_MATCH_FULLSCAN 使用完全扫描,忽略查询词汇.
* SPH_MATCH_EXTENDED2 类似 SPH_MATCH_EXTENDED ,并支持评分和权重.
*/
class Sphinx
{
static public $instance;
static public $server = 'default';
//默认服务器
static private $host = '127.0.0.1';
//默认端口
static private $port = 9312;
//超时15秒
protected $timeout = 15;
protected $_data = [];
protected $_keywords = '';
protected $_indexName = '';
private $_page = [
'page' => 1,
'page_count' => 1,
'item_count' => 0,
'item_per_page' => 20,
'item_list' => [],
];
/**
* @var \SphinxClient
*/
private $conn;
/**
* @return Sphinx
*/
static public function Instance()
{
$class = get_called_class();
if (empty(self::$instance)) {
self::$instance = new $class();
//$config = Config::get_search_sphinx(self::$server);
//self::$host = $config['host'];
//self::$port = $config['port'];
}
return self::$instance;
}
/**
* 建立链接
*
* @return Sphinx
*/
public function init()
{
$this->conn = new \SphinxClient();
$this->conn->setServer(self::$host, self::$port);
$this->conn->setMatchMode(SPH_MATCH_ANY);//匹配查询词中的任意一个.
$this->conn->setMaxQueryTime($this->timeout);
$this->conn->SetArrayResult(true);//控制搜索结果集的返回格式
return $this;
}
/**
* 执行搜索查询
*
* @param $keyword
* @param $indexName
* @return $this
* @throws Exception
*/
public function query($keyword, $indexName)
{
if (empty($keyword) || empty($indexName)) {
throw new Exception('query error: keyword or indexName is null');
}
$this->_keywords = $keyword;
$this->_indexName = $indexName;
return $this;
}
/**
* 字段
*
* @param string $field
* @return $this
*/
public function field($field = '*')
{
$this->conn->SetSelect($field);
return $this;
}
public function getError()
{
return $this->conn->GetLastError();
}
/**
* 给服务器端结果集设置一个偏移量($offset)和从那个偏移量起向客户端返回的匹配项数目限制($limit)。
* 并且可以在服务器端设定当前查询的结果集大小($max_matches),另有一个阈值($cutoff),当找到的匹配项达到这个阀值时就停止搜索。
* 全部这些参数都必须是非负整数。
*
* @param $page
* @param int $itemPerPage
* @param int $maxMatches
* @param int $cutoff
* @return $this
*/
public function limit($page, $itemPerPage = 20, $maxMatches = 1000, $cutoff = 0)
{
$this->_page['page'] = ($page - 1) * $itemPerPage;
$this->_page['item_per_page'] = $itemPerPage;
$this->conn->SetLimits($this->_page['page'], $this->_page['item_per_page'], $maxMatches, $cutoff);
return $this;
}
/**
* 排序
*
* @param $field
* @param string $orderBy
* @return $this
* @throws Exception
*/
public function order($field, $orderBy = 'asc')
{
if (empty($field)) {
throw new Exception('order error: field is null');
}
$this->conn->SetSortMode(SPH_SORT_EXTENDED, "{$field} $orderBy");
return $this;
}
/**
* 获得数据
*
* @param bool $filter
* @return array
*/
public function get($filter = true)
{
$this->_data = $this->conn->query($this->_keywords, $this->_indexName);
return $filter ? $this->_filterData() : $this->_data;
}
/**
* 过滤结果数据
*
* @return array
*/
private function _filterData()
{
$itemList = [];
if (!empty($this->_data['total'])) {
$data = array_values($this->_data['matches']);
foreach ($data as $value) {
$itemList[] = array_merge(['id' => $value['id']],$value['attrs']);
}
$itemPerPage = min(50, $this->_page['item_per_page']);
$pageCount = ceil($this->_data['total'] / $itemPerPage);
$this->_page['page_count'] = $pageCount;
$this->_page['item_count'] = $this->_data['total'];
$this->_page['item_list'] = $itemList;
$this->_page['words'] = $this->_data['words'];
}
return $this->_data = $this->_page;
}
}
//使用方法也很简单
$sphinx = Sphinx::Instance()->init();
$result = $search->query($keyWord, $indexName)->limit($page, $itemPerPage)->order('ctime','desc')->field($field)->get();
参考文档