在上一篇文章《關係型SQL的全文搜尋 FullText 方案先行探究分析》中我簡單的介紹了一下現行常見的几種全文搜尋的方案。在其中我提到過這個由日本人開發的Mroonga全文搜尋引擎，由於此引擎在網路中鮮有資料能詢，索性我自己來寫一篇關於此引擎的用法，同時也會向大家説明此引擎在使用過程中如何避免踩坑。Mroonga的原理Mroonga實際上是一個MySQL系的關係型SQL中的外挂，但又與一般的Plugin有所區別。Mroonga實際上在MySQL中會被當作一個存儲引擎所被SQL辨識。然而實際上Mroonga其實只是一個中間件，他的核心帶了一個完整的Groonga。與SphinxSearch的MySQL外挂SphinxSE作爲一個中間件的原理類似，但又與SphinxSE不同的是Mroonga中已經自帶了Groonga，所以並不需要像使用了SphinxSE后還需安裝一個SphinxSearch本體程式。不管是SphinxSE還是Mroonga，兩者實現原理基本是一致的。雖説Mroonga也算是SphinxSearch師門中的一個弟子，但在實際使用過程中就能發現，Mroonga算是洗心革面重新做了人，與祖師SphinxSearch徹底不同的是，使用的作業指令與MySQL保持了最大的相容，基本可以説是MySQL的FullText全文搜尋指令如何寫，Mroonga就如何寫，格式可以説是一模一樣。反觀SphinxSearch，正宗俄國血統，作業指令基本就突出了一個暴力美學，與正常的SQL指令相差非常大。Mroonga的使用限制Mroonga的使用限制其實是源自Groonga的内部限制。表（table）的限制：單個Key（PRIMARY KEY或UNIQUE KEY）的大小最大為4KiB，也就是4KB。單個索引中所有Key（PRIMARY KEY或UNIQUE KEY）的大小最大為4GiB，也就是4GB。單表最大的記錄數（Maximum Records）：無主鍵的表（Not have PRIMARY KEY）最大記錄條數為1073741815條。差不多10億。有主鍵（PRIMARY KEY）或者使用了B樹（B-TREE）的主鍵（PRIMARY KEY USING BTREE）的最大記錄條數為1073741823條。差不多也是10億。使用了雜湊表（HASH）的主鍵（PRIMARY KEY USING HASH）的最大記錄條數為536870912條。差不多是5億。另外，如果表中的主鍵的值（Value of PRIMARY KEY）為非數字的情況下，例如是中文，那表將會無法變更為Mroonga。表中的列（Column）的限制：單個列（Column）的數據存儲大小最大為256GiB，也就是256GB。全文索引（FULLTEXT INDEX）的限制：單個全文索引中不重複條目的情況下最大可索引268435455條記錄，約2.68億。單個全文索引的大小最大為256GiB，也就是256GB。列（Column）中的值（Value）的限制：列（Column）中的值（Value）不能為NULL。如果你設置了NULL將會根據類型被自動轉換爲相應的值。擧個例子就是，比如你的值是DATE或者DATETIME，NULL將會被轉換成0或者1970-01-01 00:00:00。字符串類型（String）的值將會被轉換成空字符（empty）。其實與NULL沒區別但就是不能是NULL的意思。此限制根據官方的説明說的是只會影響存儲模式（Storage Mode），但實際上也同樣會影響中間件模式（Wrapper Mode）的索引内容。這限制直接導致了以下類似的SQL指令無法使用：... VALUES (NULL)... WHERE xx = NULL無法使用的SQL指令：CREATE TABLE (...) CHARSET not_listed_charset_aboveINSERT INTO (geometry_column_name) VALUES (GeomFromText('LineString(...)'))INSERT INTO (...) VALUES (NULL)START TRANSACTION僅支援以下類型的字符集（Character）：ASCIIBINARYCP932EUCJPMSKOI8RLATIN1SJISUJISUTF8UTF8MB4字符集（Character）的使用是可以支援該字符集的排序規則（Collations）的。例如utf8_general_ci等。Mroonga的工作模式選擇Mroonga有兩種工作模式，一種是存儲模式（Storage Mode），另一種是中間件模式（或者稱為包裝器模式，Wrapper Mode）。這兩種模式的作業原理不一樣，下面將簡單説明一下兩種模式的原理與有什麽優缺點。存儲模式Storage Mode在此模式下，Mroonga將作爲一種存儲引擎與MyISAM或InnoDB一樣，直接提供存儲功能與SQL進行交互。在MySQL中我們直接查看的話，表的存儲引擎都會展示為Mroonga。但由於Mroonga實際上只是個中間件，實際上數據都會存儲在Groonga中。所以我們可以簡單的理解為，在此模式下，所有的數據并不存放在SQL中，而是直接存儲在Groonga中。存儲模式的優點：存儲模式帶來的優點是顯而易見的，一是更高的搜尋速度與更低的性能開銷。這裏我拿Mroonga與MyISAM進行對比。衆所周知，MyISAM在執行搜尋作業（SELECT）之時，會對所涉及的表自動加上讀取鎖（Read Lock），同時在更改數據之時（UPDATE/DELETE/INSERT等），又會對所涉及的表自動加上寫入鎖（Write Lock）。但Mroonga在執行搜尋作業的時候并不會對表進行加鎖作業，所以搜尋效能會比MyISAM所使用的性能開銷更小。同時在數據更新之後Groonga内部能做到即時更新索引，且開銷會比中間件模式更低。二是在存儲模式下，數據只會存儲一份。這意味著占用硬碟的空間會比中間件模式更小。這個優點其實說的有點牽强，但我們可以來看看現在企業級固態硬碟SSD的價格與容量，在荷包不是特別充裕的情況下，這點就有點重要了。存儲模式的缺點：但存儲模式并不是全是優點，相反此模式下的缺點也非常突出。首先，存儲模式下的Mroonga存儲引擎不支援事務（Transaction）。這點上來説，如果你之前使用的是MyISAM引擎準備遷移至Mroonga的話，這是沒問題的。但如果你使用的是InnoDB，那就千萬別直接遷移至Mroonga的存儲模式。缺點二，在不使用SSD固態硬碟的情況下，同時表内數據量較大之時，除了全文搜尋Full Text Search以外，一定時間内的首次查詢速度都會比MyISAM慢。因爲Groonga使用的是列存儲（Column Based Storage）而不是行存儲（Row Based Storage），Mroonga需要從Groonga中提取數據經過轉換后才會交給SQL，這個轉換過程很明顯是需要時間和性能開銷的。然而如果只是取一行數據的話，那這個轉換過程的時間與性能的開銷可以忽略不計。但如果一次取個几千條幾萬條數據呢？例如這句LIMIT 9900, 100我要取這10000條數據中的最后100條且我要求是這行的所有數據都取出來並不使用全文搜尋的方式而使用一般的SELECT，那Mroonga就需要同時定位所有的列到此位置並同時取出進行轉換，同時Groonga的檔案存儲方式使用了分散式存儲，按照列分散的存儲在了不同的文件中，這個讀取數據過程將會占用非常高的IO資源。如果這時使用的是HDD機械硬碟，將直接導致查詢速度肉眼可見的龜速。但這個問題其實也是可以解決的，優化的辦法就是使用SSD固態硬碟。因爲隨便一個正常的SSD固態硬碟，讀取的IOPS都是幾萬以上。而HDD機械硬碟的讀取IOPS都是70~100 IOPS，就算是10000RPM的企業級SAS HDD，其讀取的IOPS也不會超過230 IOPS。但以至於爲何是一定時間内的首次查詢速度都會比MyISAM慢呢而不是每次都慢的原因是有快取（Cache）的存在。當然如果快取過期了下次查詢的速度又會慢了。但爲何全文搜尋Full Text Search的速度就沒龜速呢，直接原因就是全文搜尋是在Groonga建立的FullText Index索引中進行特徵碼搜尋，之後就能直接快速的提出所需要的數據。缺點三，這個問題其實是個玄學問題。Mroonga的存儲模式穩定性存在不確定因素。這個其實我并沒有直接的測試數據與證據能直接證明Mroonga作爲存儲引擎的可靠性不行，但由於這種直接替代的存儲引擎的作業方式是否能保證數據的安定？這個可能還需要時間的檢驗。綜上所述，如果要使用存儲模式進行作業，我能給出的建議是：使用可靠性和性能都高的SSD固態硬碟，同時記憶體最好大於或者等於16GB，CPU性能最好不要太弱（比如Celeron或者Atom就算了吧）。在使用SELECT進行查詢作業的時候，做到需要什麽值取什麽值，例如SELECT value1, value2，不要直接SELECT *全部取了。如果是從老數據庫遷移過來的話，最好是MyISAM遷移至Mroonga。如果可以建議將整個庫的所有表都變更為Mroonga存儲引擎，避免出現JOIN了不是Mroonga的表影響性能。備份作業一定要做好。很明顯，此模式比較適合不想做太多改動就能用上全文搜尋引擎和主要是以搜尋為主要服務的項目。唯獨不適用于需要使用InnoDB特性的項目，例如EC電子商務平臺等對支援事務作業的項目。中間件模式（包裝器模式，Wrapper Mode）在此模式下，Mroonga將作爲SQL處理模組（SQL Handler）與存儲引擎之間的一個中間件。Mroonga只會處理FullText的全文搜尋SQL指令。按照官方給出的説明圖中我們可以看出Mroonga作爲一個存儲引擎展現給SQL處理模組，但實際數據將會全部交給其他存儲引擎，同時將所有除FullText全文搜尋指令以外的指令都交由其存儲引擎來進行處理。實際上我們可以這麽理解，Mroonga的中間件模式下，對MySQL而言，MySQL依舊是將Mroonga認作一個存儲引擎，實際上Mroonga内部包了一層其他存儲引擎做數據的存儲。但官方未説明的是，Groonga此時又做了什麽呢。根據我翻看其原始程式碼的結果來看，實際上此實現并非想象中的複雜，Mroonga作爲一個中間件，除了SELECT指令以外的SQL指令，都將會同時發給其他存儲引擎與Groonga。意思就是Groonga内依舊留有一份數據。但由於Mroonga只在處理FullText全文搜尋指令之時，才會只向Groonga發送搜尋指令。其他的SELECT指令都會只交給其他存儲引擎處理。但由於Groonga并不支援事務（Transaction）作業，如果使用的是InnoDB的特性或者一些特有機能的話，例如事務作業，Mroonga將會在事務作業結束後根據結果向Groonga同步一次存儲引擎内的數據並更新全文搜尋索引。然而實際上并非所有的InnoDB事務作業指令都能成功轉換為非事務作業，所以Mroonga會定期向Groonga發送一次同步存儲引擎内的數據並重建索引的作業指令。中間件模式的優點：很明顯，中間件模式下能支援其他存儲引擎的特性了。例如InnoDB的事務作業等等。實際上這個模式下的原理與我們使用其他獨立的全文搜尋引擎的原理是一樣的。例如SphinxSearch的直接連接SQL模式而非通過SphinxSE插件連接，在全文搜尋引擎内部與SQL中都存有一份數據來做各自的處理。這樣就能實現互不干擾互相獨立作業。中間件模式的缺點：中間件模式也非常明顯，通過上面的原理分析我們能很輕鬆的看出在數據更改作業下，寫入的性能開銷會非常大。事實上也是如此。畢竟要同時向兩邊寫入數據更新，如是使用了一些Groonga本身并不能支援的特性的情況下，例如事務作業，Mroonga只能采取全量數據更新並重建索引的作業。當然，如果在表中數據量並不多的情況下，這個處理資源占用并不會非常明顯。但如果是一個幾十萬起步的表，在執行以上操作之時將會占用大量的CPU、IO與記憶體資源。缺點二，在使用了一些Groonga本身并不能支援的特性的情況下，更改寫入數據之後在一定時間内會出現存儲引擎内的數據内容與Groonga中的數據内容與全文搜尋索引不一致的情況。造成這點的原因也可以從上面說的原理中看出，重新更新數據與重建索引是需要一定處理時間的，在處理完成之前數據肯定是會出現不同步的情況。缺點三，這問題其實還是個玄學問題。還是這個中間件模式的可靠性存疑。雖然Mroonga在中間件模式下的實現原理與SphinxSearch的普通索引模式一致，但不代表Mroonga就與SphinxSearch一樣并不會影響到MySQL。例如SphinxSearch在自身進程崩潰的情況下，他并不會影響到MySQL的作業。但Mroonga并不是這樣。Mroonga就算是使用中間件模式，SQL處理模組依舊是將此表認定為Mroonga存儲引擎並將所有的指令都交給Mroonga來進行處理。而就算使用了其他存儲引擎進行存儲，也都是被包在Mroonga中。所以能很明顯看出一點就是，如果Mroonga的中間件出現了些什麽問題，最壞的情況也會導致數據損毀。綜上所述，如果要使用中間件模式進行作業，我能給出的建議是：使用可靠性和性能都高的SSD固態硬碟，同時記憶體最好大於或者等於16GB，CPU性能最好不要太弱（比如Celeron或者Atom就算了吧）。建議將所有可能涉及到FullText全文搜尋作業的表都轉變為此模式以免造成JOIN了不是Mroonga的表影響性能。備份作業一定一定一定要做好。此模式很明顯，如果你不是使用什麽InnoDB的特性的話，例如事務作業等等，我并不推薦使用此模式。我們對數據庫的要求最低底綫是保證數據安全。但很明顯此模式下會造成數據損毀的隱患并不小。這個道理其實很簡單，結構越複雜的東西出錯的可能性越高。Mroonga的安裝Mroonga的安裝我大致給他分成兩種，一種是獨立安裝Mroonga，另一種是直接使用高版本MariaDB自帶的Mroonga。獨立安裝Mroonga獨立安裝Mroonga的話可以參考他們的官方文檔《如何安裝Mroonga》，因爲針對系統與使用的SQL不用，都有不一樣的安裝方法，這裏就不細講了。畢竟以後如果安裝方式有任何更新，官方文檔肯定更新的最快，所以我在這邊細説如何安裝也沒有任何意義。這種方式適合使用MySQL、Percona或者是沒有自帶Mroonga的MariaDB版本或者想在已經自帶了Mroonga的MariaDB上安裝新版本的Mroonga。使用自帶Mroonga的MariaDB這種方式其實對於像我這種大部分都在用MariaDB的人來説，是最簡單的方式。雖然簡單，但MariaDB自帶的Mroonga版本并不是最新的，且并不是所有的MariaDB版本都自帶了Mroonga。如果要使用自帶Mroonga的MariaDB，需要安裝以下版本號或以上的MariaDB：在上圖所示的MariaDB版本或者更新的版本中，我們只需使用MySQL Client連接至MariaDB的命令行控制臺中使用以下命令就能啓用：mysql> INSTALL SONAME 'ha_mroonga'; 但如果你現在所安裝的MariaDB版本中并無自帶Mroonga并且同時使用的是Ubuntu或者Debian的系統，且你的MariaDB是通過APT包管理器安裝的，你可以使用以下命令安裝Mroonga的MariaDB外挂：sudo apt-get install mariadb-plugin-mroonga 除此之外，請使用獨立安裝Mroonga的方式進行安裝。安裝完畢后，我們可以在MySQL命令行控制臺中使用以下命令查看Mroonga引擎的情況：mysql> SHOW ENGINES; 正常的話此命令的展示結果類似下面這樣：... *************************** 8. row *************************** Engine: Mroonga Support: YES Comment: CJK-ready fulltext search, column store Transactions: NO XA: NO Savepoints: NO ... 如果你想能獲取到Groonga中的最後一條插入的數據的ID，可以使用以下的命令新增一個用戶自定義函數（UDF, User-Defined Function）：mysql> CREATE FUNCTION last_insert_grn_id RETURNS INTEGER SONAME 'ha_mroonga.so'; 這樣就能使用last_insert_grn_id這個函數獲取到Groonga中的最後一條插入的數據的ID。Mroonga的使用正如本文標題所説，我的目的是用Mroonga的全文搜尋取代LIKE模糊搜尋機能。看到這裏肯定有人會問，爲什麽拿一個全文搜尋引擎來替代LIKE模糊搜尋機能呢？這個問題的答案其實很簡單，我需要更高的搜尋性能和更低的伺服器性能占用。且我研究了這麽多年的全文搜尋引擎的經驗來看，一般全文搜尋引擎中都要經歷一個斷詞的作業。然而根據我的實際經驗來看，實際上斷詞這個作業只是爲了降低全文搜尋引擎壓力而必須的一個作業而已，實際上還會因爲斷詞誤差導致搜尋結果不準確。我對搜尋的定義其實就是要求他對關鍵詞準確匹配，在 CJK 語言（中國語 Chinese，日本語 Japanese，韓國語 Korean）中實際上準確率最高的搜尋方式就是將一句話例如“我吃飽了”拆成一個個字進行搜尋。在LIKE模糊搜尋下我一般是這麽處理：SELECT ... WHERE (`field` LIKE '%我%') AND (`field` LIKE '%吃%') AND (`field` LIKE '%飽%') AND (`field` LIKE '%了%') LIMIT 50; 類似如此的處理方式來進行搜尋。當然，肉眼可見在一個大型數據庫中這搜尋速度將會十分龜速，以上這句搜尋指令將會對全表進行最低4次的掃描。以此爲基礎我要如何使用Mroonga進行替代操作呢？接下來將會從零開始説明如何一步步從模糊搜尋切換為Mroonga。1. 將表的存儲改爲Mroonga不管是要用存儲模式還是中間件模式，都得將表的存儲引擎改爲Mroonga，唯一的區別就只是是否對表的備注説明中加入模式的關鍵詞。將現有的表更改為存儲模式：mysql> ALTER TABLE `table` ENGINE = Mroonga; 將現有的表更改為中間件模式：mysql> ALTER TABLE `table` ENGINE = Mroonga COMMENT = 'engine "MyISAM"'; 這裏的MyISAM可以選擇InnoDB。但如果只是新建一個表，那一切就容易的多了。新建一個表並使用存儲模式：mysql> CREATE TABLE `table` ( id INT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(255), FULLTEXT INDEX (content) ) ENGINE = Mroonga DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; utf8mb4與utf8mb4_general_ci也可按需要選擇。新建一個表並使用中間件模式：mysql> CREATE TABLE `table` ( id INT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(255), FULLTEXT INDEX (content) ) ENGINE = Mroonga COMMENT = 'engine "MyISAM"' DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; 從上面的指令可以很簡單的看出，存儲模式與中間件模式的選擇只是COMMENT的内容有無而已。中間件模式需要對COMMENT加上相應的標記COMMENT = 'engine "MyISAM"'。2. 建立全文索引並選擇相應的解析器由於Mroonga的目的是做到最大兼容，所以我們要使用全文搜尋之前必須得為此建立一個全文搜尋索引。同時，Mroonga的解析器的概念與MySQL的FullText索引的解析器概念也是一樣的。唯一不同的是，Mroonga爲了更方便的熱切換解析器，解析器選擇依舊是用了對COMMENT添加描述關鍵詞來進行設置。Mroonga的解析器其實有15種可選，由於我的目標是替代模糊搜尋，所以我選擇的解析器為TokenBigramSplitSymbolAlphaDigit。由於正常的模糊搜尋是會對空格敏感的，所以要做到100%替代的話不能使用TokenBigramIgnoreBlankSplitSymbolAlphaDigit這個解析器。其他的解析器的説明可以參考Mroonga官方的説明文檔《如何為全文搜尋索引指定解析器》。新建全文搜尋索引並指定解析器：mysql> FULLTEXT INDEX (column_fulltext) COMMENT 'tokenizer "TokenBigramSplitSymbolAlphaDigit"' 當然，這個解析器是可以熱更換的，我們直接修改COMMENT的内容即可。更換全文搜尋索引的解析器：mysql> ALTER TABLE `DataBase`.`table` ADD FULLTEXT `column_fulltext` (`column`) COMMENT 'tokenizer \"TokenBigramSplitSymbolAlphaDigit\"'; 從上面的指令我們還是能看出Mroonga的設計理念，爲了最大化兼容MySQL的正常SQL指令，并沒有使用一些非正常指令。但是這設定解析器的方式是有限制的，限制來源於MySQL。因爲在MySQL 5.5版本開始才支持對索引設置COMMENT描述内容。所以如果你使用的是低於這個版本的MySQL，需要你在my.cnf中設定一個固定的參數mroonga_default_tokenizer=TokenBigramSplitSymbolAlphaDigit來進行指定解析器的操作。然而這樣就無法做到熱設置解析器了。3. 使用全文搜尋索引進行全文搜尋到這步，我們就已經可以開始使用Mroonga來進行全文搜尋了。Mroonga的全文搜尋指令與正常的MySQL FullText全文搜尋使用的語法結構是一致的。Mroonga的全文搜尋模式有兩種，一種是普通的布林搜尋模式（BOOLEAN MODE），另一種是自然語言模式（NATURAL LANGUAGE MODE）。簡單來説，就是布林搜尋模式下只有匹配與不匹配，沒有其他結果，類似的使用場景為精準搜尋。而自然語言模式下提供的並不是一個精準搜尋而是提供一個近似内容，類似的使用場景爲搜尋關鍵字補全候選功能等。其實這兩個模式也是MySQL的FullText所提供的標準模式，所以用法也與MySQL沒有區別。回歸到我的目標上，我要替代模糊搜尋，例如以下這條LIKE指令：SELECT ... WHERE (`field` LIKE '%我%') AND (`field` LIKE '%吃%') AND (`field` LIKE '%飽%') AND (`field` LIKE '%了%') LIMIT 50; 使用Mroonga我們需要這麽寫：SELECT ...USE INDEX(column_fulltext) WHERE (MATCH(field) AGAINST('*D+ "我" "吃" "飽" "了"' IN BOOLEAN MODE)) LIMIT 50; 同時以下這條指令與上面這條指令的效果一致：SELECT ...USE INDEX(column_fulltext) WHERE (MATCH(field) AGAINST('*D+ "我"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "吃"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "飽"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "了"' IN BOOLEAN MODE)) LIMIT 50; 以上是關鍵詞的AND搜尋示例。那如果我要使用的是多個關鍵詞中只要出現一個就命中的OR模式呢？例如以下這條指令：SELECT ... WHERE (`field` LIKE '%我%' OR `field` LIKE '%吃%' OR `field` LIKE '%飽%' OR `field` LIKE '%了%') LIMIT 50; 使用Mroonga我們需要這麽寫：SELECT ...USE INDEX(column_fulltext) WHERE (MATCH(field) AGAINST('*D+ "我" OR "吃" OR "飽" OR "了"' IN BOOLEAN MODE)) LIMIT 50; 那如果我要AND和OR混寫呢？無問題，例如以下這條：SELECT ... WHERE (`field` LIKE '%我%') AND (`field` LIKE '%吃%') AND (`field` LIKE '%飽%') AND (`field` LIKE '%了%') AND (`field` LIKE '%塞%' OR `field` LIKE '%林%' OR `field` LIKE '%老%' OR `field` LIKE '%目%') LIMIT 50; 使用Mroonga我們可以這麽寫：SELECT ...USE INDEX(column_fulltext) WHERE (MATCH(field) AGAINST('*D+ "塞" OR "林" OR "老" OR "目"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "我" "吃" "飽" "了"' IN BOOLEAN MODE)) LIMIT 50; 同理，也可以這樣寫：SELECT ...USE INDEX(column_fulltext) WHERE (MATCH(field) AGAINST('*D+ "塞" OR "林" OR "老" OR "目"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "我"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "吃"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "飽"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "了"' IN BOOLEAN MODE)) LIMIT 50; 那麽假如我要在搜尋的同時去掉某些關鍵詞呢？例如以下這樣的指令：SELECT ... WHERE (`field` LIKE '%我%') AND (`field` LIKE '%吃%') AND (`field` LIKE '%飽%') AND (`field` LIKE '%了%') AND (`field` LIKE '%塞%' OR `field` LIKE '%林%' OR `field` LIKE '%老%' OR `field` LIKE '%目%') AND (`field` NOT LIKE '%靠北%') LIMIT 50; 使用Mroonga我們可以這麽寫：SELECT ...USE INDEX(column_fulltext) WHERE (MATCH(field) AGAINST('*D+ "塞" OR "林" OR "老" OR "目" -"靠北"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "我" "吃" "飽" "了" -"靠北"' IN BOOLEAN MODE)) LIMIT 50; 或者這樣：SELECT ...USE INDEX(column_fulltext) WHERE (MATCH(field) AGAINST('*D+ "塞" OR "林" OR "老" OR "目"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "我"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "吃"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "飽"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ "了"' IN BOOLEAN MODE)) AND (MATCH(field) AGAINST('*D+ -"靠北"' IN BOOLEAN MODE)) LIMIT 50; 以上就是使用Mroonga進行全文搜尋的一些例子。我們可以總結一下：AND關鍵詞使用方式： ...AGAINST('*D+ "我" "吃"' IN ... 或者 ...AGAINST('*D+ "我"' IN ... AND ...AGAINST('*D+ "吃"' IN ... OR關鍵詞使用方式： ...AGAINST('*D+ "我" OR "吃"' IN ... 不需要的關鍵詞使用方式： ...AGAINST('*D+ "我" OR "吃" -"靠北"' IN ... 可以看到，如果我們希望過濾掉搜尋結果中出現某個關鍵詞的結果，我們可以使用-關鍵詞的方式設置一個過濾。這麽看下來，可能大家還有最後一個問題，這個*D+是什麽鬼。其實這個*D+的用處是指定默認搜尋模式為AND搜尋。如果使用的*D那就是默認模式為OR搜尋。如果不加此模式指定的話，那我們必須在每個關鍵詞前指定這個關鍵是究竟是AND還是OR還是NOT。他們的關係是這樣的：'+關鍵詞' 代表著這個關鍵詞為AND，必須存在。'-關鍵詞' 代表著這個關鍵詞為NOT，不能出現。'關鍵詞' 什麽都不加的話代表著這個關鍵詞為OR，有就出現沒有就算了。同理，如果我們不指定默認運算符的話，將下面這條指令變成Mroonga指令：SELECT ... WHERE (`field` LIKE '%我%') AND (`field` LIKE '%吃%') AND (`field` LIKE '%飽%') AND (`field` LIKE '%了%') AND (`field` LIKE '%塞%' OR `field` LIKE '%林%' OR `field` LIKE '%老%' OR `field` LIKE '%目%') AND (`field` NOT LIKE '%靠北%') LIMIT 50; 變成不設置默認搜尋模式的Mroonga搜尋指令的話就應該是這樣：SELECT ...USE INDEX(column_fulltext) WHERE (MATCH(field) AGAINST('+"我" +"吃" +"飽" +"了" "塞" "林" "老" "目" -"靠北"' IN BOOLEAN MODE)) LIMIT 50; 是不是更簡單了呢？是不是有人想問了爲什麽一開始不告訴大家有這麽一種簡單的寫法？其實原因很簡單，這種簡單的寫法很容易出現定義不清晰導致搜尋出不是想要的結果，而且有很多複雜的搜尋指令是完全無法實現的。所以盡量還是別使用這種簡單的指令來進行搜尋。最後，其實Mroonga還支持正則表達式的搜尋，但前提條件是將解析器改爲TokenRegexp(COMMENT 'tokenizer "TokenRegexp")，示例如下：SELECT ...USE INDEX(column_fulltext) WHERE MATCH(field) AGAINST ('*SS content @~ "\\\\A/var/log/auth"' IN BOOLEAN MODE); 具體的正則表達式搜尋可參考官方文檔《如何使用正則表達式進行搜尋》。總結説實話，我最開始尋找全文搜尋引擎的原因就是需要找一個能花最少力氣修改原始程式碼就能從模糊搜尋遷移的方案。在我測試過幾乎所有的全文搜尋引擎之後，幾乎沒有一款能滿足我的需求。直到我知道了Mroonga的存在之後進行了相應的測試，我發現Mroonga最合適的用途就是這個，花最少的力氣替代LIKE模糊搜尋。當然我并不是說其他的全文搜尋引擎不好，只是不合適這個需求。誠然，我承認Mroonga的效能在所有全文搜尋引擎裏并不是最好的，但我要求本來也不高，只要能比MySQL原生的模糊搜尋快就行了。事實上Mroonga也勝任了這點。Mroonga力求不過多改變MySQL的使用體驗應該說是他最大的亮點。所以儅你有類似的需求的時候，不妨來考慮一下Mroonga。