這篇文章主要介紹了MySQL優(yōu)化問題有哪些的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Mysql優(yōu)化問題有哪些文章都會有所收獲,下面我們一起來看看吧。
專注于為中小企業(yè)提供網(wǎng)站設(shè)計、網(wǎng)站制作服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)崇禮免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了上千多家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
對于大部分程序員來說,在開發(fā)過程中排查SQL基本是空白。但隨著行業(yè)的內(nèi)卷,對一開發(fā)過程越來越重視和專業(yè),其中一項就是開發(fā)過程中盡可能解決掉SQL問題,避免生產(chǎn)才暴露SQL問題。那么在開發(fā)過程中如何方便的進(jìn)行程序的SQL排查呢?
其思路還是使用Mysql的慢日志來實(shí)現(xiàn):
首先在開發(fā)過程中也需要開啟數(shù)據(jù)庫Mysql的慢查詢
SET GLOBAL slow_query_log='on';
其次設(shè)置慢SQL的最小時間
注意:這里時間單位是s秒但是有6位小數(shù)因此可以表示到微妙的時間力度,一般單表SQL執(zhí)行時間在20ms之內(nèi)為宜,反之理解就是在開發(fā)過程中,如果你執(zhí)行的sql語句超過了20ms則你需要去關(guān)注它。
SET GLOBAL long_query_time=0.02;
為方便操作可以把慢SQL記錄到表中而不是文件中
SET GLOBAL log_output='TABLE';
最后通過mysql.slow_log表就可以查詢到記錄的慢SQL
在勇哥給大家開發(fā)的軟件中,也提供了圖形化的界面來一鍵幫助大家快速實(shí)現(xiàn)上述功能。
生成SQL問題的排查就相對復(fù)雜一點(diǎn)點(diǎn),但是整體的思路還是通過慢SQL來排查,具體思路如下:
首先開啟數(shù)據(jù)庫Mysql的慢查詢
SET GLOBAL slow_query_log='on';
其次設(shè)置慢SQL的最小時間
SET GLOBAL long_query_time=0.02;
一般生成時把慢SQL放到文件
SET GLOBAL log_output='FILE';
下載慢SQL日志文件到本地
最后關(guān)閉數(shù)據(jù)庫Mysql的慢查詢
著重注意:生產(chǎn)的慢SQL最好在使用時,才去開啟,用完后關(guān)閉,避免日志記錄影響到業(yè)務(wù)性能
SET GLOBAL slow_query_log='off';
SQL調(diào)優(yōu)融合多方面的知識,總體來說常見從表結(jié)構(gòu)、表索引、兩方面來優(yōu)化。
舉個例子來理解:就一個性別字段,用tinyint(1)存儲占用1字節(jié),用int(1)存儲占用4個字節(jié),如果有100W條記錄,那么用int存儲的表就比tinyint存儲的表文件大小多2.8M左右,因此在讀取int類型存儲的表時文件大,讀速度相比讀tinyint的慢。這其實(shí)就是為什么說要合理使用字段類型長度的本質(zhì):就是減少存儲的文件大小,以提供讀性能。
當(dāng)然有的朋友就可能說2.8M并不影響大局,因此可以忽略。對于此想法勇哥要補(bǔ)充一嘴:一個表假設(shè)有10個字段,你的系統(tǒng)一共有30個表,那么再看一下多出的文件大小是多少?(2.8Mx10x30=840M,840M你用迅雷超級下載也要花好幾秒,這個時間在計算機(jī)里面算是很慢了...)
2.1、冗余設(shè)計背景——臨時表
Mysql內(nèi)部存在一種特殊且輕量級的臨時表,它是被Mysql自動創(chuàng)建和刪除的。主要在SQL的執(zhí)行過程中使用臨時表來存儲某些操作的中間結(jié)果,該過程由 MySQL 自動完成,用戶無法手工干預(yù),且這種內(nèi)部表對用戶來說是不可見的。
內(nèi)部臨時表在 SQL 語句的優(yōu)化過程中非常重要,MySQL 中的很多操作都要依賴于內(nèi)部臨時表來進(jìn)行優(yōu)化操作。但是使用內(nèi)部臨時表需要創(chuàng)建表以及中間數(shù)據(jù)的存取代價,所以在寫 SQL 語句的時候應(yīng)該盡量去避免使用臨時表。
那么場景的那些場景Mysql內(nèi)部會使用臨時表呢?
多表關(guān)聯(lián)查詢(JOIN)中,order by 或group by使用的列不是第一個表的列
group by 的列不是索引列時
distinct和group by 聯(lián)合使用
order by 語句中使用了distinct關(guān)鍵字
group by 的列時索引列,但數(shù)據(jù)量過大時
2.2、如何查看是否使用內(nèi)部臨時表?
通過Explain關(guān)鍵字或者工具的功能按鈕,查看SQL的執(zhí)行過程,在結(jié)果中的Extra列中如果出現(xiàn)Using temporary關(guān)鍵字,則說明你的SQL語句在執(zhí)行時使用了臨時表。
如下圖,角色Role表和角色組Role_Group是多對1的關(guān)系,在關(guān)聯(lián)查詢的時候,排序使用role_group的id排序則會使用臨時表(見下圖1),如果排序使用role的id則不會使用臨時表(見圖2)。
2.3、如何解決不使用內(nèi)部臨時表?
這個問題解決有兩個方案,一是調(diào)整SQL語句避免使用臨時表,另外一個方案就是在表中冗余存儲。比如2.2中的圖一例子如果一定要按照role_group的id排序,則可以按照role表中的group_id排序,而這列正是冗余存儲的role_group表中id列值。
分庫分表不僅用于大數(shù)量情況下的優(yōu)化,其中垂直分表還可以使用到SQL調(diào)優(yōu)下。(這里我就不去解釋垂直和水平分表了,感興趣的私信我)
例如:一個文章表一般設(shè)計不會包括文章內(nèi)容這個大字段。
文章內(nèi)容這個大字段是單獨(dú)放置到一張表中
為什么文章表要采用以上設(shè)計而不把字段合并到一表中呢?
我們先來計算一道數(shù)學(xué)題,假設(shè)一篇文章總共1M大小,其中文章內(nèi)容,824KB,其余字段200KB,這樣的文章一共有100W條,則:
方案一,如果用一個表存儲,則這個表大小是100W*1M=100WM
方案二,如果用垂直分表存儲,則基本表時200KBx100W,內(nèi)容表824KBx100W
我們在前端有文章列表和文章詳情兩個頁面,分別要直接從數(shù)據(jù)庫中查詢相關(guān)內(nèi)容,則:
方案一,文章列表和文章詳情的查詢都會從100WM數(shù)據(jù)中查詢
方案二,文章列表會從200KBx100W中查詢,文章詳情會從824KBx100W中查詢(當(dāng)前也可能還需要從200KBx100W中查詢)
說到這里,相信大家心中應(yīng)該有一個清晰的答案了吧!垂直拆表可以讓不同業(yè)務(wù)場景的查詢的數(shù)據(jù)量不同,常常這個數(shù)據(jù)量往往小于總表數(shù)據(jù)量,這就比從固定很大小的量中查詢更靈活和高效率。
大多數(shù)人對應(yīng)索引的理解層次都在“索引可以加快查詢的速度”,然而這句話勇哥要補(bǔ)充下半句“索引可以加快查詢的速度,也可以減慢數(shù)據(jù)插入或修改的速度”。
如果一個表有5個索引,那么可以簡單的把一個索引當(dāng)成一個表,則這就會有1張表+6張索引表=相當(dāng)于有6張表,那么這6張表在什么時候會操作呢?我們來計算一下:
insert操作,數(shù)據(jù)插入后,需要去對5張索引表插入索引數(shù)據(jù)
delete操作,數(shù)據(jù)刪除后,需要去把5張索引表中的索引刪除
update操作
如果修改了索引列的數(shù)據(jù),則先修改數(shù)據(jù),還需要修改索引表中的索引
如果沒有修改索引列的數(shù)據(jù),則只修改數(shù)據(jù)表
select操作
如果命中查詢索引,則先查詢索引,再查數(shù)據(jù)表
如果沒命中查詢索引,則直接查數(shù)據(jù)表
通過以上的計算,你會神奇的發(fā)現(xiàn),索引個數(shù)越多,對于insert、delete、update操作是有影響的,而且是負(fù)影響。所以對于索引竟可能評估其帶來的影響小于查詢的收益,才去添加,而不是盲目的添加。
復(fù)合索引指的是包括有多個列的索引,它能有效的減少表的索引個數(shù),平衡了多個字段需要多個索引直接的性能平衡,但是再使用復(fù)合索引的時候,需要注意索引列個數(shù)和順序的問題。
先說列個數(shù)的問題,指的是一個復(fù)合索引中包括的列字段太多影響性能的問題,主要是對update操作的性能影響,如下紅字:
如果修改了索引列的數(shù)據(jù),則先修改數(shù)據(jù),還需要修改索引表中的索引,如果索引列個數(shù)越多則修改該索引的概率越大
如果沒有修改索引列的數(shù)據(jù),則只修改數(shù)據(jù)表
再說復(fù)合索引中列順序的問題,是指索引的最左匹配原則,即最左優(yōu)先,在檢索數(shù)據(jù)時從聯(lián)合索引的最左邊開始匹配,這個比較容易理解,就不多做闡述。
索引無法存儲null值,當(dāng)使用is null或is not nulli時會全表掃描
like查詢以"%"開頭
對于復(fù)合索引,查詢條件中沒有給出索引中第一列的值時
mysql內(nèi)部評估全表掃描比索引快時
or、!=、<>、in、not in等查詢也可能引起索引失效
表達(dá)是與否概念的字段,必須使用 is_xxx 的方式命名,數(shù)據(jù)類型為 unsigned tinyint
。 說明:任何字段如果為非負(fù)數(shù),則必須是 unsigned。
字段允許適當(dāng)冗余,以提高查詢性能,但必須考慮數(shù)據(jù)一致。e.g. 商品類目名稱使用頻率高,字段長度短,名稱基本一成不變,可在相關(guān)聯(lián)的表中冗余存儲類目名稱,
避免關(guān)聯(lián)查詢
。冗余字段遵循:
不是頻繁修改的字段;
不是 varchar 超長字段,更不能是 text 字段。
在 varchar 字段上建立索引時,必須指定索引長度,沒必要對全字段建立索引,根據(jù)實(shí)際文本區(qū)分度決定索引長度即可。
頁面搜索嚴(yán)禁左模糊或者全模糊,如果需要請通過搜索引擎來解決。 說明:索引文件具有 B-Tree 的最左前綴匹配特性,如果左邊的值未確定,那么無法使用此索引。
如果有 order by 的場景,請注意利用索引的有序性。order by 最后的字段是組合索引的一部分,并且放在索引組合順序的最后,避免出現(xiàn) file_sort 的情況,影響查詢性能。
正例:where a=? and b=? order by c; 索引: a_b_c。
反例:索引中有范圍查找,那么索引有序性無法利用,如 WHERE a>10 ORDER BY b; 索引 a_b 無法排序。
利用延遲關(guān)聯(lián)或者子查詢優(yōu)化超多分頁場景。 說明:MySQL 并不是跳過 offset 行,而是取 offset+N 行,然后返回放棄前 offset 的行,返回 N 行。當(dāng) offset 特別大的時候,效率會非常的低下,要么控制返回的總頁數(shù),要么對超過閾值的頁數(shù)進(jìn)行 SQL 改寫。
建組合索引的時候,區(qū)分度最高的在最左邊。
SQL 性能優(yōu)化的目標(biāo),至少要達(dá)到 range 級別,要求是 ref 級別,最好是 consts。
不要使用 count(列名) 或 count(常量) 來替代 count(),count() 是 SQL92 定義的標(biāo)準(zhǔn)統(tǒng)計行數(shù)的語句,跟數(shù)據(jù)庫無關(guān),跟 NULL 和非 NULL 無關(guān)。 說明:count(*) 會統(tǒng)計值為 NULL 的行,而 count(列名) 不會統(tǒng)計此列為 NULL 值的行。
count(distinct column)
計算該列除 NULL 外的不重復(fù)行數(shù)。注意,count(distinct column1,column2)
如果其中一列全為 NULL,那么即使另一列用不同的值,也返回為 0。
當(dāng)某一列的值全為 NULL 時,count(column)
的返回結(jié)果為 0,但 sum(column)
的返回結(jié)果為 NULL,因此使用 sum() 時需注意 NPE 問題。 可以使用如下方式來避免 sum 的 NPE 問題。
SELECT IF(ISNULL(SUM(g), 0, SUM(g))) FROM table;
使用 ISNULL()
來判斷是否為 NULL 值。 說明:NULL 與任何值的直接比較都為 NULL。
不得使用外鍵與級聯(lián),一切外鍵概念必須在應(yīng)用層解決。 說明:以學(xué)生和成績的關(guān)系為例,學(xué)生表的 student_id 是主鍵,成績表的 student_id 則為外鍵。如果更新學(xué)生表中的 student_id,同時觸發(fā)成績表中的 student_id 更新,即為級聯(lián)更新。外鍵與級聯(lián)更新適用于單機(jī)低并發(fā),不適合分布式、高并發(fā)集群;級聯(lián)更新是強(qiáng)阻塞,存在數(shù)據(jù)庫更新風(fēng)暴的風(fēng)險;外鍵影響數(shù)據(jù)庫的插入速度。
禁止使用存儲過程。存儲過程難以調(diào)試和擴(kuò)展,更沒有移植性。
in
操作能避免則避免。若實(shí)在避免不了,需要仔細(xì)評估 in 后面的集合元素數(shù)量,控制在 1000 個之內(nèi)。
POJO 類的布爾屬性不能加 is,而數(shù)據(jù)庫字段必須加 is_,要求在 resultMap 中進(jìn)行字段與屬性的映射。
sql.xml
配置參數(shù)使用:#{}, #param#
,不要使用 ${},此種方式容易出現(xiàn) SQL 注入。
@Transactional
事務(wù)不要濫用。事務(wù)會影響數(shù)據(jù)庫的 QPS。另外,使用事務(wù)的地方需要考慮各方面的回滾方案,包括緩存回滾、搜索引擎回滾、消息補(bǔ)償、統(tǒng)計修正等。
關(guān)于“Mysql優(yōu)化問題有哪些”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“Mysql優(yōu)化問題有哪些”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。