前言

我几乎从来没有遇到过性能问题, 毕竟项目真的太小了. 一般上只要用常识去建 index 就可以了.

但是这并不阻碍我对知识的追求. 这篇是关于一些性能优化和原理的内容. 纯属学习, 希望未来有机会用到.

参考

SQL Server – 树结构 (二叉树, 红黑树, B-树, B+树) (必看)

Fish Li – 看懂SqlServer查询计划 (必看)

Sql Server中的表访问方式Table Scan, Index Scan, Index Seek (必看)

预读, 物理读, 逻辑读 (必看)

YouTube – Join Pattern (必看)

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)的更多相关文章

  1. SQL Server 执行计划中的扫描方式举例说明

    SQL Server 执行计划中的扫描方式举例说明 原文地址:http://www.cnblogs.com/zihunqingxin/p/3201155.html 1.执行计划使用方式 选中需要执行的 ...

  2. sql server 执行计划(execution plan)介绍

    大纲:目的介绍sql server 中执行计划的大致使用,当遇到查询性能瓶颈时,可以发挥用处,而且带有比较详细的学习文档和计划,阅读者可以按照我计划进行,从而达到对执行计划一个比较系统的学习. 什么是 ...

  3. SQL Server 执行计划操作符详解(2)——串联(Concatenation )

    本文接上文:SQL Server 执行计划操作符详解(1)--断言(Assert) 前言: 根据计划,本文开始讲述另外一个操作符串联(Concatenation),读者可以根据这个词(中英文均可)先幻 ...

  4. SQL Server 执行计划缓存

    标签:SQL SERVER/MSSQL SERVER/数据库/DBA/内存池/缓冲区 概述 了解执行计划对数据库性能分析很重要,其中涉及到了语句性能分析与存储,这也是写这篇文章的目的,在了解执行计划之 ...

  5. SQL Server执行计划那些事儿(3)——书签查找

    接下来的文章是记录自己曾经的盲点,同时也透漏了自己的发展历程(可能发展也算不上,只能说是瞎混).当然,一些盲点也在工作和探究过程中慢慢有些眉目,现在也愿意发扬博客园的奉献精神,拿出来和大家分享一下. ...

  6. SQL Server执行计划那些事儿(2)——查找和扫描

    接下来的文章是记录自己曾经的盲点,同时也透漏了自己的发展历程(可能发展也算不上,只能说是瞎混).当然,一些盲点也在工作和探究过程中慢慢有些眉目,现在也愿意发扬博客园的奉献精神,拿出来和大家分享一下. ...

  7. 引用:初探Sql Server 执行计划及Sql查询优化

    原文:引用:初探Sql Server 执行计划及Sql查询优化 初探Sql Server 执行计划及Sql查询优化 收藏 MSSQL优化之————探索MSSQL执行计划 作者:no_mIss 最近总想 ...

  8. SQL Server 执行计划操作符详解(3)——计算标量(Compute Scalar)

    接上文:SQL Server 执行计划操作符详解(2)--串联(Concatenation ) 前言: 前面两篇文章介绍了关于串联(Concatenation)和断言(Assert)操作符,本文介绍第 ...

  9. SQL SERVER 执行计划各字段注释

    SET SHOWPLAN_ALL使 Microsoft® SQL Server™ 不执行 Transact-SQL 语句.相反,SQL Server 返回有关语句执行方式和语句预计所需资源的详细信息. ...

  10. 学习如何看懂SQL Server执行计划(一)——数据查询篇

    一.数据查询部分 1. 看到执行计划有两种方式,对sql语句按Ctrl+L,或按Ctrl+M打开显示执行计划窗口每次执行sql都会显示出相应的执行计划 2. 执行计划的图表是从右向左看的 3. SQL ...

随机推荐

  1. C++11标准库<chrono><future> <atomic><condition_variable><mutex><thread>梳理 (5万字)

    <chrono> C++11中提供了日期和时间相关的库chrono. chrono库主要包含三种类型的类:时间间隔duration.时钟clocks.时间点time point. 时间间隔 ...

  2. yearrecord——一个类似痕迹墙的React数据展示组件

    介绍一下自己做的一个类似于力扣个人主页提交记录和GitHub主页贡献记录的React组件. 下图分别是力扣个人主页提交记录和GitHub个人主页的贡献记录,像这样类似痕迹墙的形式可以比较直观且高效得展 ...

  3. [oeasy]python0140_导入_import_from_as_namespace_

    导入import 回忆上次内容 上次学习了 try except   注意要点 半角冒号 缩进 输出错误信息   有错就报告 不要隐瞒 否则找不到出错位置 还可以用traceback把 系统报错信息原 ...

  4. Masked Popcount 题解

    背景 罚了一发,太菜了.为什么我终于有时间的时候她要考试? 题意 给你 \(n,m\),问 \(\sum_{i=0}^{n}popcount(i \&m)\). 其中 \(\&\) 代 ...

  5. Fusion Compute install

    分区选择默认 配置网络 (使用tab和上下左右 会有红色阴影表示当前选中部分) 密码有复杂度要求 这里输huawei12#$ 一个vrm单节点 两个vrm为主备 FC由vrm与can组成 Vrm提供管 ...

  6. python私有变量和方法

    python私有变量和方法 1,私有变量和私有方法无法被继承 2,私有变量和私有方法可以放在普通方法(实例方法)里面被子类继承 class A: def __init__(self, name): s ...

  7. Jmeter函数助手14-TestPlanName

    TestPlanName函数获取当前测试计划保存的文件名称.该函数没有参数,直接引用即可${__TestPlanName}.

  8. 【VMware】虚拟机 VMware WorkStation Pro 下载安装(Windows)

    官网地址: 下载地址:[VMware WorkStation Pro 15.5 For Windows] https://www.vmware.com/cn/products/workstation- ...

  9. 【Redis】05 持久化

    持久化概述 Redis提供了不同的持久性选项: 1.RDB持久性按指定的时间间隔执行数据集的时间点快照. 2.AOF持久性会记录服务器接收的每个写入操作,这些操作将在服务器启动时再次播放,以重建原始数 ...

  10. 如何将AI模型与CAE(计算机辅助工程)结合 —— AI大模型能否用于CAE有限元分析和数值模拟仿真的工业软件领域?

    引自: https://www.zhihu.com/question/611863569/answer/3271029434?utm_id=0 有限元分析中的三个要素,几何模型,本构模型和边界条件. ...