InnoDB全文索引


### 如果想了解全文索引,可以直接將本文復制到mysql的新建查詢中,依次執行,即可了解全文索引的相關內容及特性。

-- InnoDB全文索引

-- 建表
CREATE TABLE fts_a (
	FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL,
	body TEXT,
PRIMARY KEY(FTS_DOC_ID)
);

-- 插入數據
INSERT INTO fts_a
	SELECT NULL, 'Please poorridge in the pot';
INSERT INTO fts_a
	SELECT NULL, 'Please poorridge hot, pease porridge cold';
INSERT INTO fts_a
	SELECT NULL, 'Nine days old';
INSERT INTO fts_a
	SELECT NULL, 'Some like it hot, some like it cold';
INSERT INTO fts_a
	SELECT NULL, 'Some like it in the pot';
INSERT INTO fts_a
	SELECT NULL, 'Nine days old';
INSERT INTO fts_a
	SELECT NULL, 'I like code days';

-- 建立全文索引
CREATE FULLTEXT INDEX idx_fts ON fts_a(body);

-- 創建完成后觀察表fts_a中數據
SELECT * FROM fts_a;

-- 通過設置參數innodb_ft_aux_table來查看分詞對應的信息
SET GLOBAL innodb_ft_aux_table="mytest/fts_a";   

-- 查看分詞對應的信息
SELECT * FROM information_schema.INNODB_FT_INDEX_TABLE;

-- 每個word都對應一個DOC_ID 和 POSITION, (7,7)第一個7代表了第7個文檔的,第二個7代表在文檔中的起始位置
-- 除此之外還有FIRST_DOC_ID, LAST_DOC_ID, DOC_COUNT 分別代表了該word第一次出現的文檔ID,最后一次出現的文檔ID,以及該word一共在多少個文檔中存在。

-- 此時我們刪除ID為7的文檔
DELETE FROM mytest.fts_a WHERE FTS_DOC_ID=7;

-- 此時再次查看分詞對應的信息,發現分詞信息未發生變化
SELECT * FROM information_schema.INNODB_FT_INDEX_TABLE;

-- 這是因為InnoDB並不會直接刪除索引中對應的記錄,而是將刪除的文檔ID插入到DELETED表,因此我們進行如下查詢:
SELECT * FROM information_schema.INNODB_FT_DELETED;

-- 可以看到刪除的文檔ID已經插入到了INNODB_FT_DELETED表中,這樣看將來索引只會越來越多而不會減少,如果想要徹底刪除倒排索引中該文檔的分詞信息,可以執行如下操作:

	-- 設置此參數為1,將僅進行倒排索引的操作,否則還會對Cardinality重新統計等其它操作
	SET GLOBAL innodb_optimize_fulltext_only=1;  

	-- 徹底刪除索引
	OPTIMIZE TABLE mytest.fts_a;
	-- 此時第三次查看分詞對應的信息,發現分詞信息已經更新
	SELECT * FROM information_schema.INNODB_FT_INDEX_TABLE;
	-- 但是,INNODB_FT_DELETED中的記錄仍在:
	SELECT * FROM information_schema.INNODB_FT_DELETED;
	-- 並且,會將徹底刪除的文檔ID記錄到表INNNODB_FT_BEING_DELETED中。
	SELECT * FROM information_schema.INNODB_FT_BEING_DELETED;
	-- 並且不允許再次插入這個文檔ID,否則數據庫會異常 // Invalid InnoDB FTS Doc ID
	INSERT INTO fts_a
		SELECT 7, 'I like code days';
		
-- 還有一個概念,stopword列表,該列表中的word不需要進行索引分詞操作。比如 the 這個單詞沒有實際意義,故不進行分詞。
-- InnoDB有一張默認的stopword表,在information_schema架構下,表名為INNODB_FT_DEFAULT_STOPWORD, 默認共有36個stopword。
-- 你也可以通過參數innodb_ft_server_stopword_table來自定義stopword列表,如:
CREATE TABLE user_stopword(
	value VARCHAR(30)
)	ENGINE = INNODB;

SET GLOBAL innodb_ft_server_stopword_table="mytest/user_stopword";

-- 當前InnoDB的全文索引還存在以下限制:v5.6
-- 每張表只能有一個全文檢索的索引
-- 由多列組合而成的全文檢索的索引列必須使用相同的字符集與排序規則
-- 不支持沒有單詞界定符的語言,如中文、日語、韓語


-- 全文檢索的使用1(Natural Language)

	-- 普通查詢
	SELECT * FROM fts_a WHERE body LIKE '%Please%';  -- explain查看 type ALL
	-- 使用全文檢索,根據相關性進行降序排序(如果未建立全文索引,使用下面兩條語句會拋出異常can't find FULLTEXT)
	SELECT * FROM fts_a WHERE MATCH(body) AGAINST('porridge' IN NATURAL LANGUAGE MODE);  -- explain查看 type fulltext
	SELECT * FROM fts_a WHERE MATCH(body) AGAINST('porridge'); -- IN NATURAL LANGUAGE MODE 是默認模式,可以省略
		-- 你會發現FTS_DOC_ID為2 的排在第一位,這是由於porridge在文檔2中出現了2次,具有更高的相關性
		-- 相關性四個條件:
			-- word是否在文檔中出現
			-- word在文檔中出現的次數
			-- word在索引列中的數量
			-- 多少個文檔包含該word
	
	-- 統計MATCH函數得到的結果數量
	SELECT count(*) FROM fts_a WHERE MATCH(body) AGAINST ('Porridge' IN NATURAL LANGUAGE MODE);
	SELECT COUNT(IF(MATCH (body) AGAINST ('Porridge' IN NATURAL LANGUAGE MODE), 1, NULL)) AS count FROM fts_a;
		-- 以上兩個SQL,從內部運行來看第二個執行的可能更快些,因為第一條還需要進行相關性的排序統計。
	
	-- 查看相關性
		SELECT FTS_DOC_ID, body, MATCH(body) AGAINST('Porridge' IN NATURAL LANGUAGE MODE) AS Relevance FROM fts_a;
		-- 但是對於在stopword表中的單詞是查詢不到相關性的
		SELECT FTS_DOC_ID, body, MATCH(body) AGAINST('the' IN NATURAL LANGUAGE MODE) AS Relevance FROM fts_a;
		-- 另外參數 innodb_ft_min_token_size和innodb_ft_max_token_size控制查詢字符的長度,小於最小值,大於最大值的都會忽略改詞的搜索。
		-- 最小值默認為3,最大值默認為84
		

-- 全文檢索的使用2(Boolean)
	-- 下面的SQL返回有please或有hot的文檔
		SELECT * FROM fts_a WHERE MATCH(body) AGAINST('Please hot' IN BOOLEAN	MODE); 
	-- +號表示必須存在
		SELECT * FROM fts_a WHERE MATCH(body) AGAINST('+Please +hot' IN BOOLEAN	MODE);  
	-- -號表示一定不存在
		SELECT * FROM fts_a WHERE MATCH(body) AGAINST('+Please -hot' IN BOOLEAN	MODE); 
	-- (no operator) 表示該word是可選的, 但是如果出現, 其相關性會更高
	-- @distance 表示查詢的多個單詞之間的距離是否在distance之內,distance的單位是字節。這種全文檢索的查詢也稱為Proximity Search。如MATCH(body) AGAINST('"Please pot"@30' IN BOOLEAN MODE) 表示字符串Please和pot之間的距離需在30字節內。
		SELECT FTS_DOC_ID, body FROM fts_a WHERE MATCH(body) AGAINST('"Please pot" @30' IN BOOLEAN MODE);
		SELECT FTS_DOC_ID, body FROM fts_a WHERE MATCH(body) AGAINST('"Please pot" @5' IN BOOLEAN MODE);
		SELECT FTS_DOC_ID, body FROM fts_a WHERE MATCH(body) AGAINST('"Please pot" @4' IN BOOLEAN MODE);
		SELECT FTS_DOC_ID, body FROM fts_a WHERE MATCH(body) AGAINST('"hot Porridge" @2' IN BOOLEAN MODE);
		SELECT FTS_DOC_ID, body FROM fts_a WHERE MATCH(body) AGAINST('"Please Pease" @3' IN BOOLEAN MODE);
			-- 在5.7.17版本下,經過上面的幾個SQL測試,僅英文時,distance單位是4個字符(字節),即distance為2時,可以間隔8個字符(字節)
	-- > 表示出現該單詞時增加相關性, 增加1
		SELECT FTS_DOC_ID,body, MATCH(body) AGAINST('like pot' IN BOOLEAN	MODE) AS Relevance FROM fts_a;
		SELECT FTS_DOC_ID,body, MATCH(body) AGAINST('like >pot' IN BOOLEAN	MODE) AS Relevance FROM fts_a;
	-- < 表示出現該單詞時降低相關性  減少0.4左右, 可以減至負數
		SELECT FTS_DOC_ID,body, MATCH(body) AGAINST('like >pot <some' IN BOOLEAN	MODE) AS Relevance FROM fts_a;
	-- ~ 表示允許出現該單詞,但是出現時相關性為負(全文檢索查詢允許負相關性)。
	-- * 表示以該單詞開頭的單詞,如lik*, 表示可以是lik、like,又或者likes。
		SELECT * FROM fts_a WHERE MATCH(body) AGAINST('po*' IN BOOLEAN	MODE);
	-- " 表示短語,而不加雙引號,表示有please或有hot的文檔
		INSERT INTO fts_a SELECT NULL, 'Please hot tea to me';
		SELECT * FROM fts_a WHERE MATCH(body) AGAINST('"Please hot "' IN BOOLEAN	MODE);  -- 短語不可分割
		SELECT * FROM fts_a WHERE MATCH(body) AGAINST('Please hot ' IN BOOLEAN	MODE); 
	

-- 全文檢索的使用3(Query Expansion)
	-- MySQL還支持全文檢索的擴展查詢。這種查詢通常是在查詢的關鍵詞太短,用戶需要implied knowledge(隱含知識)時進行。對於單詞database的查詢,用戶可能希望查詢的不僅僅是包含database的文檔,可能還包含MySQL、Oracle、DB2、RDBMS的單詞。而這時可以使用Query Expansion 模式來開啟全文檢索的implied knowledge。
	-- 通過在查詢短語中添加 WITH QUERY EXPANSION 或 IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION 可以開啟blind query expansion(又稱為automatic relevance feedback)。該查詢分為兩個階段。
		-- 第一階段:根據搜索的單詞進行全文索引查詢
		-- 第二階段:根據第一階段產生的分詞再進行一次全文檢索的查詢。
		-- 來看一個具體的例子,創建表articles:
			CREATE TABLE articles (
				id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
				title VARCHAR(200),
				body TEXT,
					FULLTEXT (title, body)
					
			) ENGINE=InnoDB;
			
			INSERT INTO articles (title, body) VALUES
			('MySQL Tutorial', 'DBMS stands for DataBase ...'),
			('How To Use MySQL Well','After you went through a ...'),
			('Optimizing MySQL','In this tutorial we will show ...'),
			('1001 MySQL Tricks', '1. Never run mysqld as root. 2. ...'),
			('MySQL vs. YourSQL','In the following database comparison...'),
			('MYSQL Security', 'When configured properly, MySQL...'),
			('Tuning DB2', 'For IBM database ...'),
			('IBM History', 'DB2 history for IBM ...');
			-- 在這個表中並沒有顯式創建FTS_DOC_ID列, 因此InnoDB會自動創建該列,並添加唯一索引,
			-- 此外,表articles的全文檢索索引是根據列title和body的聯合索引
			-- 接着根據database關鍵字進行的全文檢索查詢。
	
		-- 使用擴展查詢與使用 NATURAL查詢的區別。
			SELECT * FROM articles WHERE MATCH(title, body) AGAINST('database' IN NATURAL LANGUAGE MODE); -- 3個文檔
			SELECT * FROM articles WHERE MATCH(title, body) AGAINST('database' WITH QUERY EXPANSION); -- 8個文檔
	
	
	


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM