歡迎光臨
每天分享高質量文章

從程式員的角度深入理解MySQL

作者:張豐哲

連結:https://www.jianshu.com/p/aa1f0f29b4f8

前言

作為一名工作了4年的程式猿,今天我將站在程式員的角度以MySQL為例探索資料庫的奧秘!

資料庫基本原理

我對DB的理解

第一:資料庫的組成:儲存 + 實體


不必多說,資料當然需要儲存;儲存了還不夠,顯然需要提供程式對儲存的操作進行封裝,對外提供增刪改查的API,即實體。


一個儲存,可以對應多個實體,這將提高這個儲存的負載能力以及高可用;多個儲存可以分佈在不同的機房、地域,將實現容災。


第二:按Block or Page讀取資料


用大腿想也知道,資料庫不可能按行讀取資料(Why? ? ^_^)。實質上,資料庫,如Oracle/MySQL,都是基於固定大小(比如16K)的物理塊(Block or Page,我這裡就不區分統一稱為Block)來實現排程和管理的。要知道Block是資料庫的概念,如何對應到檔案系統呢?顯然需要指出“這個Block的地址在哪裡”,當查詢到地址後,讀取固定大小的資料就相當於完成了Block的讀取了。


資料庫很聰明的,它不會僅僅只讀取需要讀取的Block,它還會替我們把附近的Block塊都讀取載入至記憶體。實際上,這是為了減少IO次數,提高命中率。事實上,一個Block塊的附近Block也是熱點資料,這種處理方式很有必要!


第三:磁碟IO是資料庫的效能瓶頸


毫無疑問,資料在磁碟上,少不了磁碟IO。什麼磁頭旋轉,定位磁軌,定址的過程,就不說了,我們是程式員,也管不了這些。但是這個過程確實是非常耗時的,和記憶體讀取不是一個數量級,所以後來出現了很多方式來減少IO,提升資料庫效能。


比如,增加記憶體,讓資料庫把資料更多的載入至記憶體。記憶體雖好,但也不能濫用,為什麼這麼說呢?假設資料庫中有100G資料,如果都載入至記憶體,也就說資料庫要管理100G磁碟資料+100G記憶體資料,你說累不累?(資料庫要處理磁碟和記憶體的對映關係,資料的同步,還要對記憶體資料進行清理,如果涉及資料庫事務,又是一系列複雜操作……)不過這裡需要指出的是,為了加快記憶體查詢速度,資料庫一般對記憶體進行HASH存放。


比如,利用索引,索引相比記憶體,是一個價效比非常高的東西,後文詳細介紹MySQL的索引原理。


比如,利用效能更好的磁碟…(和咱們就沒關係呢)


第四,提出一些問題思考下:


為什麼我們說利用delete刪除一個表的資料較trancate一個表要慢?


【一個按行查詢刪除,多費勁;一個基於Block的體系結構刪除】
為什麼我們說要小表驅動大表?
【小表驅動大表會快?什麼鬼?MN和NM不是一樣的麼?有鬼的地方,就有索引!】

探索MySQL索引背後的原理

對於絕大數的應用系統,讀寫比例在10:1,甚至100:1,而且insert/update很難出現效能問題,遇到最多的,最棘手的就是select了,select最佳化是重中之重,顯然少不了索引!


說起MySQL的索引,我們會冒出很多這些東西:BTree索引/B+Tree索引/Hash索引/聚集索引/非聚集索引…這麼多,暈頭!

索引到底是什麼,想解決什麼問題?

老生常談了,官網說MySQL索引是一種資料結構,索引的目的就是為了提高查詢效率。


說白了,不使用索引的話,磁碟IO次數比較多!要想減少磁碟IO次數,怎麼辦?


我們想透過不斷縮小想要獲取的資料的範圍來篩選出最終想要的結果,把每次查詢資料的磁碟IO次數控制在一個很小的數量級,最好是常數數量級。

為了應對上述問題,B+Tree索引出來了!

Hello,B+Tree

在MySQL中,不同儲存引擎對索引的實現方式是不同的,這裡將重點分析MyISAM和Innodb。

MyISAM引擎的B+Tree索引結構

我們知道對於MyISAM引擎而言,資料檔案和索引檔案是分離的。從圖中也可以看出,透過索引查詢到後,就得到了資料的物理地址,然後根據地址定位資料檔案中的記錄即可。這種方式也叫”非聚集索引”。


而對於Innodb引擎而言,資料檔案本身是索引檔案!通俗點說,葉子節點上,MyISAM儲存的是記錄的物理地址,而Innodb上儲存的是資料內容,這種方式即”聚集索引”。


另外一點需要註意的是,對於Innodb而言,主鍵索引中葉子節點儲存的是資料內容,而普通索引的葉子節點中儲存的是主鍵值!也就是說,對於Innodb的普通索引欄位查詢,先透過普通索引的B+Tree查詢到主鍵後,然後透過主鍵索引的B+Tree進行查詢。從這裡你可以看出,對於Innodb而言,主鍵的建立非常重要!

而對於MyISAM而言,主鍵索引和普通索引僅僅的區別在於主鍵只需要查詢到一條記錄即可停止,而普通索引允許重覆,找到一條記錄後需要繼續查詢,在結構上沒有區別,如上圖所示。

深入B+Tree

提幾個問題:

為什麼B+Tree把真實的資料放到葉子節點,而不是內層節點?

為什麼我們說索引欄位要盡可能短,最好是單調遞增的?

為什麼複合索引存在最左匹配原則?

範圍查詢(>,

關於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上儲存的是資料,要比較的話,需要把所有的資料都應用上函式,顯然成本太大。

想建立索引,看看區分度

索引雖然物美價廉,但是也別亂來。count(distinct col) / count(*)可以算一下col的區分度,顯然對於主鍵而言,就是1。區分度太低的話,可以考慮下,是否還有必要建立索引呢?

Hash索引

這裡並不是要深入分析Hash索引,而是要說明一下Hash的思想真是無處不在!
在MySQL的Memory儲存引擎中,存在hash函式,給一個key,透過hash函式進行計算得到地址,所以通常情況下,hash索引查詢,會非常快,O(1)的速度。但是也存在hash衝突,和HashMap一樣,透過單連結串列的形式解決。


思考下,hash索引是否支援範圍查詢呢?


顯然是不支援的,它只能給一個KEY去查詢。就如同HashMap一樣,查詢key包含”zhangfengzhe”的,會很快麼?

SQL最佳化神器:explain

SQL最佳化的場景很多,網上的技巧也很多,完全記不住!


要想徹底解決這個問題,我想只有把索引背後的資料結構和原理做適當的理解,遇到書寫SQL或者SQL慢查詢的時候,我們有基礎去分析,再利用好explain工具去驗證,就應該問題不大呢。


explain查詢的結果,可以告訴你哪些索引正在被使用,表是如何被掃描的等等。這裡我將演示個Demo。

資料表student:

註意複合索引(age,address)

符合最左字首匹配

複合索引失效

OK,到這裡,準備結束了,查詢容易,最佳化不易,且寫且珍惜!


●編號435,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

 

Web開發

更多推薦18個技術類微信公眾號

涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

贊(0)

分享創造快樂