How PostgreSQL organizes data
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 ...
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ên Disk
- Không gian của RAM thì ít hơn Disk
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.