СУБД. Лекция 5

СУБД
Навроцкий Артем
Лекция 5

Индексирование

Индексы используются для:

Индексирование B-tree

Индексирование B-tree (пример)

CREATE TABLE people (
	last_name VARCHAR(50) NOT NULL,
	first_name VARCHAR(50) NOT NULL,
	dob DATE NOT NULL,
	gender ENUM(m, f) NOT NULL,
	KEY (last_name, first_name, dob)
);

Индексирование B-tree (особенности)

Можно:

Нельзя:

Индексирование ХЕШ-индексы

CREATE TABLE testhash (
	fname VARCHAR(50) NOT NULL,
	lname VARCHAR(50) NOT NULL,
	KEY USING HASH (fname)
) ENGINE=MEMORY;
fname lname
Arjen Lentz
Baron Schwartz
Peter Zaitsev
Vadim Tkachenko

Индексирование ХЕШ-индексы


f('Arjen') = 2323
f('Baron') = 7437
f('Peter') = 8784
f('Vadim') = 2458
Ячейка Значение
2323 Указатель на строку 1
2458 Указатель на строку 4
7437 Указатель на строку 2
8784 Указатель на строку 3
SELECT lname FROM testhash WHERE fname = 'Peter';

Индексирование B-tree (особенности)

Индексирование. Специфика MySQL

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

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

Общий журнал

general_log_file = <имя_файла>

Журнал медленных запросов

slow_query_log_file = <имя_файла>
slow_query_log = 1
long_query_time = 2.5
log_queries_not_using_indexes = 1
log_slow_admin_statements = 1
log_throttle_queries_not_using_indexes = 60

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

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

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

Долго выполняющиеся запросы

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

Запросы, больше всего нагружающие сервер

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

Новые запросы

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

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

SHOW STATUS
Bytes_received и Bytes_sent
Количество байтов, соответственно полученных и отправленных сервером.
Com_*
Команды, которые сервер выполняет.
reated_*
Временные таблицы и файлы, созданные во время выполнения запроса.
Handler_*
Операции подсистемы хранения.
Select_*
Различные типы планов выполнения операции соединения.
Sort_*
Разнообразная информация о сортировке.

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

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 …

EXPLAIN

JOIN-стратегии

MERGE JOIN
Соединение двух отсортированных последовательностей.
Работает быстро и за один проход обоих списков.
HASH JOIN
Меньшее отношение помещается в хэш-таблицу. Затем для каждой строки из большей таблицы выполняется поиск значений, соответствующих условию соединения.
Соединение только по условию эквивалентности.
NEASTED LOOP
Соединение вложенными циклами.

EXPLAIN (графическое представление)

Столбцы EXPLAIN: id

EXPLAIN SELECT (SELECT 1 FROM sakila.actor LIMIT 1) FROM sakila.film;
Id select_type table
1 PRIMARY film
2 SUBQUERY actor
EXPLAIN SELECT film_id
FROM (SELECT MAX(film_id) as film_id FROM sakila.film) AS der;
Id select_type table
1 PRIMARY <derived2>
2 DERIVED
EXPLAIN SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3;
Id select_type table
1 PRIMARY
2 UNION
3 UNION

Столбцы EXPLAIN: select_type

PRIMARY
Самый внешний запрос.
SUBQUERY
Запрос SELECT, который содержится в подзапросе, находящемся во фразе SELECT (иными словами, не во фразе FROM).
DERIVED
Значение DERIVED означает, что данный запрос SELECT является подзапросом во фразе FROM.
UNION
Второй и последующий запросы SELECT, входящие в объединение UNION, помечаются признаком UNION.
UNION RESULT
Запрос SELECT, применяемый для выборки результатов из временной таблицы, созданной в ходе выполнения UNION.

Столбцы EXPLAIN: table

EXPLAIN SELECT film.film_id
FROM sakila.film
	INNER JOIN sakila.film_actor USING (film_id)
	INNER JOIN sakila.actor USING (actor_id);
Id select_type table
1 SIMPLE actor
2 SIMPLE film_actor
3 SIMPLE film

Столбцы EXPLAIN: table

EXPLAIN SELECT
	actor_id,
	(SELECT 1 FROM sakila.film_actor
	          WHERE film_actor.actor_id = der_1.actor_id LIMIT 1)
FROM (
	SELECT actor_id
	FROM sakila.actor LIMIT 5
) AS der_1
UNION ALL
SELECT film_id,
(SELECT @var1 FROM sakila.rental LIMIT 1)
FROM (
	SELECT film_id,
	(SELECT 1 FROM sakila.store LIMIT 1)
	FROM sakila.film LIMIT 5
) AS der_2;

Столбцы EXPLAIN: table

Id select_type table
1 PRIMARY <derived3>
3 DERIVED actor
2 DEPENDENT SUBQUERY film_actor
4 UNION <derived6>
6 DERIVED film
7 SUBQUERY store
5 UNCACHEABLE SUBQUERY rental

Столбцы EXPLAIN: table

Столбцы EXPLAIN: type

ALL
Этот подход обычно называют сканированием таблицы.
index
То же, что сканирование таблицы, только MySQL просматривает ее в порядке, задаваемом индексом, а не в порядке следования строк.
range
Просмотр диапазона – это ограниченная форма сканирования индекса. Просмотр начинается в определенной точке индекса и возвращает строки в некотором диапазоне значений.
ref
Это доступ по индексу (иногда он называется поиском по индексу (index lookup)), в результате которого возвращаются строки, соответствующие единственному заданному значению.
eq_ref
Это поиск по индексу в случае, когда MySQL точно знает, что будет возвращено не более одного значения.
const, system
Эти типы доступа MySQL применяет, когда в процессе оптимизации какую-то часть запроса можно преобразовать в константу.
NULL
Этот метод означает, что MySQL сумела разрешить запрос на фазе оптимизации, так что в ходе выполнения вообще не потребуется обращаться к таблице или индексу.

Столбцы EXPLAIN: possible_keys, key

EXPLAIN SELECT actor_id, film_id FROM sakila.film_actor\G

************************* 1. row *************************
id: 1
select_type: SIMPLE
table: film_actor
type: index
possible_keys: NULL
key: idx_fk_film_id
key_len: 2
ref: NULL
rows: 5143
Extra: Using index

Столбцы EXPLAIN: possible_keys, key

EXPLAIN SELECT actor_id, film_id
FROM sakila.film_actor WHERE actor_id=4; 

...| type | possible_keys | key     | key_len |...
...| ref  | PRIMARY       | PRIMARY | 2       |...

CREATE TABLE t (
    a char(3) NOT NULL,
    b int(11) NOT NULL,
    c char(1) NOT NULL,
    PRIMARY KEY (a,b,c)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
EXPLAIN SELECT a FROM t WHERE a = 'sak' AND b = 112;

... |type | possible_keys | key     | key_len |...
... | ref | PRIMARY       | PRIMARY | 13      |...

Столбцы EXPLAIN: ref

EXPLAIN SELECT STRAIGHT_JOIN f.film_id
FROM sakila.film AS f
    INNER JOIN sakila.film_actor AS fa
        ON f.film_id=fa.film_id AND fa.actor_id = 1
    INNER JOIN sakila.actor AS a USING(actor_id);

...| table |...| key                | key_len | ref                    |...
...| a     |...| PRIMARY            | 2       | const                  |...
...| f     |...| idx_fk_language_id | 1       | NULL                   |...
...| fa    |...| PRIMARY            | 4       | const,sakila.f.film_id |...

Столбцы EXPLAIN: rows

EXPLAIN SELECT f.film_id
FROM sakila.film AS f
    INNER JOIN sakila.film_actor AS fa USING(film_id)
    INNER JOIN sakila.actor AS a USING(actor_id);

...| rows |...
...|  200 |...
...|   13 |...
...|    1 |...

Столбцы EXPLAIN: extra

Using index
Означает, что MySQL воспользуется покрывающим индексом, чтобы избежать доступа к самой таблице.
Using where
Означает, что сервер произведет дополнительную фильтрацию строк, отобранных подсистемой хранения.
Using temporary
Означает, что MySQL будет применять временную таблицу для сортировки результатов запроса.
Using filesort
Означает, что MySQL прибегнет к обычной сортировке для упорядочения результатов, а не станет читать строки из таблицы в порядке, задаваемом индексом.
range checked for each record
Означает, что подходящего индекса не нашлось, поэтому сервер будет заново искать индекс при обработке каждой строки в операции соединения.

Индексирование: изоляция столбца

SELECT ... 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);

Индексирование: кластерные индексы

Индексирование: кластерные индексы (+)

Индексирование: кластерные индексы (-)

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

CREATE TABLE layout_test (
	col1 int NOT NULL,
	col2 int NOT NULL,
	PRIMARY KEY(col1),
	KEY(col2)
);

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

CREATE TABLE layout_test (
	col1 int NOT NULL,
	col2 int NOT NULL,
	PRIMARY KEY(col1),
	KEY(col2)
);

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

CREATE TABLE layout_test (
	col1 int NOT NULL,
	col2 int NOT NULL,
	PRIMARY KEY(col1),
	KEY(col2)
);

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

CREATE TABLE layout_test (
	col1 int NOT NULL,
	col2 int NOT NULL,
	PRIMARY KEY(col1),
	KEY(col2)
);

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

CREATE TABLE layout_test (
	col1 int NOT NULL,
	col2 int NOT NULL,
	PRIMARY KEY(col1),
	KEY(col2)
);

INSERT: Сурогатный ключ

INSERT: Натуральный ключ

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

EXPLAIN SELECT store_id, film_id FROM sakila.inventory\G

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: inventory
   partitions: NULL
         type: index
possible_keys: NULL
          key: idx_store_id_film_id
      key_len: 3
          ref: NULL
         rows: 4581
     filtered: 100.00
        Extra: Using index

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

EXPLAIN SELECT actor_id, last_name
FROM sakila.actor WHERE last_name = 'HOPPER'\G

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: actor
   partitions: NULL
         type: ref
possible_keys: idx_actor_last_name
          key: idx_actor_last_name
      key_len: 137
          ref: const
         rows: 2
     filtered: 100.00
        Extra: Using index

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

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 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; Using index

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

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 FROM sakila.rental
WHERE rental_date = '2005-05-25'
ORDER BY inventory_id DESC\G

*************************** 1. row ***************************
         type: ref
possible_keys: rental_date
          key: rental_date
         rows: 1
        Extra: Using where; Using index

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

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 FROM sakila.rental
WHERE rental_date = '2005-05-25'
ORDER BY inventory_id DESC, customer_id ASC\G

*************************** 1. row ***************************
         type: ref
possible_keys: rental_date
          key: rental_date
         rows: 1
        Extra: Using where; Using index; Using filesort

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

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 FROM sakila.rental
WHERE rental_date = '2005-05-25'
ORDER BY inventory_id, staff_id\G

*************************** 1. row ***************************
         type: ref
possible_keys: rental_date
          key: rental_date
         rows: 1
        Extra: Using index condition; Using filesort

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

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 FROM sakila.rental
WHERE rental_date = '2005-05-25'
ORDER BY rental_date, inventory_id\G

*************************** 1. row ***************************
         type: ref
possible_keys: rental_date
          key: rental_date
         rows: 1
        Extra: Using where; Using index

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

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 FROM sakila.rental
WHERE rental_date = '2005-05-25'
ORDER BY customer_id\G

*************************** 1. row ***************************
         type: ref
possible_keys: rental_date
          key: rental_date
         rows: 1
        Extra: Using where; Using index; Using filesort

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

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 FROM sakila.rental
WHERE rental_date = '2005-05-25' AND inventory_id IN (1, 2)
ORDER BY customer_id\G

*************************** 1. row ***************************
         type: range
possible_keys: rental_date,idx_fk_inventory_id
          key: rental_date
         rows: 2
        Extra: Using where; Using index; Using filesort
Навроцкий Артем
@gent: navrotskiy@corp.mail.ru
Спасибо за внимание!