自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

MySQL中的SQL優(yōu)化建議那么多,該如何有的放矢

數(shù)據(jù)庫(kù) MySQL
今天早上看到同事的一個(gè)優(yōu)化需求,優(yōu)化的時(shí)間其實(shí)不多,但是對(duì)于這條SQL的優(yōu)化思考了很多,希望有一些參考。

 今天早上看到同事的一個(gè)優(yōu)化需求,優(yōu)化的時(shí)間其實(shí)不多,但是對(duì)于這條SQL的優(yōu)化思考了很多,希望有一些參考。

[[282433]]

業(yè)務(wù)同學(xué)提供的SQL如下:

  1. SELECT  
  2. b.order_id 
  3. FROM 
  4. SELECT 
  5. a.order_id, 
  6. a.order_time AS create_time 
  7. FROM 
  8. trade_order a 
  9. WHERE 
  10. a.user_id = 12345678 
  11. 。。。。。。 
  12. AND a.deleted = 0 
  13. UNION 
  14. SELECT 
  15. v.order_id, 
  16. v.create_time 
  17. FROM 
  18. virtual_order v 
  19. WHERE 
  20. v.user_id = 12345678 
  21. 。。。。 
  22. ORDER BY 
  23. order_id DESC 
  24. AS b 
  25. LIMIT 0, 
  26.  10; 

根據(jù)反饋,這條SQL的執(zhí)行時(shí)長(zhǎng)在200毫秒,在壓測(cè)情況下會(huì)到500毫秒左右,從業(yè)務(wù)層面來(lái)看,目前是不滿足需求的,想看看我們有沒(méi)有優(yōu)化的建議。

第一印象這條SQL執(zhí)行時(shí)長(zhǎng)200~500毫秒,要優(yōu)化好像可打的牌不多啊,如果要想得到一個(gè)可接受的基準(zhǔn)值,當(dāng)然反饋會(huì)是越快越好。所以從這個(gè)角度來(lái)看,我們不妨按照毫秒級(jí)優(yōu)化的標(biāo)準(zhǔn)來(lái)看,這條SQL需要做哪些補(bǔ)充的工作。

首先通過(guò)SQL看下邏輯情況,整體的邏輯是按照用戶id去查詢兩個(gè)數(shù)據(jù)源(trade_order和virtual_order),從兩個(gè)數(shù)據(jù)源查詢出10條單號(hào)數(shù)據(jù)返回。這個(gè)用戶在兩個(gè)數(shù)據(jù)源中可能有單號(hào),也可能沒(méi)有,只要有匹配的就返回,累計(jì)返回10條,看起來(lái)是為了去重才選擇了union的組合方式。

先不看表結(jié)構(gòu)信息,我大體有了如下的建議:

  1. union的模式更建議采用union all,兩個(gè)數(shù)據(jù)源存在數(shù)據(jù)重合應(yīng)該是不合理的。
  2. 查詢語(yǔ)句里面使用了order_time但是數(shù)據(jù)返回壓根沒(méi)有用到,建議去掉
  3. SQL層面承載了太多的數(shù)據(jù)處理壓力,比如多數(shù)據(jù)源,去重和過(guò)濾,分頁(yè),是不是可以做下精簡(jiǎn)。

當(dāng)然到了這里,和業(yè)務(wù)的需求就產(chǎn)生了脫節(jié),這就屬于那種看啥都不順眼的狀態(tài),總想找出點(diǎn)問(wèn)題來(lái),而且對(duì)于業(yè)務(wù)同學(xué)來(lái)說(shuō),哪怕十個(gè)八個(gè)需求,你得有一個(gè)需求的收益更高,他們采用其他需求的可能性才越大,否則就是不作為了。

所以到了這里,我們開(kāi)始做下分析,要優(yōu)化SQL不看看執(zhí)行計(jì)劃是不過(guò)關(guān)的,在執(zhí)行前,我的大體感覺(jué)表數(shù)據(jù)量很大,應(yīng)該是生成了派生表,然后在數(shù)據(jù)去重過(guò)濾層面的消耗比較大,而兩個(gè)子查詢來(lái)說(shuō),返回的結(jié)果集應(yīng)該很少。 預(yù)測(cè)的執(zhí)行情況是:

1)子查詢trade_order應(yīng)該很快,毫米級(jí)響應(yīng)

2)子查詢virtual_order應(yīng)該也很快,但是最后有一個(gè)order by操作,可能代價(jià)略高

3)union的去重過(guò)濾代價(jià)相對(duì)較大,涉及到兩個(gè)結(jié)果集的合并,如果返回結(jié)果較多,可能是瓶頸

從執(zhí)行結(jié)果來(lái)看,讓我有些意外,其中virtual_order的返回結(jié)果竟然有40多萬(wàn)行,相當(dāng)于直接走了全表掃描。

 

而其他的部分也會(huì)收到相關(guān)影響,所以后續(xù)的處理都會(huì)受到影響。

為了快速定位問(wèn)題,我把兩個(gè)子查詢拆開(kāi)單獨(dú)執(zhí)行,查看執(zhí)行計(jì)劃,這是分析瓶頸最快的一種處理思路。

  1. >>explain SELECT 
  2.     -> v.order_id, 
  3.     -> v.create_time 
  4.     -> FROM 
  5.     -> virtual_order v 
  6.     -> WHERE 
  7.     -> v.user_id = 12345678 
  8.     。。。; 

執(zhí)行計(jì)劃如下:

 

可以看到是直接走了全表掃描,這是一個(gè)基礎(chǔ)需求,不會(huì)業(yè)務(wù)同學(xué)漏了索引吧,然后查看表結(jié)構(gòu):

  1. CREATE TABLE `virtual_order` ( 
  2.   `order_id` varchar(255) NOT NULL COMMENT '訂單ID'
  3. 。。。 
  4.   `user_id` varchar(255) DEFAULT NULL COMMENT '用戶ID'
  5. 。。。 
  6.   `refund` tinyint(3) DEFAULT NULL COMMENT ' 是否退款(1:無(wú),2:是)'
  7.   `atc_pay_status` int(3) NOT NULL DEFAULT '0' COMMENT '支付狀態(tài)'
  8. 。。。 
  9.   PRIMARY KEY (`order_id`), 
  10.   KEY `order_status` (`order_status`), 
  11.   KEY `user_id` (`user_id`), 
  12.   KEY `prepaid_account` (`prepaid_account`) 
  13. ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

發(fā)現(xiàn)user_id是走了索引的,那么問(wèn)題來(lái)了,user_id既然是索引,但是為什么SQL語(yǔ)句中依然走了全表掃描呢?

此處思考10秒鐘,繼續(xù)往下看。

其實(shí)這個(gè)時(shí)候問(wèn)題的邊界都很清晰了,SQL語(yǔ)句很簡(jiǎn)單,索引也存在,走了全表掃描,在MySQL中可以暫時(shí)排除直方圖的影響,目前在5.7版本中還不存在直方圖的特性,那么結(jié)果只有一個(gè):字段的類型產(chǎn)生了隱式類型轉(zhuǎn)換。

這個(gè)部分可以參考這篇的一篇文章

MySQL中需要重視的隱式轉(zhuǎn)換

比如初始化語(yǔ)句如下:

  1. create table test(id int primary key,name varchar(20) ,key idx_name(name)); 
  2. insert into test values(1,'10'),(2,'20'); 

然后我們使用如下的兩條語(yǔ)句進(jìn)行執(zhí)行計(jì)劃的對(duì)比測(cè)試。

  1. explain select * from test where name=20; 
  2. explain select * from test where  name=’20’; 

在name列為字符類型時(shí),得到的執(zhí)行計(jì)劃列表如下:

可以很明顯的看到,在name為字符串類型時(shí),如果where條件為name=20,則執(zhí)行全索引掃描,查看warning信息會(huì)明確提示:

Message: Cannot use range access on index 'idx_name' due to type or collation conversion on field 'name'

所以此處的問(wèn)題也顯而易見(jiàn)了。

修改了子查詢的條件為字符后,整個(gè)SQL的執(zhí)行效率就立馬好多了。

使用sql_no_cache的方式測(cè)試。

SQL修改前性能:

  1. +-----------------------+ 
  2.  
  3. rows in set (0.27 sec) 
  4.  
  5. 修改后性能: 
  6.  
  7. +-----------------------+ 
  8.  
  9. rows in set (0.00 sec) 

然后再次查看執(zhí)行計(jì)劃,就都規(guī)規(guī)矩矩了,這樣我們就解決了瓶頸問(wèn)題,而那些規(guī)范,更好的改進(jìn)就可以逐步展開(kāi)了,而從建議的角度來(lái)看,采用的概率也會(huì)高一些。

 

當(dāng)然在這個(gè)基礎(chǔ)上確實(shí)有一些補(bǔ)充的建議,在定位瓶頸之后也可以攤開(kāi)來(lái)說(shuō)了。

優(yōu)化不是一錘子買賣,在這個(gè)基礎(chǔ)上,也發(fā)現(xiàn)了一些其他的問(wèn)題,可以看下這個(gè)表的表結(jié)構(gòu)信息,其實(shí)能夠發(fā)現(xiàn)一些設(shè)計(jì)上的小問(wèn)題。

1) 表字段的字符型基本都是varchar(255),需要盡可能避免這種使用習(xí)慣,對(duì)于存儲(chǔ)性能的開(kāi)銷會(huì)有顯著影響

2)使用的int類型 int(3),這種使用對(duì)于int還是存儲(chǔ)4個(gè)字節(jié),但是有限范圍大大減少,可以考慮更小的數(shù)值類型

3)表的索引比較松散,可以根據(jù)業(yè)務(wù)模型創(chuàng)建復(fù)合索引,比如user_id和status的結(jié)合場(chǎng)景更多,應(yīng)該創(chuàng)建的是(user_id,status)的復(fù)合索引

責(zé)任編輯:武曉燕 來(lái)源: 楊建榮的學(xué)習(xí)筆記
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)