├── 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 | [![Join the chat at https://gitter.im/rgordeev/pgcourse](https://badges.gitter.im/Join%20Chat.svg)](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 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
schemanametablenametableowner
pg_catalogpg_statisticpostgres
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 | --------------------------------------------------------------------------------