PostgreSQL学习系列—EXPLAIN ANALYZE查询计划解读

PostgreSQL命令EXPLAIN ANALYZE是日常工作中了解和优化SQL查询过程所用到的最强大工具,后接如SELECT ...UPDATE ...或者DELETE ...等SQL语句,命令执行后并不返回数据,而是输出查询计划,详细说明规划器通过何种方式来执行给定的SQL语句。

下面是从Postgres Using EXPLAIN提取的查询:

EXPLAIN ANALYZE SELECT *
FROM tenk1 t1, tenk2 t2
WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2
ORDER BY t1.fivethous;

它生成的查询计划:

                                                                 QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=717.34..717.59 rows=101 width=488) (actual time=7.761..7.774 rows=100 loops=1)
   Sort Key: t1.fivethous
   Sort Method: quicksort  Memory: 77kB
   ->  Hash Join  (cost=230.47..713.98 rows=101 width=488) (actual time=0.711..7.427 rows=100 loops=1)
         Hash Cond: (t2.unique2 = t1.unique2)
         ->  Seq Scan on tenk2 t2  (cost=0.00..445.00 rows=10000 width=244) (actual time=0.007..2.583 rows=10000 loops=1)
         ->  Hash  (cost=229.20..229.20 rows=101 width=244) (actual time=0.659..0.659 rows=100 loops=1)
               Buckets: 1024  Batches: 1  Memory Usage: 28kB
               ->  Bitmap Heap Scan on tenk1 t1  (cost=5.07..229.20 rows=101 width=244) (actual time=0.080..0.526 rows=100 loops=1)
                     Recheck Cond: (unique1 < 100)
                     ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=101 width=0) (actual time=0.049..0.049 rows=100 loops=1)
                           Index Cond: (unique1 < 100)
 Planning time: 0.194 ms
 Execution time: 8.008 ms

Postgres构建了一个规划节点的树结构,以表示所采取的不同操作,其中root根和每个->指向其中一个操作。在某些情况下,EXPLAIN ANALYZE会提供除执行时间和行数之外的额外执行统计信息,例如上面例子中的SortHash。除第一个没有->的行之外的任何行都是诸如此类的信息,因此查询的结构是:

Sort
└── Hash Join
    ├── Seq Scan
    └── Hash
        └── Bitmap Heap Scan
            └── Bitmap Index Scan

每个树分支代表子动作,从里到外以确定哪个是“第一个”发生(尽管同一级别的节点顺序可能不同)。

tenk_unique1索引上执行的第一个操作是Bitmap Index Scan

->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=101 width=0) (actual time=0.049..0.049 rows=100 loops=1)
      Index Cond: (unique1 < 100)

这对应于SQL WHERE t1.unique1 < 100。Postgres查找与条件unique1 < 100匹配的行位置。此处不会返回行数据本身。成本估算(cost=0.00..5.04 rows=101 width=0)意味着Postgres预期将“花费” 任意计算单位的 5.04 来找到这些行。0.00是此节点开始工作的成本(在这种情况下,即为查询的启动时间)。rows是此索引扫描将返回的预估行数,width是这些返回行的预估大小(以字节为单位)(0是因为这里只关心位置,而不是行数据的内容)。

因为使用了ANALYZE选项运行EXPLAIN,所以查询被实际执行并捕获了计时信息。(actual time=0.049..0.049 rows=100 loops=1)表示索引扫描执行了1次(loops 值),结果返回了100行,实际时间是0 ..如果节点执行了多次,实际时间是每次迭代的平均值,可以将该值乘以循环次数以获取实际时间。基于成本的最小/最大时间的概念,范围值也可能会有所不同。通过这些值,我们可以为该查询生成一个成本比率,每个成本单位为0.049ms / 5.04单位≈0.01ms/单位。

索引扫描的结果将传递给Bitmap Heap Scan操作。在此节点中,Postgres将获取别名为t1的tenk1表中行的位置,根据unique1 < 100条件筛选并获取行。

->  Bitmap Heap Scan on tenk1 t1  (cost=5.07..229.20 rows=101 width=244) (actual time=0.080..0.526 rows=100 loops=1)
      Recheck Cond: (unique1 < 100)

当乘以之前计算的0.01值时,我们可以得到成本预期的大概时间(229.20 - 5.07)*0.01≈2.24ms,同时每行实际时间为除以4后的结果:0.526ms。这可能是因为成本估算是取的上限而不是取所有需读取的行,也或者因为Recheck条件总是生效。

和表顺序读取行(a Seq Scan)相比,Bitmap Index ScanBitmap Heap Scan关联操作成本要昂贵得多,但是因为在这种情况下只需要访问相对较少的行,所以关联操作最终会变得更快。通过在获取行之前将行按照物理顺序排序来进一步加速,这会将单独获取的成本降到最低。节点名称中的“Bitmap”完成了排序操作。

表扫描的结果(tenk1表中满足unique1 < 100条件的那些行)将在读取时被插入到内存的哈希表中。正如我们从成本中看到的那样,这根本不需要时间。

->  Hash  (cost=229.20..229.20 rows=101 width=244) (actual time=0.659..0.659 rows=100 loops=1)
      Buckets: 1024  Batches: 1  Memory Usage: 28kB

哈希节点包括散列桶(hash buckets)和批次数(batches)相关的信息,以及内存使用峰值情况。如果批次> 1,则还会包括未显示的磁盘使用信息。内存占用在100行* 244字节= 24.4 kB时是有意义的,它非常接近28kB,我们假定这是哈希键本身所占用的内存。

接下来,Postgres从别名为t2的tenk2表读取所有的10000行,并根据tenk1表行的Hash检查它们。散列连接意味着将一个表的行输入到内存中的散列(先前的操作中已构建),之后扫描另一个表的行,并根据散列表探测其值以进行匹配。在第二行可以看到“匹配”的条件,Hash Cond: (t2.unique2 = t1.unique2)。请注意,因为查询是从tenk1和tenk2中选择所有值,所以在散列连接期间每行的宽度加倍。

->  Hash Join  (cost=230.47..713.98 rows=101 width=488) (actual time=0.711..7.427 rows=100 loops=1)
      Hash Cond: (t2.unique2 = t1.unique2)
      ->  Seq Scan on tenk2 t2  (cost=0.00..445.00 rows=10000 width=244) (actual time=0.007..2.583 rows=10000 loops=1)

现在已经收集了满足条件的所有行,可以对结果集进行排序Sort Key: t1.fivethous

Sort  (cost=717.34..717.59 rows=101 width=488) (actual time=7.761..7.774 rows=100 loops=1)
  Sort Key: t1.fivethous
  Sort Method: quicksort  Memory: 77kB

Sort节点包含排序算法quicksort相关的信息 ,排序是在内存中还是在磁盘上完成(这将极大地影响速度),以及排序所需的内存/磁盘空间量。

熟悉如何解读查询计划会非常有助于优化查询。例如,Seq Scan节点通常表示添加索引的必要性,读取速度可能要快得多。

翻译并编辑,原文出处:https://thoughtbot.com/blog/reading-an-explain-analyze-query-plan

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342