• [技术干货] PostgreSql 重建索引的操作
    PostgreSql数据库的重建索引时通过REINDEX命令来实现的,如reindexindex_name;其语法是:REINDEX { INDEX | TABLE | DATABASE | SYSTEM } name [ FORCE ];下面解释下说明情况下需要:1、当由于软件bug或者硬件原因导致的索引不再可用,索引的数据不再可用;2、当索引包含许多空的或者近似于空的页,这个在b-tree索引会发生。Reindex会腾出空间释放哪些无用的页(页就是存放数据的一个单位,类似于block)。3、PostgreSql数据库系统修改了存储参数,需要重建不然就会失效(如修改了fillfactor参数);4、创建并发索引时失败,遗留了一个失效的索引。这样的索引不会被使用,但重构后能用。一个索引的重构不能并发的执行。下面介绍下重构索引命令的参数:1、INDEX 重构指定的索引;2、TABLE 重构指定表的所有索引,包括下级TOAST表;3、DATABASE重构指定数据库的所有索引,系统共享索引也会被执行。需要注意的是这个级别的重构不能再一个事务块中执行。4、SYSTEM 重构这个系统的索引包含当前的数据库。共享系统中的索引页是被包含的,但是用户自己的表是不处理的,同样也不能在一个事务块中执行。5、Name 按照不同级别索引的名称。6、FORCE 已经被废除即使写了也是被忽略的。示例:REINDEX INDEX my_index; REINDEX TABLE my_table; REINDEX DATABASE broken_db;另外需要注意的是:1、重建索引不同的级别的重构需要不同的权限,比如table那么就需要有这个表的权限即需要有操作索引的权限,如超级用户postgres拥有这个权限。2、重构索引的目的是为了当索引的数据不可信时,即对于成本的计算会出现偏差较大,无益于优化器得到最优的执行计划以至于性能优化失败。3、重构索引类似于先删除所有再创建一个索引,但是索引的条目是重新开始的。重构时当前索引是不能写的,因为此时有排他锁。4、在8,1版本之前REINDEX DATABASE 只包含系统索引,并不是期望的所有指定数据库的索引。7.4版本之前REINDEX TABLE不会自动执行下级TOAST tables。关于TOAST tables的含义:TOAST直接翻译的话就是切片面包(slicedbread)的意思,全称是The Oversized-Attribute Storage Technique,为什么会有OVERSIZED-ATTRIBUTE呢?原因很简单,因为在PostgreSQL,一条记录不能跨PAGE存储,跨越PAGE的话必须使用TOAST(即unaligned,与原表分开存储)存储。TOAST表不能独立创建,只有当普通表包含了main,extended或external存储格式的字段时,系统会自动创建一个和普通表关联的TOAST表。当一条记录(tuple)在存储时(如果压缩的话算压缩后的大小)大于TOAST_TUPLE_THRESHOLD(通常是2kB)这个值时,会存储到TOAST表。而此时在普通表的该字段处包含了一个指向TOAST的tableoid和chunk_id的数据,从而能够找到该字段的记录。
  • [技术干货] PostgreSQL 实现sql放入文件批量执行
    ostgreSQL sql放入文件,登入数据库之后批量执行1. 建立测试sql:vi aa.sql插入:猜测每条sql语句是用;分隔的,function中的多个;也会自动识别。create table tb1(id integer); insert into tb1 select generate_series(1,10); select * from tb1; delete from tb1 where id<3; select * from tb1;2. 将aa.sql放入 ./src/postgresql-9.3.5/src/tutorial下(./src/postgresql-9.3.5/src/tutorial是PostgreSQL自动识别的目录,当然也可以放在任意目录,比如/home/postgres/aa.sql)3. 切换用户登入su postgres psql postgres4. 执行:当输入\i时候,会自动检测到./src/postgresql-9.3.5/src/tutorial下的文件,PostgreSQL的测试例子也放在此目录下postgres=# \i aa.sql (\i /home/postgres/aa.sql) id | name ----+------ 1 | join 2 | join 3 | join 4 | join 5 | join 6 | join 7 | join 8 | join 9 | join 10 | join (10 rows) CREATE TABLE INSERT 0 10 id ---- 1 2 3 4 5 6 7 8 9 10 (10 rows) DELETE 2 id ---- 3 4 5 6 7 8 9 10 (8 rows) postgres=# 第二个例子:vi bb.sql:写入一个function:create function func1()returns void as $$ declare begin delete from person where id>5; delete from tb1 where id>5; end $$language plpgsql; select func1();切换到postgres,登入之后执行:执行前:postgres=# select * from person ; id | name ----+------ 1 | join 2 | join 3 | join 4 | join 5 | join 6 | join 7 | join 8 | join 9 | join 10 | join (10 rows) postgres=# select * from tb1 ; id ---- 3 4 5 6 7 8 9 10 (8 rows)执行:postgres=# \i bb.sql CREATE FUNCTION func1 ------- (1 row)执行后:postgres=# select * from person ; id | name ----+------ 1 | join 2 | join 3 | join 4 | join 5 | join (5 rows) postgres=# select * from tb1 ; id ---- 3 4 5 (3 rows) postgres=# 5. 也可以使用psql命令执行pslq -d postgres -U postgres -f /home/postgres/aa.sql
  • [运维管理] roach restore clean为什么把postgresql.sql等文件删除了
    roach备份一个gaussdb A库到磁盘上,然后在另一个gaussdb集群中按照roach restore clean restore-new-cluster进行恢复,恢复报找不到config文件.查看日志发现clean过程把postgresql.conf等文件都删除了,然后另一个gaussdb集群就挂了。然后尝试启动集群也起不来了也是报pg等配置文件没有。问要怎么处理?重装另一个gaussdb集群?这个恢复过程问题出在哪儿?
  • [技术干货] 跨库操作dblink和postgres_fdw
    # 跨库操作dblink和postgres_fdw 插件介绍: 使用dblink和postgres_fdw可以实现跨库操作其他PostgreSQL库。 # 系统要求 PostgreSQL 9.5+ 与要连接的其他PostgreSQL网络连通 # dblink 1、新建dblink插件。 ```sql CREATE EXTENSION dblink; ``` 2、连接远程数据库 ```sql --SELECT dblink_connect('', ''); SELECT dblink_connect('mydb', 'dbname=postgres port=5432 host=localhost'); dblink_connect ---------------- OK (1 row) ``` 该函数有两个参数:connname和connstr,其中connname是可选参数。 connname:要用于这个连接的名字。如果被忽略,将打开一个未命名连接并且替换掉任何现有的未命名连接。 connstr:数据库连接串信息,格式为:hostaddr= port=端口号> dbname=数据库名> user=用户名> password=密码> 3、执行sql命令 ```sql --执行查询 SELECT * FROM dblink('mydb', 'select * from test') as test(id integer, info varchar(8)); id | info ----+------ 1 | a 1 | b (2 rows) --执行插入 SELECT dblink_exec('mydb', 'insert into test values (3,''c'');'); dblink_exec ------------- INSERT 0 1 (1 row) SELECT * FROM dblink('mydb', 'select * from test') as test(id integer, info varchar(8)); id | info ----+------ 1 | a 2 | b 3 | c (3 rows) ``` 4、查询连接状态 ```sql SELECT dblink_get_connections(); dblink_get_connections ------------------------ {mydb} (1 row) ``` 5、关闭远程连接 ```sql SELECT dblink_disconnect('mydb'); ``` 更多内容请参考[dblink](https://www.postgresql.org/docs/12/dblink.html) # postgres_fdw 1、新建postgres_fdw插件 ```sql CREATE EXTENSION postgres_fdw; ``` 2、然后使用CREATE SERVER创建一个外部服务器 ```sql CREATE SERVER foreign_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'x.x.x.x', port '5432', dbname 'foreign_db'); ``` 3、使用CREATE USER MAPPING一个用户映射,每一个用户映射都代表你想允许一个数据库用户访问一个外部服务器。 ```sql --指定要映射到外部服务器的本地数据库用户名local_user CREATE USER MAPPING FOR local_user SERVER foreign_server OPTIONS (user 'foreign_user', password 'password'); ``` 4、为每一个你想访问的远程表使用CREATE FOREIGN TABLE或者IMPORT FOREIGN SCHEMA创建一个外部表 ```sql CREATE FOREIGN TABLE foreign_table ( id integer, info varchar(8) ) SERVER foreign_server OPTIONS (schema_name 'some_schema', table_name 'some_table'); ``` 5、查询 ```sql SELECT * FROM foreign_table; id | info ----+------ 1 | a 2 | b 3 | c (3 rows) ``` 更多内容请参考[postgres_fdw](https://www.postgresql.org/docs/12/postgres-fdw.html)
  • [问题求助] OpenGauss或者RDS for Postgresql
    想问下OpenGauss或者RDS for Postgresql是否集成的有PostGIS3?
  • [技术干货] PostgreSQL插件之pg_hint_plan
    # pg_hint_plan pg_hint_plan通过特殊形式的注释中的提示短语来控制执行计划。 ## 概要 PostgreSQL使用基于代价的优化器,该优化器的代价计算利用的是数据统计信息,而不是静态规则。对于一条SQL语句,优化器会估计所有可能的执行计划的代价,然后最终选择代价最低的执行计划。由于优化器不会考虑列之间的相关性,因此,最终选出来的执行计划可能并不是完美的。 `pg_hint_plan`允许我们在sql语句中通过特殊格式的提示来调整执行计划,达到优化执行计划的目的。 ## 使用方法 ### 基本使用 pg_hint_plan在目标SQL语句中读取特殊格式的提示短语。提示短语以字符`/*+`开始,`*/`结尾。提示短语由提示名和参数(用括号包起来,两个参数之间用空格分隔)组成。为了增加可读性,每一个提示短语可另起一行。 在下面的示例中,`HashJoin`被选择为连接方法,并使用`Seq Scan`对表`pgbench_accounts`进行扫描。 ```sql postgres=# /*+ postgres*# HashJoin(a b) postgres*# SeqScan(a) postgres*# */ postgres-# EXPLAIN SELECT * postgres-# FROM pgbench_branches b postgres-# JOIN pgbench_accounts a ON b.bid = a.bid postgres-# ORDER BY a.aid; QUERY PLAN --------------------------------------------------------------------------------------- Sort (cost=31465.84..31715.84 rows=100000 width=197) Sort Key: a.aid -> Hash Join (cost=1.02..4016.02 rows=100000 width=197) Hash Cond: (a.bid = b.bid) -> Seq Scan on pgbench_accounts a (cost=0.00..2640.00 rows=100000 width=97) -> Hash (cost=1.01..1.01 rows=1 width=100) -> Seq Scan on pgbench_branches b (cost=0.00..1.01 rows=1 width=100) (7 rows) ``` ## 提示表 在上一节中,介绍了`pg_hint_plan`中提示短语的使用,但是当查询语句无法编辑时,是很不方便的。这种情况,可以将提示放在特殊的表`hint_plan.hints`中,表结构如下所示: | 列名 | 描述 | | ----------------- | ------------------------------------------------------------ | | id | 提示行唯一标识符。该列按顺序自动填充。 | | norm_query_string | 与要提示的查询相匹配的模式。查询中的常量必须替换为`?`,空格很重要。 | | application_name | 应用该提示的会话名称。空字符串表示任何application_name的会话。 | | hints | 提示短语。其中不包括注释标记 | 如何使用表`hint_plan.hints` ```sql postgres=# INSERT INTO hint_plan.hints(norm_query_string, application_name, hints) postgres-# VALUES ( postgres(# 'EXPLAIN (COSTS false) SELECT * FROM t1 WHERE t1.id = ?;', postgres(# '', postgres(# 'SeqScan(t1)' postgres(# ); INSERT 0 1 postgres=# UPDATE hint_plan.hints postgres-# SET hints = 'IndexScan(t1)' postgres-# WHERE id = 1; UPDATE 1 postgres=# DELETE FROM hint_plan.hints postgres-# WHERE id = 1; DELETE 1 ``` 用户在创建`pg_hint_plan`插件时,默认拥有表`hint_plan.hints`的权限,且提示表中的优先级高于SQL语句中提示短语的优先级。 ### 提示类型 根据对象类型以及如何影响计划提示短语可分为六类。扫描方法、连接方法、连接顺序、行号校正、并行查询和GUC设置。您可以在[提示列表](####提示列表)中看到每种类型的提示短语列表。 #### 扫描方法提示 扫描方法提示对目标表强制执行特定的扫描方法。pg_hint_plan通过表的别名(如果存在别名)识别目标表。扫描方法可以是,`SeqScan`、`IndexScan`等。 扫描提示对普通表、继承表、UNLOGGED表、临时表和系统表都有效。对于外部表、表函数、VALUES子句、通用表表达式(CTEs)、视图和子查询扫描提示无效。 ```sql postgres=# /*+ postgres*# SeqScan(t1) postgres*# IndexScan(t2 t2_pkey) postgres*# */ postgres-# SELECT * FROM table1 t1 JOIN table table2 t2 ON (t1.key = t2.key); ``` #### 连接方法提示 连接方法提示对涉及表的连接方法进行强制指定。 连接方法提示对普通表、继承表、UNLOGGED表、临时表、外部表、系统表、表函数、VALUES子句、通用表表达式(CTEs)有效。对视图和子查询无效。 #### 连接顺序提示 此提示“ Leading”在两个或多个表上强制执行连接顺序。有两种执行方法。一种是强制执行特定的连接顺序,但不限制每个连接级别的方向。另一个加入连接方向。详细信息请参见[提示列表](####提示列表)。 ```sql postgres=# /*+ postgres*# NestLoop(t1 t2) postgres*# MergeJoin(t1 t2 t3) postgres*# Leading(t1 t2 t3) postgres*# */ postgres-# SELECT * FROM table1 t1 postgres-# JOIN table table2 t2 ON (t1.key = t2.key) postgres-# JOIN table table3 t3 ON (t2.key = t3.key); ``` #### 行号校正提示 行号纠正提示会纠正由于计划器限制而导致连接的行号错误估计。 ```sql postgres=# /*+ Rows(a b #10) */ SELECT... ; --设置连接结果的行号为10 postgres=# /*+ Rows(a b +10) */ SELECT... ; --将行号增加10 postgres=# /*+ Rows(a b -10) */ SELECT... ; --将行号减10 postgres=# /*+ Rows(a b *10) */ SELECT... ; --将行号扩大为原来的10倍 ``` #### 并行查询提示 并行查询提示在扫描时会强制并行执行配置。第三个参数表示强度。soft表示pg_hint_plan只更改max_parallel_worker_per_gather。hard更改所有参数,使提示数字有效。并行查询提示对普通表、继承表、UNLOGGED表和系统表有效;对外部表、表函数、VALUSES子句、通用表表达式(CTEs)、视图和子查询无效。可以通过指定内部表的真实名或别名来作为目标对象。 下面的示例表示每个表执行查询的方式不同。 ```sql postgres=# explain /*+ Parallel(c1 3 hard) Parallel(c2 5 hard) */ SELECT c2.a FROM c1 JOIN c2 ON (c1.a = c2.a); QUERY PLAN ------------------------------------------------------------------------------- Hash Join (cost=2.86..11406.38 rows=101 width=4) Hash Cond: (c1.a = c2.a) -> Gather (cost=0.00..7652.13 rows=1000101 width=4) Workers Planned: 3 -> Parallel Seq Scan on c1 (cost=0.00..7652.13 rows=322613 width=4) -> Hash (cost=1.59..1.59 rows=101 width=4) -> Gather (cost=0.00..1.59 rows=101 width=4) Workers Planned: 5 -> Parallel Seq Scan on c2 (cost=0.00..1.59 rows=59 width=4) postgres=# EXPLAIN /*+ Parallel(tl 5 hard) */ SELECT sum(a) FROM tl; QUERY PLAN ----------------------------------------------------------------------------------- Finalize Aggregate (cost=693.02..693.03 rows=1 width=8) -> Gather (cost=693.00..693.01 rows=5 width=8) Workers Planned: 5 -> Partial Aggregate (cost=693.00..693.01 rows=1 width=8) -> Parallel Seq Scan on tl (cost=0.00..643.00 rows=20000 width=4) ``` #### 设置临时的GUC参数 在计划时更改GUC参数,如果计划器的配置参数没有与其它任何提示冲突,所设置的GUC就有效。同样的GUC参数,以最后一次设置为准。 ```sql postgres=# /*+ Set(random_page_cost 2.0) */ postgres-# SELECT * FROM table1 t1 WHERE key = 'value'; ``` #### 提示列表 ![image.png](https://bbs-img-cbc-cn.obs.cn-north-1.myhuaweicloud.com/data/forums/attachment/forum/202103/18/1943211xo7sdwv1sejjqxe.png)
  • [热门活动] 【打卡帖】0元限时学《7天玩转PostgreSQL基础训练营》,大奖等你来哦!
    课程链接:https://education.huaweicloud.com/courses/course-v1:HuaweiX+CBUCNXD048+Self-paced/courseware/fb989107b5ea4f368fb11447ea2d8b63/a75cf56dbace4384993b27d81160bd92/ 华为云 · 7天实战营系列活动2021年 -- 7天玩转PostgreSQL基础实战营让你7天轻松掌握PG!完成打卡任务,将有机会获得三合一数据线、神秘潮酷盲盒、旅行五件套等多重豪礼哦~限额免费招募,快来报名吧!活动参与流程  Step1. 请点击链接,先报名活动活动报名链接:https://developer.huaweicloud.com/activity/PostgreSQL.html(本次活动涉及代金券发放,请务必填写,数量有限,先到先得)Step2. 添加群助手好友并回复“PostgreSQL”或“PG”,免费入群!Step3. 入群后,群助手会通知开课时间;Step3. 开课后,请按照节奏学习视频课程,完成每日打卡任务(打卡任务完成后,请在本帖回复截图) 活动时间  招募期:2021.03.23-2021.04.07培训期:2021.04.08-2021.04.16课程大纲  专家介绍活动奖励活动规则1.活动参与方式:招募期流程:(1)注册华为云中国站账号并登录;(2)点击活动页中的“立即报名”按钮先填写报名表;(3)扫描二维码,添加社群助手微信(18209232786 ),回复“PostgreSQL”或“PG”,小助手拉您入群。训练期流程:(1)微信社群助手每日发布当日课程内容及规则,学员完成任务并打卡成功,获得阶段性奖励。 训练期内打卡规则:(1) 打卡时间:每日0:00~23:59;(2) 打卡方式:Step1:请打开当天的“打卡作业”(每一章的第3小节);Step2:按照要求完成作业;Step3:按以下格式在打卡贴内回复完成后的打卡截图:第一行:DayX(X=1到7)+华为云账号+微信昵称            (打卡作业截图)eg:Day1+grandmaster+华为云1号小助手      (打卡作业截图)(3) 打卡须知:① 打卡需按照打卡方式要求操作;② 如当天打卡任务有打卡示例,学员所回复打卡截图中必须需显示华为云中国站账号,且为当日操作截图;③ 采用闯关打卡模式,每日只计算一次有效打卡;④ 若当日未进行打卡,训练期内设置3次补打卡机会,超过3次不可再补打卡;⑤ 社群助手将对每日的打卡内容进行审核,审核周期为3~5个工作日,未按照要求上传打卡任务,将被视为无效打卡,不计入打卡天数。2.训练期内评奖方式:(1)完成第1天打卡完成第1天打卡任务,可获得抽奖机会,奖品为三合一数据线1个 ;奖品数量上限为80份,中奖概率(≤100%)为“80/完成连续打卡1天任务的总人数”;兑奖时间不可超过训练期第二天,过期无效。(2)连续打卡3天连续完成3天打卡可获得抽奖机会,奖品为精美盲盒奖励1份,奖品数量上限为50份,中奖概率(≤100%)为“50/完成连续打卡3天任务的总人数”;兑奖时间不可超过训练期第四天,过期无效。(3)连续打卡7天连续完成7天打卡可直接获得华为云云享专家直通车机会,奖品为旅行5件套1个,奖品数量上限为30份,中奖概率(≤100%)为“30/完成连续打卡7天任务的总人数”;兑奖时间不可超过训练期第八天,过期无效。3.奖品发放说明:每位参加活动的用户理解并同意,为联系获奖用户以及奖品发放的需要,用户须在参与活动之时提供诸如姓名、联系方式、电子邮箱、通讯地址等真实个人信息,活动主办方将仅为前述目的以及适用法律规定的最小限度内收集和使用用户的个人信息,本次活动所收集的个人信息将在活动结束后删除。(用户在向华为云提交个人信息之前,应阅读、了解华为云《隐私政策声明》;用户参加本活动视为理解并同意华为云《隐私政策声明》,华为云《隐私政策声明》网页地址如下:https://www.huaweicloud.com/declaration/sa_prp.html)。获奖用户在领奖界面填写获奖信息,活动结束且用户填写完整领奖信息后14个工作日内,将统一发出奖品,所有实物奖品包邮,不额外收取任何费用。由于获奖用户自身原因(包括但不限于提供的联系方式有误、身份不符或者通知领奖后超过30天未领取等)造成奖品无法发送的,视为获奖用户放弃领奖。为保证活动的公平公正,华为云有权对恶意刷活动资源(“恶意”是指为获取资源而异常注册账号等破坏活动公平性的行为),利用资源从事违法违规行为的用户收回抽奖及奖励资格。本活动规则由华为云在法律规定范围内进行解释。华为云保留不时更新、修改或删除本活动规则的权利。所有参加本活动的用户,均视为认可并同意遵守《华为云用户协议》,包括以援引方式纳入《华为云用户协议》的《可接受的使用政策》、《法律声明》、《隐私政策声明》、相关服务等级协议(SLA),以及华为云服务网站规定的其他协议和政策(统称为“云服务协议”)的约束。云服务协议链接的网址:http://www.huaweicloud.com/declaration/sa_cua.html如果您不同意本活动规则和云服务协议的条款,请勿参加本活动。
  • [技术干货] PostgreSQL插件之pg_cron
    pg_cron是一个使用cron语法的定时任务调度程序。作为数据库插件,它可以在数据库内部执行 PostgreSQL 的命令。 # 1 cron语法 标准cron语法,*表示每个时间段运行,特定数字表示仅在此时间。 ```cron ┌───────────── min (0 - 59) │ ┌────────────── hour (0 - 23) │ │ ┌─────────────── day of month (1 - 31) │ │ │ ┌──────────────── month (1 - 12) │ │ │ │ ┌───────────────── day of week (0 - 6) (0 to 6 are Sunday to │ │ │ │ │ Saturday, or use names; 7 is also Sunday) │ │ │ │ │ │ │ │ │ │ ``` **注意** 1. 定时任务使用GMT时间执行; 2. 使用 crontab.guru 工具,可以方便地创建任务调度规则; # 2 注意事项 1. pg_cron需要后台守护进程,因此启动数据库前,需要将pg_cron放到shared_preload_libraries中; 2. 定时任务不会在备机上运行,但当备机升主后,定时任务会自动启动; 4. 定时任务会以任务创建者的权限执行; 5. 一个实例可以并行运行多个任务,但同一时间某个任务仅能运行一个; 6. 某个任务,需要等待前一个定时任务结束,那么该任务会进入等待队列,且会在前一个任务结束后尽快启动; # 3 使用方法 ## 创建或删除插件 ```sql -- 创建插件 CREATE EXTENSION IF NOT EXISTS pg_cron; -- 删除 DROP EXTENSION IF EXISTS pg_cron; ``` ## 创建任务 ```sql -- 任务1:每周六上午3:30(GMT),删除旧数据 SELECT cron.schedule('30 3 * * 6', $$DELETE FROM events WHERE event_time now() - interval '1 week'$$); -- 任务2:每天上午 10:00(GMT),执行vacuum -- 注意:任务以nightly-vacuum为命名,该接口 1.3 版本上才支持。 SELECT cron.schedule('nightly-vacuum', '0 10 * * *', 'VACUUM'); ``` ## 查看任务 ```sql SELECT * FROM cron.job; ``` ## 删除任务 ```sql SELECT cron.unschedule(job_id) ``` # 4 配置参数 | 参数名 | 功能 | 默认值 | 需要重启 | | ---- | ---- | ---- | ---- | |cron.database_name|定时任务元信息所在的数据库|postgres|是| |cron.log_statement|执行任务前,是否将sql打印到日志|true|是| |cron.log_run|是否将任务的执行信息存储到**job_run_details**表中|true|是| |cron.host|要执行定时任务的host名|localhost|是| |cron.use_background_workers|使用后台work进程,而非客户端连接执行任务|false|是| |cron.max_running_jobs|可以同时运行的job数量|5|是| **注意** 1. 1.3 及以上的版本,支持参数cron.log_run和cron.use_background_workers; 2. 1.3 及以上的版本,设置cron.use_background_workers为true后,cron.host不再生效,使用起来更加方便; 3. 使用前,需要将cron.database_name修改为创建定时任务的数据库;
  • [技术干货] PostgreSQL插件之hll
    摘要HyperLogLog(hll)是一个用于计算集合中不重复的元素个数问题的算法,精确的计算需要与基数成比例的内存量,这对于非常大的数据集是不实际的。概率基数估算(如HyperLogLog算法)使用的内存要比这少得多,但代价是仅获得一个基数的近似值。背景在现实生活中,我们通常需要统计一个集合中不重复的元素个数。在数据库中通过使用如下的SQL语句进行精确统计:CREATE TABLE tbl(id INT, a INT); INSERT INTO tbl VALUES(1, 1), (2, 1), (3, 2), (4, 2), (5, 2), (6, 3); SELECT COUNT(DISTINCT a) FROM tbl;  count  -------      3 (1 row)为了计算 count-distinct 通常使用哈希或者排序的方式计算不重复元素个数。但是,如果数据量比较大,哈希或者排序消耗的内存较大。HyperLogLog 是处理这个问题的一个概率算法,但是其消耗资源较少。postgresql-hll插件引入了一个新的数据类型hll,它是一个HyperLogLog数据结构。下面对其使用进行详细介绍。支持版本PostgreSQL 12PostgreSQL 11PostgreSQL 10PostgreSQL 9.6PostgreSQL 9.5安装在华为云使用postgresql-hll,请参考:https://support.huaweicloud.com/usermanual-rds/rds_09_0043.html 进行安装和卸载。使用CREATE TABLE hll_tbl (     id      integer,     set     hll ); --- 插入空的HLL INSERT INTO hll_tbl(id, set) VALUES (1, hll_empty()); --- 增加一个被散列的整数值到HLL中 UPDATE hll_tbl SET set = hll_add(set, hll_hash_integer(12345)) WHERE id = 1; --- 增加一个被散列的字符串到HLL中 UPDATE hll_tbl SET set = hll_add(set, hll_hash_text('hello world')) WHERE id = 1; --- 计算HLL的基数 SELECT hll_cardinality(set) FROM hll_tbl WHERE id = 1;数据仓库示例我们假设有一个事实表,记录了用户对我的站点的访问、他们做了什么以及他们来自哪里。表中有上亿行数据,表扫描需要几分钟(或者至少需要很多秒)。CREATE TABLE facts (     date            date,     user_id         integer,     activity_type   smallint,     referrer        varchar(255) ); insert into facts select timestamp '2014-01-10 10:00:00' +        random() * (timestamp '2021-01-20 20:00:00' - timestamp '2014-01-10 10:00:00'),        generate_series(1, 10000000), 1, 'no use'; insert into facts select timestamp '2014-01-10 10:00:00' +        random() * (timestamp '2021-01-20 20:00:00' - timestamp '2014-01-10 10:00:00'),        generate_series(1, 10000000), 1, 'no use';如果想快速(毫秒级别)知道每天有多少独立用户访问了站点,可以建立一个聚合表:CREATE TABLE daily_uniques (     date            date UNIQUE,     users           hll ); INSERT INTO daily_uniques(date, users)     SELECT date, hll_add_agg(hll_hash_integer(user_id))     FROM facts     GROUP BY 1;我们首先对user_id进行散列,然后按天将这些散列后的值聚合为一个hll。现在我们可以计算每天的hll基数:SELECT date, hll_cardinality(users) FROM daily_uniques;如果想要得到这周独立的用户访问数呢?SELECT hll_cardinality(hll_union_agg(users))  FROM daily_uniques  WHERE date >= '2012-01-02'::date AND date <= '2012-01-08'::date;操作符插件中已经添加了一些操作符,使用hll变得不那么冗长。它们是最常用函数的简单别名。散列函数hll_hash_boolean(boolean)hll_hash_smallint(smallint)hll_hash_integer(integer)hll_hash_bigint(bigint)hll_hash_bytea(bytea)hll_hash_text(text)hll_hash_any(any)示例如下:SELECT hll_hash_boolean(TRUE); SELECT hll_hash_boolean(TRUE, 123/*hash seed*/); SELECT hll_hash_smallint(4::smallint); SELECT hll_hash_smallint(4::smallint, 123/*hash seed*/); SELECT hll_hash_integer(21474836); SELECT hll_hash_integer(21474836, 123/*hash seed*/); SELECT hll_hash_bigint(223372036854775808); SELECT hll_hash_bigint(223372036854775808, 123/*hash seed*/); SELECT hll_hash_bytea(E'\\xDEADBEEF'); SELECT hll_hash_bytea(E'\\xDEADBEEF', 123/*hash seed*/); SELECT hll_hash_text('foobar'); SELECT hll_hash_text('foobar', 123/*hash seed*/); SELECT hll_hash_any(123); SELECT hll_hash_any(123, 123/*hash seed*/);注意:hll_hash_any会动态地分派给适当的特定类型的函数,这使得它比它包装的特定类型的函数要慢。只有在不知道输入类型时才使用它。聚集函数如果要从表或结果集中创建一个hll,请使用hll_add_agg。这里的命名并不是特别有创意:它是一个聚合函数,将值添加到空的hll中。SELECT date, hll_add_agg(hll_hash_integer(user_id))     FROM facts     GROUP BY 1;上面的示例将为每个包含每天用户的日期提供一个hll。如果您想汇总已经存储到单个hll中的一个hll列表,请使用hll_union_agg。再次说明:它是一个聚合函数,将值合并到一个空的hll中。SELECT EXTRACT(MONTH FROM date), hll_cardinality(hll_union_agg(users))     FROM daily_uniques     GROUP BY 1;窗口是hll功能的另一个主要例子。执行滑动窗口惟一计数通常涉及一些generate_series技巧,对于已经计算过的滑动窗口则非常简单。SELECT date, #hll_union_agg(users) OVER seven_days     FROM daily_uniques     WINDOW seven_days AS (ORDER BY date ASC ROWS 6 PRECEDING);
  • [技术干货] 通过JDBC连接PostgreSQL数据库
    通过JDBC连接实例的方式有无需下载SSL证书连接和用户下载SSL证书连接两种,其中使用SSL证书连接通过了加密功能,具有更高的安全性。前提条件用户需要具备以下技能:熟悉计算机基础知识。了解java编程语言。了解JDBC基础知识。驱动获取及使用JDBC驱动下载地址:https://jdbc.postgresql.org/download.htmlJDBC接口使用指南请参考:https://jdbc.postgresql.org/documentation/head/index.html使用SSL证书连接说明:该方式属于SSL连接模式,需要下载SSL证书,通过证书校验并连接数据库。你可以在“实例管理”页面,单击实例名称进入“基本信息”页面,单击“数据库信息”模块“SSL”处的下载图标,下载根证书或**包。通过JDBC连接PostgreSQL数据库,代码中的JDBC链接格式如下:jdbc:postgresql://<instance_ip>:<instance_port>/<database_name>?sslmode=verify-full&sslrootcert=<ca.pem>参数说明:参数说明<instance_ip>如果通过弹性云服务器连接,“instance_ip”是主机IP,即“基本信息”页面该实例的“内网地址”。如果通过连接了公网的设备访问,“instance_ip”为该实例已绑定的“弹性公网IP”。<instance_port>端口,默认5432,当前端口,参考“基本信息”页面该实例的“数据库端口”。<database_name>数据库名,即需要连接的数据库名(默认的管理数据库是postgres)。sslmodessl连接模式,默认全认证模式。sslrootcertssl连接CA证书路径,该文件需放在执行该命令的路径下。连接PostgreSQL数据库的java代码,可参考以下示例import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class MyConnTest { final public static void main(String[] args) { Connection conn = null; // set sslmode here. // with ssl certificate and path. String url = "jdbc:postgresql://192.168.0.225:5432/my_db_test?sslmode=verify-full&sslrootcert=/home/Ruby/ca.pem"; try { Class.forName("org.postgresql.Driver"); conn = DriverManager.getConnection(url, "root", "password"); System.out.println("Database connected"); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM mytable WHERE columnfoo = 500"); while (rs.next()) { System.out.println(rs.getString(1)); } rs.close(); stmt.close(); conn.close(); } catch (Exception e) { e.printStackTrace(); System.out.println("Test failed"); } finally { // release resource .... } } }无证书连接说明:该方式属于SSL连接模式,但不对服务端进行证书校验,用户无需下载SSL证书。通过JDBC连接RDS PostgreSQL数据库实例,代码中的JDBC链接格式如下:jdbc:postgresql://<instance_ip>:<instance_port>/<database_name>?sslmode=disable连接PostgreSQL数据库的java代码,可参考以下示例:import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class MyConnTest { final public static void main(String[] args) { Connection conn = null; // set sslmode here. // no ssl certificate, so do not specify path. String url = "jdbc:postgresql://192.168.0.225:5432/my_db_test?sslmode=disable"; try { Class.forName("org.postgresql.Driver"); conn = DriverManager.getConnection(url, "root", "password"); System.out.println("Database connected"); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM mytable WHERE columnfoo = 500"); while (rs.next()) { System.out.println(rs.getString(1)); } rs.close(); stmt.close(); conn.close(); } catch (Exception e) { e.printStackTrace(); System.out.println("Test failed"); } finally { // release resource .... } } }
  • [技术干货] PostgreSQL13新特性介绍
    PostgreSQL 13.0版本已于2020-09-24发行,目前最新版为PostgreSQL 13.1。PostgreSQL 13版本中包含了令人欣喜的新特性和优化,其中典型的有:l  Btree索引优化(引入Deduplication技术)l  支持增量排序l  支持并行vacuum indexl  支持可信插件l  支持扩展统计信息l  支持hash aggregation使用磁盘存储 本文我们将选取几个PostgreSQL 13中的重点新特性进行介绍,让读者一睹为快。1.       Btree索引优化如果一张表的某些列值不是唯一的,那么将会存在很多重复值,一旦该列上建有索引的话,那么索引也会有很多重复的索引项。在PostgreSQL 13中引入了deduplicate功能,该功能将会定期的将重复的索引项合并,有效地减少了索引的存储空间。随着索引项的减少,重建索引、vacuum索引的速度都将获得提升,查询的效率也会得到提升。     下面我们分别在PostgreSQL 12 和 13 版本中创建唯一索引和包含大量重复项的索引,比较一下效果。测试脚本如下:CREATE TABLE test_tbl (id int4, str varchar(64), time timestamp without time zone); INSERT INTO test_tbl (id, str, time) SELECT round(100000000*random()), 'test_tbl_000' || num, '2020-12-31 24:00:00' FROM generate_series(1,6000000) num; CREATE UNIQUE INDEX idx_on_str ON test_tbl USING BTREE(str); CREATE INDEX idx_on_time ON test_tbl USING BTREE(time);    idx_on_str是唯一索引;idx_on_time是非唯一索引,存储的索引项都一样。    PostgreSQL 12 中 查看索引大小,如下:     在PostgreSQL 13 中查看索引大小:     根据以上信息可以看出,创建索引后,唯一索引在12版本13版本大小一致,都是232MB;但是非唯一索引的差别较大,13版本的非唯一索引大小约为12版本的1/3不到,减小了很多存储空间。2.       增量排序增量排序用在多列排序的场景下来加快排序速度。例如,下面的SQL:SELECT * FROM table ORDER BY c1, c2 LIMIT 10;如果c1已经排好序,那么只需要在此基础上对c2进行增量排序即可。PostgreSQL 13 版本新增了enable_incrementalsort参数控制是否开启增量排序,此参数默认开启,可以通过set enable_incremental_sort = off 关闭。下面我们在100万条数据的表上测试一下增量排序的效果:未打开增量排序:  打开增量排序:   未打开增量排序前,sql执行时间为222ms,打开增量排序之后,执行时间变为0.213ms,性能相比未开启增量排序时提升了好几个数量级。3.       支持并行VACUUM  PostgreSQL 13支持了对索引的并行VACUUM,可以通过指定parallel选项打开该功能。使用该功能存在如下限制:l  目前仅限于索引的并行vacuum,每个索引会分配一个vacuum worker。l  不支持在加上FULL选项后使用。l  只有在至少包含有2个及以上索引的表上使用parallel选项才有效。l  索引的大小超过min_parallel_index_scan_size下面我们准备一张包含300万条数据的表,表上建了两个索引,在PostgreSQL 13上对比下正常vacuum和并行vacuum的效果:先看在PostgreSQL 13正常vacuum的情况:再看下在PostgreSQL 13下并行vacuum的执行情况:从上图比较可以看到,启用并行vacuum之后时间由356ms减少到42ms,提升了8倍多。4.     可信插件        PostgreSQL 13允许 非superuser安装一些被指定为可信的插件到自己的数据库中,只要该用户拥有 CREATE 权限即可。之前的版本是当用户想安装一个插件的时候,必须具有superuser权限。在pg13里将不需要这样了:只要某个插件被标记为“trusted”,那么就不再需要superuser权限了。插件是否可信,取决于插件的control文件,只要在control文件里设置了trusted = true 那么该插件就是可信的。   我们创建一个普通用户,然后用该用户登录数据库去创建插件会失败:       然后我们在pg_stat_statements.control文件里加上trusted = true之后再去尝试就成功了:   5.       总结PostgreSQL 13带来了很多令人欣喜的新特性和优化,本文限于篇幅只介绍了其中4个。包括逻辑复制支持分区表,扩展统计信息,跟踪wal使用信息等很多很有意思的功能读者可以自行探索。详细说明请参考社区官方Release Notes:https://www.postgresql.org/docs/13/release-13.html
  • [技术干货] 【转载】技术实践丨PostgreSQL插件之pg_dirtyread “闪回查询“
     摘要:Oracle数据库有时候不小心删除掉数据,想查询这些数据,或者恢复数据,就可以使用带有as of子句的select语句进行闪回查询。PG粉有福了,下面介绍一种类似“闪回查询”插件 pg_dirtyread,可以读取未被vacuum的dead数据。github主页:https://github.com/df7cb/pg_dirtyread1.2 released:https://www.postgresql.org/message-id/20170923211004.uh27ncpjarkucrhd%40msg.credativ.de一、我们一起看下官网的3个例子:语法:SELECT * FROM pg_dirtyread('tablename') AS t(col1 type1, col2 type2, ...);样例1: 删除找回  CREATE EXTENSION pg_dirtyread;     -- Create table and disable autovacuum   CREATE TABLE foo (bar bigint, baz text);       ALTER TABLE foo SET (     autovacuum_enabled = false, toast.autovacuum_enabled = false   );  --测试方便,先把自动vacuum关闭掉。     INSERT INTO foo VALUES (1, 'Test'), (2, 'New Test');     DELETE FROM foo WHERE bar = 1;       SELECT * FROM pg_dirtyread('foo') as t(bar bigint, baz text);    bar   │   baz   ─────┼──────────        1     │ Test        2     │ New Test可以看到, 被删除的记录(1, 'Test')已经可以查询到。样例2:列被drop的情况 CREATE TABLE ab(a text, b text);     INSERT INTO ab VALUES ('Hello', 'World');       ALTER TABLE ab DROP COLUMN b;     DELETE FROM ab;       SELECT * FROM pg_dirtyread('ab') ab(a text, dropped_2 text);      a   │ dropped_2   ───────┼───────────    Hello │ World可以看到,虽然b列被drop掉了,但是仍然可以读取到数据。如何指定列:这里使用dropped_N来访问第N列,从1开始计数。局限:由于PG删除了原始列的元数据信息,因此需要在表列名中指定正确的类型,这样才能进行少量的完整性检查。包括类型长度、类型对齐、类型修饰符,并且采取的是按值传递。样例3:系统列SELECT * FROM pg_dirtyread('foo')            AS t(tableoid oid, ctid tid, xmin xid, xmax xid, cmin cid, cmax cid, dead boolean,            bar bigint, baz text);    tableoid │ ctid  │ xmin │ xmax │ cmin │ cmax │ dead │ bar │        baz   ──────────┼───────┼──────┼──────┼──────┼──────┼──────┼─────┼───────────────────         41823 │ (0,1) │ 1484 │ 1485 │    0 │    0 │ t    │   1 │ Delete   41823 │ (0,2) │ 1484 │    0 │    0 │    0 │ f    │   2 │ Insert         41823 │ (0,3) │ 1484 │ 1486 │    0 │    0 │ t    │   3 │ Update   41823 │ (0,4) │ 1484 │ 1488 │    0 │    0 │ f    │   4 │ Not deleted         41823 │ (0,5) │ 1484 │ 1489 │    1 │    1 │ f    │   5 │ Not updated         41823 │ (0,6) │ 1486 │    0 │    0 │    0 │ f    │   3 │ Updated         41823 │ (0,7) │ 1489 │    0 │    1 │    1 │ t    │   5 │ Not quite updated         41823 │ (0,8) │ 1490 │    0 │    2 │    2 │ t    │   6 │ Not inserted可以看到,xmax和ctid可以被恢复了。 oid只在11以及更早的版本中才能被恢复。二、支持的版本10和11已经支持,2.0以后的版本已经支持12和13,社区还是很活跃。三、实现分析核心代码有2部分:1、dirtyread_tupconvert.c 主要实现了dirtyread_convert_tuples_by_name,通过列名进行元组转换,处理列原信息被清理以及存在表继承的情况,关键部分是数组:attrMap[],下标从1开始。重点分析下dirtyread_do_convert_tupleHeapTuple dirtyread_do_convert_tuple(HeapTuple tuple, TupleConversionMap *map, TransactionId oldest_xmin) {       /*      * Extract all the values of the old tuple, offsetting the arrays so that      * invalues[0] is left NULL and invalues[1] is the first source attribute;      * this exactly matches the numbering convention in attrMap.      */     heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1); //+1是因为是从下标1开始,从旧的元组中把数据的值获取到       /*      * Transpose into proper fields of the new tuple. 这部分是重点,在这里完成转换      */     for (i = 0; i < outnatts; i++)     {         int         j = attrMap;           if (j == DeadFakeAttributeNumber)          //场景1:明确是dead,直接调用内核的函数HeapTupleIsSurelyDead即可,         //定义在tqual.c中,其它场景可以使用HeapTupleSatisfiesVacuum、HeapTupleSatisfiesMVCC等等,这里明确是dead,所以使用HeapTupleIsSurelyDead         {             outvalues = HeapTupleIsSurelyDead(tuple                     , oldest_xmin);             outisnull = false;         }         else if (j < 0) //场景2:系统列,交给函数heap_getsysattr来处理。             outvalues = heap_getsysattr(tuple, j, map->indesc, &outisnull);         else         {   //场景3:最常见的场景,直接获取即可。             outvalues = invalues[j];             outisnull = inisnull[j];         }     }       return heap_form_tuple(map->outdesc, outvalues, outisnull); //重新包装为tuple格式 }2、pg_dirtyread.c 面向客户的接口在这里实现。重点分析下 Datum pg_dirtyread(PG_FUNCTION_ARGS)第1部分    if (SRF_IS_FIRSTCALL()),这部分比较套路化     {         superuser校验         PG_GETARG_OID获取表的oid         heap_open打开表         get_call_result_type计算结果校验,不支持复合类型         BlessTupleDesc(tupdesc) 拿到表结构         usr_ctx->map = dirtyread_convert_tuples_by_name(usr_ctx->reltupdesc,                         funcctx->tuple_desc, "Error converting tuple descriptors!");  //关键的一步,这里使用dirtyread_convert_tuples_by_name函数,。         heap_beginscan(usr_ctx->rel, SnapshotAny...),开始启动表扫描,这里使用了SnapshotAny        }第2部分,不断的获取每一行,然后对每一行进行转换,直到扫描结束。  if ((tuplein = heap_getnext(usr_ctx->scan, ForwardScanDirection)) != NULL)     {         if (usr_ctx->map != NULL)         {             tuplein = dirtyread_do_convert_tuple(tuplein, usr_ctx->map, usr_ctx->oldest_xmin);             SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuplein));         }         else             SRF_RETURN_NEXT(funcctx, heap_copy_tuple_as_datum(tuplein, usr_ctx->reltupdesc));     }     else     {         heap_endscan(usr_ctx->scan); //结束扫描         heap_close(usr_ctx->rel, AccessShareLock); //关闭表         SRF_RETURN_DONE(funcctx);     }整体上实现并不是很复杂,理解了这些后,就可以在此基础上增加自己的功能了。 而PG的魅力就在于此--架构的开放性,可以让开发者迅速地开发自己的“小程序”出来。
  • [技术干货] 技术实践丨PostgreSQL开启Huge Page场景分析
    PostgreSQL用户经常发现,服务端在连接数较大的情况下,会出现系统内存消耗过多的情况,严重者可能会造成OOM。但是服务端配置的共享内存(shared_buffers,wal_buffers等)是一定的,为什么内存会持续增加呢?这就与PostgreSQL的多进程架构有关了,下面我们来分析下。1. 大规格PG实例内存使用率较高分析为了保证物理内存能得到充分的利用,避免内存空间浪费,Linux把进程当前使用的内存部分加载到物理内存里,而不使用的部分则暂不加载。PostMaster进程注册共享内存时,系统只是分配一个虚拟的地址空间,并不直接分配物理内存。当有实际的内存访问时,CPU才会将虚拟地址映射到物理内存的一个地址上。维护这个映射关系的就是PageTable,它负责将虚拟内存地址转换成物理内存地址。Linux的内存管理采取的是分页存取机制:把较大的物理内存分为了一个个固定大小(4kB)的内存页进行管理。每块内存页通过PageTable中的一个元组来维护虚拟/物理内存之间的映射。CPU为了提高虚拟/物理内存之间的转换效率,也会在TLB中缓存一定量的Page Table元组。对于PostgreSQL这种多进程架构程序来说,当服务端使用的共享内存较大,且并发连接数较多时,由于操作系统对于每个进程都要维护单独的内存映射,PageTable中的元组数目将会变得非常多,所占用的内存大小也会特别大。2. Huge Page改善措施Linux为了应对这种场景,降低多进程下PageTable的内存消耗。自从2.6及以上内核版本提供了内存页大小为2MB的管理方式,称为Huge Page。如果使用Huge Page的话,相同物理内存使用量的情况下内存页的数目变少,减少了PageTable元组的条目个数,从而降低了系统的内存占用。作为世界上最先进的开源数据库,PostgreSQL也适配了Linux的Huge Page特性,服务端在注册共享内存时,会通过配置参数huge_pages来决定是否申请大页内存。postgresql.conf:huge_pages = on             -- 注册共享内存时必须使用大页huge_pages = try            -- 注册共享内存时首先考虑大页,若系统提供的大页内存不足时,则全部使用普通页huge_pages = off            -- 注册共享内存时不使用大页真实应用场景:某PG用户将实例(shared_buffers = 64GB)部署在一台内存为256GB的ECS上,业务繁忙时ECS内存使用率为85%,PageTable占用内存120GB。而开启Huge Page后相同业务场景的内存使用率降低到50%以下,PageTable大小仅300M!3. PG实例开启Huge Page操作步骤(1)查看操作系统的Huge Page大小grep Hugepage /proc/meminfo(2)估算PostgreSQL实例需要的Huge Page使用量:128GB/2MB * 1.2 = 78643(3)/etc/sysctl.conf中添加:vm.nr_hugepages = 78643(4)重新加载系统配置参数:sysctl –p(5)确认是否配置成功。可以看到Huge Page总数为78643(6)确认PG配置文件打开huge_pages(7)启动PostgreSQL服务端,可以看到系统中的空闲Huge Page已经减少,部分大页已经被共享内存使用。4. Huge Page使用建议虽然Huge Page在一定场景下可以改善服务端内存使用过高的情况,但不是鼓励所有的PG实例都使用大页,盲目的开启Huge Page可能引起服务端的性能下降。下面我们根据Huge Page的优缺点来分析下使用场景。Huge Page优势:(1)CPU的TLB可以缓存的物理地址空间更大,从而提升TLB的命中率,降低CPU负载;(2)Huge Page使用的内存是不可交换(swap)的,没有内存空间换入/换出的开销;(3)极大的减少了系统维护PageTable的内存开销。Huge Page劣势:(1)Huge Page使用的内存需要预先分配;(2)Huge Page使用固定大小的内存区域,不会被释放;(3)对于写密集型的场景,Huge Page会加大Cache写冲突的发生概率。所以强烈推荐PG实例开启Huge Page的场景:共享内存使用较大(>=8GB)且连接数较多(>= 500),并且热点数据分散。不推荐PG实例开启Huge Page的场景:写业务密集,热点数据集中且内存使用较小。5.PG开启Huge Page时的注意事项(1)当配置参数huge_pages设置为on时,若PG启动时需要注册的共享内存大于操作系统提供的Huge Page大小时,数据库将无法启动。推荐将huge_pages参数设置为try,在此种场景下,PostMaster将会改为申请普通内存。(2)修改shared_buffers/wal_buffers等共享内存相关的GUC参数时,需要重新计算操作系统所需的Huge Page数,以防服务端无法启动或者部分大页内存没有被使用且无法释放而造成浪费。
  • [技术干货] PostgreSQL开启Huge Page场景分析
        PostgreSQL用户经常发现,服务端在连接数较大的情况下,会出现系统内存消耗过多的情况,严重者可能会造成OOM。但是服务端配置的共享内存(shared_buffers,wal_buffers等)是一定的,为什么内存会持续增加呢?这就与PostgreSQL的多进程架构有关了,下面我们来分析下。1. 大规格PG实例内存使用率较高分析为了保证物理内存能得到充分的利用,避免内存空间浪费,Linux把进程当前使用的内存部分加载到物理内存里,而不使用的部分则暂不加载。PostMaster进程注册共享内存时,系统只是分配一个虚拟的地址空间,并不直接分配物理内存。当有实际的内存访问时,CPU才会将虚拟地址映射到物理内存的一个地址上。维护这个映射关系的就是PageTable,它负责将虚拟内存地址转换成物理内存地址。 Linux的内存管理采取的是分页存取机制:把较大的物理内存分为了一个个固定大小(4kB)的内存页进行管理。每块内存页通过PageTable中的一个元组来维护虚拟/物理内存之间的映射。CPU为了提高虚拟/物理内存之间的转换效率,也会在TLB中缓存一定量的Page Table元组。对于PostgreSQL这种多进程架构程序来说,当服务端使用的共享内存较大,且并发连接数较多时,由于操作系统对于每个进程都要维护单独的内存映射,PageTable中的元组数目将会变得非常多,所占用的内存大小也会特别大。2. Huge Page改善措施Linux为了应对这种场景,降低多进程下PageTable的内存消耗。自从2.6及以上内核版本提供了内存页大小为2MB的管理方式,称为Huge Page。如果使用Huge Page的话,相同物理内存使用量的情况下内存页的数目变少,减少了PageTable元组的条目个数,从而降低了系统的内存占用。作为世界上最先进的开源数据库,PostgreSQL也适配了Linux的Huge Page特性,服务端在注册共享内存时,会通过配置参数huge_pages来决定是否申请大页内存。postgresql.conf:    huge_pages = on             -- 注册共享内存时必须使用大页    huge_pages = try            -- 注册共享内存时首先考虑大页,若系统提供的大页内存不足时,则全部使用普通页    huge_pages = off            -- 注册共享内存时不使用大页真实应用场景:某PG用户将实例(shared_buffers = 64GB)部署在一台内存为256GB的ECS上,业务繁忙时ECS内存使用率为85%,PageTable占用内存120GB。而开启Huge Page后相同业务场景的内存使用率降低到50%以下,PageTable大小仅300M!3. PG实例开启Huge Page操作步骤    (1)查看操作系统的Huge Page大小grep Hugepage /proc/meminfo    (2)估算PostgreSQL实例需要的Huge Page使用量:128GB/2MB * 1.2 = 78643    (3)/etc/sysctl.conf中添加:vm.nr_hugepages = 78643    (4)重新加载系统配置参数:sysctl –p    (5)确认是否配置成功。可以看到Huge Page总数为78643    (6)确认PG配置文件打开huge_pages    (7)启动PostgreSQL服务端,可以看到系统中的空闲Huge Page已经减少,部分大页已经被共享内存使用。    4. Huge Page使用建议虽然Huge Page在一定场景下可以改善服务端内存使用过高的情况,但不是鼓励所有的PG实例都使用大页,盲目的开启Huge Page可能引起服务端的性能下降。下面我们根据Huge Page的优缺点来分析下使用场景。Huge Page优势:(1)CPU的TLB可以缓存的物理地址空间更大,从而提升TLB的命中率,降低CPU负载;(2)Huge Page使用的内存是不可交换(swap)的,没有内存空间换入/换出的开销;(3)极大的减少了系统维护PageTable的内存开销。Huge Page劣势:(1)Huge Page使用的内存需要预先分配;(2)Huge Page使用固定大小的内存区域,不会被释放;(3)对于写密集型的场景,Huge Page会加大Cache写冲突的发生概率。所以强烈推荐PG实例开启Huge Page的场景:共享内存使用较大(>=8GB)且连接数较多(>= 500),并且热点数据分散。不推荐PG实例开启Huge Page的场景:写业务密集,热点数据集中且内存使用较小。5.PG开启Huge Page时的注意事项(1)当配置参数huge_pages设置为on时,若PG启动时需要注册的共享内存大于操作系统提供的Huge Page大小时,数据库将无法启动。推荐将huge_pages参数设置为try,在此种场景下,PostMaster将会改为申请普通内存。(2)修改shared_buffers/wal_buffers等共享内存相关的GUC参数时,需要重新计算操作系统所需的Huge Page数,以防服务端无法启动或者部分大页内存没有被使用且无法释放而造成浪费。
  • [问题求助] 欧拉2.8是否支持docker安装postgresql 12.4
    鲲鹏欧拉2.8已安装docker,是否支持用docker安装postgresql 12.4数据库
总条数:112 到第
上滑加载中