热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

您所不了解的Postgres10功能:CREATESTATISTICS(译)

原文地址:https:www.citusdata.comblog20180306postgres-planner-and-its-usage-of-statisti

您所不了解的Postgres 10功能:CREATE STATISTICS(译)

 
原文地址:https://www.citusdata.com/blog/2018/03/06/postgres-planner-and-its-usage-of-statistics/,本文统一将原文中的“planner”译做“优化器”
 
 
 
如果您对Postgres进行了一些性能优化,则可能使用过EXPLAIN。 EXPLAIN向您显示PostgreSQL计划程序为提供的语句生成的执行计划。
它显示了如何扫描语句引用的表(使用顺序扫描,索引扫描等),以及如果使用多个表,将使用哪种联接算法。但是,Postgres是依据什么信息给出执行计划的?

优化器(planner)收集统计数据是决定使用哪种计划的非常重要的参考信息。
这些统计信息使优化器(planner)可以估计执行计划的特定部分后将返回多少行,这将影响执行计划将要使用的扫描或联接算法的类型。
统计主要通过运行ANALYZE或VACUUM(以及一些DDL命令,如CREATE INDEX)来收集/更新它们。

这些统计信息由存储在pg_class和pg_statistics中。 
Pg_class基本上存储每个表和索引中的条目总数,以及它们所占用的磁盘块数。 
Pg_statistic存储有关每个列的统计信息,例如该列的值的为空的百分比,最常见的值是什么,直方图范围等。
您可以在下面的表格中查看以下示例,该示例针对针对col1收集的Postgres统计类型。
下面的查询输出显示,planner(正确)估计表中的col1列有1000个不同的值,并且还对最常见的值,频率等进行其他估计。

请注意,我们已经查询了pg_stats(该视图保存了更易读的列统计信息。)
CREATE TABLE tbl (                                                                        
    col1 int,                                                                             
    col2 int                                                                              
);                                                                                        

INSERT INTO tbl SELECT i/10000, i/100000                                                  
FROM generate_series (1,10000000) s(i);                                                   

ANALYZE tbl;                                     

select * from pg_stats where tablename = "tbl" and attname = "col1";
-[ RECORD 1 ]----------+--------------------------------
schemaname             | public
tablename              | tbl
attname                | col1
inherited              | f
null_frac              | 0
avg_width              | 4
n_distinct             | 1000
most_common_vals       | {318,564,596,...}
most_common_freqs      | {0.00173333,0.0017,0.00166667,0.00156667,...}
histogram_bounds       | {0,8,20,30,39,...}
correlation            | 1
most_common_elems      | 
most_common_elem_freqs | 
elem_count_histogram   | 

单个字段统计信息的不足
这些单列统计信息可帮助优化器(planner)预计筛选条件的选择性(这是计划程序用来估计索引扫描将选择多少行的方法)。
当查询中提供多个条件时,优化器(planner)将假定列(或where子句条件)彼此独立。
当列之间相互关联或存在相互依赖时,情况并非如此,这会使计划者估算或低估了这些条件将返回的行数。(译者注:对于相关性列,优化器预估的比实际数据行数要少)

让我们看下面的几个例子。为了使计划易于阅读,我们通过将max_parallel_workers_per_gather设置为0来关闭每个查询的并行性;
EXPLAIN ANALYZE SELECT * FROM tbl where col1 = 1;                            
                                                QUERY PLAN                                                 
-----------------------------------------------------------------------------------------------------------
 Seq Scan on tbl  (cost=0.00..169247.80 rows=9584 width=8) (actual time=0.641..622.851 rows=10000 loops=1)
   Filter: (col1 = 1)
   Rows Removed by Filter: 9990000
 Planning time: 0.051 ms
 Execution time: 623.185 ms
(5 rows)
如您在此处看到的,优化器(planner)估计col1的值为1的行数为9584,查询返回的实际行数为10000。因此,非常准确。
但是,当您在第1列和第2列中都包含过滤器时,会发生什么情况。
EXPLAIN ANALYZE SELECT * FROM tbl where col1 = 1 and col2 = 0;                            
                                                QUERY PLAN                                                
----------------------------------------------------------------------------------------------------------
 Seq Scan on tbl  (cost=0.00..194248.69 rows=100 width=8) (actual time=0.640..630.130 rows=10000 loops=1)
   Filter: ((col1 = 1) AND (col2 = 0))
   Rows Removed by Filter: 9990000
 Planning time: 0.072 ms
 Execution time: 630.467 ms
(5 rows)
优化器(planner)的估算已经降低了100倍!让我们尝试了解为什么会发生这种情况。
第一列的选择性约为0.001(1/1000),第二列的选择性为0.01(1/100),为了计算将被这两个“独立”条件过滤的行数,计划器将其选择性乘以。
因此,我们得到:选择性= 0.001 * 0.01 = 0.00001。
将其乘以表中的行数,即10000000,我们得到100。这就是计划者估计的100的来源。
如果这些列不是独立的(有多个列之间存在依赖关系),我们如何告诉优化器(planner)呢?
译者注:
早些年曾经执着地研究过SQLServer对非相关列预估的算法,
类似于pg,SQLServer从预估行数从2012版的p0*p1*p2*p3……*RowCount,演变为P0*P11/2  * P21/4 * P31/8……* RowCount,https://www.cnblogs.com/wy123/p/5790855.html
 
PostgreSQL创建统计表信息
在Postgres 10之前,没有一种简单的方法可以告诉优化器(planner)收集统计数据,这些统计数据捕获了列之间的这种关系。
但是,在Postgres 10中,有一个新功能可以解决此问题。 
CREATE STATISTICS可用于创建扩展的统计对象,这些对象告诉服务器收集有关这些有趣的相关列的额外统计信息。

 

相关列的统计信息
回到我们先前的估计问题,问题在于col2的值实际上只是col的1/10。
译者注:一个表中有两个字段c1和c2,比如c1代表“省份Id”,c2代表“县Id”,这样c1和c2就存在依赖关系。
在数据库术语中,我们可以说col2在功能上取决于col1。这意味着col1的值足以确定col2的值,并且没有两行具有相同的col1值但具有不同的col2值。
因此,col2上的第二个过滤器实际上不会删除任何行!但是,优化器(planner)可以捕获足够的统计信息来了解这一点。
我们创建一个统计对象以捕获有关这些列的功能依赖性统计并运行ANALYZE。
CREATE STATISTICS s1 (dependencies) on col1, col2 from tbl; 
ANALYZE tbl;
让我们看看planner现在提出了什么。
EXPLAIN ANALYZE SELECT * FROM tbl where col1 = 1 and col2 = 0;                            
                                                QUERY PLAN                                                 
-----------------------------------------------------------------------------------------------------------
 Seq Scan on tbl  (cost=0.00..194247.76 rows=9584 width=8) (actual time=0.638..629.741 rows=10000 loops=1)
   Filter: ((col1 = 1) AND (col2 = 0))
   Rows Removed by Filter: 9990000
 Planning time: 0.115 ms
 Execution time: 630.076 ms
(5 rows)
好多了!让我们来看看是什么帮助优化器(planner)做出了这一决定。
SELECT stxname, stxkeys, stxdependencies                                                  
  FROM pg_statistic_ext                                                                   
  WHERE stxname = "s1";   
stxname | stxkeys |   stxdependencies    
---------+---------+----------------------
 s1      | 1 2     | {"1 => 2": 1.000000}
(1 row)
综上所述,我们可以看到Postgres意识到col1完全确定col2,因此捕获该信息的系数为1。现在,所有在这两个列上都具有过滤器的查询将具有更好的估计。
 
非相关列的统计信息(ndistinct statistics)
功能依赖性是可以在列之间捕获的一种关系。您可以捕获的另一种统计数据是一组列的不同值的数量。
前面我们曾提到,计划者为每一列捕获了不同值数量的统计信息,但是当组合多个列时,这些统计信息常常是错误的
译者注:比如一个订单表中有两个字段c1和c2,比如c1代表“UserId”,c2代表订单类型“OrderType”(假如有服饰,食品,3C产品等),很明显,一个用户可以随意购买任何类型的商品,UserId和OrderType之间没有任何依赖关系
糟糕的统计数据何时会伤害我们?让我们来看一个例子。
EXPLAIN ANALYZE SELECT col1,col2,count(*) from tbl group by col1, col2;                   
                                                         QUERY PLAN                                                          
-----------------------------------------------------------------------------------------------------------------------------
 GroupAggregate  (cost=1990523.20..2091523.04 rows=100000 width=16) (actual time=2697.246..4470.789 rows=1001 loops=1)
   Group Key: col1, col2
   ->  Sort  (cost=1990523.20..2015523.16 rows=9999984 width=8) (actual time=2695.498..3440.880 rows=10000000 loops=1)
         Sort Key: col1, col2
         Sort Method: external sort  Disk: 176128kB
         ->  Seq Scan on tbl  (cost=0.00..144247.84 rows=9999984 width=8) (actual time=0.008..665.689 rows=10000000 loops=1)
 Planning time: 0.072 ms
 Execution time: 4494.583 ms
汇总行时,Postgres选择进行哈希汇总或组汇总。如果它适合哈希表在内存中,则选择哈希聚合,否则选择对所有行进行排序,然后根据col1,col2将它们分组。
现在,优化器(planner)估计的数量(等于col1和col2的不同值的数量)将为100000。
它发现它没有足够的work_mem将该哈希表存储在内存中。因此,它使用基于磁盘的排序来运行查询。
但是,正如您在计划的实际部分中看到的那样,实际行数仅为1001。也许,我们有足够的内存来将它们容纳在内存中,并进行哈希聚合。
让我们要求优化器(planner)捕获n_distinct统计信息,然后重新运行查询并找出答案。
CREATE STATISTICS s2 (ndistinct) on col1, col2 from tbl;                                  
ANALYZE tbl;


EXPLAIN ANALYZE SELECT col1,col2,count(*) from tbl group by col1, col2;                   
                                                      QUERY PLAN                                                       
-----------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=219247.63..219257.63 rows=1000 width=16) (actual time=2431.767..2431.928 rows=1001 loops=1)
   Group Key: col1, col2
   ->  Seq Scan on tbl  (cost=0.00..144247.79 rows=9999979 width=8) (actual time=0.008..643.488 rows=10000000 loops=1)
 Planning time: 0.129 ms
 Execution time: 2432.010 ms
(5 rows)
您可以看到估算值现在更加准确(即1000),查询现在快了2倍。通过运行下面的查询,我们可以看到优化器(planner)学到了什么。
SELECT stxkeys AS k, stxndistinct AS nd                                                   
  FROM pg_statistic_ext                                                                   
  WHERE stxname = "s2"; 
  k  |       nd       
-----+----------------
 1 2 | {"1, 2": 1000}
 Real-world implications
在实际的生产模式中,您总是会拥有某些列,而这些列之间具有数据库不知道的相互依存关系。我们与Citus客户一起看到的一些例子是:
  • 由于要在报表中显示按所有人分组的统计信息,因此具有月,季度和年的列。
  • 地理层次结构之间的关系,例如:具有国家,州和城市列,并按它们进行过滤/分组。
此处的示例在数据集中只有1000万行,我们已经看到,使用CREATE统计信息可以在有相关列的情况下显着改善计划,并且还可以提高性能。
在Citus用例中,我们的客户存储着数十亿行数据,而不良计划的后果可能非常严重。
在我们的示例中,当计划者选择了一个糟糕的计划时,我们不得不对1000万行进行基于磁盘的排序,想像一下数十亿行会是多么糟糕。
 
Postgres越来越好
当我们着手构建Citus时,我们明确选择了Postgres作为基础。通过扩展Postgres,我们选择了一个坚实的基础,可以随着每个发行版的不断完善。
因为Citus是纯粹的扩展,而不是分支,所以使用Citus时可以利用每个发行版中的所有出色新功能。
 
享受您正在阅读的内容吗?
如果您有兴趣阅读我们团队的更多帖子,请注册我们的每月时事通讯,并将最新内容直接发送到您的收件箱。
 
 
 

推荐阅读
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • JavaScript设计模式之策略模式(Strategy Pattern)的优势及应用
    本文介绍了JavaScript设计模式之策略模式(Strategy Pattern)的定义和优势,策略模式可以避免代码中的多重判断条件,体现了开放-封闭原则。同时,策略模式的应用可以使系统的算法重复利用,避免复制粘贴。然而,策略模式也会增加策略类的数量,违反最少知识原则,需要了解各种策略类才能更好地应用于业务中。本文还以员工年终奖的计算为例,说明了策略模式的应用场景和实现方式。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 解决VS写C#项目导入MySQL数据源报错“You have a usable connection already”问题的正确方法
    本文介绍了在VS写C#项目导入MySQL数据源时出现报错“You have a usable connection already”的问题,并给出了正确的解决方法。详细描述了问题的出现情况和报错信息,并提供了解决该问题的步骤和注意事项。 ... [详细]
  • 本文详细介绍了MySQL表分区的创建、增加和删除方法,包括查看分区数据量和全库数据量的方法。欢迎大家阅读并给予点评。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
author-avatar
月芽2502915393
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有