44
СУБД Лекция 6 Павел Щербинин

СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Embed Size (px)

Citation preview

Page 1: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

СУБД

Лекция 6

Павел Щербинин

Page 2: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Профилирование

• К каким данным MySQL обращается чаще всего

• Какие типы запросов MySQL выполняет чаще всего

• В каких состояниях преимущественно находятся потоки (threads) MySQL

• Какие подсистемы MySQL чаще всего использует для выполнения запросов

• Какие виды обращения к данным встречаются наиболее часто

• Сколько различных видов действий, например просмотра индексов, выполняет MySQL

Page 3: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Протоколирование запросов

Общий журналlog = <имя_файла>

Журнал медленных запросовlog-slow-queries = <имя_файла>long_query_time = 2log-queries-not-using-indexeslog-slow-admin-statements

# Time: 121018 9:47:00# User@Host: root[root] @ localhost []# Query_time: 0.000652 Lock_time: 0.000109 Rows_sent: 50# Rows_examined: 3268SELECT ...

Page 4: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Протоколирование запросов

• Таблица могла быть заблокирована, поэтому запрос был вынужден ждать. Величина Lock_time показывает, как долго запрос ждал освобождения блокировки.

• Данные или индексы могли к тому моменту еще отсутствовать в кэше. Это обычный случай, когда сервер MySQL только запущен или не настроен должным образом.

• Мог идти ночной процесс резервного копирования, из-за чего все операции дискового ввода/вывода замедлялись.

• Сервер мог обрабатывать в тот момент другие запросы, поэтому данный выполнялся медленнее.

Page 5: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Протоколирование запросов

Долго выполняющиеся запросыПериодические выполняемые пакетные задания действительно могут запускать долго выполняющиеся запросы, но обычные запросы не должны занимать много времени.

Запросы, больше всего нагружающие серверИщите запросы, которые потребляют большую часть времени сервера. Напомним, что короткие запросы, выполняемые очень часто, тоже могут занимать много времени.

Новые запросыИщите запросы, которых вчера не было в первой сотне, а сегодня они появились. Это могут быть новые запросы или запросы, которые обычно выполнялись быстро, а теперь замедлились из-за изменившейся схемы индексации. Либо произошли еще какие-то изменения.

Page 6: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Профилирование запросов

SET profiling = 1;

SELECT SQL_NO_CACHE `movie`.`mID`, COUNT(*)

FROM `movie`

INNER JOIN `rating` USING(`mID`)

GROUP BY `movie`.`mID`

ORDER BY COUNT(*) DESC;

SHOW PROFILES\G

******************** 1. row *********************

Query_ID: 1

Duration: 0.02596900

Query: SELECT …

Page 7: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

EXPLAIN for UPDATE

UPDATE sakila.actor

INNER JOIN sakila.film_actor USING

(actor_id)

SET

actor.last_update=film_actor.last_update;

Page 8: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

EXPLAIN for UPDATE

UPDATE sakila.actor

INNER JOIN sakila.film_actor USING

(actor_id)

SET

actor.last_update=film_actor.last_update;

EXPLAIN SELECT film_actor.actor_id

FROM sakila.actor INNER JOIN

sakila.film_actor USING (actor_id)\G

***** 1. row *****id: 1select_type: SIMPLEtable: actortype: indexpossible_keys: PRIMARYkey: PRIMARYkey_len: 2ref: NULLrows: 200Extra: Using index***** 2. row *****id: 1select_type: SIMPLEtable: film_actortype: refpossible_keys: PRIMARYkey: PRIMARYkey_len: 2ref: sakila.actor.actor_idrows: 13Extra: Using index

Page 9: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

EXPLAIN for UPDATE

UPDATE sakila.actor

INNER JOIN sakila.film_actor USING

(actor_id)

SET

actor.last_update=film_actor.last_update;

EXPLAIN SELECT

film_actor.last_update,

actor.last_update

FROM sakila.actor

INNER JOIN sakila.film_actor USING

(actor_id)\G

***** 1. row *****id: 1select_type: SIMPLEtable: actortype: ALLpossible_keys: PRIMARYkey: NULLkey_len: NULLref: NULLrows: 200Extra:***** 2. row *****id: 1select_type: SIMPLEtable: film_actortype: refpossible_keys: PRIMARYkey: PRIMARYkey_len: 2ref: sakila.actor.actor_idrows: 13Extra:

Page 10: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Изоляция столбца

SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;

SELECT ... WHERE

TO_DAYS(CURRENT_DATE) - TO_DAYS(date_col) <= 10;

Page 11: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Изоляция столбца

SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;

SELECT ... WHERE

TO_DAYS(CURRENT_DATE) - TO_DAYS(date_col) <= 10;

SELECT ... WHERE

date_col >= DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY);

Page 12: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Изоляция столбца

SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;

SELECT ... WHERE

TO_DAYS(CURRENT_DATE) - TO_DAYS(date_col) <= 10;

SELECT ... WHERE

date_col >= DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY);

SELECT ... WHERE

date_col >= DATE_SUB(2008-01-17, INTERVAL 10 DAY);

Page 13: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Кластерные индексы

Page 14: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Кластерные индексы (+)

• Вы можете хранить связанные данные рядом.

• Быстрый доступ к данным. Кластерный индекс хранит и индекс, и данные вместе в одной B-Treeструктуре, поэтому извлечение строк из кластерного индекса обычно происходит быстрее, чем сопоставимый поиск в некластерном индексе.

• Использующие покрывающие индексы запросы могут получить значение первичного ключа из листового узла.

Page 15: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Кластерные индексы (-)

• Если данные помещаются в памяти, то порядок доступа к ним не имеет значения, и тогда кластерные индексы не принесут большой пользы.

• Если вы загружаете большое количество данных в другом порядке, то по окончании загрузки имеет смысл реорганизовать таблицу с помощью команды OPTIMIZE TABLE.

• Обновление столбцов кластерного индекса обходится дорого, поскольку InnoDB вынуждена перемещать каждую обновленную строку в новое место.

• Для таблиц с кластерным индексом вставка новых строк или обновление первичного ключа, требующее перемещения строки, может приводить к расщеплению страницы.

• Полное сканирование кластерных таблиц может оказаться более медленным, особенно если строки упакованы менее плотно или хранятся непоследовательно из-за расщепления страниц.

• Вторичные (некластерные) индексы могут оказаться больше, чем вы ожидаете, поскольку в листовых узлах хранятся значения столбцов, составляющих первичный ключ.

• Для доступа к данным по вторичному индексу требуется просмотр двух индексов вместо одного.

Page 16: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Размещение данных в MyISAM

CREATE TABLE

layout_test (

col1 int NOT NULL,

col2 int NOT NULL,

PRIMARY KEY(col1),

KEY(col2)

);

Page 17: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Размещение данных в MyISAM

CREATE TABLE

layout_test (

col1 int NOT NULL,

col2 int NOT NULL,

PRIMARY KEY(col1),

KEY(col2)

);

Page 18: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Размещение данных в InnoDB

Page 19: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Размещение данных в InnoDB

Page 20: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Размещение данных

Page 21: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Стратегия индексирования

Размещение данных

Page 22: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Покрывающие индексы

• Записи индекса обычно компактнее полной строки, поэтому, если MySQL читает только индекс, то обращается к значительно меньшему объему данных.

• Индексы отсортированы по индексируемым значениям (по крайней мере, внутри страницы), поэтому для поиска по диапазону, характеризуемому большим объемом ввода/вывода, потребуется меньше операций обращения к диску.

• Большинство подсистем хранения кэширует индексы лучше, чем сами данные.

• Покрывающие индексы особенно полезны в случае таблиц InnoDB из-за кластерных индексов. Вторичные индексы InnoDB хранят значения первичного ключа строки в листовых узлах. Таким образом, вторичный индекс, который «покрывает» запрос, позволяет избежать еще одного поиска по первичному индексу.

Page 23: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Покрывающие индексы

EXPLAIN SELECT store_id, film_id

FROM sakila.inventory\G

************************** 1. row **************************id: 1select_type: SIMPLEtable: inventorytype: indexpossible_keys: NULLkey: idx_store_id_film_idkey_len: 3ref: NULLrows: 4673Extra: Using index

Page 24: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Покрывающие индексы

EXPLAIN SELECT actor_id, last_name

FROM sakila.actor WHERE last_name = HOPPER\G

************************** 1. row **************************id: 1select_type: SIMPLEtable: actortype: refpossible_keys: idx_actor_last_namekey: idx_actor_last_namekey_len: 137ref: constrows: 2Extra: Using where; Using index

Page 25: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Покрывающие индексы

CREATE TABLE rental (

...

PRIMARY KEY (rental_id),

UNIQUE KEY rental_date (rental_date,inventory_id,customer_id),

KEY idx_fk_inventory_id (inventory_id),

KEY idx_fk_customer_id (customer_id),

KEY idx_fk_staff_id (staff_id),

...

);

EXPLAIN SELECT rental_id, staff_id FROM sakila.rental

WHERE rental_date = 2005-05-25

ORDER BY inventory_id, customer_id\G

************************** 1. row **************************

type: ref

possible_keys: rental_date

key: rental_date

rows: 1

Extra: Using where

Page 26: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Покрывающие индексы

CREATE TABLE rental (

...

PRIMARY KEY (rental_id),

UNIQUE KEY rental_date (rental_date,inventory_id,customer_id),

KEY idx_fk_inventory_id (inventory_id),

KEY idx_fk_customer_id (customer_id),

KEY idx_fk_staff_id (staff_id),

...

);

EXPLAIN SELECT rental_id, staff_id FROM sakila.rental

WHERE rental_date = 2005-05-25

ORDER BY inventory_id DESC;

Page 27: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Покрывающие индексы

CREATE TABLE rental (

...

PRIMARY KEY (rental_id),

UNIQUE KEY rental_date (rental_date,inventory_id,customer_id),

KEY idx_fk_inventory_id (inventory_id),

KEY idx_fk_customer_id (customer_id),

KEY idx_fk_staff_id (staff_id),

...

);

EXPLAIN SELECT rental_id, staff_id FROM sakila.rental

WHERE rental_date = 2005-05-25

ORDER BY inventory_id DESC, customer_id ASC;

Page 28: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Покрывающие индексы

CREATE TABLE rental (

...

PRIMARY KEY (rental_id),

UNIQUE KEY rental_date (rental_date,inventory_id,customer_id),

KEY idx_fk_inventory_id (inventory_id),

KEY idx_fk_customer_id (customer_id),

KEY idx_fk_staff_id (staff_id),

...

);

EXPLAIN SELECT rental_id, staff_id FROM sakila.rental

WHERE rental_date = 2005-05-25

ORDER BY inventory_id, staff_id;

Page 29: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Покрывающие индексы

CREATE TABLE rental (

...

PRIMARY KEY (rental_id),

UNIQUE KEY rental_date (rental_date,inventory_id,customer_id),

KEY idx_fk_inventory_id (inventory_id),

KEY idx_fk_customer_id (customer_id),

KEY idx_fk_staff_id (staff_id),

...

);

EXPLAIN SELECT rental_id, staff_id FROM sakila.rental

WHERE rental_date > 2005-05-25

ORDER BY rental_date, inventory_id;

Page 30: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Покрывающие индексы

CREATE TABLE rental (

...

PRIMARY KEY (rental_id),

UNIQUE KEY rental_date (rental_date,inventory_id,customer_id),

KEY idx_fk_inventory_id (inventory_id),

KEY idx_fk_customer_id (customer_id),

KEY idx_fk_staff_id (staff_id),

...

);

EXPLAIN SELECT rental_id, staff_id FROM sakila.rental

WHERE rental_date = 2005-05-25

ORDER BY customer_id;

Page 31: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Покрывающие индексы

CREATE TABLE rental (

...

PRIMARY KEY (rental_id),

UNIQUE KEY rental_date (rental_date,inventory_id,customer_id),

KEY idx_fk_inventory_id (inventory_id),

KEY idx_fk_customer_id (customer_id),

KEY idx_fk_staff_id (staff_id),

...

);

EXPLAIN SELECT rental_id, staff_id FROM sakila.rental

WHERE rental_date = 2005-05-25 AND inventory_id IN(1,2)

ORDER BY customer_id;

Page 32: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Нормализация

• Нормализованные таблицы обычно обновляются быстрее, чем ненормализованные.

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

• Нормализованные таблицы обычно меньше по размеру, поэтому лучше помещаются в памяти и их производительность выше.

• Из-за отсутствия избыточных данных реже возникает необходимость в запросах с фразами DISTINCT или GROUP BY для извлечения списков значений.

Page 33: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Денормализация

• Все данные находятся в одной и той же таблице, что позволяет избежать соединений.

• Более эффективные стратегии индексирования

SELECT message_text, user_name

FROM message

INNER JOIN user ON message.user_id=user.id

WHERE user.account_type=premium

ORDER BY message.published DESC LIMIT 10;

SELECT message_text,user_name

FROM user_messages

WHERE account_type=premium

ORDER BY published DESC

LIMIT 10;

Page 34: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Денормализация

Таблицы счетчиков

CREATE TABLE hit_counter (

cnt int unsigned not null

) ENGINE=InnoDB;

UPDATE hit_counter SET cnt = cnt + 1;

Page 35: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Денормализация

Таблицы счетчиков

CREATE TABLE hit_counter (

slot tinyint unsigned not null primary

key,

cnt int unsigned not null

) ENGINE=InnoDB;

UPDATE hit_counter SET cnt = cnt + 1 WHERE

slot = RAND( ) * 100;

SELECT SUM(cnt) FROM hit_counter;

Page 36: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Денормализация

Таблицы счетчиков

CREATE TABLE daily_hit_counter (

day date not null,

slot tinyint unsigned not null,

cnt int unsigned not null,

primary key(day, slot)

) ENGINE=InnoDB;

INSERT INTO daily_hit_counter(day, slot, cnt)

VALUES (CURRENT_DATE, RAND( ) * 100, 1)

ON DUPLICATE KEY UPDATE cnt = cnt + 1;

Page 37: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Версионирование схемы БД

• любую версию базы данных можно обновить до любой (обычно,

самой последней) версии;

• набор SQL-запросов, реализующих миграцию между любыми двумя

версиями, можно было получить как можно быстрее и проще;

• всегда можно создать с нуля базу данных со структурой самой

последней версии

• в случае работы над разными ветками, при последующем их слиянии

ручное редактирование файлов БД было сведено к минимуму;

• откатить БД на более раннюю версию так же просто, как и обновить на

более новую

Page 38: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Метод

инкрементных изменений

•Database

|- Baseline.sql

|- 0001.03.01.sql

|- 0002.03.01.sql

|- 0003.03.01.sql

|- 0004.03.02.sql

|- 0005.03.02.sql

|- 0006.03.02.sql

|- 0007.03.02.sql

CREATE TABLE MigrationHistory

(

Id INT,

MajorVersion VARCHAR(2),

MinorVersion VARCHAR(2),

FileNumber VARCHAR(4),

Comment VARCHAR(255),

DateApplied DATETIME,

PRIMARY KEY(Id)

)

INSERT INTO

MigrationHistory ( MajorVersion, MinorVersion,

FileNumber, Comment, DateApplied )

VALUES ('03', '01', '0000', 'Baseline', NOW())

Page 39: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Метод

инкрементных изменений

• Быстрое и удобное выполнение миграции до последней версии;

• Механизм нумерации версий. Номер текущей версии хранится прямо в БД;

• Для максимального удобства нужны средства автоматизации выполнения миграций;

• Неудобно добавлять комментарии к структуре БД.;

• Возникают проблемы в процессе параллельной разработки в нескольких ветках репозитория.

Page 40: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Метод

идемпотентных изменений

•Database

|- 3.01

| |- Baseline.sql

| | - Changes.sql

|

| - 3.02

|- Baseline.sql

|- Changes.sql

IF NOT EXISTS

(

SELECT *

FROM information_schema.tables

WHERE table_name = 'myTable'

AND table_schema = 'myDb'

)

THEN

CREATE TABLE myTable

(

id INT(10) NOT NULL,

myField VARCHAR(255) NULL,

PRIMARY KEY(id)

);

END IF;

Page 41: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Метод

идемпотентных изменений

• Очень удобное выполнение миграций с любой промежуточной версии до последней — нужно всего лишь выполнить на базе данных один файл (Changes.sql);

• Потенциально возможны ситуации, в которых будут теряться данные, за этим придется следить.

• Для того, чтобы изменения были идемпотентными, нужно потратить больше времени (и кода) на их написание.

Page 42: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Метод уподобления структуры

БД исходному коду

Удобно наблюдать изменения в структуре между версиями при помощи средств системы контроля версий;

Как и любой исходный код, структуру БД удобно комментировать;

Для того, чтобы с нуля создать чистую базу данных последней версии, нужно выполнить всего лишь один файл;

Скрипты-миграции более надежны, чем в других методах, так как генерируются автоматически;

Мигрировать с новых версий на старые почти так же просто, как со старых на новые (проблемы могут возникнуть только с пресловутыми изменениями данных);

В случае слияния двух веток репозитория, merge структуры БД осуществляется проще, чем при использовании других подходов;

Page 43: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Метод уподобления структуры

БД исходному коду

Изменения данных придется хранить отдельно, и затем вручную вставлять в сгенерированные скрипты-миграции;

Вручную выполнять миграции очень неудобно, необходимы автоматизированные средства.

Page 44: СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-запросы"

Спасибо за вниманиеПавел Щербинин

[email protected]