How PostgreSQL organizes data
Bài đăng này đã không được cập nhật trong 7 năm
Như bạn đã viết trong PostgreSQL
, data
được chứa trong các tables
, các tables
lại được gộp với nhau trong 1 database
. Ở tầng cao nhất database
sẽ được lưu trữ với nhau tại các clusters
. Chúng ta có thể xem được cấu trúc của việc lưu trữ này trên disk
.
postgres=# SELECT datname, oid FROM pg_database;
datname | oid
-----------+-------
perf | 16556
template1 | 1
template0 | 16555
Ta thấy rằng có tất cả 3 databases
đang được lưu trong cluster
. Ta có thể tìm ra vị trí lưu chúng trên disk
.
$ cd $PGDATA
$ ls
base pg_clog pg_ident.conf pg_xlog postmaster.opts
global pg_hba.conf PG_VERSION postgresql.conf postmaster.pid
Thư mục $PGDATA
có chứa một subdirectory
khác là base
. Thư mục này chính là nơi chứa databases
.
$ cd ./base
$ ls -l
total 12
drwx------ 2 postgres pgadmin 4096 Jan 01 20:53 1
drwx------ 2 postgres pgadmin 4096 Jan 01 20:53 16555
drwx------ 3 postgres pgadmin 4096 Jan 01 22:38 16556
Chú ý rằng chúng ta có 3 thư mục con nằm tron $PGDATA/base
. Quay lại với kết quả của câu query
hồi nãy:
SELECT datname, oid FROM pg_database;
Ta nhận thấy rằng tên của 3 thư mục này chính là giá trị oid
tương ứng với đừng database
. Thư mục 16556
chứa database perf
, thư mục 16555
chứa database template0
.
Tiến vào sâu hơn, ta thấy.
$ cd ./1
$ ls
1247 16392 16408 16421 16429 16441 16449 16460 16472
1249 16394 16410 16422 16432 16442 16452 16462 16474
1255 16396 16412 16423 16435 16443 16453 16463 16475
1259 16398 16414 16424 16436 16444 16454 16465 16477
16384 16400 16416 16425 16437 16445 16455 16466 pg_internal.init
16386 16402 16418 16426 16438 16446 16456 16468 PG_VERSION
16388 16404 16419 16427 16439 16447 16457 16469
16390 16406 16420 16428 16440 16448 16458 16471
Lại một đống file tên là số, có thể lờ mờ đoán ra đây là các oids
, nhưn g là oids
gì thì chịu. Trong database
có chứa các table
, nên có thể đây là các oids
của table
. Kiểm chứng:
$ psql -q -d template1
template1=# SELECT relname, oid FROM pg_class;
template1=# SELECT oid, relname FROM pg_class ORDER BY oid;
oid | relname
-------+---------------------------------
1247 | pg_type
1249 | pg_attribute
1255 | pg_proc
1259 | pg_class
1260 | pg_shadow
1261 | pg_group
1262 | pg_database
16384 | pg_attrdef
16386 | pg_relcheck
... | ...
Và như vậy mỗi file tương ứng với mỗi table
trong database
. Và mỗi table
sẽ tương ứng với một oid
trong bảng pg_class
.
Trong bảng pg_class
còn có 2 column
khác, qua đó ta có thể thấy cấu trúc của PostgreSQL
.
perf=# SELECT relname, oid, relpages, reltuples FROM pg_class
perf-# ORDER BY oid
relname | oid | reltuples | relpages
--------------+------+-----------+----------
pg_type | 1247 | 143 | 2
pg_attribute | 1249 | 795 | 11
pg_proc | 1255 | 1263 | 31
pg_class | 1259 | 101 | 2
pg_shadow | 1260 | 1 | 1
pg_group | 1261 | 0 | 0
... | ... | ... | ...
Gía trị reltuples
cho ta số lượng tuples
trong mỗi table
. relpages
cho ta số lượng page
cần để lưu lại toàn bộ contents
của table
đó. Vậy giá trị này phản ánh giá trị thực tế lưu trên disk
như thế nào? Nếu bạn xem kĩ hơn một số table
, bạn sẽ thấy mối quan hệ đó.
$ ls -l 1247 1249
-rw------- 1 postgres pgadmin 16384 Jan 01 20:53 1247
-rw------- 1 postgres pgadmin 90112 Jan 01 20:53 1249
File 1247
(pg_type
) có kích thước 16384 bytes
, có được chưa trong 2 pages
, file 1249
(pg_attribute
) có kích thước 90112 bytes
chiếm 11 pages
. Vậy mỗi page
sẽ chiếm 8192(bytes)
= 8k bytes
. Trong PostgreSQL
, toàn bộ quá trình I/O
được thực thực hiện page-by-page
. Vì vậy khi thực hiện query
trên một row
của table, PostgreSQL
sẽ đọc tí ít là 1 page
, có thể là nhiều page nếu như dât trên row
. Khi update
một dòng, PostgreSQL
sẽ tạo ra một version
mới của dòng đó ở cuối table
và đánh dấu hàng được update
là invalid
.Size
Size của mỗi page
sẽ được fix là 8192 bytes
. Bạn có thể thay đổi config
, nhưng tất cả các page
sẽ cùng kích thước. Nhưng kích thước của từng row
là không có định, nó tùy thuộc vào data trong mỗi row
. Vì vậy, số lượng row
trong 1 page
là không thể tính toán được.
Nếu trong trường hợp, kích thước của 1 row
vượt quá giới hạn 8k bytes
, PostgreSQL
sẽ lưu data vào TOAST
(The Oversized Attribute Storage Technique) table. TOAST
table này được xem như là một extension
của table bình thường. Nó chứa data
mà không chứa vừa vào table
bình thường.
Indexes
cũng sẽ được trong 1 pages
, page
mà chứa data
được gọi là heap page
, còn page
chứa index
được gọi là index page
. Nó cũng giống như một page
bình thường, không thể biết số lượng row
trong 1 page
, nếu quá lớn nó cũng được lưu trong TOAST
.
Page Caching
Có 2 nguyên tắc cơ bảng mà tất cả các Database System
đều tuân thủ theo chính là:
- Truy cập
RAM
thì nhanh hơn trênDisk
- Không gian của
RAM
thì ít hơnDisk
Theo đó, PostgreSQL
tối giản truy cập I/O
trên Disk
bằng cách lưu data
được truy cập nhiều lần trên RAM
. Khi server
được start
, PostgreSQL
tạo ra một cấu trúc dữ liệu gọi là Buffer Cache
. Buffer Cache
được tổ chức dưới dạng như một tập hợp của các page 8k bytes
. Mỗi page
trong Buffer Cache
tương ứng với một trong số nhiều page
trong database. Buffer Cache
này được share
cho toàn bộ các tiến trình sử dụng database
.
Khi bạn thực hiện query
một row
trong database
, PostgreSQL
sẽ đọc heap block
chứa data, và đưa heap block
này vào Buffer Cache
này. Nếu không có chỗ chứa, PostgreSQL
sẽ remove một số block
ra khỏi Buffer Cache
. Index block
cũng sẽ được đưa vào buffer giống như heap block
.
To be continued...
All rights reserved