Читаем вместе. PostgreSQL 15 изнутри. Стр. 23 — 39

Егор Рогов. Postgres 15 изнутриКнига Егора Рогова «PostgreSQL 14 изнутри» в прошлом 2022 году. Я прочитал её черновики и решил еще летом 2022 года эту книгу прочитать. Потом вышла следующая версия, по 15-му Постгресу. Но и её я всё не читаю от начала до конца — только какие-то части.

Решил еще раз прочитать сделать еще одну попытку прочитать эту книгу всю целиком. Я хочу её прочитать не торопясь, проверяя некоторые моменты. Предлагаю читать книгу вместе.

Далее буду периодически публиковать очередные порции «прочитанного» материала — какие-то детали и эксперименты с материалом книги. Буду указывать проработанные страницы книги.

Так что мои статьи — лишь дополнение, основной материал — в книге.

Начну читать вот эту версию: PostgreSQL 15 изнутри. — М.: ДМК Пресс, 2023. — 662 с. Но уже скоро должна, я надеюсь, выйти PostgreSQL 16 изнутри, так что переключусь на неё. Наверное, первые (обзорные) страницы не особенно поменяются, так что ничего страшного не будет. Но, если всё-же будут какие-то изменения — дополню мои вышедшие статьи.

# SELECT version();
version
-----------------------------------------------------------------------------------------------------------
PostgreSQL 15.4 (Ubuntu 15.4-2.pgdg23.04+1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 12.3.0-1ubuntu1~23.04) 12.3.0, 64-bit
(1 row)

Сегодня рассмотрю несколько разделов:

Каждый из этих разделов далее в книге рассматривается более подробно, так что и я в данной статье лишь немного на них посмотрю.

Базы данных

А вы знали что утилита InitDB раньше была скриптом?

#!/bin/sh
#-------------------------------------------------------------------------
#
# initdb.sh--
#     Create (initialize) a Postgres database system.  
# 
#     A database system is a collection of Postgres databases all managed
#     by the same postmaster.  
#
#     To create the database system, we create the directory that contains
#     all its data, create the files that hold the global classes, create
#     a few other control files for it, and create one database:  the
#     template database.

Можно посмотреть, например, версию 6.4 в гитехабе — там был initdb.sh-скрипт. А в версии 8.0 — уже initdb.c.

В 2003 году переписали на Си. Можно посмотреть подробнее коммит:

git log 279598bb713829c6d718015c05bb2679e672bdf0

Начало там вот такое:

commit 279598bb713829c6d718015c05bb2679e672bdf0
Author: Bruce Momjian <bruce@momjian.us>
Date:   Mon Nov 10 22:51:16 2003 +0000

    Add C version of initdb, from Andrew Dunstan.
    
    This is his original version with a binary rmdir() we might need in the
    future.
    
    I will commit an update version with cleanups shortly.

А список баз данных можно посмотреть запросом:

SELECT
  d.datname as "Name",
  pg_catalog.pg_get_userbyid(d.datdba) as "Owner",
  pg_catalog.pg_encoding_to_char(d.encoding) as "Encoding",
  CASE d.datlocprovider WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' END AS "Locale Provider",
  d.datcollate as "Collate",
  d.datctype as "Ctype",
  d.daticulocale as "ICU Locale",
  NULL as "ICU Rules",
  pg_catalog.array_to_string(d.datacl, E'\n') AS "Access privileges"
FROM pg_catalog.pg_database d
ORDER BY 1;
   Name    |  Owner   | Encoding | Locale Provider |   Collate   |    Ctype    | ICU Locale | ICU Rules |   Access privileges   
-----------+----------+----------+-----------------+-------------+-------------+------------+-----------+-----------------------
 pavel     | pavel    | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           | 
 postgres  | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           | 
 template0 | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           | =c/postgres          +
           |          |          |                 |             |             |            |           | postgres=CTc/postgres
 template1 | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |            |           | =c/postgres          +
           |          |          |                 |             |             |            |           | postgres=CTc/postgres
(4 rows)

 

Системный каталог

Общие для всего кластера объекты хранятся без указания какой-то явной таблицы:

-- Есть указание БД
SELECT pg_relation_filepath('t_toast');
 pg_relation_filepath 
----------------------
 base/16390/16398
(1 row)

-- Нет указания БД
SELECT pg_relation_filepath('pg_database');
 pg_relation_filepath 
----------------------
 global/1262
(1 row)

А еще можно посмотреть на представление pg_stat_database:

Представление pg_stat_database содержит по одной строке со статистикой уровня базы данных для каждой базы кластера, и ещё одну для общих объектов

SELECT * FROM pg_stat_database where datid = 0\gx

Наверное, этот id=0 как раз и подразумевался под «фиктивной бд».

SELECT datid, datname FROM pg_stat_database;

Перечень объектов системного каталога можно найти в документации.

И SQL-запросами. Либо запросом к схеме pg_catalog таблице pg_class в ней:

SELECT n.nspname as "Schema",
  c.relname as "Name"
FROM pg_catalog.pg_class c
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
  AND n.nspname = 'pg_catalog'
ORDER BY 1,2;

Либо запросом к схеме pg_catalog таблице tables в ней:

SELECT table_schema as "Schema",
  table_name as "Name"
FROM information_schema.tables 
  WHERE table_type = 'BASE TABLE' 
  AND table_schema = 'pg_catalog' 
ORDER BY 1,2;

 

Схемы

Список схем можно посмотреть и таким запросом:

SELECT n.nspname AS "Name",
  pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner",
  pg_catalog.array_to_string(n.nspacl, E'\n') AS "Access privileges"
FROM pg_catalog.pg_namespace n
ORDER BY 1;

        Name        |       Owner       |           Access privileges            
--------------------+-------------------+----------------------------------------
 information_schema | postgres          | postgres=UC/postgres                  +
                    |                   | =U/postgres
 pg_catalog         | postgres          | postgres=UC/postgres                  +
                    |                   | =U/postgres
 pg_toast           | postgres          | 
 public             | pg_database_owner | pg_database_owner=UC/pg_database_owner+
                    |                   | =U/pg_database_owner
(4 rows)

Можно обратить внимание на предопределённую роль pg_database_owner. В документации пишут так:

Единственным членом роли pg_database_owner неявным образом является владелец текущей базы данных. Как и любая другая роль, она может владеть объектами или получать права доступа. Следовательно, права, данные роли pg_database_owner в базе-шаблоне, приобретёт владелец каждой базы, созданной из данного шаблона. Роль pg_database_owner не может быть членом какой-либо роли, и в неё не могут быть явно включены члены. Изначально эта роль владеет схемой public, то есть владелец базы данных управляет использованием данной схемы в своей базе.

 

Табличные пространства

SQL-запрос для просмотра списка табличных пространств:

SELECT spcname AS "Name",
  pg_catalog.pg_get_userbyid(spcowner) AS "Owner",
  pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_size(oid)) AS "Size"
FROM pg_catalog.pg_tablespace
ORDER BY 1;

    Name    |  Owner   |  Size  
------------+----------+--------
 pg_default | postgres | 29 MB
 pg_global  | postgres | 555 kB
(2 rows)

Это предопределённые табличные пространства, пользовательских я не создавал — оставил это для экспериментов на попозже.

 

Отношения

Таблица системного каталога для отношений изначально называлась pg_relation, но довольно скоро, на волне увлечения объектной ориентированностью, ее переименовали в привычный нам сейчас pg_class. Однако столбцы этой таблицы попрежнему имеют префикс rel.

Есть даже переписка этого года по этому поводу: Obsolete reference to pg_relation in comment

В гитхабе можно посмотрел Release_1_0_3, там уже есть файл pg_class, но в нём есть комментарий:

 * NOTES
 *    ``pg_relation'' is being replaced by ``pg_class''.  currently
 *    we are only changing the name in the catalogs but someday the
 *    code will be changed too. -cim 2/26/90
 *    [it finally happens.  -ay 11/5/94]

А поля начинаются с rel, не с class 🙂

\d pg_class
                     Table "pg_catalog.pg_class"
       Column        |     Type     | Collation | Nullable | Default 
---------------------+--------------+-----------+----------+---------
 oid                 | oid          |           | not null | 
 relname             | name         |           | not null | 
 relnamespace        | oid          |           | not null | 
 reltype             | oid          |           | not null |

В одной из работ он даже ввел понятие «упорядоченного отношения» (ordered relation) для таблицы, в которой порядок строк задается индексом.

Наверное, вот эта работа: Document Processing in a Relational Database System, и в ней — раздел 3. ORDERED RELATIONS.

 

Слои и файлы

Представления не содержат данных — проверим:

CREATE VIEW v_one AS SELECT 1;
CREATE VIEW

SELECT pg_relation_filepath('v_one');
 pg_relation_filepath 
----------------------
 
(1 row)

Действительно — адреса файла нет.

А у материализованного представления — есть:

CREATE MATERIALIZED VIEW v_one_m AS SELECT 1;
SELECT 1

SELECT pg_relation_filepath('v_one_m');
 pg_relation_filepath 
----------------------
 base/16390/16403
(1 row)

У обычных таблиц тоже:

CREATE TABLE t (id integer);
CREATE TABLE

SELECT pg_relation_filepath('t');
 pg_relation_filepath 
----------------------
 base/16390/16395
(1 row)

Слои:

  • Основной слой (main fork, _2, _3 и тд)
  • Слой инициализации (init fork, _init)
  • Карта свободного пространства (free space map, _fsm)
  • Карта видимости (visibility map, _vm)

 

Страницы

Посмотрим размер страницы block_size:

SHOW block_size;
 block_size 
------------
 8192
(1 row)

Надо будет попробовать с измененным размером страницы ./configure —with-blocksize собрать, поэкспериментировать.

 

TOAST

The Oversized Attributes Storage Technique.

Решил посмотреть кто придумал такую технику. В директории /postgres/src/backend/access/heap нужно переключиться на ту же 8-ю версию и посмотреть историю изменений файла tuptoaster.c (потому что с некоторой версии эта функциональность находится в файле heaptoast.c):

git log postgres/src/backend/access/heap/tuptoaster.c

И пролистываем в самый низ списка:

commit e2aef4969450da69bc759e19f62a2d6caec4a3d9
Author: Jan Wieck <JanWieck@Yahoo.com>
Date:   Tue Dec 21 00:06:44 1999 +0000

    Added empty TOASTER files and corrected some minor glitches
    in regression tests.
    
    Jan

Для бо́льшей детализации можно посмотреть сам коммит:

git show e2aef4969450da69bc759e19f62a2d6caec4a3d9

Там много строк, не буду приводить содержимое.

Проверим как работает TOAST:

CREATE TABLE t_toast (id1 integer, id2 numeric, id3 text, id4 json);

# \d+ t_toast 
                                         Table "public.t_toast"
 Column |  Type   | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
 id1    | integer |           |          |         | plain    |             |              | 
 id2    | numeric |           |          |         | main     |             |              | 
 id3    | text    |           |          |         | extended |             |              | 
 id4    | json    |           |          |         | extended |             |              | 
Access method: heap

Параметр хранения TOAST_TUPLE_TARGET (который по умолчанию 2000 байт) можно задавать на уровне таблиц с помощью команды:

ALTER TABLE t_toast SET (toast_tuple_target = 1000);

\d+ t_toast 
                                         Table "public.t_toast"
 Column |  Type   | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
 id1    | integer |           |          |         | plain    |             |              | 
 id2    | numeric |           |          |         | main     |             |              | 
 id3    | text    |           |          |         | extended |             |              | 
 id4    | json    |           |          |         | extended |             |              | 
Access method: heap
Options: toast_tuple_target=1000

Либо можно параметры хранения посмотреть SQL-запросам:

SELECT pg_catalog.array_to_string(c.reloptions || array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')
FROM pg_catalog.pg_class c
 LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)
LEFT JOIN pg_catalog.pg_am am ON (c.relam = am.oid)
WHERE c.oid = (select oid from pg_class where relname = 't_toast');
     array_to_string     
-------------------------
 toast_tuple_target=1000
(1 row)

Далее продолжим с раздела 1.2. Процессы и память.


Be the first to comment

Leave a Reply

Ваш Mail не будет опубликован.


*