數據庫是如何獲取并處理數據的
SQL是結構化查詢語言(Structured Query Language)的簡稱,用戶只需要通過SQL告訴數據庫需要什么,至于怎么去做就是數據庫管理系統(DBMS)需要考慮的問題。
上篇文章《SQL語句處理》介紹了數據庫執行的四個階段,每個階段都做了什么工作。今天這篇文章繼續探討數據庫是如何獲取到用戶所需的數據。
如下所探討的內容僅適用于Oracle單機環境,RAC環境稍有不同,不在本篇討論范圍內。
數據庫的所有操作增、刪、改、查都是在內存中完成的,為了處理數據,數據庫首先需要將數據從磁盤讀取到內存中,然后進行相應的操作。但是內存和磁盤的讀寫速度有著天壤之別。DDR4內存讀寫速度大概50G每秒(50000M),固態硬盤速度是300M每秒,是內存的二百分之一,機械硬盤的速度是100M每秒,是內存的五百分之一。為了解決兩者之間的速度差的問題,誕生了緩存的概念。緩存的作用就是避免每次獲取數據時都從緩慢的磁盤讀取,而是將之前訪問過的數據緩存在內存中,后續操作如果需要相同數據時,直接從內存獲取,大大提升讀取速度。
Oracle內存結構中有一個很重要的結構叫做DB Buffer Cache,DB Buffer Cache位于SGA中,正常系統中此部分內存占整個Oracle內存結構大絕大部分。如果DB Buffer Cache很大,幾十G,甚至是幾百G,每次在其中搜索所需的塊是否存在,也需要很長的時間,因此,為了提高效率,Oracle將DB Buffer Cache劃分為多個區域,每個區域稱為一個工作集(workset),每個工作集又被劃分為多個hash buckets,一個hash buckets管理著一個或多個數據塊。為了保證數據的一致性,hash buckets訪問是串行的,由相應的latch保護,只有獲取到相應的latch的會話才能到hash buckets上搜索數據塊。
1、查詢語句的執行
假設有如下查詢語句:
SELECT * FROM employees WHERE employee_id=199;
表employees表結構如下,employee_id是表的主鍵。
先看下上述sql語句的執行計劃
首先根據索引EMP_EMP_ID_PK查找employee_id=199鍵,獲取到鍵所對應的值ROWID,ROWID指向數據塊的真實地址。通過ROWID獲取數據塊,獲取到數據塊后,再從數據塊中獲取符合條件的數據。在此過程中至少需要訪問兩個數據塊(為了簡化過程,忽略其他數據塊),一個是鍵為199索引塊,假設為100號塊;另一個是索引塊所指向的數據塊,假設為1000號塊。
前面緩存介紹中提到,為了提升數據的訪問速度,Oracle會將訪問過的數據塊緩存到DB Buffer cache中,以備重復使用。因此上面獲取100號索引塊的時候,需要先到DB Buffer cache中查找100號索引塊是否已存在,以減少昂貴的磁盤讀。在搜索DB Buffer cache前,必須獲取相應的latch后才能進行搜索。如果未獲取到latch,此時會進入等待。待獲取到latch后便可以對DB Buffer cache搜索。
如果100號索引塊已存在于內存中,且沒有會話正在修改數據塊內容,直接訪問內存中的數據塊,獲取鍵為199的數據。如果有會話正在修改此數據塊內容,則需要通過undo數據和當前的數據塊構造一個一致性讀版本的數據塊,來讀取會話修改前的歷史版本數據。
如果100號索引塊不在內存中,首先判斷是否有其他會話正在將100號索引塊讀入內存,如果是,則等待。否則通知后臺進程將100號索引塊從磁盤讀入內存,在將100號索引塊讀入內存前,需要在內存中找到一塊合適的空閑塊,以便保存即將讀入的100號索引塊。搜索空閑塊時同樣需要獲得latch。
如果內存中沒有空閑的空間,則通知后臺刷新進程刷新臟頁,以騰出空閑空間。
如果內存中有空閑的空間,直接將100號索引塊讀入內存。獲取到ROWID后,再去讀取數據塊,讀取數據塊的過程同讀取索引塊,讀取到數據塊后再從數據塊中獲取符合條件的行,返回給客戶端。
2、DML語句執行
假設有如下更新語句:
UPDATE employees SET first_name='Hello',last_name='Kitty' WHERE employee_id=199;
update語句執行計劃如下:
DML語句都存在相應的查詢過程,因為只有查詢到了所需要修改的數據,才能進行相應的操作。查詢的過程大致與查詢語句執行過程相同。但也有如下不同點:
獲取latch時,如果是只讀操作,多個會話可以同時獲取相同的latch,如果是DML操作,則不可以同時獲得。
只讀操作多個會話可以同時讀取同一個數據塊,DML操作則不可以多個會話同時修改同一個數據塊。
獲取到數據塊后進行相應的操作,并記錄對應的undo信息和redo log信息,完成update操作。