SQL Server – 执行计划和各种 join 方式 (Execution plan & Join Pattern)
前言
我几乎从来没有遇到过性能问题, 毕竟项目真的太小了. 一般上只要用常识去建 index 就可以了.
但是这并不阻碍我对知识的追求. 这篇是关于一些性能优化和原理的内容. 纯属学习, 希望未来有机会用到.
参考
SQL Server – 树结构 (二叉树, 红黑树, B-树, B+树) (必看)
Fish Li – 看懂SqlServer查询计划 (必看)
Sql Server中的表访问方式Table Scan, Index Scan, Index Seek (必看)
预读, 物理读, 逻辑读 (必看)
YouTube – How do SQL Indexes Work
What, When, Why?
什么是 Execution Plan?
execution plan 里头包含了 query 执行时的各种 information, 比如 IO 速度, 查找了多少 rows 等等
为什么要看 Execution Plan?
当 query 慢的时候, 可以通过分析 execution plan, 知道它为什么慢, 然后做优化.
怎样优化?
优化的方法有很多, 但绝大部分的情况加 index (索引) 就可以了.
总结
当我们发现 query 慢的时候就查看 execution plan, 然后添加 index, 通常它就会变快了.
Step by Step
开启 execution plan
跑 query
select * from Test where FirstName = '781F9AB8';
看结果
execution plan 的阅读方式是 右到左 上到下
Table Scan
table scan 是最慢的, 它就是把所有 data 都 read 出来一个一个匹配, 完全没有利用到 B+ tree 的优势.
遇到这种情况就加 index.
query "where FirstName" 想查找的是 FirstName, 那就加 index 到 FirstName.
Non Clustered Index Scan
假设我们在 FirstName 加了 non clustered index.
然后 query
select FiestName from Test;
注意, 我没有放 where 条件.
结果
这里用了一个 non clustered index scan. 虽然有 index, 但其实是很慢的. 因为 scan 就表示全部扫一遍.
只是从扫 table data 变成了扫 index. index 比 table data 小, 扫起来确实会快一些, 但依然没有利用到 B+ tree 的优势.
Clustered Index Scan
有 scan 就表示扫到完. 自然它也快不到哪去. 而且 clustered index 意味着它就是 data. 所以 clustered index scan 和 table scan 可以说是等价的.
只有在一个表没有 clustered index 的情况下 (heap table) 它才会是 table scan, 一旦有 clustered index 那就是 clustered index scan.
p.s 图中的 Parallelism 是并行执行的意思, 利用了多核 CPU
Index Seek 和 Key Lookup
在 FirstName 加上 index 以后, query 带 where
select * from Test where FirstName = 'Derrick';
结果
index seek 表示用到了 index 查找, 这个才真的有善用 B+ Tree 优势. 通常优化到这个 level 就能接受了.
另外, 之所以会有 key lookup 是因为 我 select all
B+ Tree 的 data 是放在最低层树叶的. 通过上层的 index 虽然定位到了 data. 但是依然需要去最底层 read data.
这个 key lookup 指的就是这个操作过程.
如果我把 query 换成
select FirstName from Test where FirstName = '781F9AB8';
那这个 key lookup 就没了. 因为 index 已经有 FirstName data, 就不用再到叶子节点 read data 了.
我们也可以把更多 data 写入 index 来优化掉 key lookup (但通常是没有必要的, 因为 key loopup 也没有很慢)
p.s 还有一个词叫 RID lookup (row id 的意思), 它和 key loopup 差不多意思, 只是它出现在 heap table.
Clustered Index Seek
select * from Test where FirstName = '781F9AB8';
结果
clustered index seek 是最快的. 即使是 select all 也不需要 key lookup 就可以直接定位获取 data 了.
clustered index 和 non clustered index 的区别是, clustered 是 data 本身在磁盘物理的排序. 一个表只能有一个 clustered index 一种排法.
non clustered index 则是目录只有 key 没有 data, 一个表可以做很多目录, 查找时先找目录, 然后再去 read data.
总结
通常
table scan > clustered index scan > index scan > index seek > clustered index seek
scan 表示扫到完, 不管是扫 data 还是 index 都慢
seek 表示利用了 B+ Tree 二分查找, 所以快
loopup 是因为 non clustered index 只保存了 target column data, 其余 row column data 在叶节点, 所以 seek 了之后还需要 read other data from leaf.
为什么说是 "通常" 呢?
因为并不是所有情况下都是这样的. 有时候表小, 直接 scan table 反而更快.
又比如 bit column, 就是男/女. 这种情况你即使 seek 用上了 B+ Tree 二分查找也快不到哪去.
所以我们做优化的步骤是先看哪一个 query 慢.
快的 query, 哪怕它是 table scan 也不用管它.
发现慢的 query 后, 就看它是 scan 还是 seek. 然后尝试加 index 让它变成其它的方式. then 在跑跑看是否有提速.
预读, 物理读, 逻辑读 (read-ahead reads, logical reads, physical reads)
除了看 execution plan 的 scan, seek 这些. 还有一个重要的指标是看缓存.
我们知道 IO 是很慢的 (从磁盘读取数据), 因为这个是物理操作, 需要移动磁头,
而从内存或缓存中读取数据是很快的, 因为不算物理操作, 它只是通通电.
所以我们要尽量确保 query 的时候不要从磁盘读取数据.
Clear cache
为了测试方便, 我们每次 query 的时候先 clear 干净 cache
DBCC DROPCLEANBUFFERS --清空执行计划缓存
DBCC FREEPROCCACHE --清空数据缓存
开启 statistics (侦测工具)
SET STATISTICS IO ON; -- OFF 就关闭
query
select * from Test where FirstName = '0000007B';
结果
SQL Server 读取最小单位是 page, 一个 page = 8kb.
physical reads 3 表示从磁盘读了 3 次, 也就是 3 x 8kb = 24kb 的数据.
logical reads 是从缓存读取
read-ahead reads 是预读. 当 query 发出后, SQL Server 会先制作执行计划, 与此同时为了不浪费时间, 它会先去预读一些数据.
当执行计划做好后, 就执行. 这时如果 logical reads 不能满足所有结果, 那么就会去 physical reads.
当我们第二次运行相同 query 就会发现 physical reads 0. 因为之前的数据已经被缓存了 (除非我们跑上面的 clear cache command)
总结
physical reads 太多肯定就慢, 至于如何优化...我不知道, 看着办呗.
Join and Loop Pattern
除了 read row data, 连表也很讲究性能. SQL Server 有 3 种连表方式.
这 3 种方式在不同的前提下, 性能表现会不一样, SQL Server 会依据不同情况选择不同的方式.
比如表大小, 表是否已经有排序等等
Nested Loop Join
nested loop join 的过程是 for loop A 表, 拿每一条 row 再去 for loop B 表做 match.
假设 A, B 表都有 100 rows. 那么 100 x 100 就需要 10000 次 compare.
这种方式适合用在 A 表比较小, B 表有 index 的情况. 这 2 点可以大大降低找的次数.
Merge Join
merge join 的过程是先拿 AB 表做一个排序 (注意排序是很慢的)
然后 loop A 表, 拿 row 去 match B 表.
由于有排序了, 所以不需要检索所有的 B rows. 只要匹配到比较大的就可以回到 A 表下一个了.
这种方式适合用在 AB 表已经有排序好的情况 (比如 AB 表都有相同的 index)
Hash Join
hash join 的过程是先拿 AB 表每一条 row 做哈希算法.然后放入哈希表.
哈希的特色是可以算出位置. 相同位置的就表示同值.
这种方式适合用在, 你需要完全找出 AB 表所有匹配的情况. 因为有些时候我们可能只需要找出几个 A 表匹配.
那么 nested loop 会比较合适. hash 一定要全部 row 都做完才能出结果, 不像 nested loop 找一个是一个.
总结
三种方式都有它适合的场景. 同样的, 我们做优化首先是看它是否慢, 然后才想办法 (加 index) 让它换一个方式试试看是否提速.
小 tips
在做分析时, 经常需要一些 command
DBCC DROPCLEANBUFFERS --清空执行计划缓存
DBCC FREEPROCCACHE --清空数据缓存
SET STATISTICS IO ON;
SET STATISTICS TIME ON; left join TestChild WITH (INDEX([IX_TestChild_TestId])) --强制使用 index -- 强制 join pattern
left hash join
left merge join
left loop join
SQL Server – 执行计划和各种 join 方式 (Execution plan & Join Pattern)的更多相关文章
- SQL Server 执行计划中的扫描方式举例说明
SQL Server 执行计划中的扫描方式举例说明 原文地址:http://www.cnblogs.com/zihunqingxin/p/3201155.html 1.执行计划使用方式 选中需要执行的 ...
- sql server 执行计划(execution plan)介绍
大纲:目的介绍sql server 中执行计划的大致使用,当遇到查询性能瓶颈时,可以发挥用处,而且带有比较详细的学习文档和计划,阅读者可以按照我计划进行,从而达到对执行计划一个比较系统的学习. 什么是 ...
- SQL Server 执行计划操作符详解(2)——串联(Concatenation )
本文接上文:SQL Server 执行计划操作符详解(1)--断言(Assert) 前言: 根据计划,本文开始讲述另外一个操作符串联(Concatenation),读者可以根据这个词(中英文均可)先幻 ...
- SQL Server 执行计划缓存
标签:SQL SERVER/MSSQL SERVER/数据库/DBA/内存池/缓冲区 概述 了解执行计划对数据库性能分析很重要,其中涉及到了语句性能分析与存储,这也是写这篇文章的目的,在了解执行计划之 ...
- SQL Server执行计划那些事儿(3)——书签查找
接下来的文章是记录自己曾经的盲点,同时也透漏了自己的发展历程(可能发展也算不上,只能说是瞎混).当然,一些盲点也在工作和探究过程中慢慢有些眉目,现在也愿意发扬博客园的奉献精神,拿出来和大家分享一下. ...
- SQL Server执行计划那些事儿(2)——查找和扫描
接下来的文章是记录自己曾经的盲点,同时也透漏了自己的发展历程(可能发展也算不上,只能说是瞎混).当然,一些盲点也在工作和探究过程中慢慢有些眉目,现在也愿意发扬博客园的奉献精神,拿出来和大家分享一下. ...
- 引用:初探Sql Server 执行计划及Sql查询优化
原文:引用:初探Sql Server 执行计划及Sql查询优化 初探Sql Server 执行计划及Sql查询优化 收藏 MSSQL优化之————探索MSSQL执行计划 作者:no_mIss 最近总想 ...
- SQL Server 执行计划操作符详解(3)——计算标量(Compute Scalar)
接上文:SQL Server 执行计划操作符详解(2)--串联(Concatenation ) 前言: 前面两篇文章介绍了关于串联(Concatenation)和断言(Assert)操作符,本文介绍第 ...
- SQL SERVER 执行计划各字段注释
SET SHOWPLAN_ALL使 Microsoft® SQL Server™ 不执行 Transact-SQL 语句.相反,SQL Server 返回有关语句执行方式和语句预计所需资源的详细信息. ...
- 学习如何看懂SQL Server执行计划(一)——数据查询篇
一.数据查询部分 1. 看到执行计划有两种方式,对sql语句按Ctrl+L,或按Ctrl+M打开显示执行计划窗口每次执行sql都会显示出相应的执行计划 2. 执行计划的图表是从右向左看的 3. SQL ...
随机推荐
- tp5 为什么使用单例模式
首先我们要知道明确单例模式这个概念,那么什么是单例模式呢?单例模式顾名思义,就是只有一个实例.作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例, 这个类我们 ...
- django信号中的条件判断不符合时如何提示错误并返回
在Django中,如果你在信号(Signal)处理函数中需要进行条件判断,如果条件不符合,你可以触发一个异常,并在视图或其他地方捕获这个异常,然后返回相应的错误提示. 以下是一个简单的例子,演示如何在 ...
- P6680 [CCO2019] Marshmallow Molecules 题解
P6680 题意 一个 \(n\) 点 \(m\) 边的图,图无重边,无自环. 满足这样一条性质:如果三边互不相等,则三边可以构成三角形. 思路 思路简单,用集合的思想来做. 引用一下 K0stlin ...
- vscode添加python文件头模板
pycharm可以自动生成python的文件头模板,但是vscode目前还不可以(不支持python,c的似乎有插件支持了).琢磨了一下,可以通过用户代码片段来实现. 1. 什么是用户代码片段 参考文 ...
- 轻量级SpringBoot Office文档在线预览框架
框架简介 介绍:基于开源项目KkFileView源码提取出,封装成仅用于 Office文档预览(格式转换) 功能的一个通用组件; 原理是把Word转成PDF,PPT转成PDF,Excel转成HTML; ...
- SQL提高查询性能的几种方式
创建索引,提高性能 索引可以极大地提高查询性能,其背后的原理: 索引是的数据库引擎能够快速的找到表中的数据,它们类似于书籍的目录,使得你不需要逐页查找所需要的信息 索引能够帮助数据库引擎直接定位到所需 ...
- 【Java】【常用类】Calendar 日历类
Calendar 日历类,我居然念错发音,来,好好看下音标 ['kælɪndə] 卡琳达 public class DateTest { public static void main(Strin ...
- 计算机论文中的SD SE是什么 ?
Standard Deviation (SD) and Standard Error (SE): 标准差和标准误差:在统计学和数据分析中常用来描述数据的分布和估计值的精确性.SD 表示标准差,SE 表 ...
- gpg 密钥的导入、导出
参考: gpg 密钥生成.导入.导出.自动输入密码 How do I delete secret subkeys correctly? 注意: 本文不对GPG的操作做详细介绍,有深入了解者自行参考:g ...
- 人类社会学:“重男轻女"思潮的比重,从多孩男女性别比角度思考
参考资料: https://baijiahao.baidu.com/s?id=1780697594797038227 https://mbd.baidu.com/newspage/data/video ...