ser*_*erg 47 mysql sql performance
我在表中有两个索引字段 - type和userid(单个索引,而不是复合索引).
types字段值非常有限(假设它只有0或1),因此50%的表记录具有相同的值type.userid另一方面,值来自更大的集合,因此具有相同userid值的记录量很小.
这些查询中的任何一个都会比另一个运行得更快:
select * from table where type=1 and userid=5
select * from table where userid=5 and type=1
Run Code Online (Sandbox Code Playgroud)
此外,如果两个字段都没有编入索引,它会改变行为吗?
Jim*_*ans 60
SQL被设计为声明性语言,而不是程序性语言.因此,查询优化器应该不会考虑在决定如何应用它们的where子句谓词的顺序.
我可能会过度简化以下对SQL查询优化器的讨论.我在一年前写过这样的文章(这很有趣!).如果您真的想深入了解现代查询优化,请参阅O'Reilly的Dan Tow的SQL Tuning.
在简单的SQL查询优化器中,SQL语句首先被编译到关系代数操作树中.这些操作每个都将一个或多个表作为输入,并生成另一个表作为输出. 扫描是从数据库中读取表的顺序扫描. Sort生成一个已排序的表.Select生成一个表,根据某些选择条件从另一个表中选择行.Project生成一个只包含另一个表的某些列的表. Cross Product采用两个表并生成一个输出表,该输出表由每行可能的配对组成.
令人困惑的是,SQL SELECT子句被编译成关系代数Project,而WHERE子句变成了关系代数Select.FROM子句变成一个或多个连接,每个连接占用两个表并生成一个表.还有其他关系代数操作涉及集合,交集,差异和成员资格,但让我们保持这个简单.
这棵树真的需要优化.例如,如果您有:
select E.name, D.name
from Employee E, Department D
where E.id = 123456 and E.dept_id = D.dept_id
Run Code Online (Sandbox Code Playgroud)
在500个部门拥有5,000名员工,执行未经优化的树将盲目地生成一个员工和一个部门(一个交叉产品)的所有可能组合,然后选择所需的一个组合.该扫描员工会产生一个5000记录表中,扫描系将产生500记录表中,跨产品的两个表会产生250万记录表,并选择在E.id将采取2500000记录表和丢弃除了一个之外的所有记录.
[当然,查询处理器会尽量不在内存中实现所有这些中间表.]
因此,查询优化器遍历树并应用各种优化.一种方法是将每个选择分解成一组选择,一个用于原始选择的顶级条件,一个和一起.(这称为"联合正规形式".)然后,单个较小的选择在树中移动并与其他关系代数运算合并以形成更有效的选择.
在上面的示例中,优化器首先将Select on E.id = 123456 推到昂贵的Cross Product操作下方.这意味着Cross Product只生产500行(该员工和一个部门的每个组合各一行).然后顶级选择 E.dept_id = D.dept_id过滤出499个不需要的行.不错.
如果有一个关于雇员的ID字段上的索引,则优化可以结合扫描与员工的选择上E.id = 123456形成一个快速的索引查找.这意味着只有一个Employee行从磁盘而不是5,000读入内存.事情在好转.
最后的主要优化是选择 E.dept_id = D.dept_id,并将其与Cross Product结合使用.这将它变成了关系代数Equijoin操作.这本身并没有太大作用.但是如果在Department.dept_id上有一个索引,则可以将提供Equijoin的较低级别顺序Scan of Department 转换为我们一个员工的部门记录的非常快速的索引查找.
较少的优化涉及推动项目运营.如果查询的最高级别只需要E.name和D.name,并且条件需要E.id,E.dept_id和D.dept_id,那么扫描操作不必构建包含所有其他的中间表列,在查询执行期间节省空间.我们将一个非常缓慢的查询转换为两个索引查找而不是其他.
更多地关注原始问题,让我们说你得到了:
select E.name
from Employee E
where E.age > 21 and E.state = 'Delaware'
Run Code Online (Sandbox Code Playgroud)
未经优化的关系代数树在执行时将扫描5,000名员工,并生成比特拉华州中超过21岁的126名员工.查询优化器还对数据库中的值有一些粗略的了解.它可能知道E.state列具有公司所在位置的14个状态,以及有关E.age分布的信息.所以首先它会看到是否索引了任何一个字段.如果是E.state,那么使用该索引来挑选查询处理器怀疑在特拉华州根据其上次计算的统计数据的少数雇员是有意义的.如果只有E.age,查询处理器可能会认为它不值得,因为96%的员工都是22岁以上.因此,如果E.state被编入索引,我们的查询处理器会中断Select并将E.state ='Delaware'与Scan合并,将其转换为效率更高的Index Scan.
让我们说在这个例子中,E.state和E.age上没有索引.组合的Select操作发生在Employee的连续"Scan"之后.首先完成选择中的哪个条件会有所不同吗?可能不是很多.查询处理器可能会将它们保留在SQL语句中的原始顺序中,或者它可能会更复杂并查看预期的开销.从统计数据来看,它会再次发现E.state ='Delaware'条件应该更具选择性,因此它会颠倒条件并首先执行此操作,因此只有126个E.age> 21个比较而不是5,000个.或者它可能意识到字符串相等比较比整数比较昂贵得多,并且单独保留顺序.
无论如何,所有这些都是非常复杂的,你的句法条件顺序不太可能有所作为.除非您遇到真正的性能问题并且数据库供应商使用条件顺序作为提示,否则我不担心它.
And*_*mar 10
大多数查询优化器使用条件显示为提示的顺序.如果其他条件相同,他们将遵循该顺序.
但是,许多事情可以覆盖:
varchar(max)vs int)所以(对于所有SQL优化问题都是如此)除非你观察到性能问题,否则最好是为了清晰度而不是(想象的)性能进行优化.
它不应该在你的小例子中.查询优化器应该做正确的事情.您可以通过添加explain到查询的前面来确认.MySQL将告诉你如何将它们连接在一起以及为了进行连接需要搜索多少行.例如:
explain select * from table where type=1 and userid=5
如果它们没有编入索引,则可能会改变行为.