RDBMS是我們常見的一些存儲數據的倉庫,無論是做前端還是後端,都會接觸到。
我們常見的數據處理,都是通過sql來和數據庫做交互的,因此造成了許多人對數據庫認知比較模糊,底層的架構也不是很清晰,從本週開始,我們介紹些數據庫的基礎知識,來了解數據庫引擎是如何工作的,以及如何設計更好的索引的方法論,歡迎一起探討。
一、數據庫架構
我們以mysql的架構爲例,進行說明。
最上層用於連接、線程處理;第二層中包含了大多數 的核心服務,包括了對 SQL 的解析、分析、優化和緩存等功能,存儲過程、觸發器和視圖都是在這裏實現的;而第三層就是 真正負責數據的存儲和提取的存儲引擎,例如:InnoDB、MyISAM等,文中對存儲引擎的介紹都是對 InnoDB 實現的分析。
二、數據存儲結構
在整個數據庫體系結構中,我們可以使用不同的存儲引擎來存儲數據,而絕大多數存儲引擎都以二進制的形式存儲數據。
在 InnoDB 存儲引擎中,所有的數據都被邏輯地存放在表空間中,表空間(tablespace)是存儲引擎中最高的存儲邏輯單位,在表空間的下面又包括段(segment)、區(extent)、頁(page):
同一個數據庫實例的所有表空間都有相同的頁大小;默認情況下,表空間中的頁大小都爲 16KB,當然也可以通過改變 innodb_page_size 選項對默認大小進行修改,需要注意的是不同的頁大小最終也會導致區大小的不同:
從圖中可以看出,在 InnoDB 存儲引擎中,一個區的大小最小爲 1MB,頁的數量最少爲 64 個。
三、隨機讀取和順序讀取
在我們常用的sql理解中,數據是以行的形式讀取出來的,其實不然,通過上述的結構,我們可以瞭解到,單次從磁盤讀取單位是頁,而不是行,也就是說,你即便只讀取一行記錄,從磁盤中也是會讀取一頁的,當然了單頁讀取代價也是蠻高的,一般都會進行預讀,這些後續再說。
1、數據的讀取路徑
關係型數據庫管理系統最重要的一個目標就是,確保表或者索引中的數據是隨時可以用的。那麼爲了儘可能的實現這個目標,會使用內存中的緩衝池來最小化磁盤活動。
每一個緩衝池都足夠大,大到可以存放許多頁,可能是成千上萬的頁。
緩衝池管理器將盡力確保經常使用的數據被保存於池中,以避免一些不必要的磁盤讀。如果一個索引或者表頁在緩衝池中被找到,那麼將會處理很快。
如果在緩衝池中,沒有找到數據,會從磁盤服務器的緩衝區裏面去讀取。
磁盤服務器的緩衝區,如同數據庫的緩衝池讀取一樣,磁盤服務器試圖將頻繁使用的數據保留在內存中,以降低高昂的磁盤讀取成本。這個讀取成本大概會在1ms左右。
如果磁盤服務器的緩衝池中依然沒有找到數據,此時就必須要從磁盤讀取了,此時讀取又分區隨機讀取和順序讀取。
2、隨機I/O
我們必須記住一個頁包含了多條記錄,我們可能需要該頁上的所有行,也可能是其中一部分,或者是一行,但所花費的成本都是相同的,讀取一個頁,需要一次隨機I/O,大約需要10ms的時間。
時間組成:
我們可以看到,一次隨機IO需要耗時的時間還是很久的,10ms對計算機來說是一個很長的時間節點了。
3、順序讀取
如果我們需要將多個頁讀取到緩衝池中,並按順序處理它們,此時讀取的速度回變的很快,具體的原理,在B樹索引中也有過介紹,此時讀取每個頁面(4kb)所花費的時間大概爲0.1ms左右,這個時間消耗對隨機IO有很大的優勢。
以下幾種情況,會對數據進行順序讀取。
全表掃描
全索引掃描
索引片掃描
通過聚蔟索引掃描錶行
順序讀取有兩個重要的優勢:
- 同時讀取多個頁意味着平均讀取每個頁的時間將會減少。在當前磁盤服務器條件下,對於4kb大小的頁而言,這一值可能會低於0.1ms(40MB/s)
- 由於數據庫引擎知道需要讀取哪些頁,所有可以在頁被真正請求之前就提前將其讀取進來,我們稱爲預讀。
四、聚集索引和輔助索引
數據庫中的 B+ 樹索引可以分爲聚集索引(clustered index)和輔助索引(secondary index),它們之間的最大區別就是,聚集索引中存放着一條行記錄的全部信息,而輔助索引中只包含索引列和一個用於查找對應行記錄或者主鍵記錄的指針。
關於索引的B+樹結構,網上有很多文章進行分析了,我們就不在一一介紹。
引用:
https://draveness.me/mysql-innodb
-----------------------------------------------------------------------------
想看更多有趣原創的技術文章,掃描關注公衆號。
關注個人成長和遊戲研發,推動國內遊戲社區的成長與進步。