
(一)MySQL查詢對(duì)資源的消耗
? ? ? ? ? ? ? ? ? ? 為什么會(huì)慢?
? ? 先從磁盤的IO開(kāi)始分析,首先機(jī)械式磁盤,每次搜尋數(shù)據(jù)的時(shí)候都需要尋道,然后讀取數(shù)據(jù),根據(jù)比較可以看出內(nèi)存的速度是硬盤的10萬(wàn)倍!

我們要把數(shù)據(jù)從硬盤讀取到內(nèi)存的過(guò)程就需要進(jìn)行IO操作,這個(gè)過(guò)程單次IO可能需要耗費(fèi)近10ms的時(shí)間能完成讀取,同時(shí)操作系統(tǒng)又是以塊來(lái)區(qū)分,所以可以認(rèn)為一次IO操作從硬盤讀取n塊數(shù)據(jù)。通過(guò)分析可以知道只要IO操作越少,速度就越快!。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? 那么怎么減少IO操作呢?
? 接下來(lái)看看數(shù)據(jù)庫(kù)中的數(shù)據(jù)存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu)一般是B+樹(shù),B+樹(shù)的特點(diǎn)是所有的數(shù)據(jù)都是存放在葉子節(jié)點(diǎn),同時(shí)是按照順序進(jìn)行存放的!下面是B+樹(shù)的結(jié)構(gòu)

比如我們要找數(shù)據(jù)4,那么首先磁盤第一次IO是定位到P1,通過(guò)二分查找到下一塊的地址P3.這時(shí)候2次的IO讀取到了數(shù)據(jù)項(xiàng)4,可以看出讀取數(shù)據(jù)IO的次數(shù)就是B+樹(shù)的高度!
假設(shè)當(dāng)前數(shù)據(jù)表的數(shù)據(jù)為N,每個(gè)磁盤塊的數(shù)據(jù)項(xiàng)的數(shù)量是m,則有h=㏒(m+1)N,當(dāng)數(shù)據(jù)量N一定的情況下,m越大,h越?。欢鴐 = 磁盤塊的大小 / 數(shù)據(jù)項(xiàng)的大小,磁盤塊的大小也就是一個(gè)數(shù)據(jù)頁(yè)的大小,是固定的,如果數(shù)據(jù)項(xiàng)占的空間越小,數(shù)據(jù)項(xiàng)的數(shù)量越多,樹(shù)的高度越低。
是否豁然開(kāi)朗!這就是為什么每個(gè)數(shù)據(jù)項(xiàng),即索引字段要盡量的小,比如int占4字節(jié),要比bigint8字節(jié)少一半。主要是為了減少樹(shù)的高度!合適的字段類型和大小會(huì)對(duì)效率和速度產(chǎn)生一定的影響!
? ? ? ? ? ? ? ? ? ? ? ? ? 總結(jié)
一定要選擇合適的類型和大小來(lái)約束字段,可以降低B+樹(shù)的高度,從而減少IO次數(shù)。
同時(shí)建立索引的也是一種B+樹(shù)結(jié)構(gòu),通過(guò)索引來(lái)查找數(shù)據(jù)可以更少次數(shù)的IO實(shí)現(xiàn)數(shù)據(jù)的定位。
所以慢查詢的優(yōu)化就是減少IO操作,那么接下來(lái)我們通過(guò)慢查詢?nèi)罩緛?lái)分析如何優(yōu)化!
(二)MySQL日志類型
首先我們來(lái)看看MySQL的日志構(gòu)成:
? ? MySQL日志文件系統(tǒng)的組成
? ? a、錯(cuò)誤日志:記錄啟動(dòng)、運(yùn)行或停止mysqld時(shí)出現(xiàn)的問(wèn)題。
? ? b、通用日志:記錄建立的客戶端連接和執(zhí)行的語(yǔ)句。
? ? c、更新日志:記錄更改數(shù)據(jù)的語(yǔ)句。該日志在MySQL 5.1中已不再使用。
? ? d、二進(jìn)制日志:記錄所有更改數(shù)據(jù)的語(yǔ)句。還用于復(fù)制。
e、慢查詢?nèi)罩?/b>:記錄所有執(zhí)行時(shí)間超過(guò)long_query_time秒的所有查詢或不使用索引的查詢。
? ? f、Innodb日志:innodb redo log
? ? 好了我們捕獲到了我們需要的東西,那就是慢查詢?nèi)罩?!我們?lái)看看一般的慢查尋日志是否有在MySQL中開(kāi)啟:
在命令行模式下執(zhí)行:show variables like 'slow%';

slow_query_log:OFF表示沒(méi)有開(kāi)啟慢查詢,需要先開(kāi)啟來(lái)哦~
有了慢查詢?nèi)罩窘酉聛?lái)就是我們要針對(duì)性的對(duì)消耗時(shí)間超過(guò)設(shè)定的sql語(yǔ)句進(jìn)行針對(duì)性的優(yōu)化,
到了這一步我們就可以知道到底什么語(yǔ)句影響了數(shù)據(jù)庫(kù)的查詢性能!
(三)MySQL語(yǔ)句分析
有了日志,我們才能快速定位問(wèn)題。首先我們來(lái)看看系統(tǒng)給出的一個(gè)慢查詢?nèi)罩?/b>
# Time: 180112 16:50:45
# User@Host: a8591[a8591] @? [192.168.1.132]
# Thread_id: 26880039011? Schema: a8591? Last_errno: 0? Killed: 0
# Query_time: 1.336462? Lock_time: 0.000089? Rows_sent: 0? Rows_examined: 3499535? Rows_affected: 0? Rows_read: 0
# Bytes_sent: 733? Tmp_tables: 0? Tmp_disk_tables: 0? Tmp_table_sizes: 0
SET timestamp=1515747045;
SELECT * FROM `serviceRecord` WHERE ( `tag` = 120 ) AND ( `theId` = 2741372 ) LIMIT 1;
我們來(lái)解析一下日志各個(gè)參數(shù)的含義:
Query_time:1.336462? ? ? ? ? ? ? ? ? ? ? 查詢消耗的時(shí)間
Lock_time:0.000089? ? ? ? ? ? ? ? ? ? ? ? 鎖表時(shí)間
Rows_sent: 0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 發(fā)送或返回的行數(shù)
Rows_examined:3499535? ? ? ? ? ? ? ? 查詢行數(shù)? ?
解決方法:
1.先 執(zhí)行DESC SELECT * FROM `serviceRecord` WHERE ( `tag` = 120 ) AND ( `theId` = 2741372 ) LIMIT 1;進(jìn)行分析

這里的字段解析:
主要是看key是否用到了索引,這里我們發(fā)現(xiàn)key是NULL,所以這條語(yǔ)句沒(méi)有經(jīng)過(guò)任何索引.
然后分析慢日志:
這條是非常簡(jiǎn)單的查詢語(yǔ)句,但是卻查了300多萬(wàn)行的數(shù)據(jù),非常明顯沒(méi)有通過(guò)索引,查看數(shù)據(jù)庫(kù)的表結(jié)構(gòu)

分析一下,索引有兩個(gè),一個(gè)是id的主鍵索引,一個(gè)是(type,theId,recTime)的聯(lián)合索引。
我們的查詢語(yǔ)句:SELECT * FROM `serviceRecord` WHERE ( `tag` = 120 ) AND ( `theId` = 2741372 ) LIMIT 1;
很明顯沒(méi)有走任何索引導(dǎo)致了全表掃描!
根據(jù)業(yè)務(wù)是關(guān)于一些消息記錄的,theId是非常常用的一個(gè)字段,區(qū)分度也非常高,因此我們需要對(duì)theId進(jìn)行索引的建立,而tag標(biāo)簽的區(qū)分度并不算高,所以暫時(shí)沒(méi)有必要進(jìn)行索引的建立。
參考:http://blog.csdn.net/gent__chen/article/details/51159451