Slides da palestra realizada no Dia da Liberdade do Software 2016. Esta palestra mostra algumas maneiras de se obter mais performance com o banco de dados MySQL.
2. Marcelo Altmann
Técnologo em Sistemas para Internet
MySQL DBA @ IEDR
Blogueiro - blog.marceloaltmann.com
Oracle ACE Associate - MySQL
Oracle Certified Professional , MySQL 5.6 Database Administrator
Oracle Certified Professional , MySQL 5 Database Administrator
11. Configuração - Innodb Buffer Pool
● Monitorar: SHOW GLOBAL STATUS LIKE ‘Innodb_buffer_pool_read%’ :
○ Innodb_buffer_pool_reads - Leituras feitas direto no disco
○ Innodb_buffer_pool_read_requests - Leituras feitas na memória
● Innodb_buffer_pool_size (Padrão: 128M)
● Innodb_buffer_pool_dump_at_shutdown
● innodb_buffer_pool_load_at_startup
31. Índices - Full table scan
CREATE TABLE `city` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Name` char(35) NOT NULL DEFAULT '',
`CountryCode` char(3) NOT NULL DEFAULT '',
`District` char(20) NOT NULL DEFAULT '',
`Population` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1;
32. Índices - Full table scan
mysql> EXPLAIN SELECT Name, Population FROM city WHERE CountryCode='BRA' AND Population > 1000000G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: city
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4188
Extra: Using where
1 row in set (0.00 sec)
33. Índices - Full table scan
mysql> EXPLAIN SELECT Name, Population FROM city WHERE CountryCode='BRA' AND Population > 1000000G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: city
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4188
Extra: Using where
1 row in set (0.00 sec)
34. Índices - Left most part
ALTER TABLE city ADD KEY leftMost(CountryCode, Population, District);
mysql> EXPLAIN SELECT Name, Population FROM city WHERE CountryCode='BRA' AND
Population > 1000000G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: city
type: range
possible_keys: leftMost
key: leftMost
key_len: 7
ref: NULL
rows: 13
Extra: Using index condition
1 row in set (0.00 sec)
35. Índices - Left most part
ALTER TABLE city ADD KEY leftMost(CountryCode, Population, District);
mysql> EXPLAIN SELECT Name, Population FROM city WHERE CountryCode='BRA' AND
Population > 1000000G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: city
type: range
possible_keys: leftMost
key: leftMost
key_len: 7
ref: NULL
rows: 13
Extra: Using index condition
1 row in set (0.00 sec)
36. Índices - Left most part
mysql> EXPLAIN FORMAT=JSON SELECT Name, Population FROM city WHERE CountryCode='BRA' AND Population > 1000000;
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "city",
"access_type": "range",
"possible_keys": [
"CountryCode"
],
"key": "CountryCode",
"used_key_parts": [
"CountryCode",
"Population"
],
"key_length": "7",
"rows": 13,
"filtered": 100,
"index_condition": "((`world`.`city`.`CountryCode` = 'BRA') and (`world`.`city`.`Population` > 1000000))"
}
}
}
37. Índices - Covered index
ALTER TABLE city ADD KEY covered (CountryCode, Population, Name);
mysql> EXPLAIN SELECT Name, Population FROM city WHERE CountryCode='BRA' AND
Population > 1000000G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: city
type: range
possible_keys: covered
key: covered
key_len: 7
ref: NULL
rows: 13
Extra: Using where; Using index
1 row in set (0.00 sec)
38. Índices - Covered index
ALTER TABLE city ADD KEY covered (CountryCode, Population, Name);
mysql> EXPLAIN SELECT Name, Population FROM city WHERE CountryCode='BRA' AND
Population > 1000000G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: city
type: range
possible_keys: covered
key: covered
key_len: 7
ref: NULL
rows: 13
Extra: Using where; Using index
1 row in set (0.00 sec)
40. Tabelas temporárias
● GROUP BY
● UNION
● SUBQUERY + WHERE
● Memória: Memory Engine
● Disco:
○ 5.6 - MyIsam Engine
○ 5.7 - Innodb Engine
● Memory Engine:
○ Não tem suporte a campos BLOB / TEXT
● https://dev.mysql.com/doc/refman/5.6/en/internal-temporary-tables.html
41. Tabelas temporárias
● tmp_table_size - tamanho máximo de uma tabela temporária
● max_head_table_size - tamanho máximo de uma tabela que utiliza memory engine
● Tamanho tabela temporária:
○ Maior que tmp_table_size ou max_head_table_size = criada em disco
● Monitorar:
mysql> SHOW GLOBAL STATUS LIKE 'Created_tmp%tables';
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 3 |
| Created_tmp_tables | 17 |
+-------------------------+-------+
43. Identificação de queries
● SHOW [FULL] PROCESSLIST
● Slow query log
● performance_schema.events_statements_summary_by_digest
44. Identificação de queries - Processlist
● Mostra as conexões atuais e seus status
mysql> show processlist;
+----+------+-----------+-----------+---------+------+----------------+-----------------------------------------------------------------------------------------
---------+
| Id | User | Host | db | Command | Time | State | Info
|
+----+------+-----------+-----------+---------+------+----------------+-----------------------------------------------------------------------------------------
---------+
| 8 | root | localhost | employees | Query | 4 | Writing to net | SELECT MAX(salary) FROM salaries JOIN employees USING (emp_no) WHERE gender = 'M' GROUP
BY emp_no |
| 9 | root | localhost | employees | Query | 0 | init | show processlist
|
+----+------+-----------+-----------+---------+------+----------------+-----------------------------------------------------------------------------------------
---------+
2 rows in set (0.00 sec)
46. Identificação de queries - Slow Query
# Time: 160908 17:38:01
# User@Host: root[root] @ localhost [] Id: 2
# Query_time: 1.127100 Lock_time: 0.000852 Rows_sent: 1000 Rows_examined: 297918
use employees;
SET timestamp=1473370681;
SELECT d.dept_name AS 'Dept', CONCAT(em.last_name, ' ', em.first_name) AS 'Manager last, first', CONCAT(e.last_name,' ',
e.first_name, ' ', t.title) AS 'Employee last, first (title)' FROM dept_manager AS dm LEFT JOIN dept_emp AS de ON de.dept_no
= dm.dept_no LEFT JOIN departments AS d ON d.dept_no = dm.dept_no LEFT JOIN employees AS e ON e.emp_no = de.emp_no LEFT JOIN
employees AS em ON em.emp_no = dm.emp_no LEFT JOIN titles AS t ON t.emp_no = e.emp_no WHERE dm.emp_no = e.emp_no AND
dept_name = 'Sales' OR dept_name = 'Marketing' AND dm.to_date >= '2012-05-07' AND t.to_date > '2012-05-07' AND de.to_date >
'2012-05-07' ORDER BY e.last_name, e.first_name limit 1000;
# Time: 160908 17:38:27
# User@Host: root[root] @ localhost [] Id: 2
# Query_time: 4.236115 Lock_time: 0.000377 Rows_sent: 179973 Rows_examined: 2366291
SET timestamp=1473370707;
SELECT MAX(salary) FROM salaries JOIN employees USING(emp_no) WHERE gender = 'M' GROUP BY emp_no;
# Time: 160908 17:38:33
# User@Host: root[root] @ localhost [] Id: 2
# Query_time: 3.268998 Lock_time: 0.000349 Rows_sent: 179973 Rows_examined: 2366291
SET timestamp=1473370713;
SELECT MAX(salary) FROM salaries JOIN employees USING(emp_no) WHERE gender = 'M' GROUP BY emp_no;
47. Identificação de queries - performance schema
● Ativado por padrão desde a versão 5.6.6
● DIGEST:
○ SELECT Name, Population FROM city WHERE CountryCode='BRA' AND Population > 1000000;
○ SELECT Name, Population FROM city WHERE CountryCode='USA' AND Population > 5000000;
○ SELECT Name , Population FROM city WHERE CountryCode = ? AND Population > ?;
● Possibilita identificar queries :
○ Não utilizam index:
SELECT DIGEST_TEXT FROM events_statements_summary_by_digest WHERE SUM_NO_INDEX_USED > 0;
○ Usam tabelas temporárias no disco:
SELECT DIGEST_TEXT FROM events_statements_summary_by_digest WHERE SUM_CREATED_TMP_DISK_TABLES > 0;