- 論壇徽章:
- 0
|
PostgreSQL內(nèi)核分析
閆華 postgres_fan@yahoo.com.cn
1前言
一直都想寫關(guān)于PostgreSQL實(shí)現(xiàn)原理方面的文章,由于這是一個工作量巨大的工作,平時還要工作謀生,很難抽出時間來寫,希望能夠堅持下來。PostgreSQL作為歷史最悠久的開源數(shù)據(jù)庫管理系統(tǒng),有關(guān)它的詳細(xì)信息可以到http://www.postgresql.org/去查閱,這里不再贅述。在MySQL被Oracle抓在手中以后,MySQL變得前途未卜,在這種情況下,PostgreSQL顯得越發(fā)地重要。
PostgreSQL作為脫胎于學(xué)術(shù)研究的數(shù)據(jù)庫管理系統(tǒng),源代碼結(jié)構(gòu)非常地清晰,易于理解,相比之下,MySQL的代碼結(jié)構(gòu)就要亂很多(個人之見,MySQL的粉絲不要噴我)。
PostgreSQL的源代碼的規(guī)模已經(jīng)非常地大,文件眾多。初學(xué)者如果直接去看代碼,必然如墜五里霧中,難以理解,所以我的文章著重講述源代碼背后的實(shí)現(xiàn)原理,而不是詳細(xì)地敘述每個函數(shù)的輸入輸出以及每一行代碼的作用。讓讀者能從宏觀上把握系統(tǒng)結(jié)構(gòu),循著系統(tǒng)的主線去閱讀源代碼要容易地多。
這將是一系列的連載文章,閑言少敘,讓我們先從PostgreSQL的進(jìn)程結(jié)構(gòu)說起。
2 PostgreSQL進(jìn)程結(jié)構(gòu)
PostgreSQL與Oracle類似,是多進(jìn)程結(jié)構(gòu)的數(shù)據(jù)庫。在PostgreSQL中主要有postmaster, postgres, vacuum, bgwriter, pgarch, walwriter, pgstat等進(jìn)程,下面先簡單介紹一下每個進(jìn)程的作用,然后再詳細(xì)介紹每個進(jìn)程的實(shí)現(xiàn)原理。
(1)postmaster
負(fù)責(zé)在啟動數(shù)據(jù)庫的時候創(chuàng)建共享內(nèi)存并初始化各種內(nèi)部數(shù)據(jù)結(jié)構(gòu),如鎖表,數(shù)據(jù)庫緩沖區(qū)等,該進(jìn)程在數(shù)據(jù)庫中只有一個。在數(shù)據(jù)庫啟動以后負(fù)責(zé)監(jiān)聽用戶請求,創(chuàng)建postgres進(jìn)程來為用戶服務(wù)。這一點(diǎn)與Oracle的TNS listener進(jìn)程類似。
(2)postgres
負(fù)責(zé)執(zhí)行用戶發(fā)出的所有SQL語句,該進(jìn)程在數(shù)據(jù)庫中可能有多個, Oracle中叫shadow process。
(3)vacuum
負(fù)責(zé)清除數(shù)據(jù)庫中無用的歷史數(shù)據(jù)(已經(jīng)被刪除或更新的記錄)。更新優(yōu)化器的統(tǒng)計信息,確保產(chǎn)生可以接受的查詢計劃,該進(jìn)程在數(shù)據(jù)庫可能有多個,Oracle無此種類型進(jìn)程。
(4)bgwriter
負(fù)責(zé)將數(shù)據(jù)緩沖區(qū)中已被更新的數(shù)據(jù)庫寫入數(shù)據(jù)庫物理數(shù)據(jù)文件中, Oracle中對應(yīng)的進(jìn)程叫DBWR, 該進(jìn)程在數(shù)據(jù)庫中只有一個。
(5)pgarch
負(fù)責(zé)將系統(tǒng)產(chǎn)生的redo log復(fù)制到其他外部存儲介質(zhì)中, Oracle中對應(yīng)的進(jìn)程叫Archiver,該進(jìn)程在數(shù)據(jù)庫中只有一個。
(6)walwriter
負(fù)責(zé)將系統(tǒng)產(chǎn)生的redo log 寫到redo log 文件中(在pg_xlog目錄下), Oracle中對應(yīng)的進(jìn)程叫LGWR,該進(jìn)程在數(shù)據(jù)庫中只有一個。
(7)pgstat
負(fù)責(zé)收集數(shù)據(jù)庫運(yùn)行中的統(tǒng)計信息,如一個表上面進(jìn)行了多少次插入與更新操作,該進(jìn)程在數(shù)據(jù)庫中只有一個, Oracle無此種類型進(jìn)程。
2.1 Postmaster進(jìn)程
Postmaster進(jìn)程是PostgreSQL啟動以后創(chuàng)建的第一個進(jìn)程,所有的其他進(jìn)程都是由Postmaster創(chuàng)建的。與Postmaster進(jìn)程相關(guān)的大部分代碼都在src\backend\postmaster\postmaster.c文件中。Postmaster進(jìn)程的入口函數(shù)是PostmasterMain。
Postmaster進(jìn)程在系統(tǒng)啟動時首先進(jìn)行一些初始化工作,創(chuàng)建共享內(nèi)存和信號量等數(shù)據(jù)結(jié)構(gòu),創(chuàng)建pgstat等后臺進(jìn)程,然后調(diào)用ServerLoop函數(shù)進(jìn)入監(jiān)聽客戶端連接請求的狀態(tài),ServerLoop函數(shù)的代碼是一個無限循環(huán)的結(jié)構(gòu),正常的情況下,Postmaster進(jìn)程永遠(yuǎn)都不會退出ServerLoop函數(shù)。ServerLoop函數(shù)的主要功能是在收到客戶端連接請求的情況下,創(chuàng)建一個新的postgres進(jìn)程來為客戶端服務(wù)。
因為所有的其他進(jìn)程都由Postmaster進(jìn)程創(chuàng)建,所以它們都是Postmaster的子進(jìn)程。當(dāng)一個postgres、bgwriter、 walwriter或vacuum進(jìn)程因為非正常原因崩潰以后,Postmaster進(jìn)程要進(jìn)行一些清理工作,釋放它們占用的內(nèi)部資源,或者重新啟動這些進(jìn)程。postmaster.c中的reaper函數(shù)就是負(fù)責(zé)執(zhí)行這個任務(wù)的。
2.2 Postgres進(jìn)程
Postgres進(jìn)程負(fù)責(zé)執(zhí)行客戶端發(fā)出的所有的SQL語句及自定義函數(shù)。與Postgres進(jìn)程相關(guān)的代碼在src/backend/tcop/postgres.c文件中,Postgres進(jìn)程的入口函數(shù)是PostgresMain。
PostgresMain首先進(jìn)行一些初始化工作,然后使用語句for (; 進(jìn)入一個無限循環(huán)狀態(tài),等待客戶端發(fā)來命令請求,接受客戶端命令,執(zhí)行客戶端命令,將執(zhí)行結(jié)果返回給客戶端。
for (; 無限循環(huán)體首先調(diào)用ReadCommand從客戶端讀取一條命令,然后根據(jù)命令類型,調(diào)用相應(yīng)的處理函數(shù)。例如,對于可以直接執(zhí)行的SQL語句(simple query),命令類型的代碼是“Q”,處理代碼如下:
case 'Q': /* simple query */
{
const char *query_string;
/* Set statement_timestamp() */
SetCurrentStatementStartTimestamp();
query_string = pq_getmsgstring(&input_message);
pq_getmsgend(&input_message);
exec_simple_query(query_string);
send_ready_for_query = true;
語句exec_simple_query(query_string)負(fù)責(zé)解析SQL語句,生成查詢計劃,執(zhí)行查詢計劃,將查詢結(jié)果返回給客戶端。
2.3 Vacuum進(jìn)程
PostgreSQL使用的是多版本的并發(fā)控制機(jī)制,歷史數(shù)據(jù)與當(dāng)前數(shù)據(jù)都存放在同一個數(shù)據(jù)庫中,當(dāng)某些歷史數(shù)據(jù)不再有用時,應(yīng)當(dāng)把這些歷史數(shù)據(jù)從數(shù)據(jù)庫中刪除掉(這個過程叫vacuum),讓這些數(shù)據(jù)所占的存儲空間能夠被重新利用,否則數(shù)據(jù)庫所占的存儲空間會越來越大,最后會消耗掉所有的物理存儲空間,導(dǎo)致數(shù)據(jù)庫無法繼續(xù)正常運(yùn)行。Vacuum進(jìn)程負(fù)責(zé)清除無用的歷史數(shù)據(jù)
Vacuum進(jìn)程分兩種。第一種叫Vacuum控制進(jìn)程,第二種Vacuum工作進(jìn)程。自動垃圾Vacuum控制進(jìn)程在數(shù)據(jù)庫中只有一個,數(shù)據(jù)庫啟動以后這個進(jìn)程就存在,它睡眠一段時間后,就創(chuàng)建一個或多個Vacuum工作進(jìn)程來進(jìn)行垃圾收集(Vacuum控制進(jìn)程不直接創(chuàng)建Vacuum工作進(jìn)程,而是通過postmaster進(jìn)程創(chuàng)建Vacuum工作進(jìn)程),然后再進(jìn)入睡眠狀態(tài),不斷地重復(fù)這個循環(huán),每次睡眠的時間由參數(shù)autovacuum_naptime指定。Vacuum工作進(jìn)程負(fù)責(zé)真正的垃圾收集工作,參數(shù)autovacuum_max_workers控制能夠被同時創(chuàng)建的Vacuum工作進(jìn)程的個數(shù)的最大值。
與Vacuum控制進(jìn)程相關(guān)的大部分代碼都在backend/postmaster/autovacuum.c文件中,Vacuum控制進(jìn)程的入口函數(shù)是AutoVacLauncherMain。AutoVacLauncherMain首先進(jìn)行一些初始化工作,然后使用語句for (; 進(jìn)入一個無限循環(huán)狀態(tài),睡眠一端時間,檢查是否需要創(chuàng)建新的Vacuum工作進(jìn)程,如果需要,通知Postmaser創(chuàng)建新的Vacuum工作進(jìn)程,然后繼續(xù)睡眠,不斷地重復(fù)這個過程。 |
|