Лекция 09. Физическое хранение данных
В этой главе:
- Табличные пространства.
- Хранение информации на носителях
- Размеры объектов
- Работа с табличным пространством
Табличные пространства
- Табличные пространства и базы данных
- Табличные пространства по умолчанию
- Каталоги баз данных в файловой системе
- Создание табличного пространства
- Каталог pg_tblspc
- Создание объекта в табличном пространстве
- Хранение объекта в табличном пространстве
- Создание объекта в ТП по умолчанию
- Перемещение объекта в другое ТП
- Создание БД с указанием ТП
- Информация о ТП для объектов баз данных
- Массовое перемещение
Табличные пространства и базы данных
Все данные кластера баз данных должны храниться на носителях, которые не теряют информацию при выключении питания (persistent storage). Например, жестких магнитных дисках, твердотельных носителях или других блочных устройствах.
В PostgreSQL хранение данных организовано с использованием файловых систем - все данные баз хранятся в обычных файлах.
В кластере баз данных имеются глобальные объекты, не принадлежащие конкретным базам в кластере, например, пользователи. Глобальные объекты - общие для кластера и для них имеется табличное пространство pg_global. Оно создается при инициализации кластера командой initdb. При этом также создается табличное пространство по умолчанию pg_default, предназначенное для хранения объектов баз. Если нет других табличных пространств, то все данные различных баз будут находиться в pg_default. Но часто создают и другие табличные пространства. В таком случае можно назначить конкретной базе табличное пространство по умолчанию, отличное от pg_default. Более того, можно назначить отдельное табличное пространство, например, для таблицы или индекса.
Объекты одной базы могут храниться в разных табличных пространствах, и в табличном пространстве хранятся объекты разных баз.
Частые причины создания дополнительных табличных пространств:
- нехватка свободного места в файловой системе;
- хранение архивных данных на медленных носителях, а "горячих" на скоростных;
- отдельное хранение индексов от таблиц.
https://www.postgresql.org/docs/15/storage-file-layout.html.
https://www.postgresql.org/docs/15/manage-ag-tablespaces.html.
Табличные пространства по умолчанию
postgres=# \db
Список табличных пространств
Имя | Владелец | Расположение
------------+----------+--------------
pg_default | postgres |
pg_global | postgres |
(2 строки)
postgres=# \! sudo -u postgres ls -ld $PGDATA/{global,base}
drwx------ 6 postgres postgres 4096 окт 12 18:54 /pgdata/06/data/base
drwx------ 2 postgres postgres 4096 окт 12 18:54 /pgdata/06/data/global
- Метакоманда \db psql выдает список табличных пространств.
- Каталог $PGDATA/global - табличное пространство pg_global.
- Каталог $PGDATA/base - табличное пространство pg_default.
При создании кластера командой initdb автоматически создаются два табличный пространства:
- pg_global - ему соответствует каталог $PGDATA/global
- pg_default - $PGDATA/base
Получить список табличных пространств проще всего метакомандой \db клиента psql. Но можно выполнить и запрос к pg_tablespace:
postgres=# SELECT spcname FROM pg_tablespace;
spcname
------------
pg_default
pg_global
(2 строки)
https://www.postgresql.org/docs/15/storage-file-layout.html.
Каталоги баз данных в файловой системе
[postgres@p620 ~]$ oid2name
Oid Database Name Tablespace
----------------------------------
5 postgres pg_default
16409 sch_db pg_default
4 template0 pg_default
1 template1 pg_default
В каталоге файловой системы, соответствующему, табличному пространству, для каждой базы данных, использующей его, создается подкаталог с именем - oid базы данных.
Для получения oid баз данных в командной строке ОС можно использовать утилиту oid2name.
В примере:
- $PGDATA/base - каталог табличного пространства pg_default;
- подкаталоги в $PGDATA/base с именами в виде цифр соответствуют oid БД.
Хранение данных баз в каталоге табличного пространства реализуется по простому принципу: создаются индивидуальные подкаталоги для каждой базы с именами, соответствующими oid (Object ID) баз данных. Получить oid БД можно, например, так:
postgres=# SELECT oid, datname FROM pg_database;
oid | datname
-------+-----------
5 | postgres
1 | template1
4 | template0
16409 | sch_db
(4 строки)
Однако, проще воспользоваться утилитой командной строки oid2name. При вызове ее без каких-либо аргументов она выдает список oid баз данных и их табличные пространства по умолчанию. https://www.postgresql.org/docs/15/oid2name.html.
Создание табличного пространства
[postgres@p620 ~]$ mkdir -p /pgdata/06/newdisk
[postgres@p620 ~]$ psql -q
postgres=# CREATE TABLESPACE newtabspace LOCATION '/pgdata/06/newdisk'; postgres=# \db
Список табличных пространств
Имя | Владелец | Расположение
-------------+----------+--------------------
newtabspace | postgres | /pgdata/06/newdisk
pg_default | postgres |
pg_global | postgres |
(3 строки)
CREATE TABLESPACE создает табличное пространство.
LOCATION указывает каталог, в котором будут размещаться данные для этого табличного пространства.
При создании табличного пространства каталог файловой системы для его размещения должен существовать, им должен владеть пользователь ОС postgres (пользователь, от имени которого работают процессы кластера) и он должен быть доступен для postgres на запись. https://www.postgresql.org/docs/15/sql-createtablespace.html.
Получить список табличных пространств проще всего метакомандой \db.
В Pangolin можно создавать табличные пространства, защищенные шиф рованием. Подробнее о них в курсе DBP1.
Каталог pg_tblspc
[postgres@p620 ~]$ ls -l $PGDATA/pg_tblspc/
итого 0
lrwxrwxrwx 1 postgres postgres 18 окт 13 21:47 16440 -> /pgdata/06/newdisk
[postgres@p620 ~]$ psql -c "SELECT oid FROM pg_tablespace WHERE spcname = 'newtabspace'"
oid
-------
16440
(1 строка)
В результате создания табличного пространства в каталоге $PGDATA/pg_tblspc/ создается символическая ссылка с именем, совпадающим с oid созданного табличного пространства.
Ссылка указывает на каталог, который был задан после ключевого слова LOCATION при создании табличного пространства.
Созданное табличное пространство физически оформляется посредством символической ссылки в каталоге $PGDATA/pg_tblspc. Имя файла символической ссылки совпадает с oid табличного пространства, а указывает эта символическая ссылка на каталог файловой системы, в котором будут храниться данные в этом табличном пространстве. Это тот самый каталог, который был указан после ключевого слова LOCATION команды CREATE TABLESPACE. https://www.postgresql.org/docs/15/storage-file-layout.html.
Создание объекта в табличном пространстве
sch_db=# CREATE TABLE tab_in_newts ( id integer, msg text ) TABLESPACE newtabspace; CREATE TABLE
sch_db=# SELECT * FROM pg_tables WHERE tablespace = 'newtabspace' \gx
-[ RECORD 1 ]-------------
schemaname | public
tablename | tab_in_newts
tableowner | postgres
tablespace | newtabspace
hasindexes | f
hasrules | f
hastriggers | f
rowsecurity | f
При создании таблиц, индексов и материализованных представлений можно явно указать, в каком табличном пространстве они должны размещать свои данные.
Для таких объектов баз данных, как таблицы, индексы и материализованные представления можно в явном виде задавать табличное пространство, в котором эти объекты будут хранить данные. Это делается с помощью ключевого слова TABLESPACE. Если его не указать, то при создании объекта будет использовано табличное пространство, которое ДЛЯ ДАННОЙ базы является табличным пространством по умолчанию. https://www.postgresql.org/docs/15/sql-createview.html.
https://www.postgresql.org/docs/15/sql-creatematerializedview.html.
https://www.postgresql.org/docs/15/sql-createindex.html.
Хранение объекта в табличном пространстве
[postgres@p620 ~]$ psql -d sch_db -c "SELECT pg_relation_filepath('tab_in_newts')"
pg_relation_filepath
---------------------------------------------
pg_tblspc/16440/PG_15_202310091/16409/16441
(1 строка)
[postgres@p620 ~]$ ls -l $PGDATA/pg_tblspc/16440/PG_15_202310091/16409/16441
-rw------- 1 postgres postgres 0 окт 14 10:16 /pgdata/06/data/pg_tblspc/16440/PG_15_202310091/16409/16441 [postgres@p620 ~]$ oid2name -d sch_db -f 16441
From database "sch_db":
Filenode Table Name ------------------------
16441 tab_in_newts
[postgres@p620 ~]$ oid2name
All databases:
Oid Database Name Tablespace ----------------------------------
5 postgres pg_default
16409 sch_db pg_default
4 template0 pg_default
1 template1 pg_default
Здесь:
- 16440 - oid табличного прост ранства;
- 16409 - oid базы данных sch_db;
- 16441 - oid таблицы tab_in_newts.
В табличном пространстве newtabspace создана таблица tab_in_newts. Хоть таблица и пустая, но файл для хранения ее данных уже создан, он имеет нулевой размер. Узнать имя файла, сохраняющего данные таблицы, можно функцией pg_relation_filepath().
https://www.postgresql.org/docs/15/disk-usage.html.
https://www.postgresql.org/docs/15/functions-admin.html.
Какой получился путь к данным таблицы в примере?
- $PGDATA/pg_tblspc/16440 - каталог табличного пространства newtabspace;
- $PGDATA/pg_tblspc/16440/PG_15_202310091/16409 - каталог базы данных sch_db для размещения объектов этой базы в табличном пространстве newtabspace;
- $PGDATA/pgdata/06/data/pg_tblspc/16440/PG_15_202310091/16409/16441 - собственно файл для размещения данных таблицы tab_in_newts.
Убедимся, что эта таблица не разместила свои данные в табличном пространстве pg_default, являющемся для БД sch_db ТП по умолчанию:
[postgres@p620 ~]$ oid2name -d sch_db -t tab_in_newts
From database "sch_db":
Filenode Table Name
------------------------
16441 tab_in_newts
[postgres@p620 ~]$ ls -l $PGDATA/base/16409/16441
ls: невозможно получить доступ к '/pgdata/06/data/base/16409/16441': No such file or directory
Создание объекта в ТП по умолчанию
sch_db=# CREATE INDEX ON tab_in_newts(msg);
CREATE INDEX
sch_db=# SELECT * FROM pg_indexes WHERE tablename = 'tab_in_newts' \gx
-[ RECORD 1 ]--------------------------------------------------------------------------
schemaname | public
tablename | tab_in_newts
indexname | tab_in_newts_msg_idx
tablespace |
indexdef | CREATE INDEX tab_in_newts_msg_idx ON public.tab_in_newts USING btree (msg)
sch_db=# SELECT pg_relation_filepath('tab_in_newts_msg_idx');
pg_relation_filepath
----------------------
base/16409/16446
БД sch_db
(1 строка)
oid индекса
sch_db=# \! ls -l $PGDATA/base/16409/16446
-rw------- 1 postgres postgres 8192 окт 14 12:00 /pgdata/06/data/base/16409/16446
Здесь:
- base - ТП pg_default;
- 16409 - БД sch_db;
- 16446 - oid индекса.
Таблица может размещаться в одном табличном пространстве, а индекс для колонки этой таблицы - в другом. В этом примере так и произошло, поскольку команде CREATE INDEX ключевое слово TABLESPACE задано не было.
В отличие от самой таблицы (она пока пустая), размер файла с данными которой нулевой, физический размер файла с данными индекса 8Кб - это ровно одна страница. Так произошло в силу того, что первая страница индекса всегда содержит структуры, необходимые для самого индекса, соответственно, индекс не может быть нулевого размера.
Перемещение объекта в другое ТП
sch_db=# SELECT pg_relation_filepath('tab_in_newts_msg_idx');
pg_relation_filepath
----------------------
base/16409/16446
(1 строка)
sch_db=# ALTER INDEX tab_in_newts_msg_idx SET TABLESPACE newtabspace;
ALTER INDEX
sch_db=# SELECT indexname, tablespace FROM pg_indexes WHERE tablename = 'tab_in_newts';
indexname | tablespace
---------------------+-------------
tab_in_newts_msg_idx | newtabspace
(1 строка)
sch_db=# SELECT pg_relation_filepath('tab_in_newts_msg_idx');
pg_relation_filepath
---------------------------------------------
pg_tblspc/16440/PG_15_202310091/16409/16447 - данные индекса в ТП newtablespace
(1 строка)
Таблицу можно переместить в другое табличное пространство командой ALTER TABLE, индекс - ALTER INDEX и так далее. https://www.postgresql.org/docs/15/sql-altertable.html.
https://www.postgresql.org/docs/15/sql-alterindex.html.
В примере на слайде из табличного пространства pg_default переносится файл данных индекса в табличное пространство newtabspace.
Следует понимать, что перенос выполняется посредством создания нового файла (что видно а примере - изменился oid) и последовательного копирования в него данных из исходного файла. Таким образом, эта процедура создает загрузку на подсистему ввода- вывода и может продолжаться очень длительное время для больших объектов.
Создание БД с указанием ТП
sch_db=# CREATE DATABASE nts_db TABLESPACE newtabspace;
sch_db=# \x \l+ ???_db \x
-[ RECORD 1 ]------+------------
Имя | nts_db
Владелец | postgres
Кодировка | UTF8
LC_COLLATE | en_US.UTF8
LC_CTYPE | en_US.UTF8
локаль ICU |
Провайдер локали | libc
Права доступа |
Размер | 9607 kB
Табл. пространство | newtablespace
Описание |
-[ RECORD 2 ]------+------------
Имя | sch_db
Владелец | postgres
Кодировка | UTF8
LC_COLLATE | en_US.UTF8
LC_CTYPE | en_US.UTF8
локаль ICU |
Провайдер локали | libc
Права доступа |
Размер | 9687 kB
Табл. пространство | pg_default
Описание |
У каждой базы дан ных есть табличное пространство, в котором по умолчанию будут размещаться данные объектов в этой базе. В таблице системного каталога pg_catalog.pg_database в поле dattablespace для каждой базы данных запомнен oid ее табличного пространства по умолчанию.
https://www.postgresql.org/docs/15/catalogs.html.
https://www.postgresql.org/docs/15/catalog-pg-database.html.
В строках представлений, информирующих об объектах базы данных, хранящих данные на диске, есть столбец, показывающий табличное пространство для хранения данных конкретного объекта.
Например, в представлении pg_tables есть столбец tablespace. Это поле не будет пустым у тех таблиц, которые хранят данные в иных табличных пространствах, не назначенных по умолчанию для данной базы. https://www.postgresql.org/docs/15/view-pg-tables.html.
Информация о ТП для объектов баз данных
nts_db=# CREATE TABLE t_def_ts (dtme timestamp DEFAULT now());
CREATE TABLE
nts_db=# CREATE TABLE t_new_ts (dtme timestamp DEFAULT now()) TABLESPACE pg_default; CREATE TABLE
nts_db=# SELECT tablename, tablespace FROM pg_tables WHERE tablename ~ '^t';
tablename | tablespace
-----------+------------
t_def_ts |
t_new_ts | pg_default
(2 строки)
В базе nts_db создана таблица t_def_ts в табличном пространстве по умолчанию для этой базы - newtabspace.
Таблица t_new_ts, напротив, табличном пространстве, не являющемся для базы nts_db пространством по умолчанию - pg_default.
В выводе из представления pg_tables видно, что столбец tablespace пуст, что свидетельствует о расположении данных для этой таблицы в табличном пространстве по умолчанию для БД nts_db - newtabspace.
Если при создании базы данных было указано табличное пространство, отличное от pg_default, то именно явно указанное пространство и будет для этой базы данных табличным пространством по умолчанию.
Имя табличного пространства pg_default выводится в строке pg_tables для таблицы t_new_ts, поскольку оно явно было указано для этой таблицы.
Наоборот, для таблицы t_def_ts не выведено имя табличного пространства в столбце tablespace по причине того, что при создании этой таблицы было использовано табличное пространство, установленное для этой базы по умолчанию - newtabspace.