PostgreSQL的隐藏特性

我很惊讶,这还没有发布。 你在Postgres知道的任何有趣的技巧? 晦涩的configuration选项和缩放/性能特技是特别受欢迎的。

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

由于postgres比MySQL更健全,没有太多的“技巧”来报告;-)

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

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

  • 确保autovacuum已打开
  • 确保你已经通过了你的postgres.conf(有效的caching大小,共享缓冲区,工作内存…有很多选项调整)。
  • 使用pgpool或pgbouncer将您的“真实”数据库连接保持在最低限度
  • 学习如何解释和解释ANALYZE作品。 学习阅读输出。
  • CLUSTER根据索引对磁盘上的数据进行sorting。 可以显着提高大型(大部分)只读表的性能。 群集是一次性的操作:当表被随后更新时,更改不会聚集。

以下是我发现的一些有用的东西,本身不是configuration或性能相关的。

要查看当前正在发生的事情:

select * from pg_stat_activity; 

search其他function:

 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) 

让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技巧可以在这里find

由于INTERVAL的支持,Postgres拥有非常强大的date处理工具。

例如:

 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) 

您可以将许多string转换为INTERVALtypes。

复制

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

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中更快的批量插入?

  • 我最喜欢的是generate_series :最后一个干净的方式来生成虚拟行集。
  • 能够在子查询的LIMIT子句中使用相关值:

     SELECT ( SELECT exp_word FROM mytable OFFSET id LIMIT 1 ) FROM othertable 
  • 在自定义聚合中使用多个参数的能力(文档未涉及):请参阅我的博客文章中的示例。

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

一旦你了解他们,arrays真的很酷。 可以说,你想要存储一些页面之间的超链接。 你可能会开始考虑创build一个类似于这样的:

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

如果你需要对栏进行索引,并且有200,000,000个链接行(就像维基百科会给你的那样),你会发现自己拥有一个巨大的表格和一个巨大的索引。

但是,使用PostgreSQL,您可以使用这种表格格式:

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

为了获得所有的头,你可以发送一个像这样的命令(自8.4开始,unnest()就是标准的):

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

当与第一个选项比较时,这个查询的速度惊人地快(unnest()是快速的,索引是小的)。 此外,您的表格和索引将占用更less的RAM内存和HD空间,尤其是当您的arrays太长以至于压缩到Toast Table时。 数组非常强大。

注意:虽然unnest()会从数组中产生行,但是array_agg()会将行聚合成一个数组。

物化视图很容易设置:

 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; 

这将创build一个新表my_matview,其中包含my_view的列和值。 触发器或cron脚本可以设置为保持数据更新,或者如果你懒惰:

 TRUNCATE my_matview; INSERT INTO my_matview SELECT * FROM my_view; 
  • inheritance..实际上多重inheritance(就像在父 – 子“inheritance”中,不是许多web框架在使用postgres时实现的1对1关系inheritance)。

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

  • 用不同的语言编写程序,例如C,Python,Perl等(如果你是一个开发人员,而不是一个数据库pipe理员,你的生活就变得容易了)。

    而且所有的程序都可以被存储在外部(作为模块),并且可以通过指定的参数在运行时被调用或导入。 这样你可以源代码控制和轻松debugging代码。

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

    我总是发现运行一些查询和得到所有的元信息,例如它们被实现的约束名称和字段,索引名称等是非常有帮助的。

    对于我来说,当我必须加载新数据或在大表中进行大量更新时(我会自动禁用触发器和删除索引),然后在处理完成后再次轻松地重新创build它们,这一切变得非常方便。 有人撰写了一些这样的疑问做了很好的工作。

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

  • 在一个数据库下有多个模式,如果你的数据库有大量的表,你可以使用它,你可以把模式看作是类别。 所有的表(不pipe它的模式)都可以访问父数据库中的所有其他表和函数。

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

 select pg_size_pretty(200 * 1024) 

pgcrypto :比许多编程语言的encryption模块提供的更多的encryption函数,可以直接从数据库访问。 它使密码的东西非常容易,只是得到正确的。

数据库可以复制到:

createdb -T old_db new_db

该文件说:

这还不是(还)作为通用的“复制数据库”设施

但它对我来说效果不错,而且速度比

createdb new_db

pg_dump old_db | psql new_db

内存存储丢弃数据/全局variables

您可以创build一个驻留在RAM中的表空间,并在该表空间中创build表(可能未logging,在9.1中),以存储您希望跨会话共享的丢弃数据/全局variables。

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

咨询锁

这些logging在手册的一个不起眼的地方:

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

它有时比获取大量的行级锁更快,并且可以用来解决FOR UPDATE未实现的情况(例如recursionCTE查询)。

这是我最喜欢的function之一。

事务性DDL

几乎每个SQL语句在Postgres中都是事务性的。 如果closures自动提交function,则可以执行以下操作:

 drop table customer_orders; rollback; select * from customer_orders; 

范围types和排除约束

据我所知Postgres是唯一的RDBMS,可以让你创build一个约束,检查两个范围是否重叠。 一个例子是包含“有效期”和“有效期”的产品价格表:

 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 ); 

NoSQLfunction

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

 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可以将date与无穷大进行比较,而不是在将来需要“真实”date。 例如,当不使用date范围时,您可以执行以下操作

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

可写公用表expression式

您可以在一个语句中删除,插入和select:

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

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中的列数据types

 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 

你可以从上面的表中看出,我已经使用了数据types – '字符变化'作为'id'
柱。 但这是一个错误,因为我总是把整数作为id。 所以在这里使用varchar是一个不好的习惯。 所以让我们尝试将列types更改为整数。

 ALTER TABLE test ALTER COLUMN id TYPE integer; 

但它返回:

错误:列“id”不能自动转换为键入整型SQL状态:42804提示:指定一个USINGexpression式来执行转换

这意味着我们不能简单地改变数据types,因为数据已经在列中。 由于数据的types是“字符变化”,postgres不能期望它是整数,尽pipe我们只input了整数。 所以,现在,作为postgresbuild议我们可以使用“USING”expression式将我们的数据转换为整数。

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

有用。

7.)知道谁连接到数据库
这或多或less是一个监控命令。 要知道哪个用户连接到哪个数据库(包括他们的IP和端口)使用以下SQL:

 SELECT datname,usename,client_addr,client_port FROM pg_stat_activity ; 

8.)重新加载PostgreSQLconfiguration文件而不重新启动服务器

PostgreSQLconfiguration参数位于像postgresql.conf和pg_hba.conf这样的特殊文件中。 通常,您可能需要更改这些参数。 但是为了使一些参数生效,我们经常需要重新加载configuration文件。 当然,重新启动服务器将做到这一点。 但在生产环境中,重新启动数千个正在使用的数据库并不是首选,只是用于设置一些参数。 在这种情况下,我们可以重新加载configuration文件,而无需使用以下function重新启动服务器:

 select pg_reload_conf(); 

请记住,这不适用于所有的参数,一些参数的变化需要完全重启服务器才能生效。

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

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

 SHOW data_directory; 

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

 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 DEBUGGER

这里解释清楚

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

 ALTER DATABASE name RENAME TO new_name