本文来自:http://www.023dns.com/Database_mssql/5974.html

PostgreSQL优化器是基于成本的 (CBO) , (当然, 如果开启了GEQO的话, 在关联表数量超过一定阈值后, 会采用GEQO, 这主要是由于在关联表太多的情况下, 穷举法可能带来巨大的PLAN开销, 所以GEQO输出的执行计划不一定是最优的)

  本文要谈的和GEQO没什么关系, 主要是与CBO相关.

  当PostgreSQL使用CBO时, 就一定能每次都输出最优的执行计划吗?

  1. 首选我们看看CBO考察了哪些因素, 它是如何计算成本的?

  成本和扫描方式, 关联方式, 操作符, 成本因子, 数据集等都有关, 具体的计算方法可参考如下代码:

  src/backend/optimizer/path/costsize.c

  我们这里简单的列举一下, 哪些因素会影响成本计算的结果, 具体算法见costsize.c :

  -- 表有多少条记录, 影响全表扫描的 CPU处理记录的COST.

  -- 表有多少个数据块, 影响扫描数据块的成本; 例如全表扫描, 索引扫描, 都需要扫描数据块.

  -- 成本因子, 影响成本的计算结果; 例如连续或随机扫描单个数据块的成本因子, CPU从HEAP块处理一条记录的成本因子, 从INDEX块处理一条索引记录的成本因子, 执行一个操作符或函数的成本因子.

  -- 数据存储物理顺序和索引顺序的离散度, 影响索引扫描的计算成本.

  -- 内存大小, 影响索引扫描的计算成本.

  -- 列统计信息(列宽, 空值比例, 唯一值比例, 高频值及其比例, bucket, 物理顺序和索引顺序的离散度, 数组的话还有数组的统计信息, 等), 影响选择性, 即结果集行数, 最终影响索引扫描的计算成本.

  -- 创建函数或操作符时设置的成本.

  2. 然后我们看看哪些因素CBO没有考虑进去, 还有哪些因素CBO考虑进去了, 但是可能会随时发生变化的.

  PostgreSQL是否能动态的跟上这些变化?

  2.1 PostgreSQL开启自动analyze, 可以适时更新的因素如下 :

  -- 表有多少数据块, 记录数, 更新pg_class.relpages, pg_class.reltuples

  -- 列统计信息, 数据存储物理顺序和索引顺序的离散度, 更新pg_statistic

  2.2 静态配置因素 :

  -- 实际可用作缓存的内存, 因为数据库所在的操作系统中可能还运行了其他程序, 可用作缓存的内存可能会发生变化. 即使没有运行其他程序, 当数据库会话中有大量使用了work_mem时, 也会造成可用做缓存的内存发生变化.

  -- 创建函数或操作符时设置的成本, 当函数因为内部SQL或处理逻辑等变化, 可能导致函数本身的处理时间发生变化.

  2.3 未考虑的因素 :

  -- 块设备的的预读, 一般情况下一次读取时, 会预读128KB的数据.

  # blockdev --getra /dev/sda

  256

  这又有什么影响呢? 如果你要读取的数据在连续的128KB数据块中, 那么只需要一次块设备的IO. 对于数据库来说, 扫描数据时扫多少个数据块可不管这个, 都会计算成本, 因此对于不同的块设备预读配置, 或者对于不同的块设备(如机械盘和SSD), 扫描成本可能不一样. PostgreSQL块设备的性能反映在成本计算方面, 就是seq_page_cost, random_page_cost.

  这两个参数可以针对表空间设置, 也就是说, 对于不同的表空间, 可以设置不同的值, 比如我们有在SSD建立的表空间, 也有在普通机械盘上创建的表空间, 当然需要设置不同的seq_page_cost, random_page_cost值.

  但是对于预读来说, 如果发生了变更, 对实际的性能会有细微的影响, 一般应该不会一天到晚变更块设备的read ahead吧.

  2.4 generic plan cache, 即执行计划缓存.

  PostgreSQL 通过choose_custom_plan选择重新规划执行计划还是使用缓存的执行计划, 当cached plan成本大于custom的平均成本时, 会选择custom plan , 所以当统计信息正确的情况下, 可以及时发现缓存执行计划的问题并及时规划新的执行计划.

  详情请见 : src/backend/utils/cache/plancache.c

  2.5 采样精度参数default_statistics_target , 影响bucket个数, 采样的精度.

  经过一番分析, PostgreSQL使用了CBO, 就一定能"每次"都输出最优的执行计划吗?

  1. 首选要确保人为设置成本因子准确, 另外还需要打开自动analyze(适时更新 列统计信息, 块, 离散度等),

  2. 影响成本的因素还有一些是静态配置的 : 比如可用作BUFFER的内存, 函数的成本.

  3. 还有没考虑的: 预读 (甚微).

  在大多数情况下, 如果我们设置了合理的配置,那么 很少需要使用hint的. 除了以上2,3提到的两点.

  同时hint也存在比较严重的弊端, 如果将hint写在程序代码中, 一旦需要变更执行计划, 还需要改程序代码, 不灵活.

  当然, 我们不排除另一种用HINT的出发点, 比如调试. 我就想看看不同执行计划下执行效率是否和想象的一样.

  (我们也可以使用开关来控制执行计划, 但是有HINT不是更直接一点嘛)

  从长远来看, 如果仅仅从性能角度来说, 不断地改进数据库本身的优化器是比较靠谱的. 但是对于例如调试这样的需求, 有HINT更方便也是对的.

  进入主题, 大多数Oracle用户在接触到PostgreSQL后, 会问PG有没有SQL hint?

  为了让数据库按照用户的想法输出执行计划, 一般来说PostgreSQL提供了一些开关, 比如关闭全表扫描, 让它去走索引.

  关闭索引扫描, 让它去走bitmap或全表扫描, 关闭嵌套循环, 让他去走hash join或merge join等.

  但是仅仅有这些开关, 还不是非常的好用, 那么到底有没有直接点的HINT呢?

  有一个插件可以解决你的问题,:pg_hint_plan.

  pg_hint_plan利用PostgreSQL 开放的hook接口, 所以不需要改PG代码就实现了注入HINT的功能.

  /*

  * Module load callbacks

  */

  void

  _PG_init(void)

  {

  ...

  }

  由于不同PostgreSQL 版本, plan部分的代码可能会不一致, 所以pg_hint_plan也是分版本发布的源码.

  比如我要在PostgreSQL 9.4.1中测试一下这个工具.

  接下来测试一下 :

  安装

  # wget http://iij.dl.sourceforge.jp/pghintplan/62456/pg_hint_plan94-1.1.3.tar.gz

  # tar -zxvf pg_hint_plan94-1.1.3.tar.gz

  # cd pg_hint_plan94-1.1.3

  [root@db-172-16-3-150 pg_hint_plan94-1.1.3]# export PATH=/opt/pgsql/bin:$PATH

  [root@db-172-16-3-150 pg_hint_plan94-1.1.3]# which psql

  /opt/pgsql/bin/psql

  [root@db-172-16-3-150 pg_hint_plan94-1.1.3]# psql -V

  psql (PostgreSQL) 9.4.1

  # gmake clean

  # gmake

  # gmake install

  [root@db-172-16-3-150 pg_hint_plan94-1.1.3]# ll -rt /opt/pgsql/lib|tail -n 1

  -rwxr-xr-x 1 root root 78K Feb 18 09:31 pg_hint_plan.so

  [root@db-172-16-3-150 pg_hint_plan94-1.1.3]# su - postgres

  $ vi $PGDATA/postgresql.conf

  shared_preload_libraries = 'pg_hint_plan'

  pg_hint_plan.enable_hint = on

  pg_hint_plan.debug_print = on

  pg_hint_plan.message_level = log

  $ pg_ctl restart -m fast

  postgres@db-172-16-3-150-> psql

  psql (9.4.1)

  Type "help" for help.

  postgres=# create extension pg_hint_plan;

  CREATE EXTENSION

  用法举例说明 :

  postgres=# create table a(id int primary key, info text, crt_time timestamp);

  CREATE TABLE

  postgres=# create table b(id int primary key, info text, crt_time timestamp);

  CREATE TABLE

  postgres=# insert into a select generate_series(1,100000), 'a_'||md5(random()::text), clock_timestamp();

  INSERT 0 100000

  postgres=# insert into b select generate_series(1,100000), 'b_'||md5(random()::text), clock_timestamp();

  INSERT 0 100000

  postgres=# analyze a;

  ANALYZE

  postgres=# analyze b;

  ANALYZE

  postgres=# explain select a.*,b.* from a,b where a.id=b.id and a.id<10;< p="">

  QUERY PLAN

  -----------------------------------------------------------------------

  Nested Loop (cost=0.58..83.35 rows=9 width=94)

  -> Index Scan using a_pkey on a (cost=0.29..8.45 rows=9 width=47)

  Index Cond: (id < 10)

  -> Index Scan using b_pkey on b (cost=0.29..8.31 rows=1 width=47)

  Index Cond: (id = a.id)

  (5 rows)

  在没有pg_hint_plan时, 我们需要使用开关来改变PostgreSQL的执行计划

  postgres=# set enable_nestloop=off;

  SET

  postgres=# explain select a.*,b.* from a,b where a.id=b.id and a.id<10;< p="">

  QUERY PLAN

  -----------------------------------------------------------------------------

  Hash Join (cost=8.56..1616.65 rows=9 width=94)

  Hash Cond: (b.id = a.id)

  -> Seq Scan on b (cost=0.00..1233.00 rows=100000 width=47)

  -> Hash (cost=8.45..8.45 rows=9 width=47)

  -> Index Scan using a_pkey on a (cost=0.29..8.45 rows=9 width=47)

  Index Cond: (id < 10)

  (6 rows)

  postgres=# set enable_nestloop=on;

  SET

  postgres=# explain select a.*,b.* from a,b where a.id=b.id and a.id<10;< p="">

  QUERY PLAN

  -----------------------------------------------------------------------

  Nested Loop (cost=0.58..83.35 rows=9 width=94)

  -> Index Scan using a_pkey on a (cost=0.29..8.45 rows=9 width=47)

  Index Cond: (id < 10)

  -> Index Scan using b_pkey on b (cost=0.29..8.31 rows=1 width=47)

  Index Cond: (id = a.id)

  (5 rows)

  使用pg_hint_plan来改变PostgreSQL的执行计划,如下所示 :

  postgres=# /*+

  HashJoin(a b)

  SeqScan(b)

  */ explain select a.*,b.* from a,b where a.id=b.id and a.id<10;< p="">

  QUERY PLAN

  -----------------------------------------------------------------------------

  Hash Join (cost=8.56..1616.65 rows=9 width=94)

  Hash Cond: (b.id = a.id)

  -> Seq Scan on b (cost=0.00..1233.00 rows=100000 width=47)

  -> Hash (cost=8.45..8.45 rows=9 width=47)

  -> Index Scan using a_pkey on a (cost=0.29..8.45 rows=9 width=47)

  Index Cond: (id < 10)

  (6 rows)

  postgres=# /*+ SeqScan(a) */ explain select * from a where id<10;< p="">

  QUERY PLAN

  ------------------------------------------------------

  Seq Scan on a (cost=0.00..1483.00 rows=10 width=47)

  Filter: (id < 10)

  (2 rows)

  postgres=# /*+ BitmapScan(a) */ explain select * from a where id<10;< p="">

  QUERY PLAN

  ---------------------------------------------------------------------

  Bitmap Heap Scan on a (cost=4.36..35.17 rows=9 width=47)

  Recheck Cond: (id < 10)

  -> Bitmap Index Scan on a_pkey (cost=0.00..4.36 rows=9 width=0)

  Index Cond: (id < 10)

  (4 rows)

  目前pg_hint_plan支持的HINT

  http://pghintplan.sourceforge.jp/hint_list.html

  The available hints are listed below.

  

  

  

  [参考]

  1. http://pghintplan.sourceforge.jp/pg_hint_plan-en.html

  2. http://pghintplan.sourceforge.jp/pg_hint_plan.html

  3. http://pghintplan.sourceforge.jp/hint_list.html

  4. http://pghintplan.sourceforge.jp/

  5. src/backend/optimizer/path/costsize.c

  6. src/backend/utils/cache/plancache.c

PostgreSQL SQL HINT的使用说明的更多相关文章

  1. hint.css使用说明

    GitHub:http://liu12fei08fei.github.io/html/1hint.html hint.css使用说明 用途 快速实现tooltips提示样式 相关资源 官方网站GitH ...

  2. PostgreSql sql shell win10 下乱码解决

    重现步骤: 打开 SQL Shell (psql) Server [localhost]: Database [postgres]: Port ]: Username [postgres]: psql ...

  3. PostgreSQL SQL优化之NOT IN问题

    在我们平时写SQL时,如果遇到需要排除某些数据时,往往使用id <> xxx and id <> xxx,进而改进为id not in (xxx, xxx); 这样写没有问题, ...

  4. sql: postgreSQL sql script

    SELECT * from pg_class c,pg_attribute a,pg_type t where c.relname='BookKindList' and a.attnum>0 a ...

  5. postgresql sql查询结果添加序号列与每组第一个序号应用

    1.postgresql 查询每组第一个 ROW_NUMBER () OVER (partition by 字段 ORDER BY  字段  DESC) 写法:SELECT  ROW_NUMBER ( ...

  6. sql 连接的使用说明

    SQL中的left outer join,inner join,right outer join用法详解 使用关系代数合并数据 关系代数 合并数据集合的理论基础是关系代数,它是由E.F.Codd于19 ...

  7. postgresql sql修改表,表字段

    1.更改表名 alter table 表名 rename to 新表名 2.更改字段名 alter table 表名 rename 字段名 to 新字段名 3.增加列 ALTER TABLE ud_w ...

  8. postgresql sql语句 更改表名

    SELECT'alter table "public"."'|| t.tablename||'"'||' rename to "'|| "l ...

  9. Citus 分布式 PostgreSQL 集群 - SQL Reference(SQL支持和变通方案)

    由于 Citus 通过扩展 PostgreSQL 提供分布式功能,因此它与 PostgreSQL 结构兼容.这意味着用户可以使用丰富且可扩展的 PostgreSQL 生态系统附带的工具和功能来处理使用 ...

随机推荐

  1. USACO Buying Hay

    洛谷 P2918 [USACO08NOV]买干草Buying Hay https://www.luogu.org/problem/P2918 JDOJ 2592: USACO 2008 Nov Sil ...

  2. springboot学习过程中遇到的问题(遇到再总结)

    1.pom文件第一行报错 当引入的spring-boot-starter-parent版本高于2.1.1会导致pom.xml文件第一行报错   (以后找个时间彻底解决此问题) 2.servlet配置失 ...

  3. flutter环境配置window10

    第一步,配置git环境,这个作为前端的都是会的,如果你不会,去问度娘去 第二步,配置java的开发环境,这里建议下载jdk为1.8版本的,我最初使用的是如下图的jdk版本,后面和flutter版本不一 ...

  4. 11/10 <priorityQueue> 215 347

    215. Kth Largest Element in an Array 快速排序法,选择一个数,比这个数大的交换到左边,比这个数小的交换到右边. class Solution { public in ...

  5. jQuery的animate()方法做一个颜色的渐变

    需求:在1秒内,由一个颜色变到另一个颜色,不是1秒后再变色. <!DOCTYPE html> <html lang="en"> <head> & ...

  6. python3中pymysql模块的事务操作

    try:    cursor.execute(sql_1)     cursor.execute(sql_2)     cursor.execute(sql_3) except Exception a ...

  7. Python--单元四练习

    一.算24 描述: 给出4个小于10的正整数,可以使用加.减.乘.除4种运算以及括号把4个数连接起来得到一个表达式.现在问题是,是否存在一种方式使得所得表达式的结果等于24.‪‬‪‬‪‬‪‬‪‬‮‬‪ ...

  8. 解决Windows更新错误0x80240034

    Windows update错误0x80240034 笔者平台:WIn10预览版 微软官方文档: https://support.microsoft.com/en-us/help/929833/use ...

  9. 微信小程序如何进行本地调试

    1.下载并使用微信开发者工具 2.将项目导入工具 3.在项目中修改请求http地址 4.在工具上点击“测试号”,跳转到测试号管理界面设置request合法域名. 注意这里输入的测试域名要和上一步相同. ...

  10. CISCO 3750交换机堆叠

    双交换机堆叠操作 一.基本要求: ios版本要一致.专用的堆叠模块和堆叠线缆.最大堆叠个数9 二.堆叠的好处: 高密度端口.便于管理.堆叠的交换机可以看作一台交换机统一配置 三.堆叠实例: 1:分别清 ...