postgresql/lightdb的核心數據結構

哈希(utils/hash/dynahash.c,hashfn.h,便利APIhsearch.h)

  在pg內核中,大量使用了hash以便快速搜索。如catcache,portal,operator等。

  哈希創建(屬性),遍歷,hashcode計算函數。

 

列表List/ListCell

  跟c++/java的List是一樣的,底層數組。接口和宏在pg_list.h,是由於早期有一部分是使用Lisp開發的,所以用c重寫後,仍然保留了下來。正統的是dlist,也就是鏈表。大部分宏的命名規範爲xxx1,xxx2,xxx3,代表1、2、3個參數。參數重載也是類似。實現在src/backend/nodes/list.c中。

  PostgreSQL stores information about SQL queries in structures called nodes. Nodes are generic containers that have a type field and then a type-specific data section. Nodes are usually placed in Lists. A List is container with an elem element, and a next field that points to the next List. These List structures are chained together in a forward linked list. In this way, a chain of List s can contain an unlimited number of Node elements, and each Node can contain any data type. These are used extensively in the parser, optimizer, and executor to store requests and data.

鏈表(dlist)

  就是普通的鏈表實現。實現在ilist.h。那pg中什麼時候用List,什麼時候單/雙鏈表?StartAutovacuumWorker中就使用了鏈表。目前來看,絕大部分情況下還是列表比較多。

   

內存上下文(MemoryContext,mcxt.c)

  通過palloc/pfree分配和釋放,通過palloc分配的內存會在事務結束時自動釋放,避免了內存泄露。理解AllocSet context的設計和實現是關鍵。

  最重要的是理解內存層次,計算內存消耗(https://github.com/MasaoFujii/pg_cheat_funcs,pg14內置了內存上下文管理函數),各種out of memory的排查(一般是真的內存不足了,跟Linux內核的幾個參數有關係,詳見https://www.cnblogs.com/zhjh256/p/15424236.html)。

  掌握全局上下文,理解用戶上下文和事務上下文的分配與管理機制。

palloc和palloc0的區別?palloc0多了對齊處理。

 

輕量級鎖(LWLock)/barrier

  修改各種內存共享數據,典型的是共享內存段中的hash、全局變量以及數組如PGPROC等。幾乎到處都會使用,比spin要更慢,因爲不是忙等,會有喚醒間隔。一般用mutex實現,在pg中體現爲lwlock,在oracle中體現爲latch。

 

latch(數據庫專用術語),也就是c的mutex

/*
 * Latch structure should be treated as opaque and only accessed through
 * the public functions. It is defined here to allow embedding Latches as
 * part of bigger structs.
 */
typedef struct Latch
{
    sig_atomic_t is_set;
    bool        is_shared;
    int            owner_pid;
#ifdef WIN32
    HANDLE        event;
#endif
} Latch;
typedef struct WaitEvent
{
    int            pos;            /* position in the event data structure */
    uint32        events;            /* triggered events */
    pgsocket    fd;                /* socket fd associated with event */
    void       *user_data;        /* pointer provided in AddWaitEventToSet */
#ifdef WIN32
    bool        reset;            /* Is reset of the event required? */
#endif
} WaitEvent;

/* forward declaration to avoid exposing latch.c implementation details */
typedef struct WaitEventSet WaitEventSet;

/*
 * prototypes for functions in latch.c
 */
extern void InitializeLatchSupport(void);
extern void InitLatch(Latch *latch);
extern void InitSharedLatch(Latch *latch);
extern void OwnLatch(Latch *latch);
extern void DisownLatch(Latch *latch);
extern void SetLatch(Latch *latch);
extern void ResetLatch(Latch *latch);

extern WaitEventSet *CreateWaitEventSet(MemoryContext context, int nevents);
extern void FreeWaitEventSet(WaitEventSet *set);
extern int    AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
                              Latch *latch, void *user_data);
extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);

extern int    WaitEventSetWait(WaitEventSet *set, long timeout,
                             WaitEvent *occurred_events, int nevents,
                             uint32 wait_event_info);
extern int    WaitLatch(Latch *latch, int wakeEvents, long timeout,
                      uint32 wait_event_info);
extern int    WaitLatchOrSocket(Latch *latch, int wakeEvents,
                              pgsocket sock, long timeout, uint32 wait_event_info);

/*
 * Unix implementation uses SIGUSR1 for inter-process signaling.
 * Win32 doesn't need this.
 */
#ifndef WIN32
extern void latch_sigusr1_handler(void);

  latch相對於實現mutex的spinlock,latch通常知道設置者和擁有者,比如並行執行中設置gather/worker通信用的shm_mq上的收/發者,就用到了latch。mutex通常不需要知道等待者和被等待者。這樣latch和mutex的適用場景就比較清晰了。

signal

  signal是一種技術實現手段。latch一種邏輯同步概念,本質上不是一個維度對比。 

transaction(xact,詳見src/backend/access/transam/README)

  理解事務實現的三層體系(SQL層,邏輯事務層(StartTransactionCommand,實現在xact.c),事務實現細節層(low-level,主要是各種內核數據結構的設置和清理))。

  理解各種事務狀態:TransactionState表示,事務狀態詳細信息保存在數據結構TransactionStateData中,事務狀態轉換圖。

  各種類型的事務ID(虛擬,全局,本地,真正),各種事務ID及其比較。

  事務快照(TransactionSnapshot)、目錄快照(CatalogSnapshot)、歷史快照(HistoricSnapshot),註冊各種快照(RegisterSnapshot)。

 

relation/RelationData(rel.h/table.c/tableam.c)

  訪問catalog和訪問用戶表是兩個邏輯,因爲catalog的使用上下文非常明確,所以就儘可能多的硬編碼用於優化性能。比如類型都是可以強行轉義,如Form_pg_database   pgdatabase = (Form_pg_database) GETSTRUCT(tup);。甚至一些後臺邏輯都是硬編碼的,如pgstat.c/pgstat_vacuum_stat()。

  table是用戶層,relation是內部,tableAPI調用relationAPI。

  RelationData是運行時緩存relcache的元素類型,非常重要。

 

scankey

  任何一個訪問和過濾謂詞都是通過scankey實現的,運行時判斷,所以成本是比較高的。

 

  cache和catalog/catcache(catcache.c,Low-level catalog cache definitions)/relcache(Relation descriptor cache definitions,relcache.h,pg_table/pg_stat_relation相關的緩存,每個表一行)/syscache(syscache.c,System catalog cache definitions,lsyscache.h是其API友好版,一般用戶調用lsyscache中的函數)

  catcache和syscache的差異。lsyscache.c調用syscache.c,syscache.c(catalog以及被標記爲catalog的用戶表都是syscache加載的,通過systable_* scan APIs訪問,https://www.postgresql.org/docs/current/logicaldecoding-output-plugin.html#LOGICALDECODING-CAPABILITIES)調用catcache.c。

  注意可淘汰和被淘汰的狀態差異。ReleaseSysCache只是減少計數器。CatCacheRemoveCTup負責真正銷燬並釋放內存。

  relcache.h包括獲取和relation也就是表相關的任何信息,包括根據relid獲取所有的索引,分區明細等。

  catalog與pg_catalog、lt_catalog系統表的關係及initdb初始化

The values in catalog/pg_* include files are always in sync with what gets put into
the database catalogs by virtue of being the definition of the structure and contents of
the system catalog tables. The .bki files used when initdb command sets up a new
empty database cluster are generated from these .h files by genbki.pl script.

Another often used include directory is catalog/ which gives you the initial (and by
convention constant) part of most system tables so you do not need to look up things
like type identifier for int4 data type, but can use its pre-defined value
INT4OID directly. As of PostgreSQL 9.2, there are 79 constants for type IDs defined in
catalog/pgtype.

 

pgproc/myproc/mypgxact,xid/xmin

 https://vdocuments.net/inside-postgres-shared-memory-with-enterprisedb.html?page=16

pg_stat_activity(pgstat.c)

 

大量的輔助函數、宏

  在pg中,針對上述核心基礎結構的操作提供了大量的輔助函數和宏。包括很多的狀態訪問,簡單的狀態轉換,加鎖、解鎖,成員修改等。你會發現很多基礎軟件、框架、庫中都是這個模式,包括java的,但是業務應用系統卻很難應用。爲什麼呢?因爲應用是定製化的,很多應用甚至幾乎沒有什麼用戶,甚至運行不了多久,所以根本不需要維護還這麼高的成本。前三個版本的軟件你會發現基本上不需要,第四個版本開始纔會需要。以爲這些領域對象被大量用戶訪問。所以是可維護性(有維護的需求)纔有領域對象輔助函數的需求,否則就不需要了。

難點

  基礎之外的增值纔是核心,以table爲例,分區、TOAST、繼承、vacuum屬性(甚至爲vacuum定製實現)、unlogged、表空間、特殊字段類型、壓縮、存儲優化、FDW、TAM等等。核心的table存儲功能是基礎,但是同質化會很嚴重,通常核心和外圍的投入55開,而且需要花更多的時間跟蹤和管理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章