├── README.md
├── pg_dba1_guide
├── dba1_01_introduction.pdf
├── dba1_02_architecture.pdf
├── dba1_03_install.pdf
├── dba1_04_psql.pdf
├── dba1_04_psql.txt
├── dba1_05_databases.pdf
├── dba1_05_databases.txt
├── dba1_06_tablespaces.pdf
├── dba1_06_tablespaces.txt
├── dba1_07_system_catalog.pdf
├── dba1_07_system_catalog.txt
├── dba1_08_objects.pdf
├── dba1_08_objects.txt
├── dba1_09_roles.pdf
├── dba1_10_schemas.pdf
├── dba1_11_privileges.pdf
├── dba1_11_privileges.txt
├── dba1_12_configuration.pdf
├── dba1_12_configuration.txt
├── dba1_13_authentication.pdf
├── dba1_14_monitoring.pdf
├── dba1_14_monitoring.txt
├── dba1_15_maintenance.pdf
├── dba1_16_logical_backup.pdf
├── dba1_16_logical_backup.txt
├── dba1_17_physical_backup.pdf
├── dba1_17_physical_backup.txt
└── dba1_introduction.pdf
└── pg_dba1_setup
├── dba1_instructor_guide.pdf
├── dba1_setup_vm1.sh
└── dba1_setup_vm2.sh
/README.md:
--------------------------------------------------------------------------------
1 | # Администрирование PostgreSQL
2 |
3 | ## Аннотация
4 |
5 | Материалы курса по PostgreSQL от компании Postgres Professional
6 |
7 | ## Структура репозитория
8 |
9 | * pg_dba1_guide - каталог опорных конспектов и заданий
10 | * pg_dba1_setup - каталог с инструкциями и скриптами для настройки виртуальной машины
11 |
12 | ## Обсуждение курса и общение с преподавателями
13 |
14 | [](https://gitter.im/rgordeev/pgcourse?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Присоединяйтесь к каналу курса на Gitter
15 |
16 | ## Официальный интернет ресурс курса
17 |
18 | [Программа курса и регистрация](http://rgordeev.github.io/pgcourse)
19 |
20 |
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_01_introduction.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_01_introduction.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_02_architecture.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_02_architecture.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_03_install.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_03_install.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_04_psql.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_04_psql.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_04_psql.txt:
--------------------------------------------------------------------------------
1 | psql
2 |
3 | Выполнение команд SQL:
4 |
5 | => select schemaname, tablename, tableowner from pg_tables limit 10;
6 | schemaname | tablename | tableowner
7 | ------------+-----------------+------------
8 | pg_catalog | pg_statistic | postgres
9 | pg_catalog | pg_type | postgres
10 | pg_catalog | pg_authid | postgres
11 | pg_catalog | pg_attribute | postgres
12 | pg_catalog | pg_proc | postgres
13 | pg_catalog | pg_user_mapping | postgres
14 | pg_catalog | pg_attrdef | postgres
15 | pg_catalog | pg_constraint | postgres
16 | pg_catalog | pg_index | postgres
17 | pg_catalog | pg_operator | postgres
18 | (10 rows)
19 |
20 |
21 | -----------------------------------------------------------------------
22 |
23 | Команда SQL может занимать и несколько строк,
24 | точка с запятой завершает ее.
25 |
26 | => select schemaname, tablename, tableowner
27 | => from pg_tables limit 10;
28 | schemaname | tablename | tableowner
29 | ------------+-----------------+------------
30 | pg_catalog | pg_statistic | postgres
31 | pg_catalog | pg_type | postgres
32 | pg_catalog | pg_authid | postgres
33 | pg_catalog | pg_attribute | postgres
34 | pg_catalog | pg_proc | postgres
35 | pg_catalog | pg_user_mapping | postgres
36 | pg_catalog | pg_attrdef | postgres
37 | pg_catalog | pg_constraint | postgres
38 | pg_catalog | pg_index | postgres
39 | pg_catalog | pg_operator | postgres
40 | (10 rows)
41 |
42 |
43 | -----------------------------------------------------------------------
44 |
45 | Если вывод команды SQL не помещется на экран, для его отображения можно использовать
46 | специальную программу $PAGER.
47 | Использование можно включать или выключать с помощью \pset pager on|off
48 |
49 | -----------------------------------------------------------------------
50 |
51 | Для управления выводом используется команда \pset в разных вариациях.
52 | Например, можно включить отображение полей "в столбик":
53 | \pset expanded on|off
54 | или просто \x
55 |
56 | => \pset expanded on
57 | Expanded display is on.
58 |
59 | => select schemaname, tablename, tableowner from pg_tables limit 2;
60 | -[ RECORD 1 ]------------
61 | schemaname | pg_catalog
62 | tablename | pg_statistic
63 | tableowner | postgres
64 | -[ RECORD 2 ]------------
65 | schemaname | pg_catalog
66 | tablename | pg_type
67 | tableowner | postgres
68 |
69 |
70 | => \pset expanded off
71 | Expanded display is off.
72 |
73 | -----------------------------------------------------------------------
74 |
75 | Если вывод предстоит разбирать программно, то можно:
76 | а) убрать выравнивание полей
77 |
78 | => \pset format unaligned
79 | Output format is unaligned.
80 |
81 | б) убрать вывод заголовка таблицы
82 |
83 | => \pset tuples_only on
84 | Tuples only is on.
85 |
86 | в) заменить разделитель
87 |
88 | => \pset fieldsep ' '
89 | Field separator is " ".
90 |
91 | -----------------------------------------------------------------------
92 |
93 | Посмотрим на результат:
94 |
95 | => select schemaname, tablename, tableowner from pg_tables limit 10;
96 | pg_catalog pg_statistic postgres
97 | pg_catalog pg_type postgres
98 | pg_catalog pg_authid postgres
99 | pg_catalog pg_attribute postgres
100 | pg_catalog pg_proc postgres
101 | pg_catalog pg_user_mapping postgres
102 | pg_catalog pg_attrdef postgres
103 | pg_catalog pg_constraint postgres
104 | pg_catalog pg_index postgres
105 | pg_catalog pg_operator postgres
106 |
107 | ...и вернем обратно:
108 |
109 | => \pset format aligned
110 | Output format is aligned.
111 |
112 | => \pset tuples_only off
113 | Tuples only is off.
114 |
115 | -----------------------------------------------------------------------
116 |
117 | Можно включить формат вывода HTML:
118 |
119 | => \pset format html
120 | Output format is html.
121 |
122 | => select schemaname, tablename, tableowner from pg_tables limit 1;
123 |
124 |
125 | schemaname |
126 | tablename |
127 | tableowner |
128 |
129 |
130 | pg_catalog |
131 | pg_statistic |
132 | postgres |
133 |
134 |
135 | (1 row)
136 |
137 |
138 | => \pset format aligned
139 | Output format is aligned.
140 |
141 | -----------------------------------------------------------------------
142 |
143 | Можно выполнять команды shell:
144 |
145 | => \! ls | head -n 10
146 | dba1_04_psql.sh
147 | dba1_05_databases.sh
148 | dba1_06_tablespaces.sh
149 | dba1_07_system_catalog.sh
150 | dba1_08_objects.sh
151 | dba1_11_privileges.sh
152 | dba1_12_configuration.sh
153 | dba1_14_monitoring.sh
154 | dba1_16_logical_backup.sh
155 | dba1_17_physical_backup.sh
156 |
157 | => \! pwd
158 | /home/postgres/edu/dba1/practice
159 |
160 | -----------------------------------------------------------------------
161 |
162 | Для изменения текущего (для psql) каталога есть специальная команда:
163 |
164 | => \! pwd
165 | /home/postgres/edu/dba1/practice
166 |
167 | => \cd ..
168 |
169 | => \! pwd
170 | /home/postgres/edu/dba1
171 |
172 | => \cd practice
173 |
174 | -----------------------------------------------------------------------
175 |
176 | Можно установить переменную окружения:
177 |
178 | => \! echo $TEST
179 |
180 |
181 | => \setenv TEST Hello
182 |
183 | => \! echo $TEST
184 | Hello
185 |
186 | -----------------------------------------------------------------------
187 |
188 | Можно записать вывод команды в файл с помощью \o[ut]:
189 |
190 | => \o dba1_log
191 |
192 | => select schemaname, tablename, tableowner from pg_tables limit 5;
193 |
194 | На экран ничего не попало. Посмотрим в файле:
195 |
196 | => \! cat dba1_log
197 | schemaname | tablename | tableowner
198 | ------------+--------------+------------
199 | pg_catalog | pg_statistic | postgres
200 | pg_catalog | pg_type | postgres
201 | pg_catalog | pg_authid | postgres
202 | pg_catalog | pg_attribute | postgres
203 | pg_catalog | pg_proc | postgres
204 | (5 rows)
205 |
206 |
207 | Вернем вывод на экран:
208 |
209 | => \o
210 |
211 | -----------------------------------------------------------------------
212 |
213 | Запишем в файл команды SQL.
214 |
215 |
216 | => \a
217 | Output format is unaligned.
218 |
219 | => \t
220 | Tuples only is on.
221 |
222 | => \pset fieldsep ' '
223 | Field separator is " ".
224 |
225 | => \o dba1_log
226 |
227 | => select 'select count(*) from', tablename, ';' from pg_tables limit 3;
228 |
229 | => \o
230 |
231 | => \t
232 | Tuples only is off.
233 |
234 | => \a
235 | Output format is aligned.
236 |
237 | -----------------------------------------------------------------------
238 |
239 | Вот что получилось в файле:
240 |
241 | => \! cat dba1_log
242 | select count(*) from pg_statistic ;
243 | select count(*) from pg_type ;
244 | select count(*) from pg_authid ;
245 |
246 | -----------------------------------------------------------------------
247 |
248 | И выполним теперь эти команды с помощью \i[nclude]:
249 |
250 | => \i dba1_log
251 | count
252 | -------
253 | 399
254 | (1 row)
255 |
256 | count
257 | -------
258 | 339
259 | (1 row)
260 |
261 | count
262 | -------
263 | 1
264 | (1 row)
265 |
266 |
267 | -----------------------------------------------------------------------
268 |
269 | Другие способы выполнить команды из файла:
270 | а) psql < filename
271 | б) psql -f filename
272 | в) psql -c command (работает только для одной команды)
273 |
274 | Файл также можно отредактировать, не выходя из psql, с помощью \e[dit] filename,
275 | при этом используется редактор $PSQL_EDITOR (или $EDITOR, или $VISUAL)
276 |
277 | -----------------------------------------------------------------------
278 |
279 | Последняя команда SQL попадает в буфер запроса.
280 |
281 | Выполним команду и выведем содержимое буфера с помощью \p[rint]:
282 |
283 | => select now();
284 | now
285 | -------------------------------
286 | 2015-12-09 19:11:26.630647+03
287 | (1 row)
288 |
289 |
290 | => \p
291 | select now();
292 |
293 | Выполним команду, не вводя ее заново (в остальном \g аналогична точке с запятой):
294 |
295 | => \g
296 | now
297 | -------------------------------
298 | 2015-12-09 19:11:26.636028+03
299 | (1 row)
300 |
301 |
302 | -----------------------------------------------------------------------
303 |
304 | Можно записать содержимое буфера в файл с помощью \w[rite]:
305 |
306 | => \w dba1_log
307 |
308 | => \! cat dba1_log
309 | select now();
310 |
311 | Наконец, очистим буфер с помощью \r[eset]:
312 |
313 | => \r
314 | Query buffer reset (cleared).
315 |
316 | => \p
317 | Query buffer is empty.
318 |
319 | -----------------------------------------------------------------------
320 |
321 | Можно периодически выполнять команду из буфера с помощью \watch seconds
322 |
323 | Буфер также можно отредактировать с помощью \e[dit],
324 | после чего отредактированная команда выполнится.
325 |
326 | -----------------------------------------------------------------------
327 |
328 | По аналогии с shell, psql имеет собственные переменные,
329 | среди которых есть ряд встроенных (имеющих определенный смысл для psql).
330 |
331 | Установим переменную:
332 |
333 | => \set TEST Hi!
334 |
335 | Чтобы получить значение, надо предварить имя переменной двоеточием:
336 |
337 | => \echo :TEST
338 | Hi!
339 |
340 | Значение переменной можно сбросить:
341 |
342 | => \unset TEST
343 |
344 | => \echo :TEST
345 | :TEST
346 |
347 | -----------------------------------------------------------------------
348 |
349 | Можно установить значение переменной результатом запроса с помощью \gset вместо точки с запятой:
350 |
351 | => select now() as curr_time \gset
352 |
353 | => \echo :curr_time
354 | 2015-12-09 19:11:26.658894+03
355 |
356 | Значение переменной можно запросить и у пользователя с помощью \prompt text name
357 | А также указать при запуске: psql -v name=value
358 |
359 | -----------------------------------------------------------------------
360 |
361 | Без параметров \set выдает значения всех переменных:
362 |
363 | => \set
364 | AUTOCOMMIT = 'on'
365 | PROMPT1 = '%/%R%# '
366 | PROMPT2 = '%/%R%# '
367 | PROMPT3 = '>> '
368 | VERBOSITY = 'default'
369 | VERSION = 'PostgreSQL 9.4.4 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4, 64-bit'
370 | HISTFILE = 'hist'
371 | DBNAME = 'postgres'
372 | USER = 'postgres'
373 | PORT = '5432'
374 | ENCODING = 'UTF8'
375 | curr_time = '2015-12-09 19:11:26.658894+03'
376 |
377 | -----------------------------------------------------------------------
378 |
379 | С помощью серии команд, начинающихся на \d, можно смотреть информацию об объектах БД.
380 | Например:
381 |
382 | => \d+ pg_rules
383 | View "pg_catalog.pg_rules"
384 | Column | Type | Modifiers | Storage | Description
385 | ------------+------+-----------+----------+-------------
386 | schemaname | name | | plain |
387 | tablename | name | | plain |
388 | rulename | name | | plain |
389 | definition | text | | extended |
390 | View definition:
391 | SELECT n.nspname AS schemaname,
392 | c.relname AS tablename,
393 | r.rulename,
394 | pg_get_ruledef(r.oid) AS definition
395 | FROM pg_rewrite r
396 | JOIN pg_class c ON c.oid = r.ev_class
397 | LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
398 | WHERE r.rulename <> '_RETURN'::name;
399 |
400 |
401 | Подробнее эти команды будут рассмотрены позже.
402 |
403 | -----------------------------------------------------------------------
404 |
405 | При сборке с библиотекой Readline имеются дополнительные возможности.
406 |
407 | Во-первых, редактирование команд и их история, как в shell.
408 | Управляющие переменные:
409 |
410 | => \echo :HISTCONTROL
411 | :HISTCONTROL
412 |
413 | => \echo :HISTFILE
414 | hist
415 |
416 | => \echo :HISTSIZE
417 | :HISTSIZE
418 |
419 | Историю можно сохранить в файл с помощью \s filename
420 |
421 | -----------------------------------------------------------------------
422 |
423 | Во-вторых, автодополнение имен объектов БД по клавише TAB.
424 | Регистр символов при автодополнении определяется переменной:
425 |
426 | => \echo :COMP_KEYWORD_CASE
427 | :COMP_KEYWORD_CASE
428 |
429 | Конец демонстрации.
430 |
431 | -----------------------------------------------------------------------
432 |
433 | => \q
434 |
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_05_databases.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_05_databases.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_05_databases.txt:
--------------------------------------------------------------------------------
1 |
2 | psql
3 |
4 | Посмотрим список имеющихся баз:
5 |
6 | psql -l
7 | List of databases
8 | Name | Owner | Encoding | Collate | Ctype | Access privileges
9 | -----------+----------+----------+-------------+-------------+-----------------------
10 | postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
11 | template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +
12 | | | | | | postgres=CTc/postgres
13 | template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +
14 | | | | | | postgres=CTc/postgres
15 | (3 rows)
16 |
17 |
18 | Пока нас интересует только название, значение остальных полей рассмотрим позже.
19 |
20 | -----------------------------------------------------------------------
21 |
22 | Также можно посмотреть в самой базе данных:
23 |
24 | => select datname, datistemplate, datallowconn, datconnlimit from pg_database;
25 | datname | datistemplate | datallowconn | datconnlimit
26 | -----------+---------------+--------------+--------------
27 | template1 | t | t | -1
28 | template0 | t | f | -1
29 | postgres | f | t | -1
30 | (3 rows)
31 |
32 |
33 | * datistemplate - является ли база данных шаблоном;
34 | * datallowconn - разрешены ли соединения с базой данных,
35 | * datconnlimit - максимальное количество соединений (-1 = без ограничений).
36 | При необходимости флаги можно изменить с помощью обычного update.
37 |
38 | -----------------------------------------------------------------------
39 |
40 | Подключимся к базе template1 и установим расширение pgcrypto.
41 | Дальше мы увидим, что новые БД будут создаваться уже с этим расширением.
42 |
43 | => \c template1
44 | You are now connected to database "template1" as user "postgres".
45 |
46 | => create extension pgcrypto;
47 | CREATE EXTENSION
48 |
49 | Если бы расширение pgcrypto не было установлено с помощью make install,
50 | мы получили бы ошибку "ERROR: could not open extension control file..."
51 |
52 | -----------------------------------------------------------------------
53 |
54 | Теперь нам доступны функции, входящие в расширение pgcrypto.
55 | Например, можно вычислить MD5-дайджест:
56 |
57 | => select digest('Hello, world!', 'md5');
58 | digest
59 | ------------------------------------
60 | \x6cd3556deb0da54bca060b4c39479839
61 | (1 row)
62 |
63 |
64 | -----------------------------------------------------------------------
65 |
66 | Чтобы шаблон можно было использовать для создания базы, к нему не должно быть активных подключений,
67 | поэтому отключимся от базы template1.
68 |
69 | => \c postgres
70 | You are now connected to database "postgres" as user "postgres".
71 |
72 | -----------------------------------------------------------------------
73 |
74 | Для создания новой базы данных служит команда CREATE DATABASE:
75 |
76 | => create database test;
77 | CREATE DATABASE
78 |
79 | => \c test
80 | You are now connected to database "test" as user "postgres".
81 |
82 | Также можно воспользоваться утилитой createdb:
83 | $ createdb test
84 |
85 | => select datname, datistemplate, datallowconn, datconnlimit from pg_database;
86 | datname | datistemplate | datallowconn | datconnlimit
87 | -----------+---------------+--------------+--------------
88 | template1 | t | t | -1
89 | template0 | t | f | -1
90 | postgres | f | t | -1
91 | test | f | t | -1
92 | (4 rows)
93 |
94 |
95 | -----------------------------------------------------------------------
96 |
97 | По умолчанию при создании используется шаблон template1.
98 | Убедимся, что в новой базе доступно расширение pgcrypto:
99 |
100 | => select digest('Hello, world!', 'md5');
101 | digest
102 | ------------------------------------
103 | \x6cd3556deb0da54bca060b4c39479839
104 | (1 row)
105 |
106 |
107 | -----------------------------------------------------------------------
108 |
109 | А если создать базу данных из шаблона template0?
110 |
111 | => create database test0 template template0 connection limit 20;
112 | CREATE DATABASE
113 |
114 | => \c test0
115 | You are now connected to database "test0" as user "postgres".
116 |
117 | Здесь мы указали не только имя шаблона, но и максимальное количество соединений:
118 |
119 | => select datname, datistemplate, datallowconn, datconnlimit from pg_database;
120 | datname | datistemplate | datallowconn | datconnlimit
121 | -----------+---------------+--------------+--------------
122 | template1 | t | t | -1
123 | template0 | t | f | -1
124 | postgres | f | t | -1
125 | test | f | t | -1
126 | test0 | f | t | 20
127 | (5 rows)
128 |
129 |
130 | -----------------------------------------------------------------------
131 |
132 | Проверим:
133 |
134 | => select digest('Hello, world!', 'md5');
135 | ERROR: function digest(unknown, unknown) does not exist
136 | LINE 1: select digest('Hello, world!', 'md5');
137 | ^
138 | HINT: No function matches the given name and argument types. You might need to add explicit type casts.
139 |
140 | -----------------------------------------------------------------------
141 |
142 | Рассмотрим несколько полезных функций для вычисления занимаемого объема.
143 | Размер базы данных:
144 |
145 | => select pg_database_size('test0');
146 | pg_database_size
147 | ------------------
148 | 6687392
149 | (1 row)
150 |
151 |
152 | Чтобы не считать разряды, можно вывести размер в читаемом виде:
153 |
154 | => select pg_size_pretty(pg_database_size('test0'));
155 | pg_size_pretty
156 | ----------------
157 | 6531 kB
158 | (1 row)
159 |
160 |
161 | -----------------------------------------------------------------------
162 |
163 | Создадим таблицу и индекс:
164 |
165 | => create table t(n numeric);
166 | CREATE TABLE
167 |
168 | => create index t_idx on t(n);
169 | CREATE INDEX
170 |
171 | => insert into t select * from generate_series(1, 10000);
172 | INSERT 0 10000
173 |
174 | -----------------------------------------------------------------------
175 |
176 | Теперь можно вывести размер, занимаемый таблицей:
177 |
178 | => select pg_size_pretty(pg_table_size('t'));
179 | pg_size_pretty
180 | ----------------
181 | 392 kB
182 | (1 row)
183 |
184 |
185 | -----------------------------------------------------------------------
186 |
187 | А также размер индексов таблицы:
188 |
189 | => select pg_size_pretty(pg_indexes_size('t'));
190 | pg_size_pretty
191 | ----------------
192 | 240 kB
193 | (1 row)
194 |
195 |
196 | И размер таблицы вместе с индексами:
197 |
198 | => select pg_size_pretty(pg_total_relation_size('t'));
199 | pg_size_pretty
200 | ----------------
201 | 632 kB
202 | (1 row)
203 |
204 |
205 | -----------------------------------------------------------------------
206 |
207 | Созданную базу данных можно переименовать:
208 |
209 | => alter database test rename to db;
210 | ALTER DATABASE
211 |
212 | => select datname, datistemplate, datallowconn, datconnlimit from pg_database;
213 | datname | datistemplate | datallowconn | datconnlimit
214 | -----------+---------------+--------------+--------------
215 | template1 | t | t | -1
216 | template0 | t | f | -1
217 | postgres | f | t | -1
218 | test0 | f | t | 20
219 | db | f | t | -1
220 | (5 rows)
221 |
222 |
223 | -----------------------------------------------------------------------
224 |
225 | Изменить количество соединений:
226 |
227 | => alter database db connection limit 10;
228 | ALTER DATABASE
229 |
230 | => select datname, datistemplate, datallowconn, datconnlimit from pg_database;
231 | datname | datistemplate | datallowconn | datconnlimit
232 | -----------+---------------+--------------+--------------
233 | template1 | t | t | -1
234 | template0 | t | f | -1
235 | postgres | f | t | -1
236 | test0 | f | t | 20
237 | db | f | t | 10
238 | (5 rows)
239 |
240 |
241 | -----------------------------------------------------------------------
242 |
243 | Базу данных можно удалить (если к ней нет активных подключений).
244 | Поскольку мы подключены к другой базе, то удаление пройдет успешно:
245 |
246 | => \conninfo
247 | You are connected to database "test0" as user "postgres" via socket in "/tmp" at port "5432".
248 |
249 | => drop database db;
250 | DROP DATABASE
251 |
252 | => select datname, datistemplate, datallowconn, datconnlimit from pg_database;
253 | datname | datistemplate | datallowconn | datconnlimit
254 | -----------+---------------+--------------+--------------
255 | template1 | t | t | -1
256 | template0 | t | f | -1
257 | postgres | f | t | -1
258 | test0 | f | t | 20
259 | (4 rows)
260 |
261 |
262 | -----------------------------------------------------------------------
263 |
264 | Удалим и вторую базу.
265 |
266 | => \c postgres
267 | You are now connected to database "postgres" as user "postgres".
268 |
269 | => drop database test0;
270 | DROP DATABASE
271 |
272 | => select datname, datistemplate, datallowconn, datconnlimit from pg_database;
273 | datname | datistemplate | datallowconn | datconnlimit
274 | -----------+---------------+--------------+--------------
275 | template1 | t | t | -1
276 | template0 | t | f | -1
277 | postgres | f | t | -1
278 | (3 rows)
279 |
280 |
281 | -----------------------------------------------------------------------
282 |
283 | Если база данных не существует, команда DROP DATABASE будет ругаться:
284 |
285 | => drop database db;
286 | ERROR: database "db" does not exist
287 |
288 | Поэтому можно воспользоваться другой формой (она применима и к удалению других объектов):
289 |
290 | => drop database if exists db;
291 | NOTICE: database "db" does not exist, skipping
292 | DROP DATABASE
293 |
294 | Конец демонстрации.
295 |
296 | -----------------------------------------------------------------------
297 |
298 | => \q
299 |
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_06_tablespaces.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_06_tablespaces.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_06_tablespaces.txt:
--------------------------------------------------------------------------------
1 |
2 | psql
3 |
4 | При создании кластера создаются два табличных пространства:
5 |
6 | => select * from pg_tablespace;
7 | spcname | spcowner | spcacl | spcoptions
8 | ------------+----------+--------+------------
9 | pg_default | 10 | |
10 | pg_global | 10 | |
11 | (2 rows)
12 |
13 |
14 | * pg_global - общие объекты кластера,
15 | * pg_default - табличное пространство по умолчанию.
16 |
17 | -----------------------------------------------------------------------
18 |
19 | Для нового табличного пространства нужен пустой каталог,
20 | владельцем которого является пользователь postgres.
21 |
22 | cd /home/postgres
23 | mkdir ts1_dir
24 | ls -l ts1_dir
25 | total 0
26 |
27 | -----------------------------------------------------------------------
28 |
29 | Теперь можно создать табличное пространство:
30 |
31 | => create tablespace ts1 location '/home/postgres/ts1_dir';
32 | CREATE TABLESPACE
33 |
34 | Список можно получить и командой psql:
35 |
36 | => \db
37 | List of tablespaces
38 | Name | Owner | Location
39 | ------------+----------+------------------------
40 | pg_default | postgres |
41 | pg_global | postgres |
42 | ts1 | postgres | /home/postgres/ts1_dir
43 | (3 rows)
44 |
45 |
46 | -----------------------------------------------------------------------
47 |
48 | Табличное пространство может использоваться несколькими базами кластера.
49 | При этом у каждой базы есть табличное пространство по умолчанию.
50 |
51 | Создадим БД и назначим ей ts1 в качестве пространства по умолчанию:
52 |
53 | => create database test tablespace ts1;
54 | CREATE DATABASE
55 |
56 | -----------------------------------------------------------------------
57 |
58 | Теперь все создаваемые таблицы и индексы будут попадать в ts1,
59 | если явно не указать другое.
60 |
61 | Подключимся к базе:
62 |
63 | => \c test
64 | You are now connected to database "test" as user "postgres".
65 |
66 | Создадим таблицу:
67 |
68 | => create table t1(id serial, name text);
69 | CREATE TABLE
70 |
71 | => select * from pg_tables where tablename='t1';
72 | schemaname | tablename | tableowner | tablespace | hasindexes | hasrules | hastriggers
73 | ------------+-----------+------------+------------+------------+----------+-------------
74 | public | t1 | postgres | | f | f | f
75 | (1 row)
76 |
77 |
78 | Пустое поле tablespace указывает на табличное пространство по умолчанию.
79 |
80 | -----------------------------------------------------------------------
81 |
82 | Можно изменить табличное пространство по умолчанию, но при этом все таблицы
83 | физически переносятся из одного пространства в другое.
84 | Предварительно надо отключиться от базы.
85 |
86 | => \c postgres
87 | You are now connected to database "postgres" as user "postgres".
88 |
89 | => alter database test set tablespace pg_default;
90 | ALTER DATABASE
91 |
92 | => \c test
93 | You are now connected to database "test" as user "postgres".
94 |
95 | -----------------------------------------------------------------------
96 |
97 | При создании объекта можно явно указать табличное пространство:
98 |
99 | => create table t2(n numeric) tablespace ts1;
100 | CREATE TABLE
101 |
102 | => select * from pg_tables where tablename like 't_';
103 | schemaname | tablename | tableowner | tablespace | hasindexes | hasrules | hastriggers
104 | ------------+-----------+------------+------------+------------+----------+-------------
105 | public | t1 | postgres | | f | f | f
106 | public | t2 | postgres | ts1 | f | f | f
107 | (2 rows)
108 |
109 |
110 | -----------------------------------------------------------------------
111 |
112 | Каждая таблица (или индекс) хранится на диске в нескольких файлах.
113 |
114 | Таблица t1 принадлежит табличному пространству pg_default, поэтому
115 | ее следует искать в $PGDATA/base/
116 | Имя подкаталога - oid (object identifier) базы данных:
117 |
118 | => select oid, datname from pg_database where datname='test';
119 | oid | datname
120 | -------+---------
121 | 16429 | test
122 | (1 row)
123 |
124 |
125 | => select oid as dboid from pg_database where datname='test' \gset
126 |
127 | => \setenv DBOID :dboid
128 |
129 | -----------------------------------------------------------------------
130 |
131 | Имена файлов, относящиеся к таблице, будут начинаться на следующее число:
132 |
133 | => select relname, relfilenode from pg_class where relname='t1';
134 | relname | relfilenode
135 | ---------+-------------
136 | t1 | 16432
137 | (1 row)
138 |
139 |
140 | => select relfilenode as relid from pg_class where relname='t1' \gset
141 |
142 | => \setenv RELID :relid
143 |
144 | -----------------------------------------------------------------------
145 |
146 | Теперь можно посмотреть сами файлы:
147 |
148 | => \! ls -l $PGDATA/base/$DBOID/$RELID*
149 | -rw------- 1 postgres postgres 0 дек. 9 19:11 /usr/local/pgsql/data/base/16429/16432
150 |
151 | Сейчас файл один и пустой, поскольку в таблице ничего нет. Добавим строк:
152 |
153 | => insert into t1(name) select s.a::text from generate_series(1,10000) as s(a);
154 | INSERT 0 10000
155 |
156 | => vacuum t1;
157 | VACUUM
158 |
159 | -----------------------------------------------------------------------
160 |
161 | Снова посмотрим:
162 |
163 | => \! ls -l $PGDATA/base/$DBOID/$RELID*
164 | -rw------- 1 postgres postgres 442368 дек. 9 19:11 /usr/local/pgsql/data/base/16429/16432
165 | -rw------- 1 postgres postgres 24576 дек. 9 19:11 /usr/local/pgsql/data/base/16429/16432_fsm
166 | -rw------- 1 postgres postgres 8192 дек. 9 19:11 /usr/local/pgsql/data/base/16429/16432_vm
167 |
168 | Теперь видим три файла:
169 | * основной - собственно данные таблицы,
170 | * fsm (free space map) - информация о свободном месте в страницах
171 | * vm (visibility map) - информация о видимости версий строк для многоверсионности
172 |
173 | Размер каждого из файлов можно узнать, не обращаясь к файловой системе, например:
174 |
175 | => select pg_relation_size('t1','fsm');
176 | pg_relation_size
177 | ------------------
178 | 24576
179 | (1 row)
180 |
181 |
182 | -----------------------------------------------------------------------
183 |
184 | Добавим в таблицу очень длинную строку:
185 |
186 | => insert into t1(name) select string_agg(s.a::text,'.') from generate_series(1,10000) as s(a);
187 | INSERT 0 1
188 |
189 | => vacuum;
190 | VACUUM
191 |
192 | Посмотрим на размер файлов:
193 |
194 | => \! ls -l $PGDATA/base/$DBOID/$RELID*
195 | -rw------- 1 postgres postgres 442368 дек. 9 19:11 /usr/local/pgsql/data/base/16429/16432
196 | -rw------- 1 postgres postgres 24576 дек. 9 19:11 /usr/local/pgsql/data/base/16429/16432_fsm
197 | -rw------- 1 postgres postgres 8192 дек. 9 19:11 /usr/local/pgsql/data/base/16429/16432_vm
198 |
199 | Размер не изменился. Где же строка?
200 |
201 | -----------------------------------------------------------------------
202 |
203 | Если строка не помещается в страницу, длинные значения будут храниться отдельно.
204 | Это происходит прозрачно для приложения. Значение может сжиматься и распределяться
205 | по нескольким страницами.
206 | Эта технология называется TOAST (The Oversized-Attribute Storage Technique).
207 |
208 | -----------------------------------------------------------------------
209 |
210 | Найдем таблицу с данными TOAST:
211 |
212 | => select reltoastrelid as trelid from pg_class where relname='t1';
213 | trelid
214 | --------
215 | 16436
216 | (1 row)
217 |
218 |
219 | => select reltoastrelid as trelid from pg_class where relname='t1' \gset
220 |
221 | => \setenv TRELID :trelid
222 |
223 | => \! ls -l $PGDATA/base/$DBOID/$TRELID*
224 | -rw------- 1 postgres postgres 40960 дек. 9 19:11 /usr/local/pgsql/data/base/16429/16436
225 | -rw------- 1 postgres postgres 24576 дек. 9 19:11 /usr/local/pgsql/data/base/16429/16436_fsm
226 | -rw------- 1 postgres postgres 8192 дек. 9 19:11 /usr/local/pgsql/data/base/16429/16436_vm
227 |
228 | В этих файлах и хранится наша строка.
229 |
230 | -----------------------------------------------------------------------
231 |
232 | Посмотрим на индексы.
233 |
234 | => create index t1_id on t1(id);
235 | CREATE INDEX
236 |
237 | => vacuum;
238 | VACUUM
239 |
240 | -----------------------------------------------------------------------
241 |
242 | Другой способ найти путь к файлам - воспользоваться функцией:
243 |
244 | => select pg_relation_filepath('t1_id') as indexpath;
245 | indexpath
246 | ------------------
247 | base/16429/16446
248 | (1 row)
249 |
250 |
251 | => select pg_relation_filepath('t1_id') as indexpath \gset
252 |
253 | => \setenv INDEXPATH :indexpath
254 |
255 | => \! ls -l $PGDATA/$INDEXPATH*
256 | -rw------- 1 postgres postgres 245760 дек. 9 19:11 /usr/local/pgsql/data/base/16429/16446
257 |
258 | Для индексов не бывает файла vm, так как индексы не хранят информации о многоверсионности.
259 |
260 | -----------------------------------------------------------------------
261 |
262 | Если таблица находится в другом табличном пространстве, то файлы будут находиться
263 | в каталоге, указанном при создании пространства.
264 | Также до них можно добраться из $PGDATA по символьной ссылке.
265 |
266 | -----------------------------------------------------------------------
267 |
268 | Существует полезное расширение oid2name, входящее в стандартную поставку, с помощью которого
269 | можно легко связать объекты БД и файлы.
270 | Можно посмотреть все базы данных:
271 |
272 | => \! oid2name
273 | All databases:
274 | Oid Database Name Tablespace
275 | ----------------------------------
276 | 12185 postgres pg_default
277 | 12180 template0 pg_default
278 | 1 template1 pg_default
279 | 16429 test pg_default
280 |
281 | -----------------------------------------------------------------------
282 |
283 | Можно посмотреть все объекты в базе:
284 |
285 | => \! oid2name -d test
286 | From database "test":
287 | Filenode Table Name
288 | ----------------------
289 | 16432 t1
290 | 16439 t2
291 |
292 | Или все табличные пространства в базе:
293 |
294 | => \! oid2name -d test -s
295 | All tablespaces:
296 | Oid Tablespace Name
297 | ------------------------
298 | 1663 pg_default
299 | 1664 pg_global
300 | 16428 ts1
301 |
302 | -----------------------------------------------------------------------
303 |
304 | Можно по имени таблицы узнать имя файла:
305 |
306 | => \! oid2name -d test -t t1
307 | From database "test":
308 | Filenode Table Name
309 | ----------------------
310 | 16432 t1
311 |
312 | Или наоборот, по номеру файла узнать таблицу:
313 |
314 | => \! oid2name -d test -f $RELID
315 | From database "test":
316 | Filenode Table Name
317 | ----------------------
318 | 16432 t1
319 |
320 | -----------------------------------------------------------------------
321 |
322 | Уже созданную таблицу (или индекс) можно переместить в другое табличное пространство.
323 | Создадим еще одно табличное пространство ts2 и переместим в него таблицу t1.
324 |
325 | mkdir ts2_dir
326 |
327 | => create tablespace ts2 location '/home/postgres/ts2_dir';
328 | CREATE TABLESPACE
329 |
330 | => alter table t1 set tablespace ts2;
331 | ALTER TABLE
332 |
333 | => select * from pg_tables where tablename like 't_';
334 | schemaname | tablename | tableowner | tablespace | hasindexes | hasrules | hastriggers
335 | ------------+-----------+------------+------------+------------+----------+-------------
336 | public | t1 | postgres | ts2 | t | f | f
337 | public | t2 | postgres | ts1 | f | f | f
338 | (2 rows)
339 |
340 |
341 | -----------------------------------------------------------------------
342 |
343 | Можно переместить и все таблицы сразу:
344 |
345 | => alter table all in tablespace ts1 set tablespace ts2;
346 | ALTER TABLE
347 |
348 | => select * from pg_tables where tablename like 't_';
349 | schemaname | tablename | tableowner | tablespace | hasindexes | hasrules | hastriggers
350 | ------------+-----------+------------+------------+------------+----------+-------------
351 | public | t1 | postgres | ts2 | t | f | f
352 | public | t2 | postgres | ts2 | f | f | f
353 | (2 rows)
354 |
355 |
356 | -----------------------------------------------------------------------
357 |
358 | Табличное пространство можно переименовать:
359 |
360 | => alter tablespace ts1 rename to test_tablespace;
361 | ALTER TABLESPACE
362 |
363 | А также удалить...
364 |
365 | => drop tablespace test_tablespace;
366 | DROP TABLESPACE
367 |
368 | => drop tablespace ts2;
369 | ERROR: tablespace "ts2" is not empty
370 |
371 | ...но только, если оно пусто.
372 |
373 | -----------------------------------------------------------------------
374 |
375 | => drop table t1;
376 | DROP TABLE
377 |
378 | => drop table t2;
379 | DROP TABLE
380 |
381 | => drop tablespace if exists ts2;
382 | DROP TABLESPACE
383 |
384 | Конец демонстрации.
385 |
386 | -----------------------------------------------------------------------
387 |
388 | => \q
389 |
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_07_system_catalog.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_07_system_catalog.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_07_system_catalog.txt:
--------------------------------------------------------------------------------
1 |
2 | Системный каталог
3 | ~~~~~~~~~~~~~~~~~
4 |
5 | Создадим БД и тестовые объекты
6 |
7 | psql postgres -c "CREATE DATABASE dba1_07_demo;"
8 | CREATE DATABASE
9 | psql dba1_07_demo
10 |
11 | => create table master_table (id int primary key, c text not null);
12 | CREATE TABLE
13 |
14 | => comment on table master_table is 'Тестовая таблица';
15 | COMMENT
16 |
17 | => create table detail_table (id int references master_table(id), c text);
18 | CREATE TABLE
19 |
20 | => create view my_view as select * from detail_table;
21 | CREATE VIEW
22 |
23 | => comment on column my_view.id is 'Идентификатор';
24 | COMMENT
25 |
26 | -----------------------------------------------------------------------
27 |
28 | Список таблиц системного словаря:
29 |
30 | => \dt pg_catalog.*
31 | List of relations
32 | Schema | Name | Type | Owner
33 | ------------+-------------------------+-------+----------
34 | pg_catalog | pg_aggregate | table | postgres
35 | pg_catalog | pg_am | table | postgres
36 | pg_catalog | pg_amop | table | postgres
37 | pg_catalog | pg_amproc | table | postgres
38 | pg_catalog | pg_attrdef | table | postgres
39 | pg_catalog | pg_attribute | table | postgres
40 | pg_catalog | pg_auth_members | table | postgres
41 | pg_catalog | pg_authid | table | postgres
42 | pg_catalog | pg_cast | table | postgres
43 | pg_catalog | pg_class | table | postgres
44 | pg_catalog | pg_collation | table | postgres
45 | pg_catalog | pg_constraint | table | postgres
46 | pg_catalog | pg_conversion | table | postgres
47 | pg_catalog | pg_database | table | postgres
48 | pg_catalog | pg_db_role_setting | table | postgres
49 | pg_catalog | pg_default_acl | table | postgres
50 | pg_catalog | pg_depend | table | postgres
51 | pg_catalog | pg_description | table | postgres
52 | pg_catalog | pg_enum | table | postgres
53 | pg_catalog | pg_event_trigger | table | postgres
54 | pg_catalog | pg_extension | table | postgres
55 | pg_catalog | pg_foreign_data_wrapper | table | postgres
56 | pg_catalog | pg_foreign_server | table | postgres
57 | pg_catalog | pg_foreign_table | table | postgres
58 | pg_catalog | pg_index | table | postgres
59 | pg_catalog | pg_inherits | table | postgres
60 | pg_catalog | pg_language | table | postgres
61 | pg_catalog | pg_largeobject | table | postgres
62 | pg_catalog | pg_largeobject_metadata | table | postgres
63 | pg_catalog | pg_namespace | table | postgres
64 | pg_catalog | pg_opclass | table | postgres
65 | pg_catalog | pg_operator | table | postgres
66 | pg_catalog | pg_opfamily | table | postgres
67 | pg_catalog | pg_pltemplate | table | postgres
68 | pg_catalog | pg_proc | table | postgres
69 | pg_catalog | pg_range | table | postgres
70 | pg_catalog | pg_rewrite | table | postgres
71 | pg_catalog | pg_seclabel | table | postgres
72 | pg_catalog | pg_shdepend | table | postgres
73 | pg_catalog | pg_shdescription | table | postgres
74 | pg_catalog | pg_shseclabel | table | postgres
75 | pg_catalog | pg_statistic | table | postgres
76 | pg_catalog | pg_tablespace | table | postgres
77 | pg_catalog | pg_trigger | table | postgres
78 | pg_catalog | pg_ts_config | table | postgres
79 | pg_catalog | pg_ts_config_map | table | postgres
80 | pg_catalog | pg_ts_dict | table | postgres
81 | pg_catalog | pg_ts_parser | table | postgres
82 | pg_catalog | pg_ts_template | table | postgres
83 | pg_catalog | pg_type | table | postgres
84 | pg_catalog | pg_user_mapping | table | postgres
85 | (51 rows)
86 |
87 |
88 | -----------------------------------------------------------------------
89 |
90 | Список представлений системного словаря:
91 |
92 | => \dv pg_catalog.*
93 | List of relations
94 | Schema | Name | Type | Owner
95 | ------------+---------------------------------+------+----------
96 | pg_catalog | pg_available_extension_versions | view | postgres
97 | pg_catalog | pg_available_extensions | view | postgres
98 | pg_catalog | pg_cursors | view | postgres
99 | pg_catalog | pg_group | view | postgres
100 | pg_catalog | pg_indexes | view | postgres
101 | pg_catalog | pg_locks | view | postgres
102 | pg_catalog | pg_matviews | view | postgres
103 | pg_catalog | pg_prepared_statements | view | postgres
104 | pg_catalog | pg_prepared_xacts | view | postgres
105 | pg_catalog | pg_replication_slots | view | postgres
106 | pg_catalog | pg_roles | view | postgres
107 | pg_catalog | pg_rules | view | postgres
108 | pg_catalog | pg_seclabels | view | postgres
109 | pg_catalog | pg_settings | view | postgres
110 | pg_catalog | pg_shadow | view | postgres
111 | pg_catalog | pg_stat_activity | view | postgres
112 | pg_catalog | pg_stat_all_indexes | view | postgres
113 | pg_catalog | pg_stat_all_tables | view | postgres
114 | pg_catalog | pg_stat_archiver | view | postgres
115 | pg_catalog | pg_stat_bgwriter | view | postgres
116 | pg_catalog | pg_stat_database | view | postgres
117 | pg_catalog | pg_stat_database_conflicts | view | postgres
118 | pg_catalog | pg_stat_replication | view | postgres
119 | pg_catalog | pg_stat_sys_indexes | view | postgres
120 | pg_catalog | pg_stat_sys_tables | view | postgres
121 | pg_catalog | pg_stat_user_functions | view | postgres
122 | pg_catalog | pg_stat_user_indexes | view | postgres
123 | pg_catalog | pg_stat_user_tables | view | postgres
124 | pg_catalog | pg_stat_xact_all_tables | view | postgres
125 | pg_catalog | pg_stat_xact_sys_tables | view | postgres
126 | pg_catalog | pg_stat_xact_user_functions | view | postgres
127 | pg_catalog | pg_stat_xact_user_tables | view | postgres
128 | pg_catalog | pg_statio_all_indexes | view | postgres
129 | pg_catalog | pg_statio_all_sequences | view | postgres
130 | pg_catalog | pg_statio_all_tables | view | postgres
131 | pg_catalog | pg_statio_sys_indexes | view | postgres
132 | pg_catalog | pg_statio_sys_sequences | view | postgres
133 | pg_catalog | pg_statio_sys_tables | view | postgres
134 | pg_catalog | pg_statio_user_indexes | view | postgres
135 | pg_catalog | pg_statio_user_sequences | view | postgres
136 | pg_catalog | pg_statio_user_tables | view | postgres
137 | pg_catalog | pg_stats | view | postgres
138 | pg_catalog | pg_tables | view | postgres
139 | pg_catalog | pg_timezone_abbrevs | view | postgres
140 | pg_catalog | pg_timezone_names | view | postgres
141 | pg_catalog | pg_user | view | postgres
142 | pg_catalog | pg_user_mappings | view | postgres
143 | pg_catalog | pg_views | view | postgres
144 | (48 rows)
145 |
146 |
147 | -----------------------------------------------------------------------
148 |
149 | Список системных функций для определения размера объектов:
150 |
151 | => \x
152 | Expanded display is on.
153 |
154 | => \df pg_catalog.pg_*size
155 | List of functions
156 | -[ RECORD 1 ]-------+-----------------------
157 | Schema | pg_catalog
158 | Name | pg_column_size
159 | Result data type | integer
160 | Argument data types | "any"
161 | Type | normal
162 | -[ RECORD 2 ]-------+-----------------------
163 | Schema | pg_catalog
164 | Name | pg_database_size
165 | Result data type | bigint
166 | Argument data types | name
167 | Type | normal
168 | -[ RECORD 3 ]-------+-----------------------
169 | Schema | pg_catalog
170 | Name | pg_database_size
171 | Result data type | bigint
172 | Argument data types | oid
173 | Type | normal
174 | -[ RECORD 4 ]-------+-----------------------
175 | Schema | pg_catalog
176 | Name | pg_indexes_size
177 | Result data type | bigint
178 | Argument data types | regclass
179 | Type | normal
180 | -[ RECORD 5 ]-------+-----------------------
181 | Schema | pg_catalog
182 | Name | pg_relation_size
183 | Result data type | bigint
184 | Argument data types | regclass
185 | Type | normal
186 | -[ RECORD 6 ]-------+-----------------------
187 | Schema | pg_catalog
188 | Name | pg_relation_size
189 | Result data type | bigint
190 | Argument data types | regclass, text
191 | Type | normal
192 | -[ RECORD 7 ]-------+-----------------------
193 | Schema | pg_catalog
194 | Name | pg_table_size
195 | Result data type | bigint
196 | Argument data types | regclass
197 | Type | normal
198 | -[ RECORD 8 ]-------+-----------------------
199 | Schema | pg_catalog
200 | Name | pg_tablespace_size
201 | Result data type | bigint
202 | Argument data types | name
203 | Type | normal
204 | -[ RECORD 9 ]-------+-----------------------
205 | Schema | pg_catalog
206 | Name | pg_tablespace_size
207 | Result data type | bigint
208 | Argument data types | oid
209 | Type | normal
210 | -[ RECORD 10 ]------+-----------------------
211 | Schema | pg_catalog
212 | Name | pg_total_relation_size
213 | Result data type | bigint
214 | Argument data types | regclass
215 | Type | normal
216 |
217 |
218 | => \x
219 | Expanded display is off.
220 |
221 | -----------------------------------------------------------------------
222 |
223 | Получение описания функции:
224 |
225 | => \sf pg_table_size
226 | CREATE OR REPLACE FUNCTION pg_catalog.pg_table_size(regclass)
227 | RETURNS bigint
228 | LANGUAGE internal
229 | STRICT
230 | AS $function$pg_table_size$function$
231 |
232 | Редактирование функции: \ef function_name
233 |
234 | -----------------------------------------------------------------------
235 |
236 | Получение размера баз данных кластера:
237 |
238 | => select pd.datname as database_name
239 | => ,pg_database_size (pd.oid) as db_size_bytes
240 | => ,pg_size_pretty(
241 | => pg_database_size (pd.oid)
242 | => ) as db_size_text
243 | => from pg_database pd;
244 | database_name | db_size_bytes | db_size_text
245 | ---------------+---------------+--------------
246 | template1 | 6720160 | 6563 kB
247 | template0 | 6570500 | 6417 kB
248 | postgres | 6695584 | 6539 kB
249 | dba1_07_demo | 6793888 | 6635 kB
250 | test | 6834848 | 6675 kB
251 | (5 rows)
252 |
253 |
254 | -----------------------------------------------------------------------
255 |
256 | Команды \d* позволяют быстро получить информацию
257 | из таблиц системного словаря.
258 |
259 | Получить список таблиц:
260 |
261 | => \dt
262 | List of relations
263 | Schema | Name | Type | Owner
264 | --------+--------------+-------+----------
265 | public | detail_table | table | postgres
266 | public | master_table | table | postgres
267 | (2 rows)
268 |
269 |
270 | -----------------------------------------------------------------------
271 |
272 | Получить список представлений:
273 |
274 | => \dv
275 | List of relations
276 | Schema | Name | Type | Owner
277 | --------+---------+------+----------
278 | public | my_view | view | postgres
279 | (1 row)
280 |
281 |
282 | -----------------------------------------------------------------------
283 |
284 | Одной командой можно получить список нескольких типов объектов:
285 | \dtvismE
286 | t - таблицы
287 | v - представления
288 | i - индексы
289 | s - последовательности
290 | m - материализованные представления
291 | E - внешние таблицы
292 |
293 | => \dtvi
294 | List of relations
295 | Schema | Name | Type | Owner | Table
296 | --------+-------------------+-------+----------+--------------
297 | public | detail_table | table | postgres |
298 | public | master_table | table | postgres |
299 | public | master_table_pkey | index | postgres | master_table
300 | public | my_view | view | postgres |
301 | (4 rows)
302 |
303 |
304 | -----------------------------------------------------------------------
305 |
306 | Модификатор + позволяет получить дополнительную информацию.
307 | Состав дополнительной информации предопределен и зависит от типа объекта
308 |
309 | => \dt+
310 | List of relations
311 | Schema | Name | Type | Owner | Size | Description
312 | --------+--------------+-------+----------+------------+------------------
313 | public | detail_table | table | postgres | 8192 bytes |
314 | public | master_table | table | postgres | 8192 bytes | Тестовая таблица
315 | (2 rows)
316 |
317 |
318 | -----------------------------------------------------------------------
319 |
320 | Модификатор S выводит системные объекты в дополнении к пользовательским.
321 |
322 | => \dtS *table*
323 | List of relations
324 | Schema | Name | Type | Owner
325 | ------------+------------------+-------+----------
326 | pg_catalog | pg_foreign_table | table | postgres
327 | pg_catalog | pg_tablespace | table | postgres
328 | public | detail_table | table | postgres
329 | public | master_table | table | postgres
330 | (4 rows)
331 |
332 |
333 | -----------------------------------------------------------------------
334 |
335 | Для получения информации об одном объекте
336 | (таблица, представление, индекс, последовательность, внешняя таблица):
337 |
338 | => \d master_table
339 | Table "public.master_table"
340 | Column | Type | Modifiers
341 | --------+---------+-----------
342 | id | integer | not null
343 | c | text | not null
344 | Indexes:
345 | "master_table_pkey" PRIMARY KEY, btree (id)
346 | Referenced by:
347 | TABLE "detail_table" CONSTRAINT "detail_table_id_fkey" FOREIGN KEY (id) REFERENCES master_table(id)
348 |
349 |
350 | -----------------------------------------------------------------------
351 |
352 | Модификатор + работает и для \d:
353 |
354 | => \d+ my_view
355 | View "public.my_view"
356 | Column | Type | Modifiers | Storage | Description
357 | --------+---------+-----------+----------+---------------
358 | id | integer | | plain | Идентификатор
359 | c | text | | extended |
360 | View definition:
361 | SELECT detail_table.id,
362 | detail_table.c
363 | FROM detail_table;
364 |
365 |
366 | -----------------------------------------------------------------------
367 |
368 | Использование REG* типов.
369 |
370 | Напишем запрос, который выводит столбцы представления my_view.
371 | Нам потребуются таблицы системного каталога:
372 | pg_class - список объектов, включая представления
373 | pg_attribute - список столбцов
374 |
375 | -----------------------------------------------------------------------
376 |
377 | => select oid, relname from pg_class limit 5;
378 | oid | relname
379 | -------+----------------------
380 | 2619 | pg_statistic
381 | 1247 | pg_type
382 | 16458 | pg_toast_16455
383 | 16460 | pg_toast_16455_index
384 | 2840 | pg_toast_2619
385 | (5 rows)
386 |
387 |
388 | => select attrelid, attname from pg_attribute limit 5;
389 | attrelid | attname
390 | ----------+--------------
391 | 1255 | proname
392 | 1255 | pronamespace
393 | 1255 | proowner
394 | 1255 | prolang
395 | 1255 | procost
396 | (5 rows)
397 |
398 |
399 | -----------------------------------------------------------------------
400 |
401 | Две таблицы связаны по pg_class.OID - pg_attribute.attrelid
402 | Запрос на соединение двух таблиц:
403 |
404 | => select c.attrelid, c.attname
405 | => from pg_attribute c, pg_class t
406 | => where c.attrelid = t.oid
407 | => and t.relname = 'my_view';
408 | attrelid | attname
409 | ----------+---------
410 | 16474 | c
411 | 16474 | id
412 | (2 rows)
413 |
414 |
415 | -----------------------------------------------------------------------
416 |
417 | Запрос с использованием regclass:
418 |
419 | => select attrelid, attname
420 | => from pg_attribute
421 | => where attrelid = 'my_view'::regclass;
422 | attrelid | attname
423 | ----------+---------
424 | 16474 | c
425 | 16474 | id
426 | (2 rows)
427 |
428 |
429 | -----------------------------------------------------------------------
430 |
431 | => \q
432 |
433 | Посмотрим на запрос, которые выполняет psql для команды \dt
434 | psql dba1_07_demo -E -c '\dt'
435 | ********* QUERY **********
436 | SELECT n.nspname as "Schema",
437 | c.relname as "Name",
438 | CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 's' THEN 'special' WHEN 'f' THEN 'foreign table' END as "Type",
439 | pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
440 | FROM pg_catalog.pg_class c
441 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
442 | WHERE c.relkind IN ('r','')
443 | AND n.nspname <> 'pg_catalog'
444 | AND n.nspname <> 'information_schema'
445 | AND n.nspname !~ '^pg_toast'
446 | AND pg_catalog.pg_table_is_visible(c.oid)
447 | ORDER BY 1,2;
448 | **************************
449 |
450 | List of relations
451 | Schema | Name | Type | Owner
452 | --------+--------------+-------+----------
453 | public | detail_table | table | postgres
454 | public | master_table | table | postgres
455 | (2 rows)
456 |
457 |
458 | -----------------------------------------------------------------------
459 |
460 | psql postgres -c "DROP DATABASE dba1_07_demo;"
461 | DROP DATABASE
462 |
463 | Конец демонстрации.
464 |
465 | -----------------------------------------------------------------------
466 |
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_08_objects.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_08_objects.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_08_objects.txt:
--------------------------------------------------------------------------------
1 |
2 | ТАБЛИЦЫ, ТИПЫ И ОГРАНИЧЕНИЯ
3 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 |
5 | Пример создания таблицы с разными типами столбцов и ограничениями целостности:
6 |
7 | => create table people(
8 | => id numeric primary key,
9 | => name varchar(100) not null,
10 | => employed boolean,
11 | => constraint uname unique(name)
12 | => );
13 | CREATE TABLE
14 |
15 | Синтаксис соответствуют стандарту. Можно использовать различные типы данных, указывать
16 | ограничения целостности как на уровне отдельных полей, так и на уровне таблицы.
17 |
18 | -----------------------------------------------------------------------
19 |
20 | Создадим аналогичные, но временные таблицы.
21 | Для этого воспользуемся специальным синтаксисом LIKE:
22 |
23 | => begin;
24 | BEGIN
25 |
26 | => create temporary table people_temp_delete(like people)
27 | => on commit delete rows;
28 | CREATE TABLE
29 |
30 | => create temporary table people_temp_preserve(like people)
31 | => on commit preserve rows;
32 | CREATE TABLE
33 |
34 | => create temporary table people_temp_drop(like people)
35 | => on commit drop;
36 | CREATE TABLE
37 |
38 | -----------------------------------------------------------------------
39 |
40 | => insert into people_temp_delete values (1,'Иванов',true);
41 | INSERT 0 1
42 |
43 | => insert into people_temp_preserve values (1,'Иванов',true);
44 | INSERT 0 1
45 |
46 | => insert into people_temp_drop values (1,'Иванов',true);
47 | INSERT 0 1
48 |
49 | => commit;
50 | COMMIT
51 |
52 | Что произошло при фиксации изменений?
53 |
54 | -----------------------------------------------------------------------
55 |
56 | Таблица on commit delete rows пуста:
57 |
58 | => select * from people_temp_delete;
59 | id | name | employed
60 | ----+------+----------
61 | (0 rows)
62 |
63 |
64 | -----------------------------------------------------------------------
65 |
66 | Таблица on commit preserve rows сохранила строки:
67 |
68 | => select * from people_temp_preserve;
69 | id | name | employed
70 | ----+--------+----------
71 | 1 | Иванов | t
72 | (1 row)
73 |
74 |
75 | -----------------------------------------------------------------------
76 |
77 | Таблица on commit drop table больше не существует:
78 |
79 | => select * from people_temp_drop;
80 | ERROR: relation "people_temp_drop" does not exist
81 | LINE 1: select * from people_temp_drop;
82 | ^
83 |
84 | -----------------------------------------------------------------------
85 |
86 | В любом случае временные таблицы живут не дольше, чем сеанс. Завершим текущий и начнем следующий:
87 |
88 | => \q
89 |
90 | psql
91 |
92 | => select * from people_temp_delete;
93 | ERROR: relation "people_temp_delete" does not exist
94 | LINE 1: select * from people_temp_delete;
95 | ^
96 |
97 | => select * from people_temp_preserve;
98 | ERROR: relation "people_temp_preserve" does not exist
99 | LINE 1: select * from people_temp_preserve;
100 | ^
101 |
102 | -----------------------------------------------------------------------
103 |
104 | При этом обычная таблица, разумеется, осталась:
105 |
106 | => select * from people;
107 | id | name | employed
108 | ----+------+----------
109 | (0 rows)
110 |
111 |
112 | -----------------------------------------------------------------------
113 |
114 | ПОСЛЕДОВАТЕЛЬНОСТИ
115 | ~~~~~~~~~~~~~~~~~~
116 |
117 | Создадим последовательность:
118 |
119 | => create sequence s start with 2 cache 10;
120 | CREATE SEQUENCE
121 |
122 | Ее можно использовать, например, для генерации уникального ключа:
123 |
124 | => insert into people values (nextval('s'), 'Петров', false);
125 | INSERT 0 1
126 |
127 | => select currval('s');
128 | currval
129 | ---------
130 | 2
131 | (1 row)
132 |
133 |
134 | -----------------------------------------------------------------------
135 |
136 | Поскольку при создании последовательности мы указали cache 10, сеансы будут кэшировать
137 | диапазоны значений. Это приведет к тому, что два сеанса могут выдавать номера не по порядку:
138 |
139 | | psql
140 |
141 | | => select nextval('s');
142 | | nextval
143 | | ---------
144 | | 12
145 | | (1 row)
146 | |
147 |
148 | => select nextval('s');
149 | nextval
150 | ---------
151 | 3
152 | (1 row)
153 |
154 |
155 | -----------------------------------------------------------------------
156 |
157 | | => select nextval('s');
158 | | nextval
159 | | ---------
160 | | 13
161 | | (1 row)
162 | |
163 |
164 | => select nextval('s');
165 | nextval
166 | ---------
167 | 4
168 | (1 row)
169 |
170 |
171 | | => \q
172 |
173 | -----------------------------------------------------------------------
174 |
175 | ИНДЕКСЫ
176 | ~~~~~~~
177 |
178 | Создадим большую таблицу:
179 |
180 | => create table bigtable(id serial, t text);
181 | CREATE TABLE
182 |
183 | => insert into bigtable
184 | => select s.id, 'This is a line #' || s.id from generate_series(1,1000000) as s(id);
185 | INSERT 0 1000000
186 |
187 | => select count(*) from bigtable;
188 | count
189 | ---------
190 | 1000000
191 | (1 row)
192 |
193 |
194 | -----------------------------------------------------------------------
195 |
196 | Сколько времени занимает поиск одного значения в такой таблице?
197 |
198 | => \timing on
199 | Timing is on.
200 |
201 | => select * from bigtable where id=42;
202 | id | t
203 | ----+--------------------
204 | 42 | This is a line #42
205 | (1 row)
206 |
207 | Time: 74,596 ms
208 |
209 | -----------------------------------------------------------------------
210 |
211 | Теперь проиндексируем таблицу:
212 |
213 | => create unique index bigtable_id on bigtable(id);
214 | CREATE INDEX
215 | Time: 319,090 ms
216 |
217 | => select * from bigtable where id=42;
218 | id | t
219 | ----+--------------------
220 | 42 | This is a line #42
221 | (1 row)
222 |
223 | Time: 0,357 ms
224 |
225 | => \timing off
226 | Timing is off.
227 |
228 | Индекс не является универсальным средством увеличения производительности. В общем случае
229 | индекс может быть полезен, если из таблицы требуется выбрать существенно меньше данных,
230 | чем в ней находится. Кроме того, надо учитывать накладные расходы на обновление индекса
231 | при изменении таблицы.
232 |
233 | -----------------------------------------------------------------------
234 |
235 | ХРАНИМЫЕ ФУНКЦИИ
236 | ~~~~~~~~~~~~~~~~
237 |
238 | Простой пример функции на SQL:
239 |
240 | => create function random_text(len integer) returns text as $$
241 | => select string_agg(chr(trunc(65+random()*26)::integer),'') from generate_series(1,$1);
242 | => $$ language sql;
243 | CREATE FUNCTION
244 |
245 | Теперь функцию можно использовать в запросах:
246 |
247 | => select random_text(10);
248 | random_text
249 | -------------
250 | RYSYHBTHRN
251 | (1 row)
252 |
253 |
254 | -----------------------------------------------------------------------
255 |
256 | ТРИГГЕРЫ
257 | ~~~~~~~~
258 |
259 | Продемонстрируем триггер для сохранения времени последнего изменения строк.
260 | Добавим поле в таблицу people:
261 |
262 | => alter table people add column last_update timestamp;
263 | ALTER TABLE
264 |
265 | Создадим функцию, которая будут использоваться в триггере:
266 |
267 | => create function set_last_update() returns trigger as $$
268 | => begin
269 | => new.last_update := current_timestamp;
270 | => return new;
271 | => end;
272 | => $$ language plpgsql;
273 | CREATE FUNCTION
274 |
275 | -----------------------------------------------------------------------
276 |
277 | И собственно триггер:
278 |
279 | => create trigger people_last_update
280 | => before update or insert on people
281 | => for each row
282 | => execute procedure set_last_update();
283 | CREATE TRIGGER
284 |
285 | -----------------------------------------------------------------------
286 |
287 | Проверим работоспособность вставки:
288 |
289 | => insert into people(id, name) values (3, 'Сидоров');
290 | INSERT 0 1
291 |
292 | => select * from people;
293 | id | name | employed | last_update
294 | ----+---------+----------+----------------------------
295 | 2 | Петров | f |
296 | 3 | Сидоров | | 2015-12-09 19:11:33.212058
297 | (2 rows)
298 |
299 |
300 | -----------------------------------------------------------------------
301 |
302 | И обновления:
303 |
304 | => update people set employed = true where id = 2;
305 | UPDATE 1
306 |
307 | => select * from people;
308 | id | name | employed | last_update
309 | ----+---------+----------+----------------------------
310 | 3 | Сидоров | | 2015-12-09 19:11:33.212058
311 | 2 | Петров | t | 2015-12-09 19:11:33.217702
312 | (2 rows)
313 |
314 |
315 | -----------------------------------------------------------------------
316 |
317 | НАСЛЕДОВАНИЕ И СЕКЦИОНИРОВАНИЕ
318 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
319 |
320 | Будем вести ежедневный учет доходов компании, разделяя их между двумя филиалами
321 | в Москве и Санкт-Петербурге.
322 |
323 | => create table revenue(
324 | => amount money,
325 | => accounting_date date,
326 | => city char(3) check (city in ('MSK','SPB'))
327 | => );
328 | CREATE TABLE
329 |
330 | Создадим две дочерние таблицы:
331 |
332 | => create table revenue_msk() inherits (revenue);
333 | CREATE TABLE
334 |
335 | => create table revenue_spb() inherits (revenue);
336 | CREATE TABLE
337 |
338 | -----------------------------------------------------------------------
339 |
340 | Теперь требуется триггер, перенаправляющий данные из родительской таблицы в дочерние:
341 |
342 | => create function revenue_insert() returns trigger as $$
343 | => begin
344 | => if new.city = 'MSK' then
345 | => insert into revenue_msk select new.*;
346 | => else
347 | => insert into revenue_spb select new.*;
348 | => end if;
349 | => return null;
350 | => end;
351 | => $$ language plpgsql;
352 | CREATE FUNCTION
353 |
354 | => create trigger revenue_partition
355 | => before insert on revenue
356 | => for each row
357 | => execute procedure revenue_insert();
358 | CREATE TRIGGER
359 |
360 | -----------------------------------------------------------------------
361 |
362 | Проверим результат.
363 |
364 | => insert into revenue values (100.00, current_date, 'MSK');
365 | INSERT 0 0
366 |
367 | При запросе из родительской таблицы запись будет выбрана...
368 |
369 | => select * from revenue;
370 | amount | accounting_date | city
371 | ------------+-----------------+------
372 | 100.00 руб | 2015-12-09 | MSK
373 | (1 row)
374 |
375 |
376 | -----------------------------------------------------------------------
377 |
378 | ...но в самой родительской таблице ее нет...
379 |
380 | => select * from only revenue;
381 | amount | accounting_date | city
382 | --------+-----------------+------
383 | (0 rows)
384 |
385 |
386 | ...так как она была перенаправлена в дочернюю таблицу триггером.
387 |
388 | => select * from revenue_msk;
389 | amount | accounting_date | city
390 | ------------+-----------------+------
391 | 100.00 руб | 2015-12-09 | MSK
392 | (1 row)
393 |
394 |
395 | -----------------------------------------------------------------------
396 |
397 | Наполним таблицу большим количеством данных.
398 |
399 | => insert into revenue select random()*100::money, current_date-s.n, 'MSK' from generate_series(1,1000000) as s(n);
400 | INSERT 0 0
401 |
402 | => insert into revenue select random()*100::money, current_date-s.n, 'SPB' from generate_series(1,1000) as s(n);
403 | INSERT 0 0
404 |
405 | -----------------------------------------------------------------------
406 |
407 | Сравним время выполнения запросов для разных городов:
408 |
409 | => \timing on
410 | Timing is on.
411 |
412 | => select count(*) from revenue where city='SPB';
413 | count
414 | -------
415 | 1000
416 | (1 row)
417 |
418 | Time: 307,865 ms
419 |
420 | => select count(*) from revenue where city='MSK';
421 | count
422 | ---------
423 | 1000001
424 | (1 row)
425 |
426 | Time: 150,535 ms
427 |
428 | Время отличается не сильно.
429 |
430 | -----------------------------------------------------------------------
431 |
432 | Теперь добавим ограничивающие условия на дочерние таблицы:
433 |
434 | => alter table revenue_msk add constraint partition_check check (city='MSK');
435 | ALTER TABLE
436 | Time: 106,475 ms
437 |
438 | => alter table revenue_spb add constraint partition_check check (city='SPB');
439 | ALTER TABLE
440 | Time: 1,326 ms
441 |
442 | -----------------------------------------------------------------------
443 |
444 | Снова сравним время:
445 |
446 | => select count(*) from revenue where city='SPB';
447 | count
448 | -------
449 | 1000
450 | (1 row)
451 |
452 | Time: 0,500 ms
453 |
454 | => select count(*) from revenue where city='MSK';
455 | count
456 | ---------
457 | 1000001
458 | (1 row)
459 |
460 | Time: 149,263 ms
461 |
462 | => \timing off
463 | Timing is off.
464 |
465 | На этот раз время отличается значительно, так оптимизатор перестал заглядывать
466 | в ненужную секцию.
467 |
468 | -----------------------------------------------------------------------
469 |
470 | ПРЕДСТАВЛЕНИЯ
471 | ~~~~~~~~~~~~~
472 |
473 | Создадим представление для суммы дохода по городам за все время.
474 |
475 | => create view revenue_by_city as
476 | => select city, sum(amount) from revenue group by city;
477 | CREATE VIEW
478 |
479 | Запрос будет выполняться при обращении к представлению:
480 |
481 | => \timing on
482 | Timing is on.
483 |
484 | => select * from revenue_by_city;
485 | city | sum
486 | ------+-------------------
487 | SPB | 50 720.24 руб
488 | MSK | 49 981 892.78 руб
489 | (2 rows)
490 |
491 | Time: 243,205 ms
492 |
493 | -----------------------------------------------------------------------
494 |
495 | Материализованное представление сохраняет результат запроса.
496 |
497 | => create materialized view revenue_by_city_m as
498 |
499 | => select city, sum(amount) from revenue group by city;
500 | SELECT 2
501 | Time: 246,797 ms
502 |
503 | => select * from revenue_by_city_m;
504 | city | sum
505 | ------+-------------------
506 | SPB | 50 720.24 руб
507 | MSK | 49 981 892.78 руб
508 | (2 rows)
509 |
510 | Time: 0,281 ms
511 |
512 | => \timing off
513 | Timing is off.
514 |
515 | -----------------------------------------------------------------------
516 |
517 | Изменение в данных будет сразу же отражено в представлении:
518 |
519 | => insert into revenue values (1000000.00, current_date, 'SPB');
520 | INSERT 0 0
521 |
522 | => select * from revenue_by_city;
523 | city | sum
524 | ------+-------------------
525 | SPB | 1 050 720.24 руб
526 | MSK | 49 981 892.78 руб
527 | (2 rows)
528 |
529 |
530 | Но не в материализованном представлении.
531 |
532 | => select * from revenue_by_city_m;
533 | city | sum
534 | ------+-------------------
535 | SPB | 50 720.24 руб
536 | MSK | 49 981 892.78 руб
537 | (2 rows)
538 |
539 |
540 | -----------------------------------------------------------------------
541 |
542 | Изменения появятся только после обновления материализованного представления:
543 |
544 | => refresh materialized view revenue_by_city_m;
545 | REFRESH MATERIALIZED VIEW
546 |
547 | => select * from revenue_by_city_m;
548 | city | sum
549 | ------+-------------------
550 | SPB | 1 050 720.24 руб
551 | MSK | 49 981 892.78 руб
552 | (2 rows)
553 |
554 |
555 | Конец демонстрации.
556 |
557 | -----------------------------------------------------------------------
558 |
559 | => \q
560 |
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_09_roles.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_09_roles.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_10_schemas.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_10_schemas.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_11_privileges.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_11_privileges.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_11_privileges.txt:
--------------------------------------------------------------------------------
1 |
2 | СОЗДАНИЕ ОБЪЕКТОВ
3 | ~~~~~~~~~~~~~~~~~
4 |
5 | Создадим роль role1 и одноименную схему. Эта роль будет владельцем нескольких объектов.
6 |
7 | psql
8 |
9 | postgres=# create user role1;
10 | CREATE ROLE
11 |
12 | postgres=# create schema role1;
13 | CREATE SCHEMA
14 |
15 | postgres=# grant create, usage on schema role1 to role1;
16 | GRANT
17 |
18 | postgres=# \c - role1
19 | You are now connected to database "postgres" as user "role1".
20 |
21 | role1=> select current_schemas(true);
22 | current_schemas
23 | ---------------------------
24 | {pg_catalog,role1,public}
25 | (1 row)
26 |
27 |
28 | -----------------------------------------------------------------------
29 |
30 | Создадим две таблицы и функцию.
31 |
32 | role1=> create table t1(n numeric);
33 | CREATE TABLE
34 |
35 | role1=> create table t2(n numeric, m numeric);
36 | CREATE TABLE
37 |
38 | role1=> create function f1() returns integer as $$ select 1; $$ language sql;
39 | CREATE FUNCTION
40 |
41 | -----------------------------------------------------------------------
42 |
43 | Создадим роль role2, из-под которой будем обращаться к объектам role1.
44 |
45 | role1=> \c - postgres
46 | You are now connected to database "postgres" as user "postgres".
47 |
48 | postgres=# create user role2;
49 | CREATE ROLE
50 |
51 | postgres=# \c - role2
52 | You are now connected to database "postgres" as user "role2".
53 |
54 | -----------------------------------------------------------------------
55 |
56 | ПРИВИЛЕГИИ
57 | ~~~~~~~~~~
58 |
59 | Попробуем обратиться к таблице t1.
60 |
61 | role2=> select * from t1;
62 | ERROR: relation "t1" does not exist
63 | LINE 1: select * from t1;
64 | ^
65 |
66 | В чем причина ошибки?
67 |
68 | -----------------------------------------------------------------------
69 |
70 | Ошибка в том, что такой таблицы нет у нас в пути поиска:
71 |
72 | role2=> select current_schemas(true);
73 | current_schemas
74 | ---------------------
75 | {pg_catalog,public}
76 | (1 row)
77 |
78 |
79 | -----------------------------------------------------------------------
80 |
81 | Обратимся с явным указанием имени схемы:
82 |
83 | role2=> select * from role1.t1;
84 | ERROR: permission denied for schema role1
85 | LINE 1: select * from role1.t1;
86 | ^
87 |
88 | В чем причина ошибки?
89 |
90 | -----------------------------------------------------------------------
91 |
92 | Доступа к схеме нет, так как мы не суперпользователь, не владелец, и у нас нет привилегий.
93 |
94 | role2=> \dn+ role1
95 | List of schemas
96 | Name | Owner | Access privileges | Description
97 | -------+----------+----------------------+-------------
98 | role1 | postgres | postgres=UC/postgres+|
99 | | | role1=UC/postgres |
100 | (1 row)
101 |
102 |
103 | В каждой строке access privileges отображается роль, какими привилегиями она наделена и кем они ей выданы:
104 | роль=привилегии/кем_предоставлены
105 |
106 | -----------------------------------------------------------------------
107 |
108 | Выдадим доступ к схеме для role2.
109 |
110 | role2=> \c - role1
111 | You are now connected to database "postgres" as user "role1".
112 |
113 | role1=> grant create, usage on schema role1 to role2;
114 | WARNING: no privileges were granted for "role1"
115 | GRANT
116 |
117 | Почему привилегия не выдалась?
118 |
119 | -----------------------------------------------------------------------
120 |
121 | Привилегия не выдалась, так как у нас нет права ее перевыдачи.
122 |
123 | role1=> \dn+
124 | List of schemas
125 | Name | Owner | Access privileges | Description
126 | --------+----------+----------------------+------------------------
127 | public | postgres | postgres=UC/postgres+| standard public schema
128 | | | =UC/postgres |
129 | role1 | postgres | postgres=UC/postgres+|
130 | | | role1=UC/postgres |
131 | (2 rows)
132 |
133 |
134 | Пустая роль обозначает роль public (общая группа для всех ролей).
135 | Обратите внимание, что по умолчанию все имеют доступ к схеме public.
136 |
137 | -----------------------------------------------------------------------
138 |
139 | Сделаем role1 владельцем схемы role1:
140 |
141 | role1=> \c - postgres
142 | You are now connected to database "postgres" as user "postgres".
143 |
144 | postgres=# alter schema role1 owner to role1;
145 | ALTER SCHEMA
146 |
147 | postgres=# \dn+
148 | List of schemas
149 | Name | Owner | Access privileges | Description
150 | --------+----------+----------------------+------------------------
151 | public | postgres | postgres=UC/postgres+| standard public schema
152 | | | =UC/postgres |
153 | role1 | role1 | role1=UC/role1 |
154 | (2 rows)
155 |
156 |
157 | -----------------------------------------------------------------------
158 |
159 | Выдаем доступ:
160 |
161 | role1=> \c - role1
162 | You are now connected to database "postgres" as user "role1".
163 |
164 | role1=> grant create, usage on schema role1 to role2;
165 | GRANT
166 |
167 | -----------------------------------------------------------------------
168 |
169 | Попробуем снова обратиться к таблице:
170 |
171 | role1=> \c - role2
172 | You are now connected to database "postgres" as user "role2".
173 |
174 | role2=> select * from role1.t1;
175 | ERROR: permission denied for relation t1
176 |
177 | В чем причина ошибки?
178 |
179 | -----------------------------------------------------------------------
180 |
181 | На этот раз у нас есть доступ к схеме, но нет доступа к самой таблице.
182 |
183 | role2=> \dp role1.t1
184 | Access privileges
185 | Schema | Name | Type | Access privileges | Column access privileges
186 | --------+------+-------+-------------------+--------------------------
187 | role1 | t1 | table | |
188 | (1 row)
189 |
190 |
191 | Пустое поле привилегий означает, что привилегии есть только у владельца
192 | и они не менялись.
193 |
194 | -----------------------------------------------------------------------
195 |
196 | Выдадим доступ на чтение:
197 |
198 | role2=> \c - role1
199 | You are now connected to database "postgres" as user "role1".
200 |
201 | role1=> grant select on t1 to role2;
202 | GRANT
203 |
204 | -----------------------------------------------------------------------
205 |
206 | Посмотрим, как изменились привилегии:
207 |
208 | role1=> \dp t1
209 | Access privileges
210 | Schema | Name | Type | Access privileges | Column access privileges
211 | --------+------+-------+---------------------+--------------------------
212 | role1 | t1 | table | role1=arwdDxt/role1+|
213 | | | | role2=r/role1 |
214 | (1 row)
215 |
216 |
217 | Привилегии отображаются в одном и том же формате: роль=привилегии/кем_предоставлены,
218 | в зависимости от типа объекта меняется только их список. Для таблиц это:
219 | a = insert - вставка (append)
220 | r = select - чтение (read)
221 | w = update - запись (write)
222 | d = delete - удаление (delete)
223 | D = truncate - очистка (DELETE)
224 | x = reference - ссылка (x-ref?)
225 | t = trigger - триггер (trigger)
226 |
227 | -----------------------------------------------------------------------
228 |
229 | Снова попробуем обратиться к таблице:
230 |
231 | role1=> \c - role2
232 | You are now connected to database "postgres" as user "role2".
233 |
234 | role2=> select * from role1.t1;
235 | n
236 | ---
237 | (0 rows)
238 |
239 |
240 | На этот раз с привилегиями все в порядке.
241 |
242 | -----------------------------------------------------------------------
243 |
244 | Для того, чтобы обращаться к таблице без указания схемы, можно изменить путь доступа.
245 |
246 | role2=> set search_path=role1;
247 | SET
248 |
249 | role2=> select current_schemas(true);
250 | current_schemas
251 | --------------------
252 | {pg_catalog,role1}
253 | (1 row)
254 |
255 |
256 | role2=> select * from t1;
257 | n
258 | ---
259 | (0 rows)
260 |
261 |
262 | -----------------------------------------------------------------------
263 |
264 | Попробуем теперь добавить строку в таблицу:
265 |
266 | role2=> insert into t1 values (42);
267 | ERROR: permission denied for relation t1
268 |
269 | Ошибка, так как привилегия на вставку не выдана.
270 |
271 | -----------------------------------------------------------------------
272 |
273 | Выдадим доступ на вставку для всех таблиц схемы.
274 |
275 | role2=> \c - role1
276 | You are now connected to database "postgres" as user "role1".
277 |
278 | role1=> grant insert on all tables in schema role1 to role2;
279 | GRANT
280 |
281 | role1=> \dp role1.*
282 | Access privileges
283 | Schema | Name | Type | Access privileges | Column access privileges
284 | --------+------+-------+---------------------+--------------------------
285 | role1 | t1 | table | role1=arwdDxt/role1+|
286 | | | | role2=ar/role1 |
287 | role1 | t2 | table | role1=arwdDxt/role1+|
288 | | | | role2=a/role1 |
289 | (2 rows)
290 |
291 |
292 | -----------------------------------------------------------------------
293 |
294 | Попробуем вставить строки:
295 |
296 | role1=> \c - role2
297 | You are now connected to database "postgres" as user "role2".
298 |
299 | role2=> insert into t1 values (42);
300 | ERROR: relation "t1" does not exist
301 | LINE 1: insert into t1 values (42);
302 | ^
303 |
304 | Почему не найдена таблица t1?
305 |
306 | -----------------------------------------------------------------------
307 |
308 | Таблица не найдена, так как путь поиска устанавливается командой set на время сеанса,
309 | а мы подключались под другим пользователем.
310 | Чтобы установить путь поиска долговременно, надо использовать alter role set search_path.
311 | A пока будем обращаться к объектам с указанием схемы.
312 |
313 | role2=> insert into role1.t1 values (42);
314 | INSERT 0 1
315 |
316 | role2=> insert into role1.t2 values (1,2);
317 | INSERT 0 1
318 |
319 | -----------------------------------------------------------------------
320 |
321 | При этом прочитать вставленные данные мы не сможем:
322 |
323 | role2=> select * from role1.t2;
324 | ERROR: permission denied for relation t2
325 |
326 | -----------------------------------------------------------------------
327 |
328 | Привилегию чтения можно выдать на определенные столбцы:
329 |
330 | role2=> \c - role1
331 | You are now connected to database "postgres" as user "role1".
332 |
333 | role1=> grant select(m) on t2 to role2;
334 | GRANT
335 |
336 | role1=> \dp t2
337 | Access privileges
338 | Schema | Name | Type | Access privileges | Column access privileges
339 | --------+------+-------+---------------------+--------------------------
340 | role1 | t2 | table | role1=arwdDxt/role1+| m: +
341 | | | | role2=a/role1 | role2=r/role1
342 | (1 row)
343 |
344 |
345 | -----------------------------------------------------------------------
346 |
347 | Проверим доступ:
348 |
349 | role1=> \c - role2
350 | You are now connected to database "postgres" as user "role2".
351 |
352 | role2=> select * from role1.t2;
353 | ERROR: permission denied for relation t2
354 |
355 | role2=> select m from role1.t2;
356 | m
357 | ---
358 | 2
359 | (1 row)
360 |
361 |
362 | -----------------------------------------------------------------------
363 |
364 | Если необходимо, можно выдать все привилегии, не перечисляя их явно.
365 |
366 | role2=> \c - role1
367 | You are now connected to database "postgres" as user "role1".
368 |
369 | role1=> grant all on t1 to role2;
370 | GRANT
371 |
372 | role1=> \dp t1
373 | Access privileges
374 | Schema | Name | Type | Access privileges | Column access privileges
375 | --------+------+-------+---------------------+--------------------------
376 | role1 | t1 | table | role1=arwdDxt/role1+|
377 | | | | role2=arwdDxt/role1 |
378 | (1 row)
379 |
380 |
381 | -----------------------------------------------------------------------
382 |
383 | Теперь role2 доступны все действия, например, удаление строк:
384 |
385 | role1=> \c - role2
386 | You are now connected to database "postgres" as user "role2".
387 |
388 | role2=> delete from role1.t1;
389 | DELETE 1
390 |
391 | -----------------------------------------------------------------------
392 |
393 | А удаление самой таблицы?
394 |
395 | role2=> drop table role1.t1;
396 | ERROR: must be owner of relation t1
397 |
398 | В чем проблема?
399 |
400 | -----------------------------------------------------------------------
401 |
402 | Удалить таблицу может только владелец (или суперпользователь),
403 | специальной привилегии для этого не существует.
404 |
405 | -----------------------------------------------------------------------
406 |
407 | ГРУППОВЫЕ ПРИВИЛЕГИИ
408 | ~~~~~~~~~~~~~~~~~~~~
409 |
410 | Если выдать привилегию групповой роли, то входящие в нее роли (с атрибутом inherit)
411 | будут автоматически обладать и групповыми привилегиями.
412 |
413 | role2=> \du role1
414 | List of roles
415 | Role name | Attributes | Member of
416 | -----------+------------+-----------
417 | role1 | | {}
418 |
419 |
420 | Поскольку в атрибутах не числится "No inheritance", у role2 такой атрибут есть.
421 |
422 | -----------------------------------------------------------------------
423 |
424 | Проверим, сможем ли мы вызвать функцию.
425 |
426 | role2=> select role1.f1();
427 | f1
428 | ----
429 | 1
430 | (1 row)
431 |
432 |
433 | Можем. Почему?
434 |
435 | -----------------------------------------------------------------------
436 |
437 | Можем, так как роль public по умолчанию имеет привилегию на выполнение всех функций.
438 | Отзовем эту привилегию.
439 |
440 | role2=> \c - role1
441 | You are now connected to database "postgres" as user "role1".
442 |
443 | role1=> revoke execute on all functions in schema role1 from public;
444 | REVOKE
445 |
446 | -----------------------------------------------------------------------
447 |
448 | Проверим.
449 |
450 | role1=> \c - role2
451 | You are now connected to database "postgres" as user "role2".
452 |
453 | role2=> select role1.f1();
454 | ERROR: permission denied for function f1
455 |
456 | Теперь функцию вызвать нельзя, что и требовалось.
457 |
458 | -----------------------------------------------------------------------
459 |
460 | Посмотрим на примере доступа к таблицам.
461 | Есть ли доступ на очистку таблицы t2?
462 |
463 | role2=> truncate table role1.t2;
464 | ERROR: permission denied for relation t2
465 |
466 | Доступа нет.
467 |
468 | role2=> \dp role1.t2
469 | Access privileges
470 | Schema | Name | Type | Access privileges | Column access privileges
471 | --------+------+-------+---------------------+--------------------------
472 | role1 | t2 | table | role1=arwdDxt/role1+| m: +
473 | | | | role2=a/role1 | role2=r/role1
474 | (1 row)
475 |
476 |
477 | -----------------------------------------------------------------------
478 |
479 | Выдадим доступ роли public.
480 |
481 | role2=> \c - role1
482 | You are now connected to database "postgres" as user "role1".
483 |
484 | role1=> grant truncate on table t2 to public;
485 | GRANT
486 |
487 | role1=> \dp t2
488 | Access privileges
489 | Schema | Name | Type | Access privileges | Column access privileges
490 | --------+------+-------+---------------------+--------------------------
491 | role1 | t2 | table | role1=arwdDxt/role1+| m: +
492 | | | | role2=a/role1 +| role2=r/role1
493 | | | | =D/role1 |
494 | (1 row)
495 |
496 |
497 | -----------------------------------------------------------------------
498 |
499 | Проверим снова.
500 |
501 | role1=> \c - role2
502 | You are now connected to database "postgres" as user "role2".
503 |
504 | role2=> truncate table role1.t2;
505 | TRUNCATE TABLE
506 |
507 | Теперь доступ появился.
508 |
509 | -----------------------------------------------------------------------
510 |
511 | ПЕРЕДАЧА ПРАВА
512 | ~~~~~~~~~~~~~~
513 |
514 | Создадим третью роль и попробуем передать ей права на таблицу, принадлежащую role1.
515 |
516 | role2=> \c - postgres
517 | You are now connected to database "postgres" as user "postgres".
518 |
519 | postgres=# create user role3;
520 | CREATE ROLE
521 |
522 | -----------------------------------------------------------------------
523 |
524 | У роли role2 есть полный доступ к t1:
525 |
526 | postgres=# \c - role2
527 | You are now connected to database "postgres" as user "role2".
528 |
529 | role2=> \dp role1.t1
530 | Access privileges
531 | Schema | Name | Type | Access privileges | Column access privileges
532 | --------+------+-------+---------------------+--------------------------
533 | role1 | t1 | table | role1=arwdDxt/role1+|
534 | | | | role2=arwdDxt/role1 |
535 | (1 row)
536 |
537 |
538 | Но она не может передать свои привилегии:
539 |
540 | role2=> grant select on role1.t1 to role3;
541 | WARNING: no privileges were granted for "t1"
542 | GRANT
543 |
544 | -----------------------------------------------------------------------
545 |
546 | Чтобы это было возможно, role1 должна выдать role2 право перевыдачи.
547 |
548 | role2=> \c - role1
549 | You are now connected to database "postgres" as user "role1".
550 |
551 | role1=> grant select,update on t1 to role2 with grant option;
552 | GRANT
553 |
554 | role1=> \dp t1
555 | Access privileges
556 | Schema | Name | Type | Access privileges | Column access privileges
557 | --------+------+-------+-----------------------+--------------------------
558 | role1 | t1 | table | role1=arwdDxt/role1 +|
559 | | | | role2=ar*w*dDxt/role1 |
560 | (1 row)
561 |
562 |
563 | Звездочки справа от символа привилегии показывают право перевыдачи.
564 |
565 | -----------------------------------------------------------------------
566 |
567 | Теперь role2 может поделиться привилегиями, в том числе и правом перевыдачи.
568 |
569 | role1=> \c - role2
570 | You are now connected to database "postgres" as user "role2".
571 |
572 | role2=> grant select on role1.t1 to role3 with grant option;
573 | GRANT
574 |
575 | role2=> grant update on role1.t1 to role3;
576 | GRANT
577 |
578 | role2=> \dp role1.t1
579 | Access privileges
580 | Schema | Name | Type | Access privileges | Column access privileges
581 | --------+------+-------+-----------------------+--------------------------
582 | role1 | t1 | table | role1=arwdDxt/role1 +|
583 | | | | role2=ar*w*dDxt/role1+|
584 | | | | role3=r*w/role2 |
585 | (1 row)
586 |
587 |
588 | -----------------------------------------------------------------------
589 |
590 | Роль может получить одну и ту же привилегию из разных источников. Например:
591 |
592 | role2=> \c - role1
593 | You are now connected to database "postgres" as user "role1".
594 |
595 | role1=> grant update on role1.t1 to role3;
596 | GRANT
597 |
598 | role1=> \dp role1.t1
599 | Access privileges
600 | Schema | Name | Type | Access privileges | Column access privileges
601 | --------+------+-------+-----------------------+--------------------------
602 | role1 | t1 | table | role1=arwdDxt/role1 +|
603 | | | | role2=ar*w*dDxt/role1+|
604 | | | | role3=r*w/role2 +|
605 | | | | role3=w/role1 |
606 | (1 row)
607 |
608 |
609 | -----------------------------------------------------------------------
610 |
611 | Обратите внимание: если привилегия выдается суперпользователем, она выдается от имени владельца.
612 |
613 | role1=> \c - postgres
614 | You are now connected to database "postgres" as user "postgres".
615 |
616 | postgres=# grant truncate on role1.t1 to role3;
617 | GRANT
618 |
619 | postgres=# \dp role1.t1
620 | Access privileges
621 | Schema | Name | Type | Access privileges | Column access privileges
622 | --------+------+-------+-----------------------+--------------------------
623 | role1 | t1 | table | role1=arwdDxt/role1 +|
624 | | | | role2=ar*w*dDxt/role1+|
625 | | | | role3=r*w/role2 +|
626 | | | | role3=wD/role1 |
627 | (1 row)
628 |
629 |
630 | -----------------------------------------------------------------------
631 |
632 | Роль может отозвать привилегии только у той роли, которой она их непосредственно выдала.
633 | Например, role1 не сможет отозвать право перевыдачи у role3.
634 |
635 | postgres=# \c - role1
636 | You are now connected to database "postgres" as user "role1".
637 |
638 | role1=> revoke grant option for select on role1.t1 from role3;
639 | REVOKE
640 |
641 | Хотя никакой ошибки не фиксируется, но права не изменились:
642 |
643 | role1=> \dp t1
644 | Access privileges
645 | Schema | Name | Type | Access privileges | Column access privileges
646 | --------+------+-------+-----------------------+--------------------------
647 | role1 | t1 | table | role1=arwdDxt/role1 +|
648 | | | | role2=ar*w*dDxt/role1+|
649 | | | | role3=r*w/role2 +|
650 | | | | role3=wD/role1 |
651 | (1 row)
652 |
653 |
654 | -----------------------------------------------------------------------
655 |
656 | Такая же ситуация и с самой привилегией.
657 |
658 | role1=> revoke select on role1.t1 from role3;
659 | REVOKE
660 |
661 | role1=> \dp t1
662 | Access privileges
663 | Schema | Name | Type | Access privileges | Column access privileges
664 | --------+------+-------+-----------------------+--------------------------
665 | role1 | t1 | table | role1=arwdDxt/role1 +|
666 | | | | role2=ar*w*dDxt/role1+|
667 | | | | role3=r*w/role2 +|
668 | | | | role3=wD/role1 |
669 | (1 row)
670 |
671 |
672 | -----------------------------------------------------------------------
673 |
674 | В то же время роль не может отозвать привилегии у роли, которой она их выдала,
675 | если последняя успела перевыдать привилегии кому-либо еще:
676 |
677 | role1=> revoke grant option for select on role1.t1 from role2;
678 | ERROR: dependent privileges exist
679 | HINT: Use CASCADE to revoke them too.
680 |
681 | role1=> revoke select on role1.t1 from role2;
682 | ERROR: dependent privileges exist
683 | HINT: Use CASCADE to revoke them too.
684 |
685 | -----------------------------------------------------------------------
686 |
687 | В таком случае привилегии надо отзывать по всей иерархии передачи с помощью cascade.
688 |
689 | role1=> \dp t1
690 | Access privileges
691 | Schema | Name | Type | Access privileges | Column access privileges
692 | --------+------+-------+-----------------------+--------------------------
693 | role1 | t1 | table | role1=arwdDxt/role1 +|
694 | | | | role2=ar*w*dDxt/role1+|
695 | | | | role3=r*w/role2 +|
696 | | | | role3=wD/role1 |
697 | (1 row)
698 |
699 |
700 | role1=> revoke grant option for select on role1.t1 from role2 cascade;
701 | REVOKE
702 |
703 | role1=> \dp t1
704 | Access privileges
705 | Schema | Name | Type | Access privileges | Column access privileges
706 | --------+------+-------+----------------------+--------------------------
707 | role1 | t1 | table | role1=arwdDxt/role1 +|
708 | | | | role2=arw*dDxt/role1+|
709 | | | | role3=w/role2 +|
710 | | | | role3=wD/role1 |
711 | (1 row)
712 |
713 |
714 | Как видим, у role2 пропало право перевыдачи привилегии, а у role3 была отозвана сама привилегия.
715 |
716 | -----------------------------------------------------------------------
717 |
718 | Аналогично можно отозвать по иерархии и саму привилегию.
719 |
720 | role1=> revoke select on role1.t1 from role2 cascade;
721 | REVOKE
722 |
723 | role1=> \dp t1
724 | Access privileges
725 | Schema | Name | Type | Access privileges | Column access privileges
726 | --------+------+-------+---------------------+--------------------------
727 | role1 | t1 | table | role1=arwdDxt/role1+|
728 | | | | role2=aw*dDxt/role1+|
729 | | | | role3=w/role2 +|
730 | | | | role3=wD/role1 |
731 | (1 row)
732 |
733 |
734 | -----------------------------------------------------------------------
735 |
736 | ПРИВИЛЕГИИ ПО УМОЛЧАНИЮ
737 | ~~~~~~~~~~~~~~~~~~~~~~~
738 |
739 | Рассмотрим привилегии по умолчанию.
740 |
741 | role1=> \c - role1
742 | You are now connected to database "postgres" as user "role1".
743 |
744 | role1=> \ddp
745 | Default access privileges
746 | Owner | Schema | Type | Access privileges
747 | -------+--------+------+-------------------
748 | (0 rows)
749 |
750 |
751 | Изначально привилегии не настроены.
752 |
753 | -----------------------------------------------------------------------
754 |
755 | При создании, например, таблицы, role2 не получит к ней доступа, что вполне очевидно:
756 |
757 | role1=> create table t3(n numeric);
758 | CREATE TABLE
759 |
760 | role1=> \dp t3
761 | Access privileges
762 | Schema | Name | Type | Access privileges | Column access privileges
763 | --------+------+-------+-------------------+--------------------------
764 | role1 | t3 | table | |
765 | (1 row)
766 |
767 |
768 | -----------------------------------------------------------------------
769 |
770 | Но можно настроить дополнительные правила для привилегий по умолчанию:
771 |
772 | role1=> alter default privileges
773 | => for role role1
774 | => in schema role1
775 | => grant select on tables to role2;
776 | ALTER DEFAULT PRIVILEGES
777 |
778 | role1=> \ddp
779 | Default access privileges
780 | Owner | Schema | Type | Access privileges
781 | -------+--------+-------+-------------------
782 | role1 | role1 | table | role2=r/role1
783 | (1 row)
784 |
785 |
786 | -----------------------------------------------------------------------
787 |
788 | Теперь при создании любой таблицы ролью role1 в схеме role1 доступ автоматически будет предоставлен role2.
789 |
790 | role1=> drop table t3;
791 | DROP TABLE
792 |
793 | role1=> create table t3(n numeric);
794 | CREATE TABLE
795 |
796 | role1=> \dp t3
797 | Access privileges
798 | Schema | Name | Type | Access privileges | Column access privileges
799 | --------+------+-------+---------------------+--------------------------
800 | role1 | t3 | table | role1=arwdDxt/role1+|
801 | | | | role2=r/role1 |
802 | (1 row)
803 |
804 |
805 | -----------------------------------------------------------------------
806 |
807 | Конец демонстрации.
808 |
809 | -----------------------------------------------------------------------
810 |
811 | role1=> \q
812 |
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_12_configuration.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_12_configuration.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_12_configuration.txt:
--------------------------------------------------------------------------------
1 |
2 | psql
3 |
4 | КОНФИГУРИРОВАНИЕ СЕРВЕРА
5 | ~~~~~~~~~~~~~~~~~~~~~~~~
6 |
7 | ** Файл postgresql.conf
8 |
9 | -----------------------------------------------------------------------
10 |
11 | => select * from regexp_split_to_table(pg_read_file('postgresql.conf'), '\n') limit 50;
12 | regexp_split_to_table
13 | ---------------------------------------------------------------------------------
14 | # -----------------------------
15 | # PostgreSQL configuration file
16 | # -----------------------------
17 | #
18 | # This file consists of lines of the form:
19 | #
20 | # name = value
21 | #
22 | # (The "=" is optional.) Whitespace may be used. Comments are introduced with
23 | # "#" anywhere on a line. The complete list of parameter names and allowed
24 | # values can be found in the PostgreSQL documentation.
25 | #
26 | # The commented-out settings shown in this file represent the default values.
27 | # Re-commenting a setting is NOT sufficient to revert it to the default value;
28 | # you need to reload the server.
29 | #
30 | # This file is read on server startup and when the server receives a SIGHUP
31 | # signal. If you edit the file on a running system, you have to SIGHUP the
32 | # server for the changes to take effect, or use "pg_ctl reload". Some
33 | # parameters, which are marked below, require a server shutdown and restart to
34 | # take effect.
35 | #
36 | # Any parameter can also be given as a command-line option to the server, e.g.,
37 | # "postgres -c log_connections=on". Some parameters can be changed at run time
38 | # with the "SET" SQL command.
39 | #
40 | # Memory units: kB = kilobytes Time units: ms = milliseconds
41 | # MB = megabytes s = seconds
42 | # GB = gigabytes min = minutes
43 | # TB = terabytes h = hours
44 | # d = days
45 |
46 |
47 | #------------------------------------------------------------------------------
48 | # FILE LOCATIONS
49 | #------------------------------------------------------------------------------
50 |
51 | # The default values of these variables are driven from the -D command-line
52 | # option or PGDATA environment variable, represented here as ConfigDir.
53 |
54 | #data_directory = 'ConfigDir' # use data in another directory
55 | # (change requires restart)
56 | #hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file
57 | # (change requires restart)
58 | #ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file
59 | # (change requires restart)
60 |
61 | # If external_pid_file is not explicitly set, no extra PID file is written.
62 | #external_pid_file = '' # write an extra PID file
63 | # (change requires restart)
64 | (50 rows)
65 |
66 |
67 | -----------------------------------------------------------------------
68 |
69 | ** Команда ALTER SYSTEM
70 |
71 | -----------------------------------------------------------------------
72 |
73 | => select * from regexp_split_to_table(pg_read_file('postgresql.auto.conf'), '\n');
74 | regexp_split_to_table
75 | -------------------------------------------------------
76 | # Do not edit this file manually!
77 | # It will be overwritten by the ALTER SYSTEM command.
78 |
79 | (3 rows)
80 |
81 |
82 | -----------------------------------------------------------------------
83 |
84 | => ALTER SYSTEM SET work_mem TO '8mb';
85 | ERROR: invalid value for parameter "work_mem": "8mb"
86 | HINT: Valid units for this parameter are "kB", "MB", "GB", and "TB".
87 |
88 | ALTER SYSTEM выполняет проверку на допустимые значения
89 |
90 | -----------------------------------------------------------------------
91 |
92 | => ALTER SYSTEM SET work_mem TO '8MB';
93 | ALTER SYSTEM
94 |
95 | -----------------------------------------------------------------------
96 |
97 | Значение 8MB записано в файл postgresql.auto.conf
98 |
99 | => select * from regexp_split_to_table(pg_read_file('postgresql.auto.conf'), '\n');
100 | regexp_split_to_table
101 | ---------------------------------------------------
102 | # Do not edit this file manually!
103 | # It will be overwritten by ALTER SYSTEM command.
104 | work_mem = '8MB'
105 |
106 | (4 rows)
107 |
108 |
109 | -----------------------------------------------------------------------
110 |
111 | ... но не установлено
112 |
113 | => SHOW work_mem;
114 | work_mem
115 | ----------
116 | 4MB
117 | (1 row)
118 |
119 |
120 | Для установки требуется повторное считывание файлов конфигурации
121 |
122 | => select pg_reload_conf();
123 | pg_reload_conf
124 | ----------------
125 | t
126 | (1 row)
127 |
128 |
129 | => SHOW work_mem;
130 | work_mem
131 | ----------
132 | 8MB
133 | (1 row)
134 |
135 |
136 | -----------------------------------------------------------------------
137 |
138 | Для удаления строк из postgresql.auto.conf также используется ALTER SYSTEM
139 |
140 | => ALTER SYSTEM RESET work_mem;
141 | ALTER SYSTEM
142 |
143 | => select * from regexp_split_to_table(pg_read_file('postgresql.auto.conf'), '\n');
144 | regexp_split_to_table
145 | ---------------------------------------------------
146 | # Do not edit this file manually!
147 | # It will be overwritten by ALTER SYSTEM command.
148 |
149 | (3 rows)
150 |
151 |
152 | -----------------------------------------------------------------------
153 |
154 | Восстановим значение по умолчанию
155 |
156 | => select pg_reload_conf();
157 | pg_reload_conf
158 | ----------------
159 | t
160 | (1 row)
161 |
162 |
163 | => SHOW work_mem;
164 | work_mem
165 | ----------
166 | 4MB
167 | (1 row)
168 |
169 |
170 | -----------------------------------------------------------------------
171 |
172 | ** Установка параметров во время исполнения
173 |
174 | -----------------------------------------------------------------------
175 |
176 | => SET work_mem TO '8MB';
177 | SET
178 |
179 | => select set_config('work_mem', '8MB', false);
180 | set_config
181 | ------------
182 | 8MB
183 | (1 row)
184 |
185 |
186 | => UPDATE pg_settings
187 | => SET setting = '8MB'
188 | => WHERE name = 'work_mem';
189 | set_config
190 | ------------
191 | 8MB
192 | (1 row)
193 |
194 | UPDATE 0
195 |
196 | -----------------------------------------------------------------------
197 |
198 | UPDATE на pg_settings вызывает set_config()
199 |
200 | => \d+ pg_settings
201 | View "pg_catalog.pg_settings"
202 | Column | Type | Modifiers | Storage | Description
203 | ------------+---------+-----------+----------+-------------
204 | name | text | | extended |
205 | setting | text | | extended |
206 | unit | text | | extended |
207 | category | text | | extended |
208 | short_desc | text | | extended |
209 | extra_desc | text | | extended |
210 | context | text | | extended |
211 | vartype | text | | extended |
212 | source | text | | extended |
213 | min_val | text | | extended |
214 | max_val | text | | extended |
215 | enumvals | text[] | | extended |
216 | boot_val | text | | extended |
217 | reset_val | text | | extended |
218 | sourcefile | text | | extended |
219 | sourceline | integer | | plain |
220 | View definition:
221 | SELECT a.name,
222 | a.setting,
223 | a.unit,
224 | a.category,
225 | a.short_desc,
226 | a.extra_desc,
227 | a.context,
228 | a.vartype,
229 | a.source,
230 | a.min_val,
231 | a.max_val,
232 | a.enumvals,
233 | a.boot_val,
234 | a.reset_val,
235 | a.sourcefile,
236 | a.sourceline
237 | FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline);
238 | Rules:
239 | pg_settings_n AS
240 | ON UPDATE TO pg_settings DO INSTEAD NOTHING
241 | pg_settings_u AS
242 | ON UPDATE TO pg_settings
243 | WHERE new.name = old.name DO SELECT set_config(old.name, new.setting, false) AS set_config
244 |
245 |
246 | -----------------------------------------------------------------------
247 |
248 | ** Чтение значений параметров во время выполнения
249 |
250 | -----------------------------------------------------------------------
251 |
252 | => SHOW work_mem;
253 | work_mem
254 | ----------
255 | 8MB
256 | (1 row)
257 |
258 |
259 | => select current_setting ('work_mem');
260 | current_setting
261 | -----------------
262 | 8MB
263 | (1 row)
264 |
265 |
266 | => select name, setting, unit from pg_settings where name = 'work_mem';
267 | name | setting | unit
268 | ----------+---------+------
269 | work_mem | 8192 | kB
270 | (1 row)
271 |
272 |
273 | -----------------------------------------------------------------------
274 |
275 | ** Установка параметров для новых сессий на уровне БД и ролей
276 |
277 | -----------------------------------------------------------------------
278 |
279 | => ALTER DATABASE postgres SET work_mem TO '16MB';
280 | ALTER DATABASE
281 |
282 | => ALTER ROLE postgres SET work_mem TO '32MB';
283 | ALTER ROLE
284 |
285 | => ALTER ROLE postgres IN DATABASE template1 SET work_mem TO '6MB';
286 | ALTER ROLE
287 |
288 | -----------------------------------------------------------------------
289 |
290 | => SELECT r.rolname, d.datname, rs.setconfig
291 | => FROM pg_db_role_setting rs
292 | => LEFT JOIN pg_roles r ON r.oid = rs.setrole
293 | => LEFT JOIN pg_database d ON d.oid = rs.setdatabase;
294 | rolname | datname | setconfig
295 | ----------+-----------+-----------------
296 | | postgres | {work_mem=16MB}
297 | postgres | | {work_mem=32MB}
298 | postgres | template1 | {work_mem=6MB}
299 | (3 rows)
300 |
301 |
302 | -----------------------------------------------------------------------
303 |
304 | Проверяем для новых сессий
305 |
306 | => \c postgres postgres
307 | You are now connected to database "postgres" as user "postgres".
308 |
309 | => SHOW work_mem;
310 | work_mem
311 | ----------
312 | 32MB
313 | (1 row)
314 |
315 |
316 | -----------------------------------------------------------------------
317 |
318 | => \c template1 postgres
319 | You are now connected to database "template1" as user "postgres".
320 |
321 | => SHOW work_mem;
322 | work_mem
323 | ----------
324 | 6MB
325 | (1 row)
326 |
327 |
328 | -----------------------------------------------------------------------
329 |
330 | => CREATE ROLE test_role LOGIN;
331 | CREATE ROLE
332 |
333 | => \c postgres test_role
334 | You are now connected to database "postgres" as user "test_role".
335 |
336 | => SHOW work_mem;
337 | work_mem
338 | ----------
339 | 16MB
340 | (1 row)
341 |
342 |
343 | -----------------------------------------------------------------------
344 |
345 | Подключимся под postgres
346 |
347 | => \c postgres postgres
348 | You are now connected to database "postgres" as user "postgres".
349 |
350 | => drop role test_role;
351 | DROP ROLE
352 |
353 | Удаление настроек для БД и ролей
354 |
355 | => ALTER DATABASE postgres RESET work_mem;
356 | ALTER DATABASE
357 |
358 | => SELECT r.rolname, d.datname, rs.setconfig
359 | => FROM pg_db_role_setting rs
360 | => LEFT JOIN pg_roles r ON r.oid = rs.setrole
361 | => LEFT JOIN pg_database d ON d.oid = rs.setdatabase;
362 | rolname | datname | setconfig
363 | ----------+-----------+-----------------
364 | postgres | | {work_mem=32MB}
365 | postgres | template1 | {work_mem=6MB}
366 | (2 rows)
367 |
368 |
369 | -----------------------------------------------------------------------
370 |
371 | => ALTER ROLE postgres RESET ALL;
372 | ALTER ROLE
373 |
374 | => ALTER ROLE postgres IN DATABASE template1 RESET ALL;
375 | ALTER ROLE
376 |
377 | => SELECT r.rolname, d.datname, rs.setconfig
378 | => FROM pg_db_role_setting rs
379 | => LEFT JOIN pg_roles r ON r.oid = rs.setrole
380 | => LEFT JOIN pg_database d ON d.oid = rs.setdatabase;
381 | rolname | datname | setconfig
382 | ---------+---------+-----------
383 | (0 rows)
384 |
385 |
386 | => \c postgres postgres
387 | You are now connected to database "postgres" as user "postgres".
388 |
389 | -----------------------------------------------------------------------
390 |
391 | ** Установка параметров для функций
392 |
393 | -----------------------------------------------------------------------
394 |
395 | => CREATE FUNCTION f_test () RETURNS int
396 | => AS $$
397 | => DECLARE
398 | => l_work_mem text;
399 | => BEGIN
400 | => RAISE NOTICE 'work_mem: %', current_setting('work_mem');
401 | => RETURN 0;
402 | => END;
403 | => $$ LANGUAGE plpgsql;
404 | CREATE FUNCTION
405 |
406 | => ALTER FUNCTION f_test() SET work_mem TO '16MB';
407 | ALTER FUNCTION
408 |
409 | -----------------------------------------------------------------------
410 |
411 | => RESET work_mem;
412 | RESET
413 |
414 | => SHOW work_mem;
415 | work_mem
416 | ----------
417 | 4MB
418 | (1 row)
419 |
420 |
421 | => select f_test();
422 | NOTICE: work_mem: 16MB
423 | f_test
424 | --------
425 | 0
426 | (1 row)
427 |
428 |
429 | => SHOW work_mem;
430 | work_mem
431 | ----------
432 | 4MB
433 | (1 row)
434 |
435 |
436 | => drop function f_test();
437 | DROP FUNCTION
438 |
439 | -----------------------------------------------------------------------
440 |
441 | Конец демонстрации.
442 |
443 | -----------------------------------------------------------------------
444 |
445 | => \q
446 |
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_13_authentication.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_13_authentication.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_14_monitoring.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_14_monitoring.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_14_monitoring.txt:
--------------------------------------------------------------------------------
1 |
2 | СЕРВЕРНЫЕ ПРОЦЕССЫ
3 | ~~~~~~~~~~~~~~~~~~
4 |
5 | Включим сбор статистики ввода-вывода и выполнения функций.
6 |
7 | => alter system set track_io_timing=on;
8 | ALTER SYSTEM
9 |
10 | => alter system set track_functions='all';
11 | ALTER SYSTEM
12 |
13 | pg_ctl reload
14 | server signaled
15 |
16 | -----------------------------------------------------------------------
17 |
18 | Рассмотрим несколько представлений, позволяющих посмотреть собранную статистику.
19 | Начнем с информации о текущем состоянии системы.
20 | Запустим два сеанса, один из которых изменяет таблицу, а второй пытается изменить и блокируется.
21 |
22 | => create table t(n numeric);
23 | CREATE TABLE
24 |
25 | => insert into t values(42);
26 | INSERT 0 1
27 |
28 | | => begin;
29 | | BEGIN
30 |
31 | | => update t set n=n+1;
32 | | UPDATE 1
33 |
34 | || => update t set n=n+2;
35 |
36 | -----------------------------------------------------------------------
37 |
38 | Посмотрим информацию о серверных процессах.
39 |
40 | => \x
41 | Expanded display is on.
42 |
43 | => select pid, waiting, state, backend_xid, query from pg_stat_activity;
44 | -[ RECORD 1 ]----------------------------------------------------------------------
45 | pid | 32720
46 | waiting | f
47 | state | active
48 | backend_xid |
49 | query | select pid, waiting, state, backend_xid, query from pg_stat_activity;
50 | -[ RECORD 2 ]----------------------------------------------------------------------
51 | pid | 32727
52 | waiting | t
53 | state | active
54 | backend_xid | 846
55 | query | update t set n=n+2;
56 | -[ RECORD 3 ]----------------------------------------------------------------------
57 | pid | 32729
58 | waiting | f
59 | state | idle in transaction
60 | backend_xid | 845
61 | query | update t set n=n+1;
62 |
63 |
64 | -----------------------------------------------------------------------
65 |
66 | Можно сравнить с тем, что показывает операционная система:
67 |
68 | ps -o pid,command --ppid `head -n 1 $PGDATA/postmaster.pid`
69 | PID COMMAND
70 | 32654 postgres: checkpointer process
71 | 32655 postgres: writer process
72 | 32656 postgres: wal writer process
73 | 32657 postgres: autovacuum launcher process
74 | 32658 postgres: stats collector process
75 | 32720 postgres: postgres postgres [local] idle
76 | 32727 postgres: postgres postgres [local] UPDATE waiting
77 | 32729 postgres: postgres postgres [local] idle in transaction
78 |
79 | -----------------------------------------------------------------------
80 |
81 | Как можно использовать эту информацию, чтобы "убить" блокирующий сеанс?
82 | Посмотрим на блокировки.
83 |
84 | => select pid as blocked_pid from pg_stat_activity where waiting \gset
85 |
86 | => select locktype, transactionid, pid, mode, granted
87 | -> from pg_locks
88 | -> where pid=:blocked_pid and not granted;
89 | -[ RECORD 1 ]-+--------------
90 | locktype | transactionid
91 | transactionid | 845
92 | pid | 32727
93 | mode | ShareLock
94 | granted | f
95 |
96 |
97 | -----------------------------------------------------------------------
98 |
99 | Теперь мы можем вычислить блокирующий процесс и вызвать функцию для его завершения.
100 | Вот блокировка, которая нам мешает:
101 |
102 | => select transactionid as blocking_xid from pg_locks where pid=:blocked_pid and not granted \gset
103 |
104 | => select locktype, transactionid, pid, mode, granted
105 | -> from pg_locks
106 | -> where transactionid=:blocking_xid and granted;
107 | -[ RECORD 1 ]-+--------------
108 | locktype | transactionid
109 | transactionid | 845
110 | pid | 32729
111 | mode | ExclusiveLock
112 | granted | t
113 |
114 |
115 | -----------------------------------------------------------------------
116 |
117 | => select pid as blocking_pid from pg_locks where transactionid=:blocking_xid and granted \gset
118 |
119 | => select pg_terminate_backend(:blocking_pid);
120 | -[ RECORD 1 ]--------+--
121 | pg_terminate_backend | t
122 |
123 |
124 | -----------------------------------------------------------------------
125 |
126 | Проверим состояние серверных процессов.
127 |
128 | => select pid, waiting, state, backend_xid, query from pg_stat_activity;
129 | -[ RECORD 1 ]----------------------------------------------------------------------
130 | pid | 32720
131 | waiting | f
132 | state | active
133 | backend_xid |
134 | query | select pid, waiting, state, backend_xid, query from pg_stat_activity;
135 | -[ RECORD 2 ]----------------------------------------------------------------------
136 | pid | 32727
137 | waiting | f
138 | state | idle
139 | backend_xid |
140 | query | update t set n=n+2;
141 |
142 |
143 | Осталось только два, причем заблокированный успешно завершил транзакцию.
144 |
145 | -----------------------------------------------------------------------
146 |
147 | ФОНОВЫЕ ПРОЦЕССЫ
148 | ~~~~~~~~~~~~~~~~
149 |
150 | Можно посмотреть статистику по процессам background_writer и checkpointer:
151 |
152 | => select * from pg_stat_bgwriter;
153 | -[ RECORD 1 ]---------+------------------------------
154 | checkpoints_timed | 0
155 | checkpoints_req | 21
156 | checkpoint_write_time | 11032
157 | checkpoint_sync_time | 1093
158 | buffers_checkpoint | 21884
159 | buffers_clean | 0
160 | maxwritten_clean | 0
161 | buffers_backend | 13165
162 | buffers_backend_fsync | 0
163 | buffers_alloc | 14821
164 | stats_reset | 2015-12-09 19:11:06.557991+03
165 |
166 |
167 | * buffers_clean - количество страниц, записанных bgwriter
168 | * buffers_checkpoint - количество страниц, записанных checkpointer
169 | * buffers_backend - количество страниц, записанных серверными процессами
170 |
171 | -----------------------------------------------------------------------
172 |
173 | СТАТИСТИКА ОБРАЩЕНИЙ К ОБЪЕКТАМ БД
174 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
175 |
176 | Можно посмотреть статистику обращения к таблицам:
177 |
178 | => select * from pg_stat_all_tables where relid='t'::regclass;
179 | -[ RECORD 1 ]-------+-------
180 | relid | 16585
181 | schemaname | public
182 | relname | t
183 | seq_scan | 2
184 | seq_tup_read | 2
185 | idx_scan |
186 | idx_tup_fetch |
187 | n_tup_ins | 1
188 | n_tup_upd | 2
189 | n_tup_del | 0
190 | n_tup_hot_upd | 2
191 | n_live_tup | 1
192 | n_dead_tup | 2
193 | n_mod_since_analyze | 2
194 | last_vacuum |
195 | last_autovacuum |
196 | last_analyze |
197 | last_autoanalyze |
198 | vacuum_count | 0
199 | autovacuum_count | 0
200 | analyze_count | 0
201 | autoanalyze_count | 0
202 |
203 |
204 | -----------------------------------------------------------------------
205 |
206 | Существуют вариации:
207 | * pg_stat_user_tables - только таблицы пользователя
208 | * pg_stat_sys_tables - только системные таблицы
209 |
210 | И аналогичные варианты для индексов:
211 | * pg_stat_all_indexes - все индексы
212 | * pg_stat_user_indexes - только индексы пользователя
213 | * pg_stat_sys_indexes - только системные индексы
214 |
215 | -----------------------------------------------------------------------
216 |
217 | Также можно посмотреть статистику текущей транзакции:
218 |
219 | => begin;
220 | BEGIN
221 |
222 | => insert into t values (1), (2), (3);
223 | INSERT 0 3
224 |
225 | => update t set n=10 where n=1;
226 | UPDATE 1
227 |
228 | => delete from t where n < 4;
229 | DELETE 2
230 |
231 | => select * from pg_stat_xact_all_tables where relid='t'::regclass;
232 | -[ RECORD 1 ]-+-------
233 | relid | 16585
234 | schemaname | public
235 | relname | t
236 | seq_scan | 2
237 | seq_tup_read | 8
238 | idx_scan |
239 | idx_tup_fetch |
240 | n_tup_ins | 3
241 | n_tup_upd | 1
242 | n_tup_del | 2
243 | n_tup_hot_upd | 1
244 |
245 |
246 | => commit;
247 | COMMIT
248 |
249 | -----------------------------------------------------------------------
250 |
251 | Можно посмотреть статистику по всей базе данных:
252 |
253 | => select * from pg_stat_database where datname='postgres';
254 | -[ RECORD 1 ]--+------------------------------
255 | datid | 12185
256 | datname | postgres
257 | numbackends | 2
258 | xact_commit | 291
259 | xact_rollback | 30
260 | blks_read | 13360
261 | blks_hit | 2159623
262 | tup_returned | 11051230
263 | tup_fetched | 34450
264 | tup_inserted | 2001539
265 | tup_updated | 54
266 | tup_deleted | 152
267 | conflicts | 0
268 | temp_files | 2
269 | temp_bytes | 28000000
270 | deadlocks | 0
271 | blk_read_time | 0.878
272 | blk_write_time | 0
273 | stats_reset | 2015-12-09 19:11:26.516172+03
274 |
275 |
276 | -----------------------------------------------------------------------
277 |
278 | Наконец, можно посмотреть статистику в терминах ввода-вывода.
279 |
280 | => select * from pg_statio_all_tables where relid='t'::regclass;
281 | -[ RECORD 1 ]---+-------
282 | relid | 16585
283 | schemaname | public
284 | relname | t
285 | heap_blks_read | 1
286 | heap_blks_hit | 4
287 | idx_blks_read |
288 | idx_blks_hit |
289 | toast_blks_read | 0
290 | toast_blks_hit | 0
291 | tidx_blks_read | 0
292 | tidx_blks_hit | 0
293 |
294 |
295 | -----------------------------------------------------------------------
296 |
297 | Существуют вариации:
298 | * pg_statio_user_tables - только таблицы пользователя
299 | * pg_statio_sys_tables - только системные таблицы
300 |
301 | Аналогичные варианты для индексов:
302 | * pg_statio_all_indexes - все индексы
303 | * pg_statio_user_indexes - только индексы пользователя
304 | * pg_statio_sys_indexes - только системные индексы
305 |
306 | А также для последовательностей:
307 | * pg_statio_all_sequences - все последовательности
308 | * pg_statio_user_sequences - только последовательности пользователя
309 | * pg_statio_sys_sequences - только системные последовательности (которых нет)
310 |
311 | -----------------------------------------------------------------------
312 |
313 | При желании статистику можно сбросить:
314 |
315 | => select pg_stat_reset();
316 | -[ RECORD 1 ]-+-
317 | pg_stat_reset |
318 |
319 |
320 | Чтобы увидеть изменения, надо дождаться, пока коллектор запишет новый снимок...
321 |
322 | sleep 5
323 |
324 | => select * from pg_stat_database where datname='postgres';
325 | -[ RECORD 1 ]--+------------------------------
326 | datid | 12185
327 | datname | postgres
328 | numbackends | 2
329 | xact_commit | 0
330 | xact_rollback | 0
331 | blks_read | 0
332 | blks_hit | 0
333 | tup_returned | 0
334 | tup_fetched | 0
335 | tup_inserted | 0
336 | tup_updated | 0
337 | tup_deleted | 0
338 | conflicts | 0
339 | temp_files | 0
340 | temp_bytes | 0
341 | deadlocks | 0
342 | blk_read_time | 0
343 | blk_write_time | 0
344 | stats_reset | 2015-12-09 19:11:52.813824+03
345 |
346 |
347 | Можно сбросить статистику и по отдельным объектам (\df pg_stat_reset*).
348 |
349 | -----------------------------------------------------------------------
350 |
351 | СТАТИСТИКА ВЫЗОВА ФУНКЦИЙ
352 | ~~~~~~~~~~~~~~~~~~~~~~~~~
353 |
354 | Создадим функцию:
355 |
356 | => create function f() returns integer as $$ select 1; $$ language sql;
357 | CREATE FUNCTION
358 |
359 | => begin;
360 | BEGIN
361 |
362 | => select sum(f()) from generate_series(1,100);
363 | -[ RECORD 1 ]
364 | sum | 100
365 |
366 |
367 | => select * from pg_stat_xact_user_functions;
368 | (No rows)
369 |
370 | => commit;
371 | COMMIT
372 |
373 | Почему нет статистики?
374 |
375 | -----------------------------------------------------------------------
376 |
377 | Дело в том, что простые функции подставляются непосредственно в запрос.
378 | Собственно функция в данном случае не вызывается.
379 |
380 | -----------------------------------------------------------------------
381 |
382 | Создадим более сложную функцию.
383 |
384 | => create function f1() returns integer as $$ begin return 1; end; $$ language plpgsql;
385 | CREATE FUNCTION
386 |
387 | => begin;
388 | BEGIN
389 |
390 | => select sum(f1()) from generate_series(1,100);
391 | -[ RECORD 1 ]
392 | sum | 100
393 |
394 |
395 | => select * from pg_stat_xact_user_functions;
396 | -[ RECORD 1 ]------
397 | funcid | 16592
398 | schemaname | public
399 | funcname | f1
400 | calls | 100
401 | total_time | 0.218
402 | self_time | 0.218
403 |
404 |
405 | => commit;
406 | COMMIT
407 |
408 | -----------------------------------------------------------------------
409 |
410 | АНАЛИЗ ЖУРНАЛА
411 | ~~~~~~~~~~~~~~
412 |
413 | Посмотрим самый простой случай. Например, нас интересуют сообщения FATAL:
414 |
415 | grep FATAL ~postgres/logfile | tail -n 10
416 |
417 | grep FATAL /home/postgres/logfile
418 | FATAL: terminating connection due to administrator command
419 |
420 | Сообщение terminating connection вызвано тем, что мы завершали блокирующий процесс.
421 |
422 | -----------------------------------------------------------------------
423 |
424 | Немного изменим формат выдачи журнала.
425 |
426 | => alter system set log_line_prefix='!!! pid=%p: ';
427 | ALTER SYSTEM
428 |
429 | => alter system set log_duration='on';
430 | ALTER SYSTEM
431 |
432 | => alter system set log_statement='all';
433 | ALTER SYSTEM
434 |
435 | pg_ctl reload
436 | server signaled
437 |
438 | -----------------------------------------------------------------------
439 |
440 | Теперь выполним какую-нибудь команду:
441 |
442 | => select sum(random()) from generate_series(1,1000000);
443 | -[ RECORD 1 ]---------
444 | sum | 499937.818962918
445 |
446 |
447 | -----------------------------------------------------------------------
448 |
449 | И посмотрим журнал:
450 |
451 | egrep ^!!! ~postgres/logfile | tail -n 2
452 | !!! pid=32720: LOG: statement: select sum(random()) from generate_series(1,1000000);
453 | !!! pid=32720: LOG: duration: 218.037 ms
454 |
455 | -----------------------------------------------------------------------
456 |
457 | Конец демонстрации.
458 |
459 | -----------------------------------------------------------------------
460 |
461 | => alter system set track_io_timing=default;
462 | ALTER SYSTEM
463 |
464 | => alter system set track_functions=default;
465 | ALTER SYSTEM
466 |
467 | => alter system set log_line_prefix=default;
468 | ALTER SYSTEM
469 |
470 | => alter system set log_duration=default;
471 | ALTER SYSTEM
472 |
473 | => alter system set log_statement=default;
474 | ALTER SYSTEM
475 |
476 | pg_ctl reload
477 | server signaled
478 |
479 | => \q
480 | | => \q
481 | || => \q
482 |
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_15_maintenance.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_15_maintenance.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_16_logical_backup.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_16_logical_backup.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_16_logical_backup.txt:
--------------------------------------------------------------------------------
1 |
2 | COPY
3 | ~~~~
4 |
5 | Создадим базу данных и таблицу в ней.
6 |
7 | => create database db;
8 | CREATE DATABASE
9 |
10 | => \c db
11 | You are now connected to database "db" as user "postgres".
12 |
13 | => create table t(id numeric, s text);
14 | CREATE TABLE
15 |
16 | => insert into t values (1, 'Hi there!');
17 | INSERT 0 1
18 |
19 | => insert into t values (2, '');
20 | INSERT 0 1
21 |
22 | => insert into t values (3, null);
23 | INSERT 0 1
24 |
25 | -----------------------------------------------------------------------
26 |
27 | => select * from t;
28 | id | s
29 | ----+-----------
30 | 1 | Hi there!
31 | 2 |
32 | 3 |
33 | (3 rows)
34 |
35 |
36 | -----------------------------------------------------------------------
37 |
38 | Вот как выглядит таблица в выводе команды COPY:
39 |
40 | => copy t to stdout;
41 | 1 Hi there!
42 | 2
43 | 3 \N
44 |
45 | Обратим внимание на то, что пустая строка и null - разные значения,
46 | хотя выполняя select этого и не заметно.
47 |
48 | -----------------------------------------------------------------------
49 |
50 | Аналогично можно вводить данные:
51 |
52 | => truncate table t;
53 | TRUNCATE TABLE
54 |
55 | => copy t from stdin;
56 | => 1 Hi there!
57 | => 2
58 | => 3 \N
59 | => \.
60 | COPY 3
61 |
62 | -----------------------------------------------------------------------
63 |
64 | Проверим:
65 |
66 | => select * from t;
67 | id | s
68 | ----+-----------
69 | 1 | Hi there!
70 | 2 |
71 | 3 |
72 | (3 rows)
73 |
74 |
75 | => copy t to stdout;
76 | 1 Hi there!
77 | 2
78 | 3 \N
79 |
80 | -----------------------------------------------------------------------
81 |
82 | PG_DUMP
83 | ~~~~~~~
84 |
85 | Посмотрим на результат работы pg_dump в формате plain.
86 | Обратим внимание на команды установки расширения pg_crypto
87 | (оно было установлено в template1), а также на то,
88 | в каком виде сохранены данные из таблицы.
89 |
90 | -----------------------------------------------------------------------
91 |
92 | pg_dump -d db
93 | --
94 | -- PostgreSQL database dump
95 | --
96 |
97 | SET statement_timeout = 0;
98 | SET lock_timeout = 0;
99 | SET client_encoding = 'UTF8';
100 | SET standard_conforming_strings = on;
101 | SET check_function_bodies = false;
102 | SET client_min_messages = warning;
103 |
104 | --
105 | -- Name: plpgsql; Type: EXTENSION; Schema: -; Owner:
106 | --
107 |
108 | CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
109 |
110 |
111 | --
112 | -- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner:
113 | --
114 |
115 | COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
116 |
117 |
118 | --
119 | -- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner:
120 | --
121 |
122 | CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public;
123 |
124 |
125 | --
126 | -- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner:
127 | --
128 |
129 | COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions';
130 |
131 |
132 | SET search_path = public, pg_catalog;
133 |
134 | SET default_tablespace = '';
135 |
136 | SET default_with_oids = false;
137 |
138 | --
139 | -- Name: t; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
140 | --
141 |
142 | CREATE TABLE t (
143 | id numeric,
144 | s text
145 | );
146 |
147 |
148 | ALTER TABLE t OWNER TO postgres;
149 |
150 | --
151 | -- Data for Name: t; Type: TABLE DATA; Schema: public; Owner: postgres
152 | --
153 |
154 | COPY t (id, s) FROM stdin;
155 | 1 Hi there!
156 | 2
157 | 3 \N
158 | \.
159 |
160 |
161 | --
162 | -- Name: public; Type: ACL; Schema: -; Owner: postgres
163 | --
164 |
165 | REVOKE ALL ON SCHEMA public FROM PUBLIC;
166 | REVOKE ALL ON SCHEMA public FROM postgres;
167 | GRANT ALL ON SCHEMA public TO postgres;
168 | GRANT ALL ON SCHEMA public TO PUBLIC;
169 |
170 |
171 | --
172 | -- PostgreSQL database dump complete
173 | --
174 |
175 |
176 | -----------------------------------------------------------------------
177 |
178 | Посмотрим на то, как выглядит оглавление резервной копии.
179 | Для этого требуется указать формат, отличный от plain.
180 |
181 | -----------------------------------------------------------------------
182 |
183 | pg_dump --format custom -d db | pg_restore --list
184 | ;
185 | ; Archive created at Wed Dec 9 19:11:58 2015
186 | ; dbname: db
187 | ; TOC Entries: 12
188 | ; Compression: -1
189 | ; Dump Version: 1.12-0
190 | ; Format: CUSTOM
191 | ; Integer: 4 bytes
192 | ; Offset: 8 bytes
193 | ; Dumped from database version: 9.4.4
194 | ; Dumped by pg_dump version: 9.4.4
195 | ;
196 | ;
197 | ; Selected TOC Entries:
198 | ;
199 | 2077; 1262 16593 DATABASE - db postgres
200 | 5; 2615 2200 SCHEMA - public postgres
201 | 2078; 0 0 COMMENT - SCHEMA public postgres
202 | 2079; 0 0 ACL - public postgres
203 | 173; 3079 11905 EXTENSION - plpgsql
204 | 2080; 0 0 COMMENT - EXTENSION plpgsql
205 | 174; 3079 16384 EXTENSION - pgcrypto
206 | 2081; 0 0 COMMENT - EXTENSION pgcrypto
207 | 172; 1259 16594 TABLE public t postgres
208 | 2072; 0 16594 TABLE DATA public t postgres
209 |
210 | -----------------------------------------------------------------------
211 |
212 | Можно попросить pg_restore сгенерировать команды для восстановления, например,
213 | только определения таблицы t:
214 |
215 | -----------------------------------------------------------------------
216 |
217 | pg_dump --format custom -d db | pg_restore --table=t --schema-only
218 | --
219 | -- PostgreSQL database dump
220 | --
221 |
222 | SET statement_timeout = 0;
223 | SET lock_timeout = 0;
224 | SET client_encoding = 'UTF8';
225 | SET standard_conforming_strings = on;
226 | SET check_function_bodies = false;
227 | SET client_min_messages = warning;
228 |
229 | SET search_path = public, pg_catalog;
230 |
231 | SET default_tablespace = '';
232 |
233 | SET default_with_oids = false;
234 |
235 | --
236 | -- Name: t; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
237 | --
238 |
239 | CREATE TABLE t (
240 | id numeric,
241 | s text
242 | );
243 |
244 |
245 | ALTER TABLE t OWNER TO postgres;
246 |
247 | --
248 | -- PostgreSQL database dump complete
249 | --
250 |
251 | Конец демонстрации.
252 |
253 | -----------------------------------------------------------------------
254 |
255 | => \q
256 |
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_17_physical_backup.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_17_physical_backup.pdf
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_17_physical_backup.txt:
--------------------------------------------------------------------------------
1 |
2 | НАСТРОЙКА НЕПРЕРЫВНОГО АРХИВИРОВАНИЯ
3 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 |
5 | Внесем необходимые изменения в файл postgresql.conf.
6 | В данном случае мы просто скопируем заранее подготовленный файл...
7 |
8 | cp -n /usr/local/pgsql/data/postgresql.conf /home/postgres/postgresql.conf.old
9 | cp dba1_17_postgresql.conf /usr/local/pgsql/data/postgresql.conf
10 |
11 | -----------------------------------------------------------------------
12 |
13 | Вот какие параметры необходимо установить:
14 | * wal_level не ниже archive,
15 | * archive_mode = on,
16 | * archive_command - команда копирования.
17 |
18 | Проверим:
19 |
20 | egrep wal_level|archive_mode|archive_command dba1_17_postgresql.conf
21 | wal_level = archive
22 | archive_mode = on
23 | archive_command = 'test ! -f /home/postgres/archivedir/%f && cp %p /home/postgres/archivedir/%f'
24 |
25 | -----------------------------------------------------------------------
26 |
27 | Требуется разрешить подключение к серверу по протоколу репликации.
28 | Сделаем это для локальных подключений:
29 |
30 | sed -i s/#\(local\s\+replication\)/\1/ /usr/local/pgsql/data/pg_hba.conf
31 | tail -n 5 /usr/local/pgsql/data/pg_hba.conf
32 | # Allow replication connections from localhost, by a user with the
33 | # replication privilege.
34 | local replication postgres trust
35 | #host replication postgres 127.0.0.1/32 trust
36 | #host replication postgres ::1/128 trust
37 |
38 | -----------------------------------------------------------------------
39 |
40 | Очистим каталог /home/postgres/archivedir, куда будут сохраняться сегменты WAL.
41 |
42 | rm -rf /home/postgres/archivedir
43 | mkdir /home/postgres/archivedir
44 | ls -l /home/postgres/archivedir
45 | total 0
46 |
47 | -----------------------------------------------------------------------
48 |
49 | Перезапустим сервер, чтобы изменения вступили в силу.
50 |
51 | pg_ctl restart -w -m fast -l /home/postgres/logfile
52 | waiting for server to shut down.... done
53 | server stopped
54 | waiting for server to start.... done
55 | server started
56 |
57 | -----------------------------------------------------------------------
58 |
59 | Убедимся, что при переходе на новый сегмент WAL старый будет попадать в архив.
60 | Подключимся к серверу и сделаем какие-нибудь изменения, например, создадим таблицу.
61 |
62 | psql
63 |
64 | => create table backup_example(t text);
65 | CREATE TABLE
66 |
67 | -----------------------------------------------------------------------
68 |
69 | Поскольку для переключения на новый сегмент WAL надо сделать много изменений
70 | (либо задать параметр archive_timeout, что мы не сделали),
71 | то просто переключимся на новый сегмент вручную.
72 |
73 | Вот какой сегмент используется сейчас:
74 |
75 | => select pg_xlogfile_name(pg_current_xlog_location());
76 | pg_xlogfile_name
77 | --------------------------
78 | 000000010000000000000011
79 | (1 row)
80 |
81 |
82 | -----------------------------------------------------------------------
83 |
84 | А вот какой будет после переключения:
85 |
86 | => select pg_switch_xlog();
87 | pg_switch_xlog
88 | ----------------
89 | 0/11177D60
90 | (1 row)
91 |
92 |
93 | => insert into backup_example values ('После переключения сегмента');
94 | INSERT 0 1
95 |
96 | => select pg_xlogfile_name(pg_current_xlog_location());
97 | pg_xlogfile_name
98 | --------------------------
99 | 000000010000000000000012
100 | (1 row)
101 |
102 |
103 | -----------------------------------------------------------------------
104 |
105 | Проверим содержимое каталога-архива:
106 |
107 | ls -l /home/postgres/archivedir
108 | total 7872
109 | -rw------- 1 postgres postgres 8060928 дек. 9 19:12 000000010000000000000011
110 |
111 | Все в порядке, архивирование работает.
112 | Обратите внимание: первые восемь цифр в номере сегмента - это номер текущей ветви времени.
113 |
114 | -----------------------------------------------------------------------
115 |
116 | СОЗДАНИЕ БАЗОВОЙ РЕЗЕРВНОЙ КОПИИ
117 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
118 |
119 | Теперь самое время создать базовую резервную копию.
120 | Убедимся, что в кластере нет пользовательских табличных пространств, так как
121 | их наличие усложнило бы процесс резервирования и восстановления.
122 |
123 | => \db
124 | List of tablespaces
125 | Name | Owner | Location
126 | ------------+----------+----------
127 | pg_default | postgres |
128 | pg_global | postgres |
129 | (2 rows)
130 |
131 |
132 | -----------------------------------------------------------------------
133 |
134 | Выполним команду pg_basebackup. Используем формат по умолчанию (plain),
135 | в качестве каталога для сохранения используем /home/postgres/basebackupdir.
136 |
137 | rm -rf /home/postgres/basebackupdir
138 | mkdir /home/postgres/basebackupdir
139 | pg_basebackup --pgdata=/home/postgres/basebackupdir "--label=Base backup test"
140 |
141 | -----------------------------------------------------------------------
142 |
143 | Проверим содержимое каталога:
144 |
145 | ls -l /home/postgres/basebackupdir
146 | total 112
147 | -rw------- 1 postgres postgres 199 дек. 9 19:12 backup_label
148 | drwx------ 7 postgres postgres 4096 дек. 9 19:12 base
149 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 global
150 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_clog
151 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_dynshmem
152 | -rw------- 1 postgres postgres 4467 дек. 9 19:12 pg_hba.conf
153 | -rw------- 1 postgres postgres 1636 дек. 9 19:12 pg_ident.conf
154 | drwx------ 4 postgres postgres 4096 дек. 9 19:12 pg_logical
155 | drwx------ 4 postgres postgres 4096 дек. 9 19:12 pg_multixact
156 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_notify
157 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_replslot
158 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_serial
159 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_snapshots
160 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_stat
161 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_stat_tmp
162 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_subtrans
163 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_tblspc
164 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_twophase
165 | -rw------- 1 postgres postgres 4 дек. 9 19:12 PG_VERSION
166 | drwx------ 3 postgres postgres 4096 дек. 9 19:12 pg_xlog
167 | -rw------- 1 postgres postgres 84 дек. 9 19:12 postgresql.auto.conf
168 | -rw------- 1 postgres postgres 21343 дек. 9 19:12 postgresql.conf
169 |
170 | -----------------------------------------------------------------------
171 |
172 | Заглянем в файл метки:
173 |
174 | cat /home/postgres/basebackupdir/backup_label
175 | START WAL LOCATION: 0/13000028 (file 000000010000000000000013)
176 | CHECKPOINT LOCATION: 0/13000028
177 | BACKUP METHOD: streamed
178 | BACKUP FROM: master
179 | START TIME: 2015-12-09 19:12:03 MSK
180 | LABEL: Base backup test
181 |
182 | -----------------------------------------------------------------------
183 |
184 | Посмотрим в каталог pg_xlog:
185 |
186 | ls -l /home/postgres/basebackupdir/pg_xlog
187 | total 4
188 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 archive_status
189 |
190 | Тут пусто, что и следовало ожидать.
191 |
192 | -----------------------------------------------------------------------
193 |
194 | ВОССТАНОВЛЕНИЕ
195 | ~~~~~~~~~~~~~~
196 |
197 | Сделаем еще несколько изменений в таблице.
198 |
199 | => insert into backup_example values ('Еще изменение');
200 | INSERT 0 1
201 |
202 | => select pg_switch_xlog();
203 | pg_switch_xlog
204 | ----------------
205 | 0/14000170
206 | (1 row)
207 |
208 |
209 | => insert into backup_example values ('Прямо перед катастрофой');
210 | INSERT 0 1
211 |
212 | -----------------------------------------------------------------------
213 |
214 | Будем считать, что в этот момент сервер сломался.
215 |
216 | pg_ctl stop -m fast -w
217 | waiting for server to shut down.... done
218 | server stopped
219 |
220 | -----------------------------------------------------------------------
221 |
222 | Скопируем pg_xlog на случай, если там остались неперенесенные в архив сегменты.
223 |
224 | rm -rf /home/postgres/backup_xlog
225 | mkdir /home/postgres/backup_xlog
226 |
227 | cp -r $PGDATA/pg_xlog/* /home/postgres/backup_xlog
228 |
229 | -----------------------------------------------------------------------
230 |
231 | После этого все содержимое PGDATA можно удалять.
232 | (На продуктивной среде имеет смысл скопировать содержимое каталога до того момента,
233 | как восстановление будет успешно завершено и проверено.)
234 |
235 | rm -rf $PGDATA/*
236 |
237 | ls -l /usr/local/pgsql/data
238 | total 0
239 |
240 | -----------------------------------------------------------------------
241 |
242 | Восстанавливаемся из базовой резервной копии:
243 | cp -r /home/postgres/basebackupdir/* $PGDATA
244 |
245 | ls -l /usr/local/pgsql/data
246 | total 112
247 | -rw------- 1 postgres postgres 199 дек. 9 19:12 backup_label
248 | drwx------ 7 postgres postgres 4096 дек. 9 19:12 base
249 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 global
250 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_clog
251 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_dynshmem
252 | -rw------- 1 postgres postgres 4467 дек. 9 19:12 pg_hba.conf
253 | -rw------- 1 postgres postgres 1636 дек. 9 19:12 pg_ident.conf
254 | drwx------ 4 postgres postgres 4096 дек. 9 19:12 pg_logical
255 | drwx------ 4 postgres postgres 4096 дек. 9 19:12 pg_multixact
256 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_notify
257 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_replslot
258 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_serial
259 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_snapshots
260 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_stat
261 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_stat_tmp
262 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_subtrans
263 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_tblspc
264 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 pg_twophase
265 | -rw------- 1 postgres postgres 4 дек. 9 19:12 PG_VERSION
266 | drwx------ 3 postgres postgres 4096 дек. 9 19:12 pg_xlog
267 | -rw------- 1 postgres postgres 84 дек. 9 19:12 postgresql.auto.conf
268 | -rw------- 1 postgres postgres 21343 дек. 9 19:12 postgresql.conf
269 |
270 | -----------------------------------------------------------------------
271 |
272 | И копируем на место сохраненный pg_xlog:
273 | cp -r /home/postgres/backup_xlog/* $PGDATA/pg_xlog
274 |
275 | ls -l /usr/local/pgsql/data/pg_xlog
276 | total 131080
277 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000013
278 | -rw------- 1 postgres postgres 296 дек. 9 19:12 000000010000000000000013.00000028.backup
279 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000014
280 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000015
281 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000016
282 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000017
283 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000018
284 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000019
285 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 00000001000000000000001A
286 | drwx------ 2 postgres postgres 4096 дек. 9 19:12 archive_status
287 |
288 | -----------------------------------------------------------------------
289 |
290 | Теперь надо создать файл recovery.conf.
291 | Можно было бы воспользоваться шаблоном:
292 |
293 | ls -l /usr/local/pgsql/share/recovery.conf.sample
294 | -rw-r--r-- 1 root root 5587 окт. 22 19:28 /usr/local/pgsql/share/recovery.conf.sample
295 |
296 | -----------------------------------------------------------------------
297 |
298 | Но в нашем случае файл будет состоять из одного параметра,
299 | Поэтому просто создадим его.
300 |
301 | cat < $PGDATA/recovery.conf
302 | restore_command = 'cp /home/postgres/archivedir/%f %p'
303 | EOF
304 |
305 | cat /usr/local/pgsql/data/recovery.conf
306 | restore_command = 'cp /home/postgres/archivedir/%f %p'
307 |
308 | -----------------------------------------------------------------------
309 |
310 | Можно стартовать сервер.
311 |
312 | pg_ctl start -w -l /home/postgres/logfile
313 | waiting for server to start..... done
314 | server started
315 |
316 | Восстановление завершено!
317 |
318 | -----------------------------------------------------------------------
319 |
320 | Проверим это. Подключимся к серверу:
321 |
322 | => \q
323 |
324 | psql
325 |
326 | И проверим содержимое таблицы.
327 |
328 | => select * from backup_example;
329 | t
330 | -----------------------------
331 | После переключения сегмента
332 | Еще изменение
333 | Прямо перед катастрофой
334 | (3 rows)
335 |
336 |
337 | Как видим, все последние изменения успешно восстановлены.
338 |
339 | -----------------------------------------------------------------------
340 |
341 | Что стало с ветвью времени после восстановления?
342 |
343 | => select pg_xlogfile_name(pg_current_xlog_location());
344 | pg_xlogfile_name
345 | --------------------------
346 | 000000020000000000000016
347 | (1 row)
348 |
349 |
350 | Как видим, номер увеличился на единицу.
351 |
352 | -----------------------------------------------------------------------
353 |
354 | В каталоге pg_xlog появился файл, соответствующий этой ветви:
355 |
356 | => select left( pg_xlogfile_name(pg_current_xlog_location()) ,8) as timeline \gset
357 |
358 | => \setenv TIMELINE :timeline
359 |
360 | => \! ls -l $PGDATA/pg_xlog/$TIMELINE.history
361 | -rw------- 1 postgres postgres 42 дек. 9 19:12 /usr/local/pgsql/data/pg_xlog/00000002.history
362 |
363 | -----------------------------------------------------------------------
364 |
365 | Вот какая информация в нем есть:
366 |
367 | => \! cat $PGDATA/pg_xlog/$TIMELINE.history
368 | 1 0/16000090 no recovery target specified
369 |
370 | -----------------------------------------------------------------------
371 |
372 | Этот файл подлежит архивации вместе с сегментами WAL:
373 |
374 | ls -l /home/postgres/archivedir
375 | total 98312
376 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000011
377 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000012
378 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000013
379 | -rw------- 1 postgres postgres 296 дек. 9 19:12 000000010000000000000013.00000028.backup
380 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000014
381 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000015
382 | -rw------- 1 postgres postgres 16777216 дек. 9 19:12 000000010000000000000016
383 | -rw------- 1 postgres postgres 42 дек. 9 19:12 00000002.history
384 |
385 | -----------------------------------------------------------------------
386 |
387 | Конец демонстрации.
388 | Восстановим настройки.
389 |
390 | => \q
391 |
392 | cp /home/postgres/postgresql.conf.old /usr/local/pgsql/data/postgresql.conf
393 | pg_ctl restart -m fast -l /home/postgres/logfile
394 | waiting for server to shut down.... done
395 | server stopped
396 | server starting
397 |
398 | -----------------------------------------------------------------------
399 |
--------------------------------------------------------------------------------
/pg_dba1_guide/dba1_introduction.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_guide/dba1_introduction.pdf
--------------------------------------------------------------------------------
/pg_dba1_setup/dba1_instructor_guide.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgordeev/pgcourse/5fa963386d760e9bea413f232bc5be040ad83ef6/pg_dba1_setup/dba1_instructor_guide.pdf
--------------------------------------------------------------------------------
/pg_dba1_setup/dba1_setup_vm1.sh:
--------------------------------------------------------------------------------
1 | sudo apt-get install libreadline6 libreadline6-dev zlib1g zlib1g-dev docbook docbook-dsssl docbook-xsl openjade1.3 opensp xsltproc
2 | mkdir -p $HOME/DBA1/practice
3 | chmod 755 $HOME/DBA1
4 | chmod 777 $HOME/DBA1/practice
5 |
--------------------------------------------------------------------------------
/pg_dba1_setup/dba1_setup_vm2.sh:
--------------------------------------------------------------------------------
1 | wget https://ftp.postgresql.org/pub/source/v9.4.4/postgresql-9.4.4.tar.gz
2 | tar xzf postgresql-9.4.4.tar.gz
3 | cd postgresql-9.4.4/
4 | ./configure
5 | make world
6 | sudo make install
7 | cd contrib/oid2name
8 | sudo make install
9 | cd ../pgcrypto
10 | sudo make install
11 |
12 | sudo adduser postgres
13 | sudo mkdir /usr/local/pgsql/data
14 | sudo chown postgres /usr/local/pgsql/data
15 |
16 | sudo bash -c "echo 'export PATH=/usr/local/pgsql/bin:$PATH' >> /home/postgres/.profile"
17 | sudo bash -c "echo 'export PGDATA=/usr/local/pgsql/data' >> /home/postgres/.profile"
18 |
19 | sudo -i -u postgres pg_ctl initdb -o "-k"
20 |
--------------------------------------------------------------------------------