Оптимизатор запросов
в MariaDB 10.0 —
теперь и без индексов!
Сергей Голубчик
MariaDB
Об этом докладе
●

Engine Independent Statistics

●

Что это такое

●

Зачем это нужно

●

Как считается

●

Где хранится

●

Примеры, примеры, примеры
Статистика данных: как это было
●

Оптимизатор спрашивает движок:
●

«сколько примерно строк в таблице?»

●

«сколько стоит просканировать всю таблицу?»

●

«сколько стоит прочитать столько-то строк из такого-то индекса?»

●

●

«сколько раз в среднем каждое значение встречается в таком-то
индексе?»

«сколько значений в таком-то индексе попадает в диапазон от… и
до…?»
Пример: нестабильность
●

DBT-3: сложные аналитические запросы
●

Q7: Volume Shipping Query

●

6 InnoDB таблиц
– 7 разных планов
– 12 минут, 25 минут, часы (таймаут)
Q8: National Market Share Query
–

–
–
–

8 InnoDB таблиц
4 разных плана
от 7 минут до 1.2 часа
Пример: неправда
MariaDB [test]> CREATE TABLE t1 (a INT, b INT, c INT, KEY(a,b)) ENGINE=MyISAM;
MariaDB [test]> INSERT t1 VALUES (RAND()*1e5,RAND()*1e5,RAND()*1e5);
... 400 000 rows ...
MariaDB [test]> SELECT COUNT(DISTINCT a) AS cardinality FROM t1;
+-------------+
| cardinality |
+-------------+
|
97794 |
+-------------+
MariaDB [test]> SELECT cardinality FROM information_schema.statistics
--> WHERE table_name='t1' AND column_name='a';
+-------------+
| cardinality |
+-------------+
|
98304 |
+-------------+
MariaDB [test]> ALTER TABLE t1 ENGINE=InnoDB;
MariaDB [test]> SELECT cardinality FROM information_schema.statistics
--> WHERE table_name='t1' AND column_name='a';
+-------------+
| cardinality |
+-------------+
|
196914 |
+-------------+
Независимая статистика
●

стабильная

●

точная

●

одинаковая для всех движков
Как это сделано
●

новые таблицы в mysql

●

конфигурационные переменные

●

расширенная команда ANALYZE TABLE
Таблицы
●

mysql.table_stats

●

mysql.index_stats

●

mysql.column_stats
Переменные
●

@@use_stat_tables
●
●

complementary

●
●

never
preferably

@@optimizer_use_condition_selectivity
●

1 … 5
Команда ANALYZE TABLE
●

ANALYZE TABLE table_name

●

ANALYZE TABLE table_name PERSISTENT FOR ALL

●

ANALYZE TABLE table_name PERSISTENT FOR
COLUMNS (column1, column2, ...)

INDEXES (index1, index2, ...)
Что считается
●

по таблицам
●

●

количество строк

по индексам
●

обратная мощность
Что считается
●

по столбцам
●

минимальное и максимальное значения

●

процент NULL-ов

●

средняя длина значения

●

обратная мощность

●

распределение значений (гистограмма)
Пример
CREATE TABLE t1 (a INT, b INT);
INSERT t1 SELECT RAND()*1e5, RAND()*1e5;
. . . . . . . . . .
-- 512 тысяч строк
CREATE TABLE t2 (c INT, d INT, KEY(c), KEY(d));
INSERT t2 SELECT FLOOR(RAND()*1e5/2)*2, FLOOR(RAND()*1e5/3)*3 FROM t1;
CREATE TABLE t3 (e INT, f INT, KEY(f));
INSERT t3 SELECT * FROM t2;
ANALYZE TABLE t3;
SET OPTIMIZER_USE_CONDITION_SELECTIVITY=1;
SELECT * FROM t1, t2, t3 WHERE t3.f=t1.a AND t2.c=t1.a AND t3.e<5;
--> 1075 rows in set (1 min 19.25 sec)
SET OPTIMIZER_USE_CONDITION_SELECTIVITY=3;
SELECT * FROM t1, t2, t3 WHERE t3.f=t1.a AND t2.c=t1.a AND t3.e<5;
--> 1075 rows in set (13.17 sec)
Пример
MariaDB [test]> . employees.sql
MariaDB [employees]> ANALYZE TABLE departments, dept_emp, dept_manager,
-> employees, salaries, titles;
MariaDB [employees]> SET OPTIMIZER_USE_CONDITION_SELECTIVITY=1;
Query OK, 0 rows affected (0.01 sec)
MariaDB [employees]> SELECT * FROM departments JOIN dept_emp USING (dept_no)
-> JOIN employees USING (emp_no) JOIN titles USING (emp_no) WHERE title='Manager';
24 rows in set (15.13 sec)
MariaDB [employees]> SET OPTIMIZER_USE_CONDITION_SELECTIVITY=3;
Query OK, 0 rows affected (0.01 sec)
MariaDB [employees]> SELECT * FROM departments JOIN dept_emp USING (dept_no)
-> JOIN employees USING (emp_no) JOIN titles USING (emp_no) WHERE title='Manager';
24 rows in set (0.53 sec)
Пример
MariaDB [employees]> EXPLAIN EXTENDED SELECT * FROM departments JOIN dept_emp USING
(dept_no)
-> JOIN employees USING (emp_no) JOIN titles USING (emp_no) WHERE title='Manager';
+------+-------------+--------+---------+-------+----------+-----------------------+
| id
| table
| type
| key
| rows | filtered | Extra
|
+------+-------------+--------+---------+-------+----------+-----------------------+
|
1 | departments | ALL
| NULL
|
9 |
100.00 |
|
|
1 | dept_emp
| ref
| dept_no | 36844 |
100.00 |
|
|
1 | employees
| eq_ref | PRIMARY |
1 |
100.00 |
|
|
1 | titles
| ref
| PRIMARY |
1 |
100.00 | Using index condition |
+------+-------------+--------+---------+-------+----------+-----------------------+
MariaDB [employees]> SET OPTIMIZER_USE_CONDITION_SELECTIVITY=3;
MariaDB [employees]> EXPLAIN EXTENDED SELECT * FROM departments JOIN dept_emp USING
(dept_no)
-> JOIN employees USING (emp_no) JOIN titles USING (emp_no) WHERE title='Manager';
+------+-------------+--------+---------+--------+----------+-----------------------------| id
| table
| type
| key
| rows
| filtered | Extra
+------+-------------+--------+---------+--------+----------+-----------------------------|
1 | titles
| ALL
| NULL
| 443308 |
14.29 | Using where
|
1 | employees
| eq_ref | PRIMARY |
1 |
100.00 |
|
1 | dept_emp
| ref
| PRIMARY |
1 |
100.00 |
Неравномерное распределение
●

Таблица скачиваний какого-то продукта N

●

Пусть, пользователи могут регистрироваться
–

и, где-то 1% на самом деле это делает
… WHERE Registered = 'YES' …

990 000

YES

1,000,000 rows
среднее количество строк на значение: 500,000

NO

10 000
Registered
Гистограммы
●

histogram_size=20 или любое число больше нуля

●

optimizer_use_condition_selectivity=4 или 5

●

histogram_type=single_prec_hb или double_prec_hb
Гистограммы равной высоты
Пример
MariaDB [employees]> SET OPTIMIZER_USE_CONDITION_SELECTIVITY=3;
Query OK, 0 rows affected (0.00 sec)
MariaDB [employees]> SELECT * FROM employees JOIN salaries
-> USING (emp_no) JOIN titles USING (emp_no) WHERE salary > 100000;
166014 rows in set (18.79 sec)

MariaDB [employees]> SET OPTIMIZER_USE_CONDITION_SELECTIVITY=4;
Query OK, 0 rows affected (0.00 sec)
MariaDB [employees]> SELECT * FROM employees JOIN salaries
-> USING (emp_no) JOIN titles USING (emp_no) WHERE salary > 100000;
166014 rows in set (6.64 sec)
Пример
MariaDB [employees]> EXPLAIN EXTENDED SELECT * FROM employees JOIN salaries
-> USING (emp_no) JOIN titles USING (emp_no) WHERE salary > 100000;
+------+-----------+--------+---------+--------+----------+-------------+
| id
| table
| type
| key
| rows
| filtered | Extra
|
+------+-----------+--------+---------+--------+----------+-------------+
|
1 | titles
| ALL
| NULL
| 443308 |
100.00 |
|
|
1 | employees | eq_ref | PRIMARY |
1 |
100.00 |
|
|
1 | salaries | ref
| PRIMARY |
9 |
48.68 | Using where |
+------+-----------+--------+---------+--------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)
MariaDB [employees]> SET OPTIMIZER_USE_CONDITION_SELECTIVITY=4;
Query OK, 0 rows affected (0.00 sec)
MariaDB [employees]> EXPLAIN EXTENDED SELECT * FROM employees JOIN salaries
-> USING (emp_no) JOIN titles USING (emp_no) WHERE salary > 100000;
+------+-----------+--------+---------+---------+----------+-------------+
| id
| table
| type
| key
| rows
| filtered | Extra
|
+------+-----------+--------+---------+---------+----------+-------------+
|
1 | salaries | ALL
| NULL
| 2844047 |
4.76 | Using where |
|
1 | employees | eq_ref | PRIMARY |
1 |
100.00 |
|
|
1 | titles
| ref
| PRIMARY |
1 |
100.00 |
|
+------+-----------+--------+---------+---------+----------+-------------+
3 rows in set, 1 warning (0.01 sec)
Планы
●

Другие типы гистограмм (например, равной ширины)

●

Гистограммы для индексов (из нескольких столбцов!)

●

Учет корреляций между столбцами
Вопросы?
Спасибо за внимание!

Cергей Голубчик, Monty Program AB

  • 1.
    Оптимизатор запросов в MariaDB10.0 — теперь и без индексов! Сергей Голубчик MariaDB
  • 2.
    Об этом докладе ● EngineIndependent Statistics ● Что это такое ● Зачем это нужно ● Как считается ● Где хранится ● Примеры, примеры, примеры
  • 3.
    Статистика данных: какэто было ● Оптимизатор спрашивает движок: ● «сколько примерно строк в таблице?» ● «сколько стоит просканировать всю таблицу?» ● «сколько стоит прочитать столько-то строк из такого-то индекса?» ● ● «сколько раз в среднем каждое значение встречается в таком-то индексе?» «сколько значений в таком-то индексе попадает в диапазон от… и до…?»
  • 4.
    Пример: нестабильность ● DBT-3: сложныеаналитические запросы ● Q7: Volume Shipping Query ● 6 InnoDB таблиц – 7 разных планов – 12 минут, 25 минут, часы (таймаут) Q8: National Market Share Query – – – – 8 InnoDB таблиц 4 разных плана от 7 минут до 1.2 часа
  • 5.
    Пример: неправда MariaDB [test]>CREATE TABLE t1 (a INT, b INT, c INT, KEY(a,b)) ENGINE=MyISAM; MariaDB [test]> INSERT t1 VALUES (RAND()*1e5,RAND()*1e5,RAND()*1e5); ... 400 000 rows ... MariaDB [test]> SELECT COUNT(DISTINCT a) AS cardinality FROM t1; +-------------+ | cardinality | +-------------+ | 97794 | +-------------+ MariaDB [test]> SELECT cardinality FROM information_schema.statistics --> WHERE table_name='t1' AND column_name='a'; +-------------+ | cardinality | +-------------+ | 98304 | +-------------+ MariaDB [test]> ALTER TABLE t1 ENGINE=InnoDB; MariaDB [test]> SELECT cardinality FROM information_schema.statistics --> WHERE table_name='t1' AND column_name='a'; +-------------+ | cardinality | +-------------+ | 196914 | +-------------+
  • 6.
  • 7.
    Как это сделано ● новыетаблицы в mysql ● конфигурационные переменные ● расширенная команда ANALYZE TABLE
  • 8.
  • 9.
  • 10.
    Команда ANALYZE TABLE ● ANALYZETABLE table_name ● ANALYZE TABLE table_name PERSISTENT FOR ALL ● ANALYZE TABLE table_name PERSISTENT FOR COLUMNS (column1, column2, ...) INDEXES (index1, index2, ...)
  • 11.
    Что считается ● по таблицам ● ● количествострок по индексам ● обратная мощность
  • 12.
    Что считается ● по столбцам ● минимальноеи максимальное значения ● процент NULL-ов ● средняя длина значения ● обратная мощность ● распределение значений (гистограмма)
  • 13.
    Пример CREATE TABLE t1(a INT, b INT); INSERT t1 SELECT RAND()*1e5, RAND()*1e5; . . . . . . . . . . -- 512 тысяч строк CREATE TABLE t2 (c INT, d INT, KEY(c), KEY(d)); INSERT t2 SELECT FLOOR(RAND()*1e5/2)*2, FLOOR(RAND()*1e5/3)*3 FROM t1; CREATE TABLE t3 (e INT, f INT, KEY(f)); INSERT t3 SELECT * FROM t2; ANALYZE TABLE t3; SET OPTIMIZER_USE_CONDITION_SELECTIVITY=1; SELECT * FROM t1, t2, t3 WHERE t3.f=t1.a AND t2.c=t1.a AND t3.e<5; --> 1075 rows in set (1 min 19.25 sec) SET OPTIMIZER_USE_CONDITION_SELECTIVITY=3; SELECT * FROM t1, t2, t3 WHERE t3.f=t1.a AND t2.c=t1.a AND t3.e<5; --> 1075 rows in set (13.17 sec)
  • 14.
    Пример MariaDB [test]> .employees.sql MariaDB [employees]> ANALYZE TABLE departments, dept_emp, dept_manager, -> employees, salaries, titles; MariaDB [employees]> SET OPTIMIZER_USE_CONDITION_SELECTIVITY=1; Query OK, 0 rows affected (0.01 sec) MariaDB [employees]> SELECT * FROM departments JOIN dept_emp USING (dept_no) -> JOIN employees USING (emp_no) JOIN titles USING (emp_no) WHERE title='Manager'; 24 rows in set (15.13 sec) MariaDB [employees]> SET OPTIMIZER_USE_CONDITION_SELECTIVITY=3; Query OK, 0 rows affected (0.01 sec) MariaDB [employees]> SELECT * FROM departments JOIN dept_emp USING (dept_no) -> JOIN employees USING (emp_no) JOIN titles USING (emp_no) WHERE title='Manager'; 24 rows in set (0.53 sec)
  • 15.
    Пример MariaDB [employees]> EXPLAINEXTENDED SELECT * FROM departments JOIN dept_emp USING (dept_no) -> JOIN employees USING (emp_no) JOIN titles USING (emp_no) WHERE title='Manager'; +------+-------------+--------+---------+-------+----------+-----------------------+ | id | table | type | key | rows | filtered | Extra | +------+-------------+--------+---------+-------+----------+-----------------------+ | 1 | departments | ALL | NULL | 9 | 100.00 | | | 1 | dept_emp | ref | dept_no | 36844 | 100.00 | | | 1 | employees | eq_ref | PRIMARY | 1 | 100.00 | | | 1 | titles | ref | PRIMARY | 1 | 100.00 | Using index condition | +------+-------------+--------+---------+-------+----------+-----------------------+ MariaDB [employees]> SET OPTIMIZER_USE_CONDITION_SELECTIVITY=3; MariaDB [employees]> EXPLAIN EXTENDED SELECT * FROM departments JOIN dept_emp USING (dept_no) -> JOIN employees USING (emp_no) JOIN titles USING (emp_no) WHERE title='Manager'; +------+-------------+--------+---------+--------+----------+-----------------------------| id | table | type | key | rows | filtered | Extra +------+-------------+--------+---------+--------+----------+-----------------------------| 1 | titles | ALL | NULL | 443308 | 14.29 | Using where | 1 | employees | eq_ref | PRIMARY | 1 | 100.00 | | 1 | dept_emp | ref | PRIMARY | 1 | 100.00 |
  • 16.
    Неравномерное распределение ● Таблица скачиванийкакого-то продукта N ● Пусть, пользователи могут регистрироваться – и, где-то 1% на самом деле это делает … WHERE Registered = 'YES' … 990 000 YES 1,000,000 rows среднее количество строк на значение: 500,000 NO 10 000 Registered
  • 17.
    Гистограммы ● histogram_size=20 или любоечисло больше нуля ● optimizer_use_condition_selectivity=4 или 5 ● histogram_type=single_prec_hb или double_prec_hb
  • 18.
  • 19.
    Пример MariaDB [employees]> SETOPTIMIZER_USE_CONDITION_SELECTIVITY=3; Query OK, 0 rows affected (0.00 sec) MariaDB [employees]> SELECT * FROM employees JOIN salaries -> USING (emp_no) JOIN titles USING (emp_no) WHERE salary > 100000; 166014 rows in set (18.79 sec) MariaDB [employees]> SET OPTIMIZER_USE_CONDITION_SELECTIVITY=4; Query OK, 0 rows affected (0.00 sec) MariaDB [employees]> SELECT * FROM employees JOIN salaries -> USING (emp_no) JOIN titles USING (emp_no) WHERE salary > 100000; 166014 rows in set (6.64 sec)
  • 20.
    Пример MariaDB [employees]> EXPLAINEXTENDED SELECT * FROM employees JOIN salaries -> USING (emp_no) JOIN titles USING (emp_no) WHERE salary > 100000; +------+-----------+--------+---------+--------+----------+-------------+ | id | table | type | key | rows | filtered | Extra | +------+-----------+--------+---------+--------+----------+-------------+ | 1 | titles | ALL | NULL | 443308 | 100.00 | | | 1 | employees | eq_ref | PRIMARY | 1 | 100.00 | | | 1 | salaries | ref | PRIMARY | 9 | 48.68 | Using where | +------+-----------+--------+---------+--------+----------+-------------+ 3 rows in set, 1 warning (0.00 sec) MariaDB [employees]> SET OPTIMIZER_USE_CONDITION_SELECTIVITY=4; Query OK, 0 rows affected (0.00 sec) MariaDB [employees]> EXPLAIN EXTENDED SELECT * FROM employees JOIN salaries -> USING (emp_no) JOIN titles USING (emp_no) WHERE salary > 100000; +------+-----------+--------+---------+---------+----------+-------------+ | id | table | type | key | rows | filtered | Extra | +------+-----------+--------+---------+---------+----------+-------------+ | 1 | salaries | ALL | NULL | 2844047 | 4.76 | Using where | | 1 | employees | eq_ref | PRIMARY | 1 | 100.00 | | | 1 | titles | ref | PRIMARY | 1 | 100.00 | | +------+-----------+--------+---------+---------+----------+-------------+ 3 rows in set, 1 warning (0.01 sec)
  • 21.
    Планы ● Другие типы гистограмм(например, равной ширины) ● Гистограммы для индексов (из нескольких столбцов!) ● Учет корреляций между столбцами
  • 22.
  • 23.