mysql自我优化-优化select语句(六)


首先这里要简要说明,该文章翻译自 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个键寻找。参见“服务器系统变量”。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值