PostgreSQL的隐藏特性[关闭]
Posted
技术标签:
【中文标题】PostgreSQL的隐藏特性[关闭]【英文标题】:Hidden Features of PostgreSQL [closed] 【发布时间】:2010-10-20 03:59:36 【问题描述】:我很惊讶这还没有发布。您在 Postgres 中知道哪些有趣的技巧?晦涩的配置选项和缩放/性能技巧特别受欢迎。
我相信我们可以在相应的mysql thread 上击败 9 cmets :)
【问题讨论】:
【参考方案1】:我真正喜欢 Postgres 的一件事是列中支持的一些数据类型。例如,有一些列类型用于存储Network Addresses 和Arrays。这些列类型的相应函数 (Network Addresses / Arrays) 让您可以在查询中执行许多复杂操作,而这些操作必须通过 MySQL 或其他数据库引擎中的代码处理结果来完成。
【讨论】:
如果标准类型不适合您,您可以轻松创建自己的类型!【参考方案2】:1.) 当您需要附加通知进行查询时,可以使用嵌套注释
SELECT /* my comments, that I would to see in PostgreSQL log */
a, b, c
FROM mytab;
2.) 从数据库中的所有 text
和 varchar
字段中删除尾随空格。
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 varying’ 柱子。但这是一个错误,因为我总是将整数作为 id。所以在这里使用 varchar 是一个 不好的做法。所以让我们尝试将列类型更改为整数。
ALTER TABLE test ALTER COLUMN id TYPE integer;
但它会返回:
错误:列“id”不能自动转换为整数 SQL state: 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 调试器
解释清楚here
【讨论】:
+1 代表 2 ,3, 6 , 9【参考方案3】:这是我最喜欢的鲜为人知的功能列表。
事务性 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
表中,然后显示被移动的行。
【讨论】:
【参考方案4】:由于对 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 类型。
【讨论】:
【参考方案5】:重命名旧数据库比 mysql 更方便。只需使用以下命令:
ALTER DATABASE name RENAME TO new_name
【讨论】:
【参考方案6】:由于 postgres 比 MySQL 更健全,因此没有那么多“技巧”可以报告 ;-)
manual 有一些不错的 performance 提示。
其他一些与性能相关的注意事项:
确保已打开 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)
【讨论】:
谢谢。编辑:添加了关于集群的信息。 显示数据库大小是 8.4 beta psql 中 "\l" 的功能之一,我注意到了。在那之前,我认为 8.3 有一个 pg_size_pretty() 函数来美化字节大小。 感谢您的提示!不知道 pg_size_pretty。我已经更新了我的答案以包含它。 replace(answer, 'per say', 'per se')【参考方案7】:您无需学习如何解读“解释分析”输出,有一个工具:http://explain.depesz.com
【讨论】:
【参考方案8】:可以通过以下方式复制数据库:
createdb -T old_db new_db
文档说:
这不是(还)打算用作通用“复制数据库”工具
但它对我来说效果很好,而且比
快得多createdb new_db
pg_dump old_db | psql new_db
【讨论】:
【参考方案9】:一次性数据/全局变量的内存存储
您可以在 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 查询)。
【讨论】:
在 RAM 中创建表空间是一个非常的坏主意。不要这样做,您将面临对整个数据库造成严重且不可恢复的损坏的风险。使用UNLOGGED
表。【参考方案10】:
继承..infact 多重继承(如父子“继承”而不是许多 Web 框架在使用 postgres 时实现的一对一关系继承)。
PostGIS(空间扩展),一个出色的插件,提供全面的几何函数集和开箱即用的坐标存储。广泛用于许多开源地理库(例如 OpenLayers、MapServer、Mapnik 等),并且绝对优于 MySQL 的空间扩展。
用不同的语言编写程序,例如C、Python、Perl 等(如果您是开发人员而不是 db-admin,那么编写代码会让您的生活更轻松)。
此外,所有过程都可以存储在外部(作为模块),并且可以在运行时通过指定的参数调用或导入。这样您就可以轻松地对代码进行源代码控制和调试。
关于数据库中实现的所有对象(即表、约束、索引等)的庞大而全面的目录。
我总是发现运行少量查询并获取所有元信息非常有帮助,例如,约束名称和实现它们的字段,索引名称等。
对我来说,当我必须加载新数据或在大表中进行大量更新(我会自动禁用触发器并删除索引)并在处理完成后轻松地重新创建它们时,这一切都变得非常方便。有人在编写少量此类查询方面做得非常出色。
http://www.alberton.info/postgresql_meta_info.html
一个数据库下有多个schema,如果你的数据库有很多表,你可以使用它,你可以把schema看成是类别。所有表(无论其架构如何)都可以访问父数据库中存在的所有其他表和函数。
【讨论】:
+1 我不敢相信多重继承已经落后了。【参考方案11】:一旦你了解了数组,它们就真的很酷了。 假设您想在页面之间存储一些超链接。您可能首先考虑创建一个类似这样的表:
CREATE TABLE hyper.links (
tail INT4,
head INT4
);
如果您需要索引 tail 列,并且您有 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 中。
【讨论】:
【参考方案12】:物化视图很容易设置:
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;
【讨论】:
【参考方案13】:select pg_size_pretty(200 * 1024)
【讨论】:
在PostgreSQL 9.3
中尝试过这个错误
@WingedPanther 你的错误是什么?这里 9.3,它也有一个错误(回到 2009 年没有错误),修复是你需要将整数转换为大整数:pg_size_pretty((200 * 1024)::bigint)
是的,就是这样【参考方案14】:
到目前为止,我最喜欢的是generate_series
:终于找到了一种生成虚拟行集的干净方法。
能够在子查询的LIMIT
子句中使用相关值:
SELECT (
SELECT exp_word
FROM mytable
OFFSET id
LIMIT 1
)
FROM othertable
在自定义聚合中使用多个参数的能力(文档未涵盖):有关示例,请参见 the article in my blog。
【讨论】:
+1,generate_series() 正是您在很多事情上所需要的(例如,当您需要一个“虚拟表”时)。第二个 sn-p 看起来也很有趣。【参考方案15】:pgcrypto:比许多编程语言的加密模块提供的加密功能更多,所有这些都可以直接从数据库中访问。它使密码学的东西变得非常容易,只需正确即可。
【讨论】:
【参考方案16】:让 postgresql 执行得更好的最简单技巧(当然,除了设置和使用正确的索引之外)只是给它更多的 RAM 来使用(如果你还没有这样做的话) .在大多数默认安装中,shared_buffers 的值太低(在我看来)。你可以设置
shared_buffers
在 postgresql.conf 中。将此数字除以 128 以获得 postgres 可以要求的内存量(以 MB 为单位)的近似值。如果你足够高,这将使 postgresql 飞起来。不要忘记重启 postgresql。
在 Linux 系统上,当 postgresql 无法重新启动时,您可能已经达到 kernel.shmmax 限制。将其设置得更高
sysctl -w kernel.shmmax=xxxx
要在两次启动之间保持这种状态,请将 kernel.shmmax 条目添加到 /etc/sysctl.conf。
可以在这里找到一大堆 Postgresql 技巧:
http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks【讨论】:
【参考方案17】:复制
我会开始的。每当我从 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';
另见此处: Faster bulk inserts in sqlite3?
【讨论】:
这对于 csv 导入也很方便。 在最近的版本(至少 8.3,可能更早)中,如果您在与 COPY 相同的事务中创建或截断您正在填充的表,它不会触及 WAL 日志,您会得到甚至更快的性能。 postgresql.org/docs/8.3/static/populate.html以上是关于PostgreSQL的隐藏特性[关闭]的主要内容,如果未能解决你的问题,请参考以下文章