http://dev.mysql.com/doc/refman/5.7/en/cost-model.html
DECISION_CONST = 0.2
if( (INDEX_SIZE/BUFFER_POOL_SIZE) < DECISION_CONST ){
in_memory_estimation = 1.0
}else if( (INDEX_SIZE/BUFFER_POOL_SIZE) > 1.0 ){
in_memory_estimation = 0.0
}else{
in_memory_estimation = 1.0 –
(((INDEX_SIZE/BUFFER_POOL_SIZE) - DECISION_CONST) / (1.0 - DECISION_CONST))
}
mysql> EXPLAIN FORMAT=JSON
select * from emp where name between 'a' and 'b';
...
"table": {
"table_name": "emp",
"access_type": "range",
"possible_keys": [
"ix_name"
],
...
"key_length": "53",
"rows_examined_per_scan": 169,
"rows_produced_per_join": 169,
"filtered": "100.00",
"index_condition": "(`test`.`emp`.`name` between 'a' and 'b‘..
"cost_info": {
"read_cost": "203.81",
"eval_cost": "33.80",
"prefix_cost": "237.61",
"data_read_per_join": "10K"
}...
mysql> EXPLAIN FORMAT=JSON
select * from emp;
...
"table": {
"table_name": "emp",
"access_type": "ALL",
"rows_examined_per_scan": 3083,
"rows_produced_per_join": 3083,
"filtered": "100.00",
"cost_info": {
"read_cost": "10.00",
"eval_cost": "616.60",
"prefix_cost": "626.60",
"data_read_per_join": "192K"
},
...
set optimizer_trace="enabled=on";
select * from emp where name in ('matt', 'smith') and dept_no=2;
select * from information_schema.optimizer_trace;
set optimizer_trace="enabled=off";
"rows_estimation": [
{
"table": "`emp`",
"range_analysis": {
"table_scan": {
"rows": 3083,
"cost": 628.7
},
"potential_range_indexes": [
{
"index": "PRIMARY",
"usable": false,
"cause": "not_applicable"
},
{
"index": "ix_name",
"usable": true, ...
},
{
"index": "ix_deptno",
"usable": true, ...
}
],
"range_scan_alternatives": [
{
"index": "ix_name",
"ranges": [...],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 2,
"cost": 4.41,
"chosen": true
},
{
"index": "ix_deptno",
"ranges": [...],
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": false,
"rows": 276,
"cost": 332.21,
"chosen": false,
"cause": "cost"
}
],...
"chosen_range_access_summary": {
"range_access_plan": {
"type": "range_scan",
"index": "ix_name",
"rows": 2,
"ranges": [...]
},
"rows_for_plan": 2,
"cost_for_plan": 4.41,
"chosen": true
}
Access Path
of
Driving Table
Driving
Table
Driven
Table
JoinCondition
Filtering
Access Path
of
Driving Table
JoinCondition
Filtering
Driving
Table
Driven
Table
SELECT *
FROM t1 AS t1a JOIN t1 AS t1b ON t1a.idx_col=t1b.idx_col
WHERE t1b.non_idx_col=5;
---+-------+------+---------+---------+-------------+------+------------
id | table | type | key | key_len | ref | rows | Extra
---+-------+------+---------+---------+-------------+------+------------
1 | t1a | ALL | NULL | NULL | NULL | 1000 | Using where
1 | t1b | ref | idx_col | 5 | t1a.idx_col | 8 | Using where
---+-------+------+---------+---------+-------------+------+------------
Operation | Fanout-ratio
----------+----------------------------------------
= | MAX(0.005, 1/rowcount)
<=> | SEL(=)
<,<= | MAX(1/3, 1/rowcount)
>, >= | MAX(1/3, 1/rowcount)
BETWEEN | MAX(1/9, 1/rowcount)
LIKE | SEL(BETWEEN)
IS NULL | SEL(=)
NOT OP | 1 - SEL(OP)
Fulltext | SEL(=)
AND | P(A and B) = P(A) * P(B)
OR | P(A or B) = P(A) + P(B) - P(A and B)
<col> IN | MIN(#items_in_list * SEL(=), 1/2)
<list> IN | SEL(col_1 IN) * ... * SEL(col_n IN)
XOR | P(A) + P(B) - 2*P(A and B)
mysql> EXPLAIN SELECT * FROM employee
-> WHERE hire_date BETWEEN "2012-01-01" AND "2012-06-01" AND
-> first_name="John";
+----+----------+------+---------------+------+-------+------+----------+
| id | table | type | possible_keys | key | ref | rows | filtered |
+----+----------+------+---------------+------+-------+------+----------+
| 1 | employee | ref | name,h_date | name | const | 8 | 16.31 |
+----+----------+------+---------------+------+-------+------+----------+
mysql> EXPLAIN
-> SELECT *
-> FROM employee JOIN department ON employee.dept_no=department.dept_no
-> WHERE employee.first_name="John" AND
-> employee.hire_date BETWEEN "2012-01-01" AND "2012-06-01";
+----+------------+--------+------------------+---------+---------+------+----------+
| id | table | type | possible_keys | key | ref | rows | filtered |
+----+------------+--------+------------------+---------+---------+------+----------+
| 1 | employee | ref | name,h_date,dept | name | const | 8 | 16.31 |
| 1 | department | eq_ref | PRIMARY | PRIMARY | dept_no | 1 | 100.00 |
+----+------------+--------+------------------+---------+---------+------+----------+
SELECT fd1, fd2, fd3 FROM tab WHERE fd4=‘MATT';
-> After rewrite
SELECT fd1, fd2, fd3 FROM tab USE INDEX (ix_fd4) WHERE fd4=‘MATT';
SELECT * FROM table_a UNION ALL SELECT * FROM table_b;
SELECT * FROM tab WHERE (fd1, fd2) IN ( (1,2), (2,3), (3,4) );
-- // MySQL 5.6
-- // -> Optimizer have to check
SELECT * FROM tab WHERE (fd1=1 AND fd2=2) OR (fd1=2 AND fd2=3) OR (fd1=3 AND fd2=4);
-- // c.f. even MySQL 5.7mysql> EXPLAIN SELECT * FROM emp FORCE INDEX(ix_name_deptno) WHERE (name, dept_no) > ('z', 10000);
+----+-------------+-------+------+------+------+------+----------+-------------+
| id | select_type | table | type | key | ref | rows | filtered | Extra |
+----+-------------+-------+------+------+------+------+----------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | 3083 | 100.00 | Using where |
+----+-------------+-------+------+------+------+------+----------+-------------+
mysql> SELECT COUNT(*) FROM emp FORCE INDEX(ix_name_deptno) WHERE (name, dept_no) > ('z', 10000);
-> 2
SELECT *
FROM (SELECT * FROM t1 WHERE fd1=1) AS derived
WHERE ...
-- // -> Merge to outer query
SELECT * FROM t1 WHERE fd1=1 AND ...
performance_schema_events_stages_history_size=10
performance_schema_events_statements_history_size=10
performance_schema_events_waits_history_size=10
These are not NOT index hints, Optimizer hints