淺談數(shù)據(jù)庫優(yōu)化

淺談數(shù)據(jù)庫優(yōu)化

在面試的時候經(jīng)常會被問到數(shù)據(jù)庫如何優(yōu)化的?面試的時候最擔心遇到這種寬泛的問題渔嚷。如果你直接說:“建索引” 類似這樣簡單的回答肢专,個人感覺這種回答很不專業(yè)妻枕,面試官也不會喜歡這樣的回答。雖然我們不需要像DBA一樣對數(shù)據(jù)庫有非常深入的研究葛账,但是至少應該對這種問題有個系統(tǒng)全面的回答會比較好柠衅。
當然,我們不單單是為了面試籍琳,在實際生產(chǎn)中我們也需要知道一些最基本的優(yōu)化方法菲宴。就在最近我們組依賴的服務線上就突然出現(xiàn)掛掉的情況贷祈,很久才恢復,后來發(fā)現(xiàn)是出現(xiàn)慢查詢導致數(shù)據(jù)庫連接池撐爆了喝峦,所以掌握數(shù)據(jù)庫優(yōu)化技巧是非常重要的势誊。在這里就總結幾條優(yōu)化方法。
PS:下面介紹的方法都是針對MySQL數(shù)據(jù)庫優(yōu)化谣蠢。

MySQL 架構解析

MySQL架構解析個人感覺這篇文章分析的非常透徹詳細键科。這里就不照搬過來了。

優(yōu)化策略

數(shù)據(jù)類型優(yōu)化

數(shù)據(jù)類型的優(yōu)化主要是指選取什么類型漩怎。需要遵循“小而簡單”的原則勋颖。因為這樣的數(shù)據(jù)類型占用的內(nèi)存、磁盤更低勋锤,CPU處理時間也更少饭玲。舉個常見的例子。

1叁执、日期類型選擇茄厘。MySQL中關于時間類型,MySQL中最小精度是秒谈宛。有Datetime次哈、Timestampint三種類型類型來存儲時間,個人推薦使用Datetime吆录。Datetime時間類型存儲的范圍比Timestamp更大窑滞,而且Timestamp因時區(qū)不同而不同,int時間顯示不夠直觀恢筝。

2哀卫、指定最大顯示寬度,不會改變存儲空間撬槽。顯示寬度與存儲大小或類型包含的值的范圍無關此改。也就是說對存儲和計算來講,指定了長度的int(1)int(12)是相同的侄柔。

3共啃、?unsigned(無符號)屬性不允許有負值,這可以使正數(shù)存儲范圍擴大一倍暂题,比如UNSIGNED TINYINT存儲的范圍是0 - 255移剪,TINYINT的范圍是-128 ~ 127。所以在沒有用到負數(shù)的情況下敢靡,建議用unsigned(無符號)挂滓。

4、存儲IP地址時最好使用無符號整數(shù),而不是字符串赶站,這樣可以節(jié)省存儲空間幔虏,Inet_ATON()將帶點兒的IP轉為數(shù)字,而Inet_NTOA可將數(shù)字轉為IP贝椿。

5想括、對一些精度要求比較高的數(shù)據(jù),有人建議使用DECIMAL,decimal需要額外的空間和計算開銷烙博。建議使用BIGINT瑟蜈,在需要精確到千分之一的時候,可以先乘以1000渣窜,再用BIGINT存铺根。

6、通常情況下列最好為NOT NULL, NULL 會使得索引失效乔宿。

7位迂、當數(shù)據(jù)量比較大的時候,不推薦使用alter table详瑞。因為alter table 會創(chuàng)建一個新結構的表掂林,并把老表中的數(shù)據(jù)插入到新表中。

8坝橡、不推薦使用Enum泻帮。因為枚舉類型是固定的字符串列表,添加和刪除的時候需要使用alter table命令计寇。

索引優(yōu)化

MySQL優(yōu)化最重要建立索引锣杂,建立好的索引可以起到事半功倍的效果。

索引結構與算法

首先要介紹索引的結構饲常。索引數(shù)據(jù)結構與原理可以參考這篇文章:MySQL索引背后的數(shù)據(jù)結構及算法原理蹲堂。這里不做過多的解釋狼讨。
看過這篇文章之后拋出一個問題:為什么不用Hash索引贝淤,而是使用B-Tree索引結構,理論上Hash索引的查詢時間復雜度只有O(1)政供。這里主要有以下5點原因:
1播聪、hash函數(shù)計算后的結果,是隨機的,如果是在磁盤上放置數(shù)據(jù)布隔。比主鍵為id為例, 那么隨著id的增長, id對應的行,在磁盤上隨機放置.
2离陶、不法對范圍查詢進行優(yōu)化。
3衅檀、無法利用前綴索引. 比如 在btree中, field列的值“hellopworld”,并加索引招刨。查詢 xx=helloword,自然可以利用索引, xx=hello,也可以利用索引,(左前綴索引)哀军。因為hash(‘helloword’),和hash(‘hello’),兩者的關系仍為隨機沉眶。
4打却、排序也無法優(yōu)化。
5谎倔、必須回行.就是說 通過索引拿到的只是數(shù)據(jù)位置,而不是數(shù)據(jù)值柳击,必須回到表中取數(shù)據(jù)。

索引注意點

  • 理想的索引應該具有下面幾點:
    1: 查詢頻繁 2: 區(qū)分度高 3: 長度小 4: 盡量能覆蓋常用查詢字段片习。

  • 哪些列不應該建立索引呢捌肴?
    1、更新非常頻繁的字段不適合建立索引藕咏。2状知、唯一性太差的字段不適合單獨創(chuàng)建索引。3孽查、不會出現(xiàn)在where子句中的字段不應該創(chuàng)建索引试幽。

  • 索引覆蓋
    索引覆蓋是指 如果查詢的列恰好是索引的一部分,那么查詢只需要在索引文件上進行,不需要回行到磁盤再找數(shù)據(jù).
    這種查詢速度非常快,稱為”索引覆蓋”

  • 不要用UUID或者隨機字符串作為主鍵值卦碾,盡量用連續(xù)增長的值
    對于innodb而言,因為節(jié)點下有數(shù)據(jù)文件,因此節(jié)點的分裂將會比較慢铺坞。對于innodb的主鍵,盡量用整型,而且是遞增的整型。如果是無規(guī)律的數(shù)據(jù),將會產(chǎn)生頁的分裂洲胖,影響速度济榨。關于UUID與自增主鍵的比較參看這篇文章

  • 索引不是創(chuàng)建的越多越好。過多的索引不可能一次性讀取到內(nèi)存绿映,索引是以索引文件的形式存儲在磁盤上擒滑。過多的索引是會產(chǎn)生磁盤I/O消耗,從而影響性能叉弦。

特定語句優(yōu)化

Count化

MyIsam的count()比較快丐一,原因是MyIsam對行數(shù)進行了存儲。一旦有條件的查詢, 速度就不再快了淹冰,尤其是where條件的列上沒有索引库车。
假如,id<100的商家都是我們內(nèi)部測試的,我們想查查真實的商家有多少?
select count(*) from lx_com where id>=100; (1000多萬行用了6.X秒)
小技巧:

select count(*) from lx_com; 快
select count(*) from lx_com where id<100; 快

Join優(yōu)化

Join語句經(jīng)常聽到“小結果集驅(qū)動大結果集”。為什么會有這句話樱拴?這和Mysql的關聯(lián)查詢原理有關柠衍。 Mysql的關聯(lián)查詢是取第一張表的一行數(shù)據(jù)去遍歷第二張表的所有數(shù)據(jù)找到匹配的行,依次遍歷第一張表的數(shù)據(jù)晶乔。有人會問MN和NM結果不是一樣的嗎珍坊?“小結果集驅(qū)動大結果集”,這句話的前提是連接字段建立了索引正罢。具體可以看下面的例子:

SELECT A.xx,B.yy 
FROM A INNER JOIN B USING(c)
WHERE A.xx IN (5,6)

上述代碼的執(zhí)行可以參照下面的偽代碼:

outer_iterator = SELECT A.xx,A.c FROM A WHERE A.xx IN (5,6);
outer_row = outer_iterator.next;
while(outer_row) {
    inner_iterator = SELECT B.yy FROM B WHERE B.c = outer_row.c;
    inner_row = inner_iterator.next;
    while(inner_row) {
        output[inner_row.yy,outer_row.xx];
        inner_row = inner_iterator.next;
    }
    outer_row = outer_iterator.next;
}

假設我們在A和B表的c列都建立了索引阵漏,可以看到外層循環(huán)無法用到索引一定會遍歷M次,但是內(nèi)層循環(huán)可以利用索引減少內(nèi)存循環(huán)的次數(shù),如果B的數(shù)據(jù)量大的話履怯,優(yōu)化效果還是非炒ㄎ蓿可觀的。

group by優(yōu)化

Group by的實質(zhì)是先排序然后分組虑乖。所以建議在group by中利用索引懦趋,這樣可以減少臨時表的創(chuàng)建以及文件排序。order by的列要和group by的一致疹味,否則也會引起臨時表
(原因是因為group by 和 order by 都需要排序仅叫,如果2者的列不一致,那必須經(jīng)過至少1次排序)糙捺。以A诫咱,B表連接為例 ,主要查詢A表的列, 那么 group by ,order by 的列盡量相同,而且列應該顯示聲明為A的列

select A.id,A.cat_id from A inne join B group by A.cat_id order by A.cat_id

Limit優(yōu)化

當表的數(shù)據(jù)非常多的時候洪灯,limit的分頁優(yōu)化可以用延遲索引坎缭。比如我們要查詢5000000后的10條記錄,用下面的語句效率是非常低的签钩。

select id,name from lx_com limit 5000000,10;

這是因為limit offset,N, 當offset非常大時, 效率極低,
可以先在子查詢語句里利用覆蓋索引掃描掏呼,然后再做一個關聯(lián)查詢,這種技術就是延遲索引
铅檩。SQL語句如下:

select id,name from lx_com inner join (select id from lx_com limit 5000000,10) as tmp using(id);

Union優(yōu)化

建議使用union all 而不是union憎夷。union all 不過濾 效率提高,如非必須,請用union all。因為 union去重的代價非常高, Mysql會把各個查詢結果插入到臨時表中昧旨,然后做唯一性檢查拾给。所以請放在程序里去重。

性能分析工具

開啟慢查詢?nèi)罩?/h4>
image.png

MySQL 慢查詢的相關參數(shù)解釋:

  • slow_query_log :是否開啟慢查詢?nèi)罩荆?表示開啟兔沃,0表示關閉蒋得。

  • log-slow-queries :舊版(5.6以下版本)MySQL數(shù)據(jù)庫慢查詢?nèi)罩敬鎯β窂健乒疏?梢圆辉O置該參數(shù)额衙,系統(tǒng)則會默認給一個缺省的文件host_name-slow.log

  • slow-query-log-file:新版(5.6及以上版本)MySQL數(shù)據(jù)庫慢查詢?nèi)罩敬鎯β窂健缰雇?梢圆辉O置該參數(shù)入偷,系統(tǒng)則會默認給一個缺省的文件host_name-slow.log

  • long_query_time :慢查詢閾值,當查詢時間多于設定的閾值時械哟,記錄日志。

使用profile語句

打開profile分析語句


image.png

查看分析列表


image.png

查看單條語句的執(zhí)行過程


image.png

使用explain語句

如果要定量分析查詢語句涉及到了多少行殿雪,可以使用explain暇咆。explain語句是非常重要的分析工具。提測之前使用explain分析一下SQL語句是一種美德。explain可以顯示如下字段:

image.png

關于每個字段的值的解釋可以參考這篇文章

這里會選擇我認為比較重要幾個字段的值通過列子進行講解爸业。

select_type 查詢類型

  • simple語句中沒有子查詢或者union
select field from table;
  • dependent subquery 子查詢中的第一個select語句其骄,依賴外部查詢結果集
select * from test.tabname where id in(select id from test.tabname2 where name='love');

以上語句有個錯誤的理解是認為按照下面兩個結果執(zhí)行

select group_concat(id) from test.tabname2 where name='love';
--內(nèi)層查詢結果:1,3,5,7,9,11,13,15,17,1
 
select * from test.tabname where id in(1,3,5,7,9,11,13,15,17,19);
image.png

通過explain發(fā)現(xiàn)其實他是先根據(jù)關聯(lián)外部tabname, 而不是先去執(zhí)行這個子查詢〕犊酰可以通過連接查詢來優(yōu)化上面的語句拯爽。

select tabname. * from test.tabname inner join test.tabname2 using(id) where tabname2.name='love';
image.png

可以看到查詢類型變成了simple簡單查詢。

  • primary最外層的select, 例子參見dependent subquery

type:很重要,顯示了連接使用了哪種類別,有無使用索引钧忽。type代表查詢執(zhí)行計劃(QEP)中指定的表使用的連接方式

從最好到最差依次為::system毯炮,const,eq_ref耸黑,ref桃煎,fulltext,ref_or_null大刊,unique_subquery为迈,index_subquery,range缺菌,index_merge葫辐,index,ALL

  • system: const的一個特例伴郁,表中只有一條記錄

  • const: where條件是以常量為單位另患,表中最多一條記錄匹配。


    image.png
  • eq_ref:最多只會有一條匹配結果蛾绎,一般是通過主鍵或是唯一索引來訪問昆箕。一般會出現(xiàn)在連接查詢的語句中。通過索引列,直接引用某1行數(shù)據(jù)

    image.png

  • ref: 它返回所有匹配某個單個值的行租冠。它用在關聯(lián)操作只使用了索引的最左前綴鹏倘,或者索引不是UNIQUE和PRIMARY KEY。連接查詢中被驅(qū)動的表索引引用查詢.顽爹。通過索引列,可以直接引用到某些數(shù)據(jù)行

    image.png

  • range: 引用范圍掃描纤泵,見上面的例子

  • all: 全表掃描效率最低

possible key 可能用到哪些索引進行查詢

key 實際用到的索引

key_len 實際用到的索引字節(jié)數(shù)

關于key_len大小的計算可以參照這篇文章

ref 列出是通過哪個字段來進行連接查詢,或者是否是通過常量(const)

extra 是EXPLAIN輸出中另外一個很重要的列镜粤,該列顯示MySQL在查詢過程中的一些詳細信息捏题。

重點關注下面幾個值:

  • Using filesort:文件排序(文件可能在磁盤,也可能在內(nèi)存)。

  • Using temporary:是指用上了臨時表, group by 與order by 不同列時,或group by ,order by 別的表的列.

  • Using index:表示使用索引肉渴,如果只有 Using index公荧,說明他沒有查詢到數(shù)據(jù)表,只用索引表就完成了這個查詢同规,這個叫覆蓋索引循狰,效率非常高窟社。

  • Using index condition:意味著查詢列的某一部分無法直接使用索引

  • Using where:過濾元素的時候出現(xiàn),也會掃描表绪钥,但是如果在條件語句中存在索引列灿里,會優(yōu)先使用帶索引的條件。explain SELECT * from test where b = '4' (b不是索引程腹,全表掃描后匣吊,通過過濾獲取所需數(shù)據(jù))

總結

本文先介紹了MySQL的架構,然后從數(shù)據(jù)類型寸潦、索引色鸳、性能分析三個角度描述如何進行數(shù)據(jù)庫優(yōu)化。相信當面試官再問你如何進行數(shù)據(jù)庫優(yōu)化的時候甸祭。你不會簡單的回答“建索引”缕碎。最后提醒大家:提測前請explain

數(shù)據(jù)庫優(yōu)化是常見的面試題池户,更多資料請見:https://zfau4tzxp5.feishu.cn/docx/ByM0dxtWoo87QtxnWtTcG0kCnpg
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咏雌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子校焦,更是在濱河造成了極大的恐慌赊抖,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寨典,死亡現(xiàn)場離奇詭異氛雪,居然都是意外死亡,警方通過查閱死者的電腦和手機耸成,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門报亩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人井氢,你說我怎么就攤上這事弦追。” “怎么了花竞?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵劲件,是天一觀的道長。 經(jīng)常有香客問我约急,道長零远,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任厌蔽,我火速辦了婚禮牵辣,結果婚禮上,老公的妹妹穿的比我還像新娘躺枕。我一直安慰自己服猪,他們只是感情好供填,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布拐云。 她就那樣靜靜地躺著罢猪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪叉瘩。 梳的紋絲不亂的頭發(fā)上膳帕,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音薇缅,去河邊找鬼危彩。 笑死,一個胖子當著我的面吹牛泳桦,可吹牛的內(nèi)容都是我干的汤徽。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼灸撰,長吁一口氣:“原來是場噩夢啊……” “哼谒府!你這毒婦竟也來了?” 一聲冷哼從身側響起浮毯,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤完疫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后债蓝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壳鹤,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年饰迹,在試婚紗的時候發(fā)現(xiàn)自己被綠了芳誓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡啊鸭,死狀恐怖锹淌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情莉掂,我是刑警寧澤葛圃,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站憎妙,受9級特大地震影響库正,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厘唾,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一褥符、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抚垃,春花似錦喷楣、人聲如沸趟大。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逊朽。三九已至,卻和暖如春曲伊,著一層夾襖步出監(jiān)牢的瞬間叽讳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工坟募, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留岛蚤,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓懈糯,卻偏偏與公主長得像涤妒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赚哗,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容