<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://www.wikicshse.ru/index.php?action=history&amp;feed=atom&amp;title=%D0%91%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%2F%D0%9B%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_3</id>
	<title>Базы данных/Лабораторная работа 3 - История изменений</title>
	<link rel="self" type="application/atom+xml" href="https://www.wikicshse.ru/index.php?action=history&amp;feed=atom&amp;title=%D0%91%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%2F%D0%9B%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_3"/>
	<link rel="alternate" type="text/html" href="https://www.wikicshse.ru/index.php?title=%D0%91%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85/%D0%9B%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_3&amp;action=history"/>
	<updated>2026-06-06T22:23:49Z</updated>
	<subtitle>История изменений этой страницы в вики</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://www.wikicshse.ru/index.php?title=%D0%91%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85/%D0%9B%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_3&amp;diff=1982&amp;oldid=prev</id>
		<title>imported&gt;Ivsavin: Migrated current public revision from wiki.cs.hse.ru</title>
		<link rel="alternate" type="text/html" href="https://www.wikicshse.ru/index.php?title=%D0%91%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85/%D0%9B%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_3&amp;diff=1982&amp;oldid=prev"/>
		<updated>2016-05-30T01:33:21Z</updated>

		<summary type="html">&lt;p&gt;Migrated current public revision from wiki.cs.hse.ru&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая страница&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;br /&gt;
Цели лабораторной работы: &lt;br /&gt;
* научиться анализировать план выполнения запросов и уменьшать сложность выполнения запросов,&lt;br /&gt;
* научиться анализировать состояние сервера СУБД, обучиться основам администрирования.&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
&lt;br /&gt;
Ваша задача в этой лабораторной работе: &lt;br /&gt;
* С помощью EXPLAIN посчитать сложность выполнения комплексных запросов из прошлой лабораторной работы, &lt;br /&gt;
* Разобраться с выводом EXPLAIN и суметь объяснять, какие части подзапроса сколько будут выполняться, сколько данных будет задействовано.&lt;br /&gt;
* С помощью индексов ускорить выполнение запросов.&lt;br /&gt;
* Проанализировать выполнение запроса после создания индексов и сравнить планы выполнений.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Создание индексов ==&lt;br /&gt;
&lt;br /&gt;
Индексы нужны для того, чтобы ускорить доступ к данным. Для каждой таблицы доступен только один самый быстрый индекс - первичный ключ. Как правило, это id в каждой таблице. Запросы, в условиях которых фильтрация происходит только по id, работают быстро, таблица при этом не сканируется целиком. &lt;br /&gt;
&lt;br /&gt;
В таблицу также можно добавить еще индексы для ускорения выполнения запросов. Они могут относиться как к одному, так и к группе атрибутов. Индексы выбирают исходя из информационной потребности пользователей: если в запросах часто встречается какая-либо комбинация условий, то индекс строят именно на эти условия.&lt;br /&gt;
&lt;br /&gt;
Добавление индексов требует дополнительного места на диске, но важнее то, что любые манипуляции с данными становятся более затратными, так как индекс в этом случае должен обновиться. Из-за этого неразумно выстраивать все возможные индексы, следует ограничиться только наиболее востребованными.&lt;br /&gt;
&lt;br /&gt;
Чтобы создать индекс, нужно использовать команду CREATE INDEX, указать имя индекса и список атрибутов, по которым он будет строиться, опционально можно указать тип индекса.&lt;br /&gt;
&lt;br /&gt;
Важно: прежде чем создавать индексы, посмотрите, сколько места занимают ваши данные. Это можно сделать средствами утилиты du или ls -a.&lt;br /&gt;
&lt;br /&gt;
Выполните в psql: &lt;br /&gt;
&lt;br /&gt;
  show data_directory; &lt;br /&gt;
&lt;br /&gt;
чтобы узнать, где хранятся файлы вашей базы. После этого выполните:&lt;br /&gt;
&lt;br /&gt;
  SELECT oid from pg_database where datname = &amp;#039;imdb&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
чтобы узнать идентификатор базы.&lt;br /&gt;
&lt;br /&gt;
Директория с файлами вашей базы находится тут: {data_directory}/base/{oid} (у меня это: /var/lib/postgresql/9.3/main/base/16384)&lt;br /&gt;
&lt;br /&gt;
Выполните команду du для этого пути (при необходимости прочитайте мануал к команде du).&lt;br /&gt;
&lt;br /&gt;
Пробуйте создавать различные индексы и проверять, как изменяется занимаемое базой место. &lt;br /&gt;
&lt;br /&gt;
=== Типы индексов ===&lt;br /&gt;
&lt;br /&gt;
Наиболее популярный тип индекса - B-дерево. Эта структура хорошо подходит для атрибутов, которые можно сравнивать между собой, выполняя операции: &amp;lt;, &amp;lt;=, =, &amp;gt;=, &amp;gt;. Также индексы этого типа поддерживают быструю проверку IS (NOT) NULL, поэтому они хорошо подходят для атрибутов с большим количеством пропусков. В-деревья ускоряют любые операции, связанные со сравнением значений, в том числе сортировку и группировку.&lt;br /&gt;
&lt;br /&gt;
Синтаксис создания индекса:&lt;br /&gt;
&lt;br /&gt;
  CREATE INDEX index_name ON table_name (column_name);&lt;br /&gt;
&lt;br /&gt;
Второй по популярности тип индекса - hash, который позволяет быстро проводить сравнения на равенство. Этот тип также подходит для условий типа WHERE column_name IN ( ... ).&lt;br /&gt;
&lt;br /&gt;
Синтаксис создания индекса:&lt;br /&gt;
&lt;br /&gt;
  CREATE INDEX index_name ON table_name USING HASH (column_name);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Функции в индексах ===&lt;br /&gt;
&lt;br /&gt;
Удобно для строк создавать также индексы независимые от регистра:&lt;br /&gt;
&lt;br /&gt;
  CREATE INDEX index_name ON table_name (lower(column_name)); &lt;br /&gt;
&lt;br /&gt;
В этом случае, выбирая WHERE lower(title) = &amp;#039;the matrix&amp;#039; приведение значений колонки не нужно, и запрос выполнится быстро.&lt;br /&gt;
&lt;br /&gt;
=== Индексы на несколько колонок ===&lt;br /&gt;
&lt;br /&gt;
Удобный для часто встречающихся комбинаций условий.&lt;br /&gt;
  &lt;br /&gt;
Синтаксис: &lt;br /&gt;
&lt;br /&gt;
  CREATE INDEX index_name ON table_name (column1_name, column2_name);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Уникальный индекс ===&lt;br /&gt;
&lt;br /&gt;
Этот индекс также добавляет проверку целостности при изменении данных.&lt;br /&gt;
&lt;br /&gt;
Синтаксис:&lt;br /&gt;
&lt;br /&gt;
  CREATE UNIQUE INDEX index_name on table_name (column_name);&lt;br /&gt;
&lt;br /&gt;
=== Условные индексы ===&lt;br /&gt;
&lt;br /&gt;
Можно создать еще более специализированный индекс на конкретные условия. &lt;br /&gt;
&lt;br /&gt;
  CREATE INDEX index_name on title (kind_id, production_year) where (production_year&amp;gt;2000 and kind_id=1);&lt;br /&gt;
&lt;br /&gt;
Этот индекс позволит выбирать фильмы выпущенные после 2000 года, при этом для записей не удовлетворяющих условию все останется по-прежнему (как скорость выборки, так и скорость изменения данных).&lt;br /&gt;
&lt;br /&gt;
== Внешние ключи ==&lt;br /&gt;
&lt;br /&gt;
Внешние ключи - это атрибуты, которые связывают сущности, поддерживая ссылочную целостность: нельзя установить значение для foreign key, которого нет в таблице на которую атрибут ссылается. Для внешних ключей СУБД использует hash-индексы.&lt;br /&gt;
&lt;br /&gt;
Пример создания внешнего ключа:&lt;br /&gt;
&lt;br /&gt;
  alter table movie_info add foreign key (movie_id) references title(id);&lt;br /&gt;
&lt;br /&gt;
Используя схему БД из второй лабораторной работы, добавьте внешние ключи, нужные для ваших запросов.&lt;br /&gt;
&lt;br /&gt;
Запрос для просмотра существующих внешних ключей:&lt;br /&gt;
&lt;br /&gt;
  SELECT&lt;br /&gt;
    tc.constraint_name, tc.table_name, kcu.column_name, &lt;br /&gt;
    ccu.table_name AS foreign_table_name,&lt;br /&gt;
    ccu.column_name AS foreign_column_name &lt;br /&gt;
  FROM &lt;br /&gt;
    information_schema.table_constraints AS tc &lt;br /&gt;
    JOIN information_schema.key_column_usage AS kcu&lt;br /&gt;
      ON tc.constraint_name = kcu.constraint_name&lt;br /&gt;
    JOIN information_schema.constraint_column_usage AS ccu&lt;br /&gt;
      ON ccu.constraint_name = tc.constraint_name&lt;br /&gt;
  WHERE constraint_type = &amp;#039;FOREIGN KEY&amp;#039; AND tc.table_name=&amp;#039;mytable&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
Пример плана запроса после создания внешнего ключа:&lt;br /&gt;
&lt;br /&gt;
  explain &lt;br /&gt;
  select * from title t&lt;br /&gt;
  join movie_info mi on mi.movie_id=t.id&lt;br /&gt;
  where t.production_year=1899;&lt;br /&gt;
&lt;br /&gt;
План запроса:&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;Hash Join  (cost=24668.15..680874.12 rows=26044 width=192)&amp;quot;&lt;br /&gt;
  &amp;quot;  Hash Cond: (mi.movie_id = t.id)&amp;quot;&lt;br /&gt;
  &amp;quot;  -&amp;gt;  Seq Scan on movie_info mi  (cost=0.00..247886.02 rows=10198002 width=98)&amp;quot;&lt;br /&gt;
  &amp;quot;  -&amp;gt;  Hash  (cost=24411.20..24411.20 rows=9436 width=94)&amp;quot;&lt;br /&gt;
  &amp;quot;        -&amp;gt;  Bitmap Heap Scan on title t  (cost=177.56..24411.20 rows=9436 width=94)&amp;quot;&lt;br /&gt;
  &amp;quot;              Recheck Cond: (production_year = 1899)&amp;quot;&lt;br /&gt;
  &amp;quot;              -&amp;gt;  Bitmap Index Scan on ok  (cost=0.00..175.20 rows=9436 width=0)&amp;quot;&lt;br /&gt;
  &amp;quot;                    Index Cond: (production_year = 1899)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== План запроса. EXPLAIN ==&lt;br /&gt;
&lt;br /&gt;
С помощью команды EXPLAIN можно посмотреть, как предположительно будет выполняться запрос и как он в итоге выполнился. Вывод команды содержит для каждой таблицы, используемой в запросе: &lt;br /&gt;
* сколько условных единиц времени уйдет на получение/изменение первой строчки&lt;br /&gt;
* сколько условных единиц времени уйдет на выполнение операции целиком&lt;br /&gt;
* сколько всего строк будет задействовано&lt;br /&gt;
&lt;br /&gt;
Условные единицы времени связаны с количеством страниц памяти, которое нужно прочитать.&lt;br /&gt;
&lt;br /&gt;
=== Основы EXPLAIN ===&lt;br /&gt;
&lt;br /&gt;
Пример команды:&lt;br /&gt;
&lt;br /&gt;
  explain &lt;br /&gt;
  select * from title t&lt;br /&gt;
  join kind_type kt on kt.id=t.kind_id&lt;br /&gt;
  where production_year=2000 and kt.id=1;&lt;br /&gt;
&lt;br /&gt;
Ее вывод:&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;Nested Loop  (cost=0.00..108304.97 rows=15233 width=146)&amp;quot;&lt;br /&gt;
  &amp;quot;  -&amp;gt;  Seq Scan on kind_type kt  (cost=0.00..1.09 rows=1 width=52)&amp;quot;&lt;br /&gt;
  &amp;quot;        Filter: (id = 1)&amp;quot;&lt;br /&gt;
  &amp;quot;  -&amp;gt;  Seq Scan on title t  (cost=0.00..108151.55 rows=15233 width=94)&amp;quot;&lt;br /&gt;
  &amp;quot;        Filter: ((kind_id = 1) AND (production_year = 2000))&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Здесь Nested Loop означает, что внутри запроса выполняется цикл. Этот цикл состоит из последовательного просмотра записей через первичный ключ (Seq Scan). &lt;br /&gt;
&lt;br /&gt;
Всего запрос предполагает вернуть примерно 15233 записи.&lt;br /&gt;
&lt;br /&gt;
Если добавить индекс:&lt;br /&gt;
&lt;br /&gt;
  create index production_year_index on title (production_year);&lt;br /&gt;
&lt;br /&gt;
То план запроса изменится:&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;Nested Loop  (cost=1153.79..57625.67 rows=15233 width=146)&amp;quot;&lt;br /&gt;
  &amp;quot;  -&amp;gt;  Seq Scan on kind_type kt  (cost=0.00..1.09 rows=1 width=52)&amp;quot;&lt;br /&gt;
  &amp;quot;        Filter: (id = 1)&amp;quot;&lt;br /&gt;
  &amp;quot;  -&amp;gt;  Bitmap Heap Scan on title t  (cost=1153.79..57472.25 rows=15233 width=94)&amp;quot;&lt;br /&gt;
  &amp;quot;        Recheck Cond: (production_year = 2000)&amp;quot;&lt;br /&gt;
  &amp;quot;        Filter: (kind_id = 1)&amp;quot;&lt;br /&gt;
  &amp;quot;        -&amp;gt;  Bitmap Index Scan on ok  (cost=0.00..1149.98 rows=62073 width=0)&amp;quot;&lt;br /&gt;
  &amp;quot;              Index Cond: (production_year = 2000)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Время получения первой строки увеличилось, так как теперь запросу нужно сначала прочитать индексы, а затем выполнять сам запрос. Но и максимальная оценка выполнения запроса уменьшилась вдвое.&lt;br /&gt;
&lt;br /&gt;
Также теперь поиск записей в title происходит не последовательным перебором, а Bitmap Heap Scan - алгоритмом поиска по B-дереву. Именно такого типа индекс был добавлен.&lt;br /&gt;
&lt;br /&gt;
=== EXPLAIN ANALIZE ===&lt;br /&gt;
&lt;br /&gt;
Опция ANALIZE позволяет сделать более точный прогноз выполнения запроса. Однако, при этом запрос будет частично выполнен, поэтому ее следует использовать в сочетании с ROLLBACK или иметь ввиду, что случайные данные будут обновлены. Также из-за выполнения запроса, эта операция может занять некоторое время (время получения первых строк результата).&lt;br /&gt;
&lt;br /&gt;
ANALIZE показывает точную оценку времени выполнения запроса в миллисекундах. И количество прохождений циклов.&lt;br /&gt;
&lt;br /&gt;
Пример запроса:&lt;br /&gt;
&lt;br /&gt;
  explain ANALYZE&lt;br /&gt;
  select * from title t where production_year=2000 and kind_id in (select id from kind_type where id=1);&lt;br /&gt;
&lt;br /&gt;
Пример вывода:&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;Nested Loop  (cost=1153.79..57625.67 rows=15233 width=94) (actual time=234.085..339.698 rows=8763 loops=1)&amp;quot;&lt;br /&gt;
  &amp;quot;  -&amp;gt;  Seq Scan on kind_type  (cost=0.00..1.09 rows=1 width=4) (actual time=0.006..0.009 rows=1 loops=1)&amp;quot;&lt;br /&gt;
  &amp;quot;        Filter: (id = 1)&amp;quot;&lt;br /&gt;
  &amp;quot;        Rows Removed by Filter: 6&amp;quot;&lt;br /&gt;
  &amp;quot;  -&amp;gt;  Bitmap Heap Scan on title t  (cost=1153.79..57472.25 rows=15233 width=94) (actual time=234.076..338.145 rows=8763 loops=1)&amp;quot;&lt;br /&gt;
  &amp;quot;        Recheck Cond: (production_year = 2000)&amp;quot;&lt;br /&gt;
  &amp;quot;        Rows Removed by Index Recheck: 1795048&amp;quot;&lt;br /&gt;
  &amp;quot;        Filter: (kind_id = 1)&amp;quot;&lt;br /&gt;
  &amp;quot;        Rows Removed by Filter: 52237&amp;quot;&lt;br /&gt;
  &amp;quot;        -&amp;gt;  Bitmap Index Scan on ok  (cost=0.00..1149.98 rows=62073 width=0) (actual time=18.910..18.910 rows=61000 loops=1)&amp;quot;&lt;br /&gt;
  &amp;quot;              Index Cond: (production_year = 2000)&amp;quot;&lt;br /&gt;
  &amp;quot;Total runtime: 340.103 ms&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== EXPLAIN (ANALYZE, BUFFERS) ===&lt;br /&gt;
&lt;br /&gt;
Опция BUFFERS показывает использование буфера во время выполнения запроса. Значение shared hit показывает, сколько блоков буфера удалось использовать, то есть избежать повторного чтения из основном памяти, shared read - сколько блоков считалось из таблиц и индексов.&lt;br /&gt;
&lt;br /&gt;
== Дополнительно ==&lt;br /&gt;
&lt;br /&gt;
* [[Базы_данных/Лабораторная_работа_3/Индексы| Подробнее про работу индексов с примерами (спойлеры)]]&lt;br /&gt;
* http://www.postgresql.org/docs/9.3/static/sql-explain.html&lt;br /&gt;
* https://habrahabr.ru/post/203320/&lt;br /&gt;
* https://habrahabr.ru/post/203386/&lt;br /&gt;
* https://habrahabr.ru/post/203484/&lt;br /&gt;
&lt;br /&gt;
== Параметры и состояние PostgreSQL-сервера ==&lt;br /&gt;
&lt;br /&gt;
В этом разделе собраны инструменты и техники для мониторинга и оценки статуса сервера СУБД и сбора статистики. &lt;br /&gt;
&lt;br /&gt;
Для мониторинга текущих запросов разработана утилита аналогичная программе top - [https://github.com/julmon/pg_activity pg_activity]. Эта программа показывает текущие запросы и используемые ресурсы.&lt;br /&gt;
&lt;br /&gt;
Просмотри статистики выполнения операций в базе:&lt;br /&gt;
&lt;br /&gt;
  select * from pg_stat_database where datname=&amp;#039;imdb&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
Некоторые значения в статистике:&lt;br /&gt;
&lt;br /&gt;
* xact_commit - количество закоммиченных транзакций (в том числе и обычных операций insert/update)&lt;br /&gt;
* xact_rollback - количество сроллбеченных транзакций (в том числе и обычных операций insert/update)&lt;br /&gt;
* blks_read - общее количество блоков, которые были считаны&lt;br /&gt;
* blks_hit - количество блоков, считанных из кэша&lt;br /&gt;
* tup_returned - количество кортежей, которые были выведены для пользователя&lt;br /&gt;
* tup_fetched - количество кортежей, которые СУБД выбирала для последующих операций (сравните со значением выше)&lt;br /&gt;
* temp_bytes - размер временных файлов, которые создаются для ускорения выборки (например, при присоединении таблиц)&lt;br /&gt;
* deadlocks - количество транзакций с взаимной блокировкой&lt;br /&gt;
&lt;br /&gt;
Попробуйте выполнить запрос, сравнив количество считываемых блоков в его плане выполнения и изменениями статистики.&lt;br /&gt;
&lt;br /&gt;
Есть еще несколько таблиц со статистикой по использованию памяти для таблиц и индексов. Единственный интерес в них представляют количества live/dead tuples. Мертвые кортежи - это те, которые уже удалены из базы логически, но на их место в памяти еще не вставлены новые.&lt;br /&gt;
&lt;br /&gt;
Дополнительно:&lt;br /&gt;
&lt;br /&gt;
* http://www.postgresql.org/docs/current/static/monitoring-stats.html&lt;br /&gt;
* https://github.com/julmon/pg_activity&lt;/div&gt;</summary>
		<author><name>imported&gt;Ivsavin</name></author>
	</entry>
</feed>