35
Эффективный MySQL Interlabs 13 мая 2013 1 / 35

Эффективный MySQL

  • View
    1.966

  • Download
    1

Embed Size (px)

DESCRIPTION

Доступна масса информации по основам оптимизации MySQL, однако среднестатистический разработчик по-прежнему склонен ее по большей части игнорировать :(. Цель семинара — еще раз обратить внимание на такие базовые вещи, как выбор оптимальных типов полей, основны построения эффективных индексов, доступные средства обеспечения ссылочной целостности. Выполнен обзор инструментов оптимизации, доступных разработчику, особое внимание уделено pt-query-digest в варианте processlist, позволяющая разработчику самостоятельно выполнить анализ производительности и обнаружить узкие места.

Citation preview

Page 1: Эффективный MySQL

Эффективный MySQL

Interlabs

13 мая 2013

1 / 35

Page 2: Эффективный MySQL

Немного истории

MySQL AB4.0 03.2003 UNION . . .

4.1 06.2004 BTREE, PREPARED, SUBQUERY . . .

5.0 09.2005 CURSOR, VIEW, TRIGGER . . .

SUN Microsystems 5.1 11.2008 PARTITIONING . . .

ORACLE 5.5 9.2010 DEFAULT INNODB, SIGNAL . . .

5.6 dev 2011 NOSQL, PERFORMANCE SCHEMA . . .

MariaDB — полностью совместимый свободный форк:

• InnoDB→ XtraDB

• динамические и виртуальныеколонки

• SphinxSE — встроенный Sphinx

• много оптимизаций, включаяподзапросы

2 / 35

Page 3: Эффективный MySQL

Типы данных

• чем меньше места — тем лучше;• чем проще тип — тем лучше;• колонка с NULL: больше места, дольшеобработка, сложнее оптимизация.

Используем минимально достаточныйсамый простой тип, избегаем NULL.

3 / 35

Page 4: Эффективный MySQL

ЧислаИспользуем минимально достаточный тип:

TINYINT 255

SMALLINT 65535

MEDIUMINT 16777215

INT 4294967295

BIGINT 18446744073709551615M

FLOAT 1.175494351E–38

3.402823466E+38

DOUBLE 2.2250738585072014E–308

1.7976931348623157E+308

DECIMAL 65 цифр

• по возможности UNSIGNED• по возможности FLOAT, диапазон DOUBLE редко нужен• важна точная дробная часть — DECIMAL

4 / 35

Page 5: Эффективный MySQL

СтрокиCHAR

• фиксированный размер• 0 - 255• быстрое изменение

VARCHAR

• размер строки + 1–2 байта• 0 - 65535• дольше измeнение

Использование:

• часто изменяемые данные• незначительная разница вдлине строк

• относительно редкоеобновление

• строки различной длины

5 / 35

Page 6: Эффективный MySQL

Бинарные строки

BINARY / VARBINARY

• просто набор байтов, не используют collation и UTF;• быстрее обрабатываются, занимают меньше места;• в остальном аналогичны CHAR и VARCHAR.

MD5, почтовые индексы, IP-адреса

6 / 35

Page 7: Эффективный MySQL

Даты

DATE 1000–01–01 — 9999–12–31

TIMESTAMP 1970–01–01 00:00:01 UTC — 2038–01–19 03:14:07 UTC

DATETIME 1000–01–01 00:00:00 — 9999–12–31 23:59:59

• если позволяет временной интервал — TIMESTAMP• поведение TIMESTAMP при INSERT/UPDATE настраивается• DATETIME — в два раза больше места, но без ограничений

7 / 35

Page 8: Эффективный MySQL

Тексты

TEXT = BLOB + encoding + collation

• отдельный способ хранения по отношению к другимполям, отдельное хранилище в InnoDB;

• индексируется только часть (max_sort_length);• снижение производительности при сложных запросах;• можно настраивать длину: TINYTEXT, MEDIUMTEXT,SMALLBLOB и т.д.

Использовать только если действительно нужно.

8 / 35

Page 9: Эффективный MySQL

Механизмы храненияMyISAM Memory Archive CSV . . . InnoDB (XtraDB)

• ручное восстановление• нет ссылочной целостности• нет транзакций• полнотекстовые индексы

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

. . . из всех для нас важнейшим является InnoDB

Всегда явно прописываем при создании таблиц.

9 / 35

Page 10: Эффективный MySQL

Индексы

Проектирование базы основано на данных, нопроектирование индексов основано на запросах

• анализ запросов, генерируемых приложением• анализ лога (медленных) запросов

SET GLOBAL log_slow_queries = ’ON’;SET GLOBAL long_query_time = 0;$ pt-query-digest /var/lib/mysql/mysql-slow.log...

10 / 35

Page 11: Эффективный MySQL

SHOW STATUS

Можно получить различную статистику текущего соединения:

SHOW PROCESSLIST;SHOW TABLE STATUS;SHOW TABLE STATUS FROM database WHERE Engine=’InnoDB’;

FLASH STATUSSHOW SESSION STATUS LIKE ’Select%’;SHOW SESSION STATUS LIKE ’Created%’;FLASH STATUS;

11 / 35

Page 12: Эффективный MySQL

Лог медленных запросовМожно включать и выключать без перезапуска сервера,результат в таблицу:

SET GLOBAL log_slow_queries = ’ON’;SET GLOBAL long_query_time = 2;SET GLOBAL log_output = ’TABLE’;SET GLOBAL log_queries_not_using_indexes = ’ON’;

SELECT start_time, query_time, rows_set, rows_examined, sql_textFROM mysql.slow_logWHERE db = ’database’;

12 / 35

Page 13: Эффективный MySQL

Профайлер

Время выполнения всех стадий обработки запроса, может бытьполезно для сложных запросов:

SET profiling=1;SELECT ...SHOW PROFILES;SHOW PROFILE FOR QUERY 1;SHOW PROFILE CPU FOR QUERY 1;...

13 / 35

Page 14: Эффективный MySQL

INFORMATION SCHEMA

Количество записей в каждой таблицы базы:

SELECT TABLE_NAME, TABLE_ROWSFROM INFORMATION_SCHEMA.TABLESWHERE TABLE_SCHEMA=’database’ORDER BY TABLE_ROWS DESC;

Некоторые запросы уже реализованы в проекте CommonSchema.

14 / 35

Page 15: Эффективный MySQL

Common Schemahttps://code.google.com/p/common-schema/

• реально сносит крышу, «jQuery для MySQL» ,• библиотека функций + скриптовый язык, реализованныйна хранимых

• отладчик для хранимых процедур• набор view для стандартных задач анализа и оптимизации

foreach($table, $schema, $engine: table in dbname){

if ($engine = ’InnoDB’)ALTER TABLE :$schema.:$table ENGINE=InnoDB ROW_FORMAT=Compact;

}

15 / 35

Page 16: Эффективный MySQL

Percona Toolkit

• pt-query-digest — поиск проблемных запросов• pt-index-usage — анализ использования индексов• pt-duplicate-key-checker — поиск дублирующихсяиндексов

http://www.percona.com/software/percona-toolkit

16 / 35

Page 17: Эффективный MySQL

pt-query-digestНе обязательно использовать серверный slow log, можнополучить клиентский с помощью PROCESSLIST:

$ pt-query-digest --processlisth=mysql.interlabs.lan,u=devel,p=password--interval=0.01 --output slowlog > slow.log

$ pt-query-digest --explainh=mysql.interlabs.lan,u=devel,p=password > explained.log

explained.log — отчет об анализе slow.log:

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

17 / 35

Page 18: Эффективный MySQL

pt-index-usage и другие

Анализирует slow.log и находит бесполезные индексы:

$ pt-index-usage --host=mysql.interlabs.lan--user=user --password=password slow.log

ALTER TABLE ‘db‘.‘table‘ DROP KEY ‘keyname‘;ALTER TABLE ‘db‘.‘another‘ DROP KEY ‘somekey‘;...

А также: pt-duplicate-key-checker, pt-table-usage и т.д.

18 / 35

Page 19: Эффективный MySQL

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

• проверка использования — поле key в EXPLAIN

ANALYZE TABLE tablename;

cardinality = количество разных значенийselectivity = cardinality / общее количество значений

• чем больше индексов — тем сложнее оптимизатору• оптимизация — только на таблице с реальными данными

19 / 35

Page 20: Эффективный MySQL

СелективностьSELECT ... FROM table WHERE x = 3;INDEX(x)

• несколько значений равно 3 — используется• 10–30% равно 3 — возможно используется• большее количество — не используется

INDEX(gender), INDEX(is_active) . . . — бесполезны

SELECT * FROM information_schema.statistics

20 / 35

Page 21: Эффективный MySQL

InnoDB: первичный ключ• UINIQUE и NOT NULL• хранит не только значения ключа, но и данные• обязателен, не указан — используется первый уникальныйили генерируется автоматически, но так делать не надо;

• последовательность должна соответствовать порядкудобавления записей, иначе неэффективная вставка;

• каждый вторичный ключ включает в себя поля первичного.

PRIMARY KEY(id)INDEX(id,x) и UNIQUE(id,x) — обычно бесполезныPRIMARY KEY(UUID) — проблема

21 / 35

Page 22: Эффективный MySQL

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

• covering index, Using index в EXPLAIN• самый производительный вариант• дополнительное место, расходы при вставке

SELECT age FROM peopleWHERE first = ’John’ AND last = ’Doe’

INDEX(first, last) — работает при поискеINDEX(first, last, age) — covering index

22 / 35

Page 23: Эффективный MySQL

Оптимальный индекс1 Все поля в WHERE, для которых =, в порядке убыванияселективности

2 Поле по которому:• WHERE range (BETWEEN, >, ...)• GROUP BY• ORDER BY — нужно учитывать порядок полей в ORDER BY

3 Дополнительные поля для covering index

SELECT a,b FROM t WHERE c=1SELECT b FROM t WHERE c=1 ORDER BY aSELECT b FROM t WHERE c=1 GROUP BY aINDEX(c,a,b)

23 / 35

Page 24: Эффективный MySQL

GROUP BY ORDER BYСоставной индекс удовлетворяет WHERE и есть еще поля виндексе — GROUP BY и ORDER BY (чаще что-то одно):

WHERE a=1 GROUP BY bWHERE a=1 ORDER BY bWHERE a=1 GROUP BY b ORDER BY bINDEX(a,b) — все работает

WHERE a=1 GROUP BY b ORDER BY cINDEX(a,b,c) — то же, что INDEX(a,b),

если не covering

WHERE b=1 AND a > 9 ORDER BY aINDEX(b,a) — работает

24 / 35

Page 25: Эффективный MySQL

ORDER BY LIMIT

• если ORDER BY использует индекс — LIMIT эффективный• иначе — обход всех записей

SELECT SQL_CALC_FOUND_ROWS ... LIMIT 10;SELECT FOUND_ROWS();

Позволяет избежать повторного SELECT с COUNT(), данные попроизводительности противоречивы.

25 / 35

Page 26: Эффективный MySQL

Когда индексы не работают

NOT

• NOT LIKE• NOT IN• NOT (expression)• <>

IN

• IN (SELECT ...)• IN (1,2,3)оптимизируется как =

Функции WHERE YEAR(date) = ’2013’

OR в WHERE WHERE first = ’John’ OR last = ’Smith’

LIKE работает только для префиксов LIKE ’John%’.

26 / 35

Page 27: Эффективный MySQL

Проблемы с сортировкой

Сортировка не использует индекс, если:

• нарушается префиксность индекса• колонка из другой таблицы или не в индексе• разнонаправленная сортировка для разных колонок• сортировка выборки не соответствует сортировке индекса• GROUP BY и ORDER BY по разным полям (с большойвероятностью)

27 / 35

Page 28: Эффективный MySQL

Индексы: антипаттерны

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

• много индексов по одиночным колонкам — оптимизаторвыберет один

• отсутствие covering-индексов• избыточные covering-индексы• FULLTEXT search

28 / 35

Page 29: Эффективный MySQL

EXPLAIN SELECTОсновной инструмент диагностики запроса

• Using index — используется покрывающий индекс;• Using where — применяется WHERE, используется ли приэтом индекс — смотри в possible_keys и key

• Using temporary — временная таблица в памяти или надиске

• Using filesort — сортировка не может быть выполнена поиндексу, file тут не при чем.

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

29 / 35

Page 30: Эффективный MySQL

Некоторые рекомендации• ORDER BY NULL помогает избежать неявной сортировки• OR можно оптимизировать, переписав в UNION• всегда явно указываем UNION ALL и UNION DISTINCT,иначе выполняется DISTINCT

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

• чем больше OFFSET, тем медленнее• DISTINCT и GROUP BY не имеют смысла вместе

В ряде случаев — только денормализация

30 / 35

Page 31: Эффективный MySQL

Главное в базе это. . .

Целостность данных

Вы всегда должны быть уверены в полноте и корректностиданных, хранимых в базе.

• первичный ключ;• уникальные ключи;• типы колонок;

• внешние ключи;• ограничения;• иногда триггеры.

31 / 35

Page 32: Эффективный MySQL

Уникальные ключи

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

CREATE UNIQUE INDEX table_field ON table(field);

ALTER TABLE table ADD UNIQUE (field);

ALTER IGNORE table ADD UNIQUE (field);остается только первая дублирующаяся запись.

32 / 35

Page 33: Эффективный MySQL

Уникальные ключи:обработка ошибок

INSERT INTO table (md5, stuff) VALUES($a, $b)ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id);

$id = SELECT LAST_INSERT_ID();

Удобно, но AUTOINCREMENT продолжает расти при ошибках!

33 / 35

Page 34: Эффективный MySQL

InnoDB: внешние ключиCREATE TABLE users ...

FOREIGN KEY fk_group(group_id)REFERENCES groups(id)ON UPDATE CASCADE ON DELETE RESTRICT

• ON UPDATE CASCADE: если изменяется groups.id, тоизменяется и group_id

• ON DELETE RESTRICT: из groups нельзя удалить запись,если есть ссылающиеся записи

• ON DELETE CASCADE: если из groups удаляется запись,ссылающиеся удаляются автоматически

• при использовании REPLACE возможны проблемы

Не зависит от клиентского кода, использовать обязательно!34 / 35

Page 35: Эффективный MySQL

Что читать

• High Performance MySQL1 — книга по оптимизации• PerconaConf 2013 slides2, особенно доклады пооптимизации

• MySQL Documents by Rick James3, нетривиальные рецептыпо решению различных проблем

• Percona Toolkit4

• Common Schema5

1http://shop.oreilly.com/product/9780596101718.do2http://form.percona.com/PLMCE13Slides.html3http://http://mysql.rjweb.org/4http://www.percona.com/software/percona-toolkit5https://code.google.com/p/common-schema/

35 / 35