關於 find_in_set 的性能問題


https://www.iteye.com/blog/jonny131-771753

 

使用一個字段來存儲多對多關系,比如 表 user中有一個字段叫 category, category存儲的是 "1,3,9" 這樣的類型的數據,實際上是category的id 用逗號分隔開來的。

 

要查詢一個用戶屬於id為2分類的用戶可以這么寫

 

 

Sql代碼   收藏代碼
  1. select * from `user` where find_in_set('2',`user`.`category`)  

 

具體find_in_set 的使用請參照手冊

http://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_find-in-set

 

 

雖然這樣很好用,但問題是如果數據量大的情況下怎么辦,性能會是問題么,手冊上有說對find_in_set 做的優化,但在沒有索引的情況下他的性能應該是個問題。

 

於是做了個測試,user 表錄入 100萬的數據,同時建立 user_category 表,每個user有 3 個分類,那么category表里有300萬條記錄。

 

Sql代碼   收藏代碼
  1. CREATE TABLE `user_category` (  
  2.   `id` int(11) NOT NULL AUTO_INCREMENT,  
  3.   `user_id` int(11) DEFAULT NULL,  
  4.   `category_id` int(11) DEFAULT NULL,  
  5.   PRIMARY KEY (`id`),  
  6.   KEY `category_id` (`category_id`),  
  7.   KEY `user_id` (`tax_id`)  
  8. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT   

 

 

現在比較一下在百萬級的數據量上使用 join 鏈接外鍵查詢和find_in_set查詢的性能

 

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

 

 

Sql代碼   收藏代碼
  1. SELECT SQL_NO_CACHE COUNT(*) FROM `user` WHERE FIND_IN_SET(65,category)  

 

 

 

2. 使用left join , 使用了右表中的索引,平均時間在0.2秒左右

 

 

Sql代碼   收藏代碼
  1. SELECT SQL_NO_CACHE COUNT(DISTINCT(`user`.id)) FROM `user`   
  2. LEFT JOIN `user_category` ON `user`.`id`= `user_category`.`user_id`  
  3. WHERE `user_category`.`category_id`=75  

 

 

所以在大數據量的情況下還是不適合用find_in_set, 不過有些表的數據可能永遠就那么點數據,這個時候為了減少表數量,倒是可以用這樣的方法做。


 

mysql中的find_in_set效率

https://www.jianshu.com/p/828fd8f97f26

1,工作中,同事說find_in_set效率可低了,不如把記錄存成多條。比如一個user_id=3對應qrcode=‘23,24,25,26’,不如存成四條記錄,qrcode改成int類型,這樣效率高。我是保持懷疑態度的。實踐是檢驗真理的唯一標准。

2,我開始在自己本地數據庫中創建測試數據,數據中的數字部分都是隨機生成的。textbook表是qrcode類型是varchar,user表的qrcode是int類型


 
textbook.png
 
user.png

textbook表創建了10萬條測試數據,其中qrcode存了四個id的字符串。
user表創建了40萬條測試數據,其中qrcode只存一個int類型的數字。


 
textbook.png

 
user.png

3,開始查詢驗證
user表用時0.110s

select * from user where qrcode=4 // 查詢出366條,用時0.110s 
 
user.png

textbook表用時:

select * from textbook where FIND_IN_SET('4',qrcode) // 查詢出370條,用時0.039s 
 
image.png

4,如果把textbook表的數據增加到40萬條,同樣的sql查詢,看看結果:
user表的查詢速度變慢了。用時0.113s。
textbook表的查詢用時 0.176s

 
user.png
 
textbook.png

此時user表和textbook表數據一樣多的時候,find_in_set的速度是不如int類型分開存儲的情況。

5,僅測試這種存儲方式對查詢速度的影響。find_in_set對速度影響並不大

6,再更新一下,忽略了一個問題,存數字的情況下,沒有建索引。給user表的qrcode字段加一個普通索引,速度提升明顯。未加索引之前,用時0.110s。加上普通索引后耗時0.040s。


 
user.png


作者:雪貝特
鏈接:https://www.jianshu.com/p/828fd8f97f26
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
 

為了測試sql語句的效率,有時候要不用緩存來查詢。
使用
SELECT SQL_NO_CACHE ...
語法即可
 
SQL_NO_CACHE的真正作用是禁止緩存查詢結果,但並不意味着cache不作為結果返回給query。
 
目前流傳的SQL_NO_CACHE不外乎兩種解釋:
1.對當前query不使用數據庫已有緩存來查詢,則當前query花費時間會多點
2.對當前query的產生的結果集不緩存至系統query cache里,則下次相同query花費時間會多點
我做了下實驗,似乎兩種都對。
 
sql_cache意思是說,查詢的時候使用緩存。
 
對SQL_NO_CACHE的解釋及測試如下:
SQL_NO_CACHE means that the query result is not cached. It does not mean that the cache is not used to answer the query.
You may use RESET QUERY CACHE to remove all queries from the cache and then your next query should be slow again. Same effect if you change the table, because this makes all cached queries invalid.
 
mysql> select count(*) from users where email = 'hello';
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (7.22 sec)
 
mysql> select count(*) from users where email = 'hello';
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.45 sec)
 
mysql> select count(*) from users where email = 'hello';
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.45 sec)
 
mysql> select SQL_NO_CACHE count(*) from users where email = 'hello';
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.43 sec)
 

總結:可以在 SELECT 語句中指定查詢緩存的選項,對於那些肯定要實時的從表中獲取數據的查詢,或者對於那些一天只執行一次的查詢,我們都可以指定不進行查詢緩存,使用 SQL_NO_CACHE 選項。
對於那些變化不頻繁的表,查詢操作很固定,我們可以將該查詢操作緩存起來,這樣每次執行的時候不實際訪問表和執行查詢,只是從緩存獲得結果,可以有效地改善查詢的性能,使用SQL_CACHE 選項。

 

================MyBatis的對CACHE的應用======================
MyBatis的flushCache和useCache的使用
 
(1)當為select語句時:
flushCache默認為false,表示任何時候語句被調用,都不會去清空本地緩存和二級緩存。
useCache默認為true,表示會將本條語句的結果進行二級緩存。
 
(2)當為insert、update、delete語句時:
flushCache默認為true,表示任何時候語句被調用,都會導致本地緩存和二級緩存被清空。
useCache屬性在該情況下沒有。
當為select語句的時候,如果沒有去配置flushCache、useCache,那么默認是啟用緩存的,所以,如果有必要,那么就需要人工修改配置,修改結果類似下面:
 
<select id="save" parameterType="XX" flushCache="true" useCache="false">
……
</select>
 
update 的時候如果 flushCache="false",則當你更新后,查詢的數據數據還是老的數據。


免責聲明!

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



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