首先这里要简要说明,该文章翻译自 mysql手册,并经过我的整合,所以文字内容较多,但是实际含金量比较高, 建议大家仔细阅读, 必有收获。
1、函数调用优化
MySQL还根据参数的类型决定何时计算函数,参数是表列还是常量值。以表列为参数的确定性函数必须在该列更改值时进行计算。
不确定性函数可能会影响查询性能。例如,某些优化可能不可用,或者可能需要更多的锁定。下面的讨论使用RAND(),但也适用于其他不确定性函数。
假设表t有这样的定义:
CREATE TABLE t (id INT NOT NULL PRIMARY KEY, col_a VARCHAR(100));
考虑这两个查询:
SELECT * FROM t WHERE id = POW(1,2);
SELECT * FROM t WHERE id = FLOOR(1 + RAND() * 49);
两个查询似乎都使用了主键查找,因为与主键进行了相等性比较,但这只适用于第一个查询:
- 第一个查询总是最多产生一行,因为带有常量参数的POW()是一个常量值,用于索引查找。
- 第二个查询包含一个表达式,使用不确定性函数RAND(),这不是常数查询中但实际上有一个新值为每一行的表t。因此,查询读取表的每一行,评估每一行的谓词,并输出所有行主键匹配的随机值。这可能是0行、1行或多行,具体取决于id列值和RAND()序列中的值。
不确定性的影响不仅限于SELECT语句。这个UPDATE语句使用了一个不确定的函数来选择要修改的行:
UPDATE t SET col_a = some_expr WHERE id = FLOOR(1 + RAND() * 49);
据推测,目的是最多更新主键与表达式匹配的一行。但是,它可能会更新零行、一行或多行,具体取决于 id列值和RAND()序列中的值 。
刚刚描述的行为对性能和复制有影响:
由于非确定性函数不会产生常量值,因此优化器无法使用可能适用的策略,例如索引查找。结果可能是表扫描。
InnoDB 可能会升级为范围键锁,而不是为一个匹配的行使用单行锁。
不能确定性执行的更新对于复制是不安全的。
困难来自这样一个事实:RAND()函数对表中的每一行求值一次。要避免多个函数计算,请使用以下技术之一:
- 将包含不确定性函数的表达式移动到单独的语句中,将值保存在变量中。在原来的语句中,将表达式替换为对变量的引用,优化器可以将其视为常量值:
SET @keyval = FLOOR(1 + RAND() * 49);
UPDATE t SET col_a = some_expr WHERE id = @keyval;
- 将随机值赋给派生表中的变量。这种方法使变量在WHERE子句中进行比较之前被赋值一次:
SET optimizer_switch = 'derived_merge=off';
UPDATE t, (SELECT @keyval := FLOOR(1 + RAND() * 49)) AS dt
SET col_a = some_expr WHERE id = @keyval;
- 如前所述,WHERE子句中的不确定性表达式可能会阻止优化并导致表扫描。但是,如果其他表达式是确定性的,则可以部分优化WHERE子句。例如:
SELECT * FROM t WHERE partial_key=5 AND some_column=RAND();
2、避免全表扫描
当MySQL使用全表扫描来解析查询时,EXPLAIN的输出在type列中显示ALL。这种情况通常发生在以下情况下:
-
该表非常小,执行表扫描比费心查找键要快。这对于行数少于 10 行且行长较短的表很常见。
-
索引列的ON或WHERE子句中没有可用的限制 。
-
您正在将索引列与常量值进行比较,并且 MySQL 已经计算出(基于索引树)常量覆盖了表的太大部分,并且表扫描会更快。请参阅 “WHERE 子句优化”。
-
您正在通过另一列使用基数较低的键(许多行与键值匹配)。在这种情况下,MySQL 假定通过使用键可能会执行许多键查找并且表扫描会更快。
对于小表,表扫描通常是合适的,性能影响可以忽略不计。对于大表,请尝试以下技术以避免优化器错误地选择表扫描:
-
使用
ANALYZE TABLE tbl_name
更新被扫描表的键分布。参见“ANALYZE TABLE Statement”。 -
对扫描的表使用
FORCE INDEX
,告诉MySQL表扫描比使用给定的索引昂贵,参考“索引提示”。
SELECT * FROM t1, t2 FORCE INDEX (index_for_column)
WHERE t1.col_name=t2.col_name;
- 使用
--max-seek -for-key=1000
选项启动mysqld,或者使用SET max_seeks_for_key=1000
来告诉优化器假设没有键扫描导致超过1000个键寻找。参见“服务器系统变量”。