MySQL在高并发连接、数据库记录数较多的情况下,SELECT ... WHERE ... LIKE'%...%'的全文搜索方式不仅效率差,而且以通配符%和_开头作查询时,使用不到索引,需要全表扫描,对数据库的压力也很大。MySQL针对这一问题提供了一种全文索引解决方案,这不仅仅提高了性能和效率(因为MySQL对这些字段做了索引来优化搜索),而且实现了更高质量的搜索。但是,至今为止,MySQL对中文全文索引无法正确支持。
中文与西方文字如英文的一个重要区别在于,西方文字以单词为单位,单词与单词之间以空格分隔。而中文以字为单位,词由一个或多个字组成,词与词之间没有空格分隔。当试图在一个含有中文字符的字段中使用全文搜索时,不会得到正确的结果,原因在于中文中没有像英文空格那样对词定界,不能以空格作为分割,对中文词语进行索引。
一、MySQL中文全文索引插件mysqlcft的特点:
1、优点:
①、精准度很高:采用自创的“三字节交叉切分算法”,对中文语句进行分割,无中文分词词库,搜索精准度远比中文分词算法高,能达到LIKE'%...%"的准确率。
②、查询速度快:查询速度比LIKE '%...%"搜索快3~50倍,文章末尾有测试结果;
③、标准插件式:以MySQL5.1全文索引的标准插件形式开发,不修改MySQL源代码,不影响MySQL的其他功能,可快速跟进MySQL新版本;
④、支持版本多:支持所有的MySQL 5.1 Release Candidate版本,即MySQL 5.1.22RC~最新的MySQL 5.1.25 RC;
⑤、支持字符集:支持包括GBK、GB2312、UTF-8、Latin1、BIG5在内的MySQL字符集(其他字符集没有测试过);
⑥、系统兼容好:具有i386和x86_64两个版本,支持32位(i386)和64位(x86_64)CPU及Linux系统;
⑦、适合分布式:非常适合MySQL Slave分布式系统架构,无词库维护成本,不存在词库同步问题。
2、缺点:
①、mysqlcft中文全文索引只适用于MyISAM表,因为MySQL只支持对MyISAM表建立FULLTEXT索引;
②、MySQL不能静态编译安装,否则无法安装mysqlcft插件;
③、基于“三字节交叉切分算法”的索引文件会比海量、ft-hightman等基于“中文分词算法”的索引文件稍大,但不是大很多。根据我的测试,mysqlcft全文索引的.MYI索引文件是.MYD数据文件的2~5倍。
配置文件中添加
[mysqld]
ft_min_word_len = 1
附:MySQL配置文件在全文索引应用中的优化
[mysqld]
# key_buffer 指定用于索引的缓冲区大小,在全文索引中,增加它可得到更好的索引处理与查询性能
key_buffer = 512M
# sort_buffer_size 为查询排序时所能使用的缓冲区大小,全文索引的SQL语句之后通常会使用ORDERBY排序,增加它可以加快SQL语句执行时间。该参数对应的分配内存是每连接独占,100个连接使用的内存将是32M*100=3200M
sort_buffer_size = 32M
# 对大于可用内存的表执行GROUP BY或ORDERBY操作,应增加read_rnd_buffer_size的值以加速排序操作后面的行读取
read_rnd_buffer_size = 64M
# 如果表出现故障或索引出错,REPAIR TABLE时用到的缓冲区大小
myisam_sort_buffer_size = 128M
# 确定使用的filesort算法的索引值大小的限值
max_length_for_sort_data = 64
# MySQL全文索引查询所用关键词最小长度限制(不要改变这项值)
ft_min_word_len = 1
# 降低UPDATE优先级,设置查询优先
low_priority_updates = 1
wgethttp://mysqlcft.googlecode.com/files/mysqlcft-1.0.0-i386-bin.tar.gz
tar zxvf mysqlcft-1.0.0-i386-bin.tar.gz
cp mysqlcft.so /usr/local/mysql1/lib/mysql/plugin/
--安装引擎
INSTALL PLUGIN mysqlcft SONAME 'mysqlcft.so';
--查看是否安装成功
SELECT * FROM mysql.plugin;
SHOW PLUGINS;
--创建索引
use test
ALTER IGNORE TABLE pa_gposts ADD FULLTEXT INDEXfull_text_title(title) WITH PARSER mysqlcft;
--修复索引
REPAIR TABLE pa_gposts QUICK;
性能比较
没有添加索引之前
SELECT * FROM pa_gposts WHERE MATCH(title) AGAINST ('医院' IN BOOLEANMODE) limit 0,30;
4 rows in set (1 min 12.69 sec)
这种查询出的结果单词的前后都要有停止字,查询速度还是很慢,因为没有索引
mysql> explain SELECT * FROM pa_gposts WHEREMATCH(title) AGAINST ('医院' IN BOOLEAN MODE) limit 0,30;
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type |table
| type | possible_keys | key
| key_len |ref
| rows
|Extra
|
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| 1 |SIMPLE
| pa_gposts | ALL
|NULL
| NULL | NULL
| NULL | 213193 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
创建索引后
SELECT * FROM pa_gposts WHERE MATCH(title) AGAINST ('医院' IN BOOLEANMODE) limit 0,30;
30 rows in set (1.07 sec)
SELECT * FROM pa_gposts WHERE title LIKE '%医院%' limit 0,30;
30 rows in set (4.81 sec)
mysql> explain SELECT * FROM pa_gposts WHERE titleLIKE '%医院%' limit 0,30;
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type |table
| type | possible_keys | key
| key_len |ref
| rows
|Extra
|
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| 1 |SIMPLE
| pa_gposts | ALL
|NULL
| NULL | NULL
| NULL | 213193 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
mysql> explain SELECT * FROM pa_gposts WHEREMATCH(title) AGAINST ('医院' IN BOOLEAN MODE) limit 0,30;
+----+-------------+-----------+----------+-----------------+-----------------+---------+------+------+-------------+
| id | select_type |table
|type
| possible_keys
|key
| key_len | ref
| rows |Extra
|
+----+-------------+-----------+----------+-----------------+-----------------+---------+------+------+-------------+
| 1 |SIMPLE
| pa_gposts | fulltext | full_text_title | full_text_title |0
|
|
1 | Usingwhere |
+----+-------------+-----------+----------+-----------------+-----------------+---------+------+------+-------------+
SELECT * FROM pa_gposts WHERE MATCH(title) AGAINST ('情侣' IN BOOLEANMODE) limit 0,30;
30 rows in set (1.93 sec)
SELECT * FROM pa_gposts WHERE title LIKE '%情侣%' limit 0,30;
30 rows in set (10.17 sec)
SELECT * FROM pa_gposts WHERE MATCH(title) AGAINST ('月光' IN BOOLEANMODE) limit 0,30;
13 rows in set (0.56 sec)
SELECT * FROM pa_gposts WHERE title LIKE '%月光%' limit 0,30;
13 rows in set (50.98 sec)
布尔全文搜索具有以下特点:
它们不使用 50%域值。.
它们不会按照相关性渐弱的顺序将行进行分类。你可以从上述问询结果中看到这一点:相关性最高的行是一个包含两个“MySQL”的行,但它被列在最后的位置,而不是开头位置。
即使没有FULLTEXT,它们仍然可以工作,尽管这种方式的搜索执行的速度非常之慢。
最小单词长度全文参数和最大单词长度全文参数均适用。
停止字适用。
布尔全文搜索的性能支持以下操作符:
+
一个前导的加号表示该单词必须 出现在返回的每一行的开头位置。
-
一个前导的减号表示该单词一定不能出现在任何返回的行中。
(无操作符)
在默认状态下(当没有指定 + 或–的情况下),该单词可有可无,但含有该单词的行等级较高。这和MATCH() ...AGAINST()不使用IN BOOLEAN MODE修改程序时的运作很类似。
> <
这两个操作符用来改变一个单词对赋予某一行的相关值的影响。 > 操作符增强其影响,而<操作符则减弱其影响。请参见下面的例子。
( )
括号用来将单词分成子表达式。括入括号的部分可以被嵌套。
~
一个前导的代字号用作否定符, 用来否定单词对该行相关性的影响。这对于标记“noise(无用信息)”的单词很有用。包含这类单词的行较其它行等级低,但因其可能会和-号同时使用,因而不会在任何时候都派出所有无用信息行。
*
星号用作截断符。于其它符号不同的是,它应当被追加到要截断的词上。
"
一个被括入双引号的短语 (‘"’)只和字面上包含该短语输入格式的行进行匹配。全文引擎将短语拆分成单词,在FULLTEXT索引中搜索该单词。
非单词字符不需要严密的匹配:短语搜索只要求符合搜索短语包含的单词且单词的排列顺序相同的内容。例如, "test phrase" 符合"test, phrase"。
若索引中不存在该短语包含的单词,则结果为空。例如,若所有单词都是禁用词,或是长度都小于编入索引单词的最小长度,则结果为空。
以下例子展示了一些使用布尔全文符号的搜索字符串:
'apple banana'
寻找包含至少两个单词中的一个的行。
'+apple +juice'
寻找两个单词都包含的行。
'+apple macintosh'
寻找包含单词“apple”的行,若这些行也包含单词“macintosh”, 则列为更高等级。
'+apple -macintosh'
寻找包含单词“apple” 但不包含单词 “macintosh”的行。
'+apple +(>turnover <strudel)'
寻找包含单词“apple”和“turnover” 的行,或包含“apple” 和“strudel”的行 (无先后顺序),然而包含“apple turnover”的行较包含“apple strudel”的行排列等级更为高。
'apple*'
寻找包含“apple”、“apples”、“applesauce”或“applet”的行。
'"some words"'
寻找包含原短语“some words”的行 (例如,包含“some words of wisdom”的行,而非包含 “some noise words”的行)。注意包围词组的‘"’符号是界定短语的操作符字符。它们不是包围搜索字符串本身的引号。
中文与西方文字如英文的一个重要区别在于,西方文字以单词为单位,单词与单词之间以空格分隔。而中文以字为单位,词由一个或多个字组成,词与词之间没有空格分隔。当试图在一个含有中文字符的字段中使用全文搜索时,不会得到正确的结果,原因在于中文中没有像英文空格那样对词定界,不能以空格作为分割,对中文词语进行索引。
一、MySQL中文全文索引插件mysqlcft的特点:
①、精准度很高:采用自创的“三字节交叉切分算法”,对中文语句进行分割,无中文分词词库,搜索精准度远比中文分词算法高,能达到LIKE'%...%"的准确率。
②、查询速度快:查询速度比LIKE '%...%"搜索快3~50倍,文章末尾有测试结果;
③、标准插件式:以MySQL5.1全文索引的标准插件形式开发,不修改MySQL源代码,不影响MySQL的其他功能,可快速跟进MySQL新版本;
④、支持版本多:支持所有的MySQL 5.1 Release Candidate版本,即MySQL 5.1.22RC~最新的MySQL 5.1.25 RC;
⑤、支持字符集:支持包括GBK、GB2312、UTF-8、Latin1、BIG5在内的MySQL字符集(其他字符集没有测试过);
⑥、系统兼容好:具有i386和x86_64两个版本,支持32位(i386)和64位(x86_64)CPU及Linux系统;
⑦、适合分布式:非常适合MySQL Slave分布式系统架构,无词库维护成本,不存在词库同步问题。
2、缺点:
①、mysqlcft中文全文索引只适用于MyISAM表,因为MySQL只支持对MyISAM表建立FULLTEXT索引;
②、MySQL不能静态编译安装,否则无法安装mysqlcft插件;
③、基于“三字节交叉切分算法”的索引文件会比海量、ft-hightman等基于“中文分词算法”的索引文件稍大,但不是大很多。根据我的测试,mysqlcft全文索引的.MYI索引文件是.MYD数据文件的2~5倍。
配置文件中添加
[mysqld]
ft_min_word_len = 1
附:MySQL配置文件在全文索引应用中的优化
[mysqld]
# key_buffer 指定用于索引的缓冲区大小,在全文索引中,增加它可得到更好的索引处理与查询性能
key_buffer = 512M
# sort_buffer_size 为查询排序时所能使用的缓冲区大小,全文索引的SQL语句之后通常会使用ORDERBY排序,增加它可以加快SQL语句执行时间。该参数对应的分配内存是每连接独占,100个连接使用的内存将是32M*100=3200M
sort_buffer_size = 32M
# 对大于可用内存的表执行GROUP BY或ORDERBY操作,应增加read_rnd_buffer_size的值以加速排序操作后面的行读取
read_rnd_buffer_size = 64M
# 如果表出现故障或索引出错,REPAIR TABLE时用到的缓冲区大小
myisam_sort_buffer_size = 128M
# 确定使用的filesort算法的索引值大小的限值
max_length_for_sort_data = 64
# MySQL全文索引查询所用关键词最小长度限制(不要改变这项值)
ft_min_word_len = 1
# 降低UPDATE优先级,设置查询优先
low_priority_updates = 1
wgethttp://mysqlcft.googlecode.com/files/mysqlcft-1.0.0-i386-bin.tar.gz
tar zxvf mysqlcft-1.0.0-i386-bin.tar.gz
cp mysqlcft.so /usr/local/mysql1/lib/mysql/plugin/
--安装引擎
INSTALL PLUGIN mysqlcft SONAME 'mysqlcft.so';
--查看是否安装成功
SELECT * FROM mysql.plugin;
SHOW PLUGINS;
--创建索引
use test
ALTER IGNORE TABLE pa_gposts ADD FULLTEXT INDEXfull_text_title(title) WITH PARSER mysqlcft;
--修复索引
REPAIR TABLE pa_gposts QUICK;
性能比较
没有添加索引之前
SELECT * FROM pa_gposts WHERE MATCH(title) AGAINST ('医院' IN BOOLEANMODE) limit 0,30;
4 rows in set (1 min 12.69 sec)
这种查询出的结果单词的前后都要有停止字,查询速度还是很慢,因为没有索引
mysql> explain SELECT * FROM pa_gposts WHEREMATCH(title) AGAINST ('医院' IN BOOLEAN MODE) limit 0,30;
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type |table
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
|
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
创建索引后
SELECT * FROM pa_gposts WHERE MATCH(title) AGAINST ('医院' IN BOOLEANMODE) limit 0,30;
30 rows in set (1.07 sec)
SELECT * FROM pa_gposts WHERE title LIKE '%医院%' limit 0,30;
30 rows in set (4.81 sec)
mysql> explain SELECT * FROM pa_gposts WHERE titleLIKE '%医院%' limit 0,30;
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type |table
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
|
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
mysql> explain SELECT * FROM pa_gposts WHEREMATCH(title) AGAINST ('医院' IN BOOLEAN MODE) limit 0,30;
+----+-------------+-----------+----------+-----------------+-----------------+---------+------+------+-------------+
| id | select_type |table
+----+-------------+-----------+----------+-----------------+-----------------+---------+------+------+-------------+
|
+----+-------------+-----------+----------+-----------------+-----------------+---------+------+------+-------------+
SELECT * FROM pa_gposts WHERE MATCH(title) AGAINST ('情侣' IN BOOLEANMODE) limit 0,30;
30 rows in set (1.93 sec)
SELECT * FROM pa_gposts WHERE title LIKE '%情侣%' limit 0,30;
30 rows in set (10.17 sec)
SELECT * FROM pa_gposts WHERE MATCH(title) AGAINST ('月光' IN BOOLEANMODE) limit 0,30;
13 rows in set (0.56 sec)
SELECT * FROM pa_gposts WHERE title LIKE '%月光%' limit 0,30;
13 rows in set (50.98 sec)
一个前导的加号表示该单词必须 出现在返回的每一行的开头位置。
一个前导的减号表示该单词一定不能出现在任何返回的行中。
在默认状态下(当没有指定 + 或–的情况下),该单词可有可无,但含有该单词的行等级较高。这和MATCH() ...AGAINST()不使用IN BOOLEAN MODE修改程序时的运作很类似。
这两个操作符用来改变一个单词对赋予某一行的相关值的影响。 > 操作符增强其影响,而<操作符则减弱其影响。请参见下面的例子。
括号用来将单词分成子表达式。括入括号的部分可以被嵌套。
一个前导的代字号用作否定符, 用来否定单词对该行相关性的影响。这对于标记“noise(无用信息)”的单词很有用。包含这类单词的行较其它行等级低,但因其可能会和-号同时使用,因而不会在任何时候都派出所有无用信息行。
星号用作截断符。于其它符号不同的是,它应当被追加到要截断的词上。
一个被括入双引号的短语 (‘"’)只和字面上包含该短语输入格式的行进行匹配。全文引擎将短语拆分成单词,在FULLTEXT索引中搜索该单词。
若索引中不存在该短语包含的单词,则结果为空。例如,若所有单词都是禁用词,或是长度都小于编入索引单词的最小长度,则结果为空。
以下例子展示了一些使用布尔全文符号的搜索字符串:
寻找包含至少两个单词中的一个的行。
寻找两个单词都包含的行。
寻找包含单词“apple”的行,若这些行也包含单词“macintosh”, 则列为更高等级。
寻找包含单词“apple” 但不包含单词 “macintosh”的行。
寻找包含单词“apple”和“turnover” 的行,或包含“apple” 和“strudel”的行 (无先后顺序),然而包含“apple turnover”的行较包含“apple strudel”的行排列等级更为高。
寻找包含“apple”、“apples”、“applesauce”或“applet”的行。
寻找包含原短语“some words”的行 (例如,包含“some words of wisdom”的行,而非包含