PostgreSQL的隐藏功能


80

我很惊讶这还没有发布。您在Postgres中了解任何有趣的技巧吗?特别欢迎模糊的配置选项和缩放/性能技巧。

我确定我们可以在相应的MySQL线程上击败9条评论:)

Answers:


76

由于postgres比MySQL更加理智,因此,关于---)报告的“窍门”并不多

手册有一些不错的性能提示。

请记住其他一些与性能相关的事项:

  • 确保打开了自动真空
  • 确保您已遍历postgres.conf(有效的高速缓存大小,共享缓冲区,工作内存...那里有很多可供调整的选项)。
  • 使用pgpool或pgbouncer将“真实”数据库连接保持在最低限度
  • 了解EXPLAIN和EXPLAIN ANALYZE的工作方式。学习阅读输出。
  • CLUSTER根据索引对磁盘上的数据进行排序。可以大大提高大型(主要是)只读表的性能。集群是一项一次性操作:表在随后进行更新时,更改不会集群。

我发现一些有用的东西,它们本身与配置或性能无关。

要查看当前情况:

select * from pg_stat_activity;

搜索杂项功能:

select * from pg_proc WHERE proname ~* '^pg_.*'

查找数据库的大小:

select pg_database_size('postgres');
select pg_size_pretty(pg_database_size('postgres'));

查找所有数据库的大小:

select datname, pg_size_pretty(pg_database_size(datname)) as size
  from pg_database;

查找表和索引的大小:

select pg_size_pretty(pg_relation_size('public.customer'));

或者,列出所有表和索引(可能更容易查看此表):

select schemaname, relname,
    pg_size_pretty(pg_relation_size(schemaname || '.' || relname)) as size
  from (select schemaname, relname, 'table' as type
          from pg_stat_user_tables
        union all
        select schemaname, relname, 'index' as type
          from pg_stat_user_indexes) x;

哦,您可以嵌套事务,回滚部分事务++

test=# begin;
BEGIN
test=# select count(*) from customer where name='test';
 count 
-------
     0
(1 row)
test=# insert into customer (name) values ('test');
INSERT 0 1
test=# savepoint foo;
SAVEPOINT
test=# update customer set name='john';
UPDATE 3
test=# rollback to savepoint foo;
ROLLBACK
test=# commit;
COMMIT
test=# select count(*) from customer where name='test';
 count 
-------
     1
(1 row)

谢谢。编辑:添加了有关群集的信息。
tommym

我注意到,显示数据库大小是8.4 beta psql中“ \ l”的功能之一。在此之前,我认为8.3具有pg_size_pretty()函数来以字节为单位来修饰大小。
araqnid

谢谢你的提示!不知道pg_size_pretty。我更新了我的答案以包含它。
tommym

3
replace(answer,'per say','per se')
asjo,2009年

23

让postgresql性能更好的最简单的技巧(当然除了设置和使用适当的索引)是给它更多的RAM(如果您还没有这样做的话)。在大多数默认安装中,shared_buffers的值太低(我认为)。你可以设定

shared_buffers

在postgresql.conf中。将该数字除以128可得出Postgres可声明的内存量(以MB为单位)的近似值。如果您足够多,这将使postgresql飞起来。不要忘记重新启动postgresql。

在Linux系统上,当postgresql不再启动时,您可能已经达到了kernel.shmmax限制。设置更高

sysctl -w kernel.shmmax=xxxx

为了使它们在引导之间持续存在,请在/etc/sysctl.conf中添加kernel.shmmax条目。

在这里可以找到大量的Postgresql技巧


17

由于有INTERVAL支持,Postgres具有非常强大的日期时间处理功能。

例如:

select NOW(), NOW() + '1 hour';
              now              |           ?column?            
-------------------------------+-------------------------------
 2009-04-18 01:37:49.116614+00 | 2009-04-18 02:37:49.116614+00
(1 row)



select current_date ,(current_date +  interval '1 year')::date;
    date             |  date            
---------------------+----------------
 2014-10-17          | 2015-10-17
(1 row)

您可以将许多字符串转换为INTERVAL类型。


15

复制

我开始 每当我从SQLite切换到Postgres时,通常都会有一些非常大的数据集。关键是用COPY FROM而不是INSERTS加载表。请参阅文档:

http://www.postgresql.org/docs/8.1/static/sql-copy.html

以下示例使用竖线(|)作为字段定界符将表复制到客户端:

COPY country TO STDOUT WITH DELIMITER '|';

要将数据从文件复制到国家/地区表:

COPY country FROM '/usr1/proj/bray/sql/country_data';

另请参见此处: sqlite3中更快的批量插入?


2
这对于csv导入也很方便。
ChristopheD

在最近的发行版中(至少为8.3,可能更早),如果在与COPY相同的事务中创建或截断要填充的表,它将不会触及WAL日志,因此您将获得更快的性能。postgresql.org/docs/8.3/static/populate.html
TREE

12
  • 我到目前为止最喜欢的是generate_series:最后一种干净的方法来生成虚拟行集。
  • 能够在LIMIT子查询的子句中使用相关值:

    SELECT  (
            SELECT  exp_word
            FROM    mytable
            OFFSET id
            LIMIT 1
            )
    FROM    othertable
    
  • 可以在自定义聚合中使用多个参数(文档未涵盖):有关示例,请参阅我的博客中的文章

1
+ 1,generate_series()正是您需要做的很多事情(例如,每当需要“虚拟表”时)。第二个片段看起来也很有趣。
j_random_hacker 2009年

9

我真正喜欢Postgres的一件事是列中支持的某些数据类型。例如,有一些用于存储网络地址数组的列类型。这些列类型的相应功能(网络地址/数组)使您可以在查询中执行很多复杂的操作,而这些操作必须通过MySQL或其他数据库引擎中的代码处理结果来完成。


2
如果标准类型不适合您,则可以轻松创建自己的类型!
bortzmeyer

8

一旦了解它们,数组真的很酷。假设您想在页面之间存储一些超级链接。您可能首先考虑创建类似这样的Table:

CREATE TABLE hyper.links (
     tail INT4,
     head INT4
);

如果您需要索引栏,并且您有200,000,000个链接行(例如Wikipedia会给您),那么您会发现自己拥有巨大的表格和巨大的索引。

但是,对于PostgreSQL,您可以改用这种Table格式:

CREATE TABLE hyper.links (
     tail INT4,
     head INT4[],
     PRIMARY KEY(tail)
);

要获得链接的所有信息,您可以发送如下命令(unnest()自8.4起是标准的):

SELECT unnest(head) FROM hyper.links WHERE tail = $1;

与第一个选项进行比较时,该查询的速度出乎意料的快(unnest()很快,而索引要小得多)。此外,您的表和索引将占用更少的RAM内存和HD空间,尤其是当阵列太长以至于它们被压缩成Toast表时。数组真的很强大。

注意:虽然unnest()会从Array中生成行,但是array_agg()会将行聚合到Array中。


6

物化视图非常容易设置:

CREATE VIEW my_view AS SELECT id, AVG(my_col) FROM my_table GROUP BY id;
CREATE TABLE my_matview AS SELECT * FROM my_view;

这将创建一个具有my_view的列和值的新表my_matview。然后可以设置触发器或cron脚本以使数据保持最新,或者如果您很懒:

TRUNCATE my_matview;
INSERT INTO my_matview SELECT * FROM my_view;

6
  • 继承..infact多重继承(如父子“继承”,而不是许多Web框架在使用postgres时实现的一对一关系继承)。

  • PostGIS(空间扩展),一个很棒的插件,提供了全面的几何功能集和开箱即用的坐标存储。广泛用于许多开源地理库(例如OpenLayers,MapServer,Mapnik等)中,并且绝对比MySQL的空间扩展更好。

  • 用不同的语言编写程序,例如C,Python,Perl等(如果您是开发人员而不是db-admin,则使您的代码编写变得容易)。

    同样,所有过程都可以存储在外部(作为模块),并且可以在运行时由指定的参数调用或导入。这样,您可以对代码进行源代码控制并轻松调试代码。

  • 关于数据库中实现的所有对象(即表,约束,索引等)的庞大而全面的目录。

    我总是发现运行少量查询并获取所有元信息(例如,约束名称和对其实施了其的字段,索引名称等)非常有用。

    对我来说,当我必须加载新数据或在大表中进行大量更新(我将自动禁用触发器和删除索引),然后在处理完成后轻松地重新创建它们时,这一切变得非常方便。有人在编写少数几个查询方面做得很好。

    http://www.alberton.info/postgresql_meta_info.html

  • 一个数据库下有多个模式,如果数据库中有大量表,则可以使用它,您可以将模式视为类别。所有表(无论其模式如何)都可以访问父数据库中存在的所有其他表和函数。


+1我简直不敢相信多重继承这么远。
亚当·根特


4
select pg_size_pretty(200 * 1024)

尝试了此PostgreSQL 9.3捕获的错误
Vivek S.

@WingedPanther你怎么了?在此9.3中,它也有一个错误(那么回溯到2009年就没有错误),解决方法是您需要将整数转换为大整数:pg_size_pretty((200 * 1024)::bigint)
Michael Buen 2014年

是的,就是这样
Vivek S.

3

pgcrypto:比许多编程语言的加密模块提供的加密功能更多,所有这些都可以直接从数据库访问。它使加密的东西变得难以置信。


3

可以使用以下方式复制数据库:

createdb -T old_db new_db

该文件说:

(尚未)打算用作通用的“ COPY DATABASE”工具

但是它对我来说效果很好,并且比

createdb new_db

pg_dump old_db | psql new_db


2

用于一次性数据/全局变量的内存存储

您可以创建一个驻留在RAM中的表空间,并在该表空间中创建表(可能是9.1中未记录的表),以存储您希望在会话之间共享的一次性数据/全局变量。

http://magazine.redhat.com/2007/12/12/tip-from-an-rhce-memory-storage-on-postgresql/

咨询锁

这些内容记录在手册的晦涩之处:

http://www.postgresql.org/docs/9.0/interactive/functions-admin.html

它有时比获取大量行级锁快,并且它们可用于解决未实现FOR UPDATE的情况(例如递归CTE查询)。


4
在RAM中创建表空间是一个非常糟糕的主意。否则,您将面临整个数据库严重且不可恢复的损坏的风险。使用UNLOGGED表格。
Craig Ringer

2

这是我最喜欢的鲜为人知的功能列表。

事务性DDL

几乎每个SQL语句在Postgres中都是事务性的。如果关闭自动提交,则可能发生以下情况:

drop table customer_orders;
rollback;
select *
from customer_orders;

范围类型和排除约束

据我所知,Postgres是唯一允许您创建一个约束来检查两个范围是否重叠的RDBMS。一个示例是一个表,其中包含带有“有效期自”和“有效期至”日期的产品价格:

create table product_price
(
   price_id      serial        not null primary key,
   product_id    integer       not null references products,
   price         numeric(16,4) not null,
   valid_during  daterange not null
);

NoSQL功能

hstore 扩展提供了一个灵活且非常快速的键/值存储,可以在数据库的某些部分需要“无模式”时使用。JSON是以无模式方式存储数据和

insert into product_price 
  (product_id, price, valid_during)
values 
  (1, 100.0, '[2013-01-01,2014-01-01)'),
  (1,  90.0, '[2014-01-01,)');


-- querying is simply and can use an index on the valid_during column
select price
from product_price
where product_id = 42
  and valid_during @> date '2014-10-17';

具有700.000行的表上的上述执行计划:

Index Scan using check_price_range on public.product_price  (cost=0.29..3.29 rows=1 width=6) (actual time=0.605..0.728 rows=1 loops=1)
  Output: price
  Index Cond: ((product_price.valid_during @> '2014-10-17'::date) AND (product_price.product_id = 42))
  Buffers: shared hit=17
Total runtime: 0.772 ms

为了避免插入有效范围重叠的行,可以定义一个简单(有效)的唯一约束:

alter table product_price
  add constraint check_price_range 
  exclude using gist (product_id with =, valid_during with &&)

无穷

Postgres可以将日期与无穷大进行比较,而不必在将来要求“真实”日期。例如,当不使用日期范围时,您可以执行以下操作

insert into product_price 
  (product_id, price, valid_from, valid_until)
values 
  (1,  90.0, date '2014-01-01', date 'infinity');

可写的公用表表达式

您可以在单个语句中删除,插入和选择:

with old_orders as (
   delete from orders
   where order_date < current_date - interval '10' year
   returning *
), archived_rows as (
   insert into archived_orders 
   select * 
   from old_orders
   returning *
)
select *
from archived_rows;

上面的命令将删除所有10年以上的订单,将其移至archived_orders表格中,然后显示已移动的行。


2

1.)当需要附加通知进行查询时,可以使用嵌套注释

SELECT /* my comments, that I would to see in PostgreSQL log */
       a, b, c
   FROM mytab;

2.)从数据库的所有textvarchar字段中删除尾随空格。

do $$
declare
    selectrow record;
begin
for selectrow in
select 
       'UPDATE '||c.table_name||' SET '||c.COLUMN_NAME||'=TRIM('||c.COLUMN_NAME||')  WHERE '||c.COLUMN_NAME||' ILIKE ''% '' ' as script
from (
       select 
          table_name,COLUMN_NAME
       from 
          INFORMATION_SCHEMA.COLUMNS 
       where 
          table_name LIKE 'tbl%'  and (data_type='text' or data_type='character varying' )
     ) c
loop
execute selectrow.script;
end loop;
end;
$$;

3.)我们可以使用窗口函数非常有效地删除重复的行:

DELETE FROM tab 
  WHERE id IN (SELECT id 
                  FROM (SELECT row_number() OVER (PARTITION BY column_with_duplicate_values), id 
                           FROM tab) x 
                 WHERE x.row_number > 1);

某些PostgreSQL的优化版本(带有ctid):

DELETE FROM tab 
  WHERE ctid = ANY(ARRAY(SELECT ctid 
                  FROM (SELECT row_number() OVER (PARTITION BY column_with_duplicate_values), ctid 
                           FROM tab) x 
                 WHERE x.row_number > 1));

4.)当我们需要确定服务器的状态时,可以使用一个函数:

SELECT pg_is_in_recovery();

5.)获取函数的DDL命令。

select pg_get_functiondef((select oid from pg_proc where proname = 'f1'));

6.)在PostgreSQL中安全地更改列数据类型

create table test(id varchar );
insert into test values('1');
insert into test values('11');
insert into test values('12');

select * from test
--Result--
id
character varying
--------------------------
1
11
12

从上表中可以看到,我使用了数据类型-“ id”
列的“ character changes” 。但这是一个错误,因为我总是将整数作为id。因此,在这里使用varchar是一个坏习惯。因此,让我们尝试将列类型更改为整数。

ALTER TABLE test ALTER COLUMN id TYPE integer;

但它返回:

错误:无法自动将“ id”列强制转换为整数SQL状态:42804提示:指定用于执行转换的USING表达式

这意味着我们不能简单地更改数据类型,因为列中已经有数据。由于数据是“字符变化的”类型,尽管我们仅输入整数,但postgres不能期望它为整数。因此,现在,正如postgres建议的那样,我们可以使用'USING'表达式将数据转换为整数。

ALTER TABLE test ALTER COLUMN id  TYPE integer USING (id ::integer);

有用。

7.)知道谁连接到数据库
这或多或少是一个监视命令。要知道哪个用户连接到哪个数据库(包括其IP和端口),请使用以下SQL:

SELECT datname,usename,client_addr,client_port FROM pg_stat_activity ;

8.)在不重新启动服务器的情况下重新加载PostgreSQL配置文件

PostgreSQL配置参数位于特殊文件中,例如postgresql.conf和pg_hba.conf。通常,您可能需要更改这些参数。但是,为了使某些参数生效,我们经常需要重新加载配置文件。当然,重新启动服务器即可。但是在生产环境中,重新启动数据库(数千人正在使用该数据库只是为了设置某些参数)不是首选。在这种情况下,我们可以使用以下功能重新加载配置文件而无需重新启动服务器:

select pg_reload_conf();

请记住,这不适用于所有参数,某些参数更改需要完全重新启动服务器才能生效。

9.)获取当前数据库集群的数据目录路径

通常,在系统中,可能会在不同的端口中设置PostgreSQL的多个实例(群集)。在这种情况下,查找哪个实例使用哪个目录(物理存储目录)是一项繁重的任务。在这种情况下,我们可以在我们感兴趣的集群中的任何数据库中使用以下命令来获取目录路径:

SHOW data_directory;

可以使用相同的功能来更改集群的数据目录,但是它需要服务器重新启动:

SET data_directory to new_directory_path;

10.)查找CHAR是否为DATE

create or replace function is_date(s varchar) returns boolean as $$
begin
  perform s::date;
  return true;
exception when others then
  return false;
end;
$$ language plpgsql;

用法:以下将返回True

select is_date('12-12-2014')
select is_date('12/12/2014')
select is_date('20141212')
select is_date('2014.12.12')
select is_date('2014,12,12')

11.)在PostgreSQL中更改所有者

REASSIGN OWNED BY sa  TO postgres;

12.)PGADMIN PLPGSQL调试器

好在这里解释


1为2369

0

重命名旧数据库很方便,而不是mysql可以做到的。只需使用以下命令:

ALTER DATABASE name RENAME TO new_name
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.