FindInSet效能優化

2021-10-07 04:02:09 字數 2672 閱讀 3067

背景:最近在mysql效能優化中遇到乙個findinset()函式的效能問題:坦然說在資料量低的情況下,find_in_set的效能還是不錯的,但是在30w左右開始嚴重劣化,到那時資料庫結構已經定下來了,拆表的代價太大,而且帶來很多冗餘資料,找了很多方法還是不行,甚至我都開始考慮用es來做了,但是自己搭一套es集群實在麻煩,最終跑到隔壁組找了個專業dba,很快就解決了我的問題,看來專業的事情還是得交給專業的人來做哦!

先介紹一下findinset吧!

不少資料表設計的時候使用乙個欄位來儲存多對多關係,比如:

表 user中有乙個欄位叫 category, category儲存的是 "1,3,9" 這樣的型別的資料,實際上是category的id 用逗號分隔開來的

要查詢乙個使用者屬於id為2分類的使用者可以這麼寫:

select * from `user` where find_in_set('2',`user`.`category`);

具體find_in_set 的使用請參照手冊:

雖然這樣很好用,但問題是如果資料量大,又無法走索引,是很慢的。
取網友的乙個例子:

user 表錄入 100萬的資料,同時建立 user_category 表,每個user有 3 個分類,那麼category表裡有300萬條記錄:

create table `user_category` (     `id` int(11) not null auto_increment,     `user_id` int(11) default null,     `category_id` int(11) default null,     primary key (`id`),     key `category_id` (`category_id`),     key `user_id` (`tax_id`)   ) engine=innodb auto_increment=1 default

現在比較一下在百萬級的資料量上使用 join 鏈結外來鍵查詢和find_in_set查詢的效能

1. 使用 find_in_set 查詢,平均時間在2.2秒左右

select sql_no_cache count(*) from `user` where find_in_set(65,category)
2. 使用left join , 使用了右表中的索引,平均時間在0.2秒左右

select sql_no_cache count(distinct(`user`.id)) from `user`    left join `user_category` on `user`.`id`= `user_category`.`user_id`   where `user_category`.`category_id`=75
這是採用一種空間換時間的辦法。但是如果實在專案後期,又無法改變表結構又該怎麼辦呢?

既然問題的核心在於:findinset函式無法走索引,那給他加上索引不久好了

那麼,全文索引登場了。

通過數值比較、範圍過濾等就可以完成絕大多數我們需要的查詢,但是,如果希望通過關鍵字的匹配來進行查詢過濾,那麼就需要基於相似度的查詢,而不是原來的精確數值比較。全文索引就是為這種場景設計的。你可能會說,用 like + % 就可以實現模糊匹配了,為什麼還要全文索引?like + % 在文字比較少時是合適的,但是對於大量的文字資料檢索,是不可想象的。全文索引在大量的資料面前,能比 like + % 快 n 倍,速度不是乙個數量級,但是全文索引可能存在精度問題。你可能沒有注意過全文索引,不過至少應該對一種全文索引技術比較熟悉:各種的搜尋引擎。雖然搜尋引擎的索引物件是超大量的資料,並且通常其背後都不是關係型資料庫,不過全文索引的基本原理是一樣的.

關於全文索引的詳情請參考這個:

和常用的模糊匹配使用 like + % 不同,全文索引有自己的語法格式,使用 match 和 against 關鍵字,比如:

select * from fulltext_test where match(content,tag) against('*** ***');

注意:match() 函式中指定的列必須和全文索引中指定的列完全相同,否則就會報錯,無法使用全文索引,這是因為全文索引不會記錄關鍵字來自哪一列。如果想要對某一列使用全文索引,請單獨為該列建立全文索引。

如:首先建立測試表,插入測試資料

create table test (

id int(11) unsigned not null auto_increment,

content text not null,

primary key(id),

fulltext key content_index(content)

) engine=myisam default charset=utf8;

insert into test (content) values ('aaaa'),('bbbb'),('cccc');

按照全文索引的使用語法執行下面查詢:

select * from test where match(content) against('aaa');

注意:mysql 中的全文索引,有兩個變數,最小搜尋長度和最大搜尋長度,對於長度小於最小搜尋長度和大於最大搜尋長度的詞語,都不會被索引。通俗點就是說,想對乙個詞語使用全文索引搜尋,那麼這個詞語的長度必須在以上兩個變數的區間內。

使用findinset需要10s左右,使用全文索引只需要0.3s左右

MySQL 優化 FIND IN SET 使用

背景最近在優化專案遇到乙個場景 資料庫表中某個字段儲存是多個字串連線的 如 合同1 contract1 對應多個聯絡人 linker1,linker2,這樣 合同a 的id 對應訂單的儲存就是 order1,order2 我們需要查詢 linker2 和 多少個合同有關 怎麼辦呢?使用 find i...

mysql效能優化 mysql效能優化

優化方式 1.空間換時間 冗餘 2.時間換空間 字段優先使用型別 int date char varchar text 索引型別 btree索引 hash索引 索引的葉子下,存放乙個資訊指向所在行的資料位址。btree有利於範圍查詢,hash有利於精確查詢。btree用的更多一些。btree索引的常...

效能優化 電量優化

使用battery historian來監測電量的情況,battery historian時google的乙個開源專案 具體安裝過程參見 當出現下列畫面,說明已經開啟 其開啟成功以後,訪問網頁如下所示 說明 這裡使用的是一台國外的vps伺服器,原本是想在本地虛擬機器實驗的,一直連線超時,就換成了vp...