做爲一名工做了4年的程序猿,今天我將站在程序員的角度以MySQL爲例探索數據庫的奧祕!程序員
第一,數據庫的組成:存儲 + 實例數據庫
沒必要多說,數據固然須要存儲;存儲了還不夠,顯然須要提供程序對存儲的操做進行封裝,對外提供增刪改查的API,即實例。數據結構
一個存儲,能夠對應多個實例,這將提升這個存儲的負載能力以及高可用;多個存儲能夠分佈在不一樣的機房、地域,將實現容災。app
第二,按Block or Page讀取數據ide
用大腿想也知道,數據庫不可能按行讀取數據(Why? ^_^)。實質上,數據庫,如Oracle/MySQL,都是基於固定大小(好比16K)的物理塊(Block or Page,我這裏就不區分統一稱爲Block)來實現調度和管理的。要知道Block是數據庫的概念,如何對應到文件系統呢?顯然須要指出「這個Block的地址在哪裏」,當查找到地址後,讀取固定大小的數據就至關於完成了Block的讀取了。函數
數據庫很聰明的,它不會僅僅只讀取須要讀取的Block,它還會替咱們把附近的Block塊都讀取加載至內存。實際上,這是爲了減小IO次數,提升命中率。事實上,一個Block塊的附近Block也是熱點數據,這種處理方式頗有必要!工具
第三,磁盤IO是數據庫的性能瓶頸性能
毫無疑問,數據在磁盤上,少不了磁盤IO。什麼磁頭旋轉,定位磁道,尋址的過程,就不說了,咱們是程序員,也管不了這些。可是這個過程確實是很是耗時的,和內存讀取不是一個數量級,因此後來出現了不少方式來減小IO,提高數據庫性能。優化
好比,增長內存,讓數據庫把數據更多的加載至內存。內存雖好,但也不能濫用,爲何這麼說呢?假設數據庫中有100G數據,若是都加載至內存,也就說數據庫要管理100G磁盤數據+100G內存數據,你說累不累?(數據庫要處理磁盤和內存的映射關係,數據的同步,還要對內存數據進行清理,若是涉及數據庫事務,又是一系列複雜操做......)不過這裏須要指出的是,爲了加快內存查找速度,數據庫通常對內存進行HASH存放。spa
好比,利用索引,索引相比內存,是一個性價比很是高的東西,後文詳細介紹MySQL的索引原理。
好比,利用性能更好的磁盤...(和我們就不要緊呢)
第四,提出一些問題思考下:
爲何咱們說利用delete刪除一個表的數據較trancate一個表要慢?
【一個按行查找刪除,多費勁;一個基於Block的體系結構刪除】
爲何咱們說要小表驅動大表?
【小表驅動大表會快?什麼鬼?M*N和N*M不是同樣的麼?有鬼的地方,就有索引!】
對於絕大數的應用系統,讀寫比例在10:1,甚至100:1,並且insert/update很難出現性能問題,遇到最多的,最棘手的就是select了,select優化是重中之重,顯然少不了索引!
提及MySQL的索引,咱們會冒出不少這些東西:BTree索引/B+Tree索引/Hash索引/彙集索引/非彙集索引...這麼多,暈頭!
老生常談了,官網說MySQL索引是一種數據結構,索引的目的就是爲了提升查詢效率。
說白了,不使用索引的話,磁盤IO次數比較多!要想減小磁盤IO次數,怎麼辦?
咱們想經過不斷縮小想要獲取的數據的範圍來篩選出最終想要的結果,把每次查找數據的磁盤IO次數控制在一個很小的數量級,最好是常數數量級。
爲了應對上述問題,B+Tree索引出來了!
在MySQL中,不一樣存儲引擎對索引的實現方式是不一樣的,這裏將重點分析MyISAM和Innodb。
咱們知道對於MyISAM引擎而言,數據文件和索引文件是分離的。從圖中也能夠看出,經過索引查找到後,就獲得了數據的物理地址,而後根據地址定位數據文件中的記錄便可。這種方式也叫"非彙集索引"。
而對於Innodb引擎而言,數據文件自己是索引文件!通俗點說,葉子節點上,MyISAM存儲的是記錄的物理地址,而Innodb上存儲的是數據內容,這種方式即"彙集索引"。
另一點須要注意的是,對於Innodb而言,主鍵索引中葉子節點存儲的是數據內容,而普通索引的葉子節點中存儲的是主鍵值!也就是說,對於Innodb的普通索引字段查找,先經過普通索引的B+Tree查找到主鍵後,而後經過主鍵索引的B+Tree進行查找。從這裏你能夠看出,對於Innodb而言,主鍵的創建很是重要!
而對於MyISAM而言,主鍵索引和普通索引僅僅的區別在於主鍵只須要查找到一條記錄便可中止,而普通索引容許重複,找到一條記錄後須要繼續查找,在結構上沒有區別,如上圖所示。
提幾個問題:
爲何B+Tree把真實的數據放到葉子節點,而不是內層節點?
爲何咱們說索引字段要儘量短,最好是單調遞增的?
爲何複合索引存在最左匹配原則?
範圍查詢(>,<,between,like)對最左匹配有什麼影響?
關於B+Tree的一些數學理論,我們就不玩了,至少一點能夠確定的是:數據表的數據量N=F(樹的高度h,每一個Block存儲的索引的個數m)。在N必定的狀況下,索引字段越小,那麼m會越大,這意味着h將越小!樹越低,固然查找的更快!
若是內層節點存放真實的數據,顯然m會變小,樹將變高。
在實際應用中,咱們應該儘量採用單調遞增的字段做爲主鍵,一方面不會使得索引的數據結構變大,減少了索引佔用的空間;另外一方面也不會頻繁的分裂B+Tree,使得效率降低。
好比複合索引(name,age,sex),B+Tree會優先比較name來肯定下一步的搜索方向。若是忽然來了個(age,sex),根本上就無從下手。這也是符合常理的,對於一本書,咱們說「找到第幾章第幾節的XXX」,從沒有據說過「找到第幾節的XXX」!這是複合索引的重要特性,即最左匹配特性。
假設存在複合索引(name,age,sex),咱們在進行select的時候,並無按照這個順序進行,而是sex = 'man' and name = 'zfz' and age = 27,是否會使用索引呢?數據庫是很聰明的,在SQL優化的時候,會自動幫助咱們調整!可是若是缺失了複合索引的第一列,數據庫也將無能爲力呢。
對於最左匹配,MySQL會一直向右匹配直到遇到範圍查詢就中止匹配。什麼意思?好比複合索引(name,age,sex),對於name = 'zhangfengzhe' and age > 26 and sex = 'man',實際上只利用到了複合索引的name列。
什麼叫「乾淨」?就是不要讓索引參與計算!好比在索引上應用函數,極可能致使索引失效。爲何呢?
其實不用想,B+Tree上存儲的是數據,要比較的話,須要把全部的數據都應用上函數,顯然成本太大。
這裏並非要深刻分析Hash索引,而是要說明一下Hash的思想真是無處不在!
在MySQL的Memory存儲引擎中,存在hash函數,給一個key,經過hash函數進行計算獲得地址,因此一般狀況下,hash索引查找,會很是快,O(1)的速度。可是也存在hash衝突,和HashMap同樣,經過單鏈表的形式解決。
思考下,hash索引是否支持範圍查詢呢?
顯然是不支持的,它只能給一個KEY去查找。就如同HashMap同樣,查找key包含"zhangfengzhe"的,會很快麼?
SQL優化的場景不少,網上的技巧也不少,徹底記不住!
要想完全解決這個問題,我想只有把索引背後的數據結構和原理作適當的理解,遇到書寫SQL或者SQL慢查詢的時候,咱們有基礎去分析,再利用好explain工具去驗證,就應該問題不大呢。
explain查詢的結果,能夠告訴你哪些索引正在被使用,表是如何被掃描的等等。這裏我將演示個Demo。
數據表student:
OK,到這裏,準備結束了,查詢容易,優化不易,且寫且珍惜!