80

我很惊讶这还没有发布。您在 Postgres 中知道哪些有趣的技巧?晦涩的配置选项和缩放/性能技巧特别受欢迎。

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

4

17 回答 17

77

由于 postgres 比 MySQL 更健全,因此没有那么多“技巧”可以报告;-)

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

其他一些与性能相关的事情要记住:

  • 确保 autovacuum 已打开
  • 确保你已经完成了你的 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)
于 2009-04-17T18:14:09.723 回答
23

让 postgresql 执行得更好的最简单的技巧(当然除了设置和使用正确的索引)就是给它更多的 RAM 来使用(如果你还没有这样做的话)。在大多数默认安装中,shared_buffers的值太低了(在我看来)。你可以设置

共享缓冲区

在 postgresql.conf 中。将此数字除以 128 以获得 postgres 可以要求的内存量(以 MB 为单位)的近似值。如果你足够高,这将使 postgresql 飞起来。不要忘记重新启动 postgresql。

在 Linux 系统上,当 postgresql 无法重新启动时,您可能已经达到 kernel.shmmax 限制。将其设置得更高

sysctl -w kernel.shmmax=xxxx

要在两次启动之间保持这种状态,请将 kernel.shmmax 条目添加到 /etc/sysctl.conf。

可以在这里找到一大堆 Postgresql 技巧

于 2009-04-17T17:41:43.577 回答
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 类型。

于 2009-04-18T01:38:52.353 回答
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 中的更快批量插入?

于 2009-04-17T17:20:22.143 回答
12
  • 到目前为止,我最喜欢的是generate_series:最后是一种生成虚拟行集的干净方法。
  • LIMIT在子查询的子句中使用相关值的能力:

    SELECT  (
            SELECT  exp_word
            FROM    mytable
            OFFSET id
            LIMIT 1
            )
    FROM    othertable
    
  • 在自定义聚合中使用多个参数的能力(文档未涵盖):请参阅我博客中的文章以获取示例。
于 2009-04-17T17:21:54.403 回答
9

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

于 2009-04-17T18:21:33.557 回答
8

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

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

如果您需要索引列,并且您有 200,000,000 个链接行(就像 wikipedia 给您的那样),您会发现自己有一个巨大的表和一个巨大的索引。

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

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 中。

于 2010-02-17T03:01:26.873 回答
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_matview,其中包含 my_view 的列和值。然后可以设置触发器或 cron 脚本以使数据保持最新,或者如果您很懒惰:

TRUNCATE my_matview;
INSERT INTO my_matview SELECT * FROM my_view;
于 2009-06-22T00:15:01.420 回答
6
  • Inheritance..infact 多重继承(如父子“继承”而不是许多 Web 框架在使用 postgres 时实现的一对一关系继承)。

  • PostGIS(空间扩展),一个出色的附加组件,提供全面的几何函数集和开箱即用的坐标存储。广泛用于许多开源地理库(例如 OpenLayers、MapServer、Mapnik 等),并且绝对优于 MySQL 的空间扩展。

  • 用不同的语言编写程序,例如 C、Python、Perl 等(如果您是开发人员而不是 db-admin,那么编写代码会让您的生活更轻松)。

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

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

    我总是发现运行少量查询并获取所有元信息非常有帮助,例如,约束名称和实施它们的字段,索引名称等。

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

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

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

于 2010-02-19T17:15:41.017 回答
6

您无需学习如何解读“解释分析”输出,有一个工具:http ://explain.depesz.com

于 2012-06-19T09:57:56.593 回答
4
select pg_size_pretty(200 * 1024)
于 2009-04-20T02:04:46.143 回答
3

pgcrypto:比许多编程语言的加密模块提供的更多的加密功能,所有这些都可以直接从数据库中访问。它使密码学的东西非常容易做到正确。

于 2009-04-18T01:31:48.063 回答
3

可以使用以下命令复制数据库:

createdb -T old_db new_db

文档说:

这不是(还)打算作为通用的“复制数据库”工具

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

createdb new_db

pg_dump old_db | psql 新数据库

于 2011-06-14T15:39:41.177 回答
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 查询)。

于 2011-05-08T10:06:40.690 回答
2

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

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

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

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'<br> 列的'character varying'。但这是一个错误,因为我总是将整数作为 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.) 获取当前Database集群的数据目录路径

可能在一个系统中,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 调试器

这里解释得很好

于 2014-10-17T05:17:53.390 回答
2

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

事务性 DDL

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

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表中,然后显示被移动的行。

于 2014-10-17T06:30:28.820 回答
0

重命名旧数据库比 mysql 更方便。只需使用以下命令:

ALTER DATABASE name RENAME TO new_name
于 2014-01-17T08:10:28.357 回答