我很惊讶这还没有发布。您在Postgres中了解任何有趣的技巧吗?特别欢迎模糊的配置选项和缩放/性能技巧。
我确定我们可以在相应的MySQL线程上击败9条评论:)
Answers:
由于postgres比MySQL更加理智,因此,关于---)报告的“窍门”并不多
请记住其他一些与性能相关的事项:
我发现一些有用的东西,它们本身与配置或性能无关。
要查看当前情况:
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)
让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技巧:
由于有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类型。
复制
我开始 每当我从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中更快的批量插入?
generate_series
:最后一种干净的方法来生成虚拟行集。能够在LIMIT
子查询的子句中使用相关值:
SELECT (
SELECT exp_word
FROM mytable
OFFSET id
LIMIT 1
)
FROM othertable
一旦了解它们,数组真的很酷。假设您想在页面之间存储一些超级链接。您可能首先考虑创建类似这样的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中。
物化视图非常容易设置:
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;
继承..infact多重继承(如父子“继承”,而不是许多Web框架在使用postgres时实现的一对一关系继承)。
PostGIS(空间扩展),一个很棒的插件,提供了全面的几何功能集和开箱即用的坐标存储。广泛用于许多开源地理库(例如OpenLayers,MapServer,Mapnik等)中,并且绝对比MySQL的空间扩展更好。
用不同的语言编写程序,例如C,Python,Perl等(如果您是开发人员而不是db-admin,则使您的代码编写变得容易)。
同样,所有过程都可以存储在外部(作为模块),并且可以在运行时由指定的参数调用或导入。这样,您可以对代码进行源代码控制并轻松调试代码。
关于数据库中实现的所有对象(即表,约束,索引等)的庞大而全面的目录。
我总是发现运行少量查询并获取所有元信息(例如,约束名称和对其实施了其的字段,索引名称等)非常有用。
对我来说,当我必须加载新数据或在大表中进行大量更新(我将自动禁用触发器和删除索引),然后在处理完成后轻松地重新创建它们时,这一切变得非常方便。有人在编写少数几个查询方面做得很好。
一个数据库下有多个模式,如果数据库中有大量表,则可以使用它,您可以将模式视为类别。所有表(无论其模式如何)都可以访问父数据库中存在的所有其他表和函数。
您无需学习如何解密“解释分析”输出,这里有一个工具:http : //explain.depesz.com
select pg_size_pretty(200 * 1024)
PostgreSQL 9.3
捕获的错误
pg_size_pretty((200 * 1024)::bigint)
用于一次性数据/全局变量的内存存储
您可以创建一个驻留在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查询)。
UNLOGGED
表格。
这是我最喜欢的鲜为人知的功能列表。
几乎每个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
);
该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
表格中,然后显示已移动的行。
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 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调试器
好在这里解释