先來看看對一個欄位做全文索引,作為一個資料庫系統需要做哪些工作?
假設一個文章表裡面包含幾個欄位:文章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,曾經在公司的伺服器上配置過,通過那次配置加深了對它的理解。中途遇到一些問題,一直想以文字的形式總結出來,以備忘。有時間會上一篇關於它的操作總結出來。
個人理解不正確之處,歡迎指正!
本文未完待補充