mysql中文进行全文索引支持问题
先来看看对一个字段做全文索引,作为一个数据库系统需要做哪些工作?
假设一个文章表里面包含几个字段:文章id、文章作者、文章标题、文章内容
比如,我们对文章内容这个字段artilce_content建立全文索引,这样方便对文章里面的内容使用关键词搜索。
数据库系统首先需要从文章内容(这个字段内容)中提取关键词,因为只有提取了关键词,才好建立类似索引目录—类似于新华字典那样的笔画页码对应关系,当你搜索一个词语”beijing”的时候,就直接去索引中查找,然后就可以定位到数据行的具体位置了,不需要扫描文章表很多行。
这就像,如果我想去新华字典中查找一个汉字,我直接按照笔画或者按照拼音来查找(这个就是新华字典的索引),避免把新华字典从头翻到尾部,这样的效率多低。没错,如果没有索引,数据库系统就是需要这样子遍历扫描整个表才能得到数据的。
ps:机器其实是死的,你写的代码让他做什么就做什么。而人有眼睛,可以通过眼睛来看,比如大体翻看一下新华字典,也许眼睛就看到我需要的汉字了呢。
数据库系统中的关于那个索引结构大体可以这样理解:
关键词 包含此关键词的文档列表(也可以理解成行)
beijing 1,2,3…
索引就是方便快速查找,根据这个索引结构,搜索的时候就可以快速定位到数据位置了。
mysql对一个字段做全文索引的时候,他从文章内容中提取关键词,
mysql的全文检索解析器在提取关键词默认是按照空格来识别单词的。也就是中文”我喜欢打篮球” 这一串文字中没有空格,那么mysql不会把这个当成好几个单词的。
如果故意进行空格分开,mysql就容易识别处理关键词,比如变成”我 喜欢 打 篮球”
像上面这样子,mysql可能会把篮球当成关键词提取出来进入索引中去。为什么只是可能?有空格只是保证mysql识别成单词,但会不会建立成索引,其实我不是很清楚。因为没看过mysql源码,不是很清楚。
因为对文章表的”文章内容”字段建立了成了全文索引,那么每次往表中新加入一行数据,数据库系统都会去提取文章新行数据中的文章内容字段,看有没有关键词,继续往索引表里面加单词或者更新数据。比如原来的索引文件有个单词”beijing”,现在新加入的文章内容中提取到有beijing这个词语,那要更新索引表了,意思是记录下新加的文档哪里存在beijing这个词语。
beijing 8,9,10
新加入的文档id为20,刚好其中文章内容被提取到了词语beijing
那么索引就要更新成如下形式:
beijing 8,9,20
这样解释是方便理解,理解就好。
大体是这样子的模式。具体实现会跟这个存在差异的。把复杂的问题解释得通俗化,简单化是而容易理解,是我进行总结的目的。
ps:mysql的一个表的所有字段的索引数据都在一张一个”表名称.MYI”文件中。
理解了上面的实现原理,
现在也好理解一句话了,这是从mysql手册中中的一句话:
对于较大的数据集,将你的资料输入一个没有FULLTEXT索引的表中,然后创建索引, 其速度比把资料输入现有FULLTEXT索引的速度更为快。
我是这么理解,比如,把100行数据同时插入文章表中,而文章内容字段是建立成全文索引,那么新插入的数据在入库的同时,要提取关键词(对文章内容字段),然后更新索引,速度当然会慢下来。关键问题就是在插入数据的时候就会去分词和更新索引。整个insert 操作就会延长时间了。
上面已经建立文章内容字段为全文索引,现在mysql是怎么进行全文查询的呢?
select id,title FROM 文章表 WHERE MATCH(article_content) AGAINST ('search keyword')
match()中指定字段名称。表示against()中出现的字符串要去哪个字段中匹配。这里可以指定多个字段
against()中的字符串,难道不是单个词语,mysql会自动对受到的字符串进行分词吗?
AGAINST ('+MySQL -YourSQL' IN BOOLEAN MODE);
+号表示,MYSQL这个单词必须出现在每一行的开头位置?
‘’里面是单词,多个单词之间需要使用空格分开吗?
来看看对英文进行分词有什么天然优势
英文的特点,每个英文单词就是一个词语,单词与单词之间有非常明显的分割符号—空格
比如
my name is wangxiaoming
this city is beijing…..
citye 和beijing可以看成是两个关键词,用来搜索
mysql内部对要提取关键词的时候,哪些可以作为关键词来建立索引呢?
它可以以空格来分割开来。把city 和 beijing都当成是两个关键词来建立索引(理解成新华字典中的笔画与页数的目录结构)。
总结一下
英文:除了少数特殊字符和标点几乎都是以空格分隔的,所以要对一段英文进行分词(也就是从中提取关键词),这样子比较容易。按照空格、逗号等特别的符号来识别即可。
来看看,中文分词为什么就比较麻烦
由于中文比较特殊,像”理发师” 理发可以是一个关键词,理发师也可以是一个关键词。关键词之间并没有明显的分割符号,我们看到紧挨着的。这就需要mysql去提取哪些是关键词。是把”理”发作为关键词来索引,还是”理发”或者”理发师”都算关键词,没法识别。
一般百度这些搜索引擎是怎么提取关键词,他们有个专门的关键词库的,也就是中文关键词词库。这样就知道把什么当成是关键词。显然,mysql是又国外人做的一个软件,它不会单独给你去做一个关键词库。
计算机怎么知道哪个算是词语呢?比如输入一串文字 ”我来自北京,我很喜欢运动、听音乐…,喜欢听周华健的歌曲”
在这一句话中,北京算不算关键词呢。”周华健”算不算关键词呢?
像我们常见的中文切词,我了解到有几种分词算法:
1、 就是根据词库来进行对比的。
2、 二元分词法
3、 统计法
对比:二元分词方法和统计方法是不依赖于词典的,而最大匹配法分词方法是依赖于词典的,词典的内容决定分词结构的好坏。
现在总结一下:MySQL不会断中文字:MySQL内建的字依据是空白、逗号和点来断词语。
mysql手册中原文如下:
FULLTEXT分析程序会通过寻找某些分隔符来确定单词的起始位置和结束位置,例如' ' (间隔符号)、 , (逗号)以及 . (句号 )。假如单词没有被分隔符分开,(例如在中文里 ), 则 FULLTEXT 分析程序不能确定一个词的起始位置和结束位置。为了能够在这样的语言中向FULLTEXT 索引添加单词或其它编入索引的术语,你必须对它们进行预处理,使其被一些诸如"之类的任意分隔符分隔开。
…………………
诸如汉语和日语这样的表意语言没有自定界符。因此, FULLTEXT分析程序不能确定在这些或其它的这类语言中词的起始和结束的位置。其隐含操作及该问题的一些工作区在12.7节,“全文搜索功能”有详细论述。
==================================结束
中文与西方文字如英文的一个重要区别在于,西方文字以单词为单位,单词与单词之间以空格分隔。而中文以字为单位,词由一个或多个字组成,词与词之间没有空格分隔。当试图在一个含有中文字符的字段中使用全文搜索时,不会得到正确的结果,原因在于中文中没有像英文空格那样对词定界,不能以空格作为分割,(于是不方便)对中文词语进行索引。
现在归纳一下,mysql不能很好地支持中文全文索引的解决办法
1、针对对myql全文检索解析器内建机制特点,白痴解决方法是,存中文字时自行塞入空白断字。
这样就适应了mysql的切词机制了。不过这样子做比较别扭。因为哪有把”我是中国人”古意分开成”我 是 中国人”这样的形式呢。
显示文章内容的时候就比较别扭,不能显示成”我 是 中国人”给用户看,需要需要自己再次处理。
2、使用切词插件。mysql应该是意识到单靠自己来提供分词,永远无法满足世界上各种各样语言的特殊需求。于是从5.1版本开始,Mysql全文检索的解析器以插件的方式提供。让大家可以以插件的形式挂到mysql下面去(实际上就是作为mysql的一个存储引擎,比如sphinx就是插件挂上去)
挂载到mysql中的插件所完成是一个什么样的角色呢?
使用插件,就是可以按照你自己的方式去分词
当数据量很大的时候,比较成熟的做法是使用专门的全文索引系统,用这些专业的全文索引系统来分词,以mysql数据库中的数据作为数据源,来分词建立索引结构。查询的时候,先从全文索引系统中查询,获取文档编号,然后根据文档编号去mysql中查询数据。对于全文搜索插件sphinx-for-chinese,曾经在公司的服务器上配置过,通过那次配置加深了对它的理解。中途遇到一些问题,一直想以文字的形式总结出来,以备忘。有时间会上一篇关于它的操作总结出来。
个人理解不正确之处,欢迎指正!
本文未完待补充