一、數據庫架構
1. 考點思維導圖
2. 如何設計一個關系型數據庫(RDBMS)
存儲(文件系統)
數據庫最主要的功能就是存儲我們的數據,因此他會有一個存儲模塊來負責存儲我們的數據,存儲模塊就相當于我們的OS文件系統,將數據最終持久化存入磁盤中,如機械硬盤或者SSD固態硬盤中亦或者他們的磁盤陣列矩陣中。
程序實例
我還還需要組織并且用到這些數據,因此需要有程序實例,用邏輯結構來映射出物理結構來,并且在程序中提供獲取以及管理數據的方式還有必要的問題追蹤機制。接下來細分一下程序的模塊,首先我們需要對數據的格式以及文件的風格進行統一的管理,即把物理數據通過邏輯的形式給組織和表示出來,于是便涉及到咱們程序的存儲管理模塊。作為一款很追求性能的軟件,我們沒有好好利用內存,因此為了更快更好的優化我們的程序,我們應該想到做項目的時候的一種普遍做法,便是引入緩存機制,把取出來的數據塊放進緩存里,下次需要的時候直接從內存返回,而不用發生io,這里需要補充的是,剛才咱們說到的一次性加載多個模塊或者頁,這些塊里包含數據行有相當的一部分并不是本次查詢我們需要的行,但是根據一旦某行數據被訪問,那么它周圍的數據也極有可能被返回的經驗,咱們緩存的非本次數據也能起到優化訪問效率的作用,一次加載的這個塊中它有abc三條數據,我們只需要a的數據,b和c也會被加載到內存中,按照經驗來說,下次b和c被訪問到的幾率是非常大的,也就是他也能提升我們的訪問性能。關于如何管理緩存有很多的方法,如LRU等,也不能說那個方法最好,某種方法適用于某個具體的場景。我們還要能提供外界指令來操作我們的數據庫,即可讀的sql語言,那我們就需要一個sql的解析模塊,將sql編譯解析轉換成機器可識別的指令,為了進一步提高sql執行效率,還是將sql緩存入緩存里,編譯好的sql方便下次來了直接進行解析就可以了,注意緩存不宜過大,算法中應該有淘汰機制,淘汰掉一些之后不常用的數據。此外,咱們做的sql操作需要記錄下來方便我們做數據庫的主從同步或者災難恢復,因此我們就需要有日志管理來記錄操作。我們還需要提供給用戶管理數據的私密空間,即權限劃分。設計系統除了考慮正常的功能還要考慮異常的情況,怎么處理異常的情況呢,我們就需要引入容災機制。為了進一步提升查詢的速度以及讓數據庫支持并發,咱們還需要引入最能夠突出數據庫特點的兩個模塊,索引管理和鎖模塊。
答案
如何設計一個關系型數據庫,首先咱們要換分成兩個模塊。
- 一個是存儲部分,該部分類似一個文件系統,來將數據持久化到存儲設備當中。
- 光有存儲是不行的,還需要程序實例模塊,來對存儲進行邏輯上的管理。而程序實例部分將包含數據的邏輯關系轉換成物理存儲關系的存儲管理模塊;優化執行效率的緩存模塊;將sql語句進行解析的sql解析模塊;記錄操作的日志管理模塊;進行多用戶管理的權限劃分模塊;災難恢復模塊;優化數據查詢效率的索引模塊以及使得數據庫支持并發操作的鎖模塊。
3. 為什么要使用索引
- 快速查詢數據
最簡單的查詢方式就是全表掃描,即將整張表的數據全部或者分批次加載到內存當中,最小的存儲單位是塊或者頁,他們是由多行數據來組成的,然后我們將這些塊都加載進來,然后逐個塊或者逐個頁去輪詢,找到我們要的目標并返回,這種方式普遍認為是非常慢的(但在數據量非常少的情況下是快的),在數據量很大的表里該方法顯然不適用了。
很多情況下我們都要避免全表掃描的發生,所以咱們的數據庫得引入一種更高效的機制,這便是索引了,它的靈感來源于字典,在字典里我們只需要把一些關鍵信息組織起來比如說偏旁部首這些,查詢的時候依據這些信息的指引就能夠查到我們的頁面很快就能查到我們要查的字了,而這些關鍵信息以及查找數據的方式便組成了我們的索引,通過索引來大幅提升查詢速度。
4. 什么樣的信息能成為索引
- 主鍵、唯一鍵以及普通鍵
把該記錄限定在一定查找范圍內的字段,就是我們剛才所說的關鍵信息,主鍵便是一個很好的索引切入點,當然其他鍵位包塊唯一鍵普通鍵等也可以作為索引。
5. 索引的數據結構
- 生成索引,建立二叉查找樹進行二分查找。變種平衡二叉樹和紅黑樹。
- 生成索引,建立B-Tree結構進行查找。
- 生成索引,建立B+-Tree結構進行查找。這是主要使用的數據結構。
- 生成索引,建立Hash結構進行查找。
6. 密集索引和稀疏索引的區別
二、優化你的索引-運用二叉查找樹
三、優化你的索引-運用B數
1. 定義
- 根節點至少包括兩個孩子;
- 樹中每個節點最多含有m個孩子(m>=2));
- 除根節點外,其他每個節點至少有ceil(m/2)個孩子;
- 所有葉子節點都位于同一層;
- 假設每個非終端節點中包含有n個關鍵字信息,其中
- Ki(i=i...n)為關鍵字,且關鍵字按順序升序排序K(i-1)<Ki
- 關鍵字的個數n必須滿足:[ceil(m/2)-1]<=n<=m-1
- 非葉子節點的指針:P[1],P[2],...,P[M];其中P[1]指向關鍵字小于K[1]的子樹,P[M]指向關鍵字大于K[M-1]的子樹,其他P[i]指向關鍵字屬于(K[i-1],K[i])的子樹。
四、優化你的索引-運用B+樹
1. 定義
B+樹是B樹的變體,其定義基本與B樹相同,除了:
- 非葉子節點的子樹指針與關鍵字個數相同;
- 非葉子節點的子樹指針P[i],指向關鍵字值[K[i], K[i+1])的子樹;
- 非葉子節點僅用來索引,數據都保存在葉子節點中;
- 所有葉子節點均有一個鏈指針指向下一個葉子節點。
2. 結論
B+Tree更適合用來做存儲索引
- B+樹的磁盤讀寫代價更低;
B+樹的內部結構并沒有指向關鍵字具體信息的指針,也就是不存放數據,只存放索引信息,因此其內部節點相對于B樹更小,如果把所有同一內部節點的關鍵字存在同一盤塊中,這個盤塊所容納的關鍵字數量也越多,一次性讀入內存中的所要查找的關鍵字也就越多,相對來說IO讀寫次數也就降低了。 - B+樹的查詢效率更加穩定;
由于內部節點并不是指向文件內容的節點,而只是葉子節點中關鍵字的索引,所以任何關鍵字的查找必須走一條從根節點到葉子節點的路,所有關鍵字查詢的長度相同,導致每一個關鍵字查詢的效率幾乎是相同的,穩定的O(lgn)。 - B+樹更有利于對數據庫的掃描。
B樹在提高IO性能的同時并沒有解決元素遍歷效率低下的問題,而B+樹只需要遍歷葉子節點就可以解決對全部關鍵字信息的掃描,所以對于數據庫中頻繁使用的范圍查詢是非常方便的,具有更高的性能。
五、優化你的索引-運用Hash以及BitMap
1. Hash特點
經過哈希函數的運算,只需經過一次定位,便能找到查詢數據所在的頭,不像B+索引需要從根節點到非葉子結點再到葉子節點最后才能訪問到我們需要的數據,這樣可能會經過多次的IO訪問,所以哈希索引的查詢效率理論上要高于B+樹索引。
2. Hash缺點
- 僅僅能滿足“=”,“IN”,不能使用范圍查詢;
由于哈希索引比較的是進行哈希運算之后的查詢值,所以他只能用于等值的過濾,不能用于基于范圍的查詢,因為經過相應的哈希算法處理之后的哈希值的大小關系,并不能保證和哈希運算前完全一樣。 - 無法被用來避免數據排序操作;
由于哈希索引是經過哈希運算之后的值,而且哈希值的大小關系并不一定和哈希運算前的鍵值完全一樣,所以數據庫無法利用索引的數據來避免任何的排序運算。 - 不能利用部分索引鍵查詢;
對于組合索引,哈希索引在計算哈希值的時候是組合鍵,就是將組合索引鍵合并之后再一起進行運算的哈希的值,而不是單獨計算哈希的值,所以經過組合索引的前面一個或幾個索引鍵進行查詢的時候,哈希索引也無法被利用,而B+樹支持利用組合索引中的部分索引的。 - 不能避免表掃描
哈希索引是將索引鍵通過哈希運算之后將運算結果的哈希值所對應的行指針信息存放在一個bucket當中,由于不同索引鍵存在相同的哈希值,所以即使取出滿足某個哈希鍵值的那些數據了,也無法從哈希索引中直接完成查詢,還是要通過訪問bucket中的實際數據進行相應的比較,所以這個是不能避免表掃描的原因。 - 遇到大量哈希值相等的情況后性能并不一定就會比B-Tree索引高;
對于選擇性比較低的索引鍵如果創建哈希索引那么將會存在大量記錄指針信息存放于一個bucket的情況,從而造成整體性能非常低下。
3. BitMap索引是個神器(位圖索引)
當表中的某個字段只有幾種值的時候,比如要表示性別的時候,只有男女兩種情況,如果僅僅是為了在這個字段上進行高效的統計此時呢用位圖索引是一個最佳的選擇,不過大家要注意的是目前很少的數據庫支持位圖索引,已知比較主流的是Oracle數據庫。
4. BitMap結構
位圖索引的結構類似于B+樹。B+樹用來定位葉子節點,這些節點包含指定鍵值的位圖段,位圖段就是位于葉子節點上,它的段信息就在這里,由于數據的值種類固定,所以在存儲方式上會先按照狀態值進行分開,然后每一種值的空間會存儲每個實際的數據行是否是這個值,在這種情況下,由于只需要存儲是與否,所以通常只需要一個bit位來存放,因此理論上一個葉子塊可以存放非常多的bit位來表示不同的行,因此用它來統計時是非常快的,下載到內存中后,幾乎是純CPU的疊加操作。
5. BitMap缺點
- 只適用于某個字段的值只有固定的幾個的這種情況;
- 它的鎖的力度非常的大,當嘗試新增或者修改一條數據的時候,通常與他在同一個位圖的數據操作都會被鎖住,因為某行所在的位置順序有可能會因為數據的添加或者刪除而發生改變,因此在發生改變的時候必須得將其鎖定以防止取錯數據,所以它不適合高并發的聯機處理系統,即咱們常見的OLTP系統,而適合于并發較少而統計運算較多的OLAP類系統。
6. 總結
通常索引的結構是B+樹,比較小眾的也有哈希結構還有BitMap等。
六、密集索引和稀疏索引的區別
1. 區別
- 密集索引文件中的每一個搜索碼值都對應一個索引值。
葉子節點保存的不僅僅是鍵值,還保存了位于同一行記錄里的其他列的信息,由于密集索引決定了表的物理排列順序,一個表只能有一個物理排列順序,所以一個表只能創建一個密集索引。 -
稀疏索引文件只為索引碼的某些值建立索引項。
葉子節點僅保存了鍵位信息以及該行數據的地址,有的稀疏索引僅保存了鍵位信息以及主鍵,定位到葉子節點之后,仍然需要通過地址或者主鍵信息進一步定位到數據。
image.png
2. MySQL
InnoDB
- 若一個主鍵被定義,該主鍵則作為密集索引;
- 若沒有主鍵被定義,該表的第一個唯一非空索引則作為密集索引;
- 若不滿足以上條件,innodb內部會生成一個隱藏主鍵(密集索引);
這個主鍵是一個6字節的列,該列的值會隨著數據的插入而自增。
也就是說InnoDB必須有一個主鍵,該主鍵必須作為唯一的密集索引而存在。 - 非主鍵索引存儲相關鍵位和其對應的主鍵值,包含二次查找;
InnoDB使用的是密集索引,將主鍵組織到一顆B+樹中,而行數據就存儲在葉子節點上,因為InnoDB的主鍵索引和對應的數據是保存在同一個文件當中的,索引檢索的時候在加載葉子節點的主鍵進入內存的同時也加載了對應的數據,即用where id = 14這樣的條件查詢主鍵則按照B+樹的檢索算法即可查找到對應的葉子節點,并獲得對應的行數據。
若對稀疏索引進行條件篩選,則需要經歷兩個步驟,第一步在稀疏索引的B+樹中檢索該鍵,定位到主鍵信息,獲取到主鍵信息之后,還需要經歷第二步,第二步就是使用這個主鍵where id=14在B+樹中在執行一遍我們B+樹的檢索操作,最終在到達葉子節點獲取整行的數據。
而MyISAM使用的均是稀疏索引,稀疏索引的兩顆B+樹看上去沒有什么不同,節點的結構完全一致,只是存儲的內容不一樣了而已,主鍵索引B+樹存儲了主鍵,輔助鍵索引B+樹存儲了輔助鍵,表數據存儲在獨立的地方,就是索引和數據是分開存儲的,這兩顆B+樹的葉子節點都使用一個地址指向真正的表數據,對于表數據來說,這兩個鍵沒有任何差別,由于索引數是獨立的,通過輔助鍵檢索無需訪問主鍵的索引數。
七、索引額外的問題之如何調優Sql
1. 為什么要使用索引
因為索引能夠讓我們避免全表掃描去查找數據,提升檢索效率
2. 什么樣的信息能成為索引
主鍵,唯一鍵等,只要是能讓數據具備一定區分性的字段,都能成為索引
3. 索引的數據結構
主流是B+樹,還有Hash結構和BitMap等,其中MySQL數據庫不支持BitMap索引,同時,基于InnoDB以及MyISAM引擎的MySQL不顯式支持Hash
4. 密集索引和稀疏索引的區別
參上
5. 如何定位并優化慢查詢Sql
具體場景具體分析
根據慢日志定位慢查詢sql
使用explain等工具分析sql
修改sql或者盡量讓sql走索引