Versteckte Funktionen von PostgreSQL [geschlossen]


80

Ich bin überrascht, dass dies noch nicht veröffentlicht wurde. Irgendwelche interessanten Tricks, die Sie in Postgres kennen? Obskure Konfigurationsoptionen und Skalierungs- / Perfektionstricks sind besonders willkommen.

Ich bin sicher, wir können die 9 Kommentare zum entsprechenden MySQL-Thread schlagen :)

Antworten:


76

Da Postgres viel vernünftiger ist als MySQL, gibt es nicht so viele "Tricks", über die man berichten kann ;-)

Das Handbuch enthält einige nützliche Tipps zur Leistung .

Einige andere leistungsbezogene Dinge, die Sie beachten sollten:

  • Stellen Sie sicher, dass das Autovakuum eingeschaltet ist
  • Stellen Sie sicher, dass Sie Ihre postgres.conf durchgesehen haben (effektive Cache-Größe, freigegebene Puffer, Arbeitsmem ... viele Optionen zum Optimieren).
  • Verwenden Sie pgpool oder pgbouncer , um Ihre "echten" Datenbankverbindungen auf ein Minimum zu beschränken
  • Erfahren Sie, wie EXPLAIN und EXPLAIN ANALYZE funktionieren. Lernen Sie, die Ausgabe zu lesen.
  • CLUSTER sortiert Daten auf der Festplatte nach einem Index. Kann die Leistung großer (meistens) schreibgeschützter Tabellen erheblich verbessern. Das Clustering ist eine einmalige Operation: Wenn die Tabelle anschließend aktualisiert wird, werden die Änderungen nicht geclustert.

Hier sind einige Dinge, die ich als nützlich empfunden habe und die per se nicht mit der Konfiguration oder Leistung zusammenhängen.

Um zu sehen, was gerade passiert:

select * from pg_stat_activity;

Suche verschiedene Funktionen:

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

Größe der Datenbank ermitteln:

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

Größe aller Datenbanken ermitteln:

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

Größe von Tabellen und Indizes ermitteln:

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

Oder um alle Tabellen und Indizes aufzulisten (wahrscheinlich einfacher, dies anzuzeigen):

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;

Oh, und Sie können Transaktionen verschachteln, Teiltransaktionen zurücksetzen ++

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)

Vielen Dank. BEARBEITEN: Informationen zu CLUSTER hinzugefügt.
Tommym

Das Anzeigen der Datenbankgröße ist eine der Funktionen von "\ l" in der Beta-Version 8.4, wie ich bemerkt habe. Bis dahin hat 8.3 meiner Meinung nach eine Funktion pg_size_pretty (), um eine Größe in Bytes zu verschönern.
Araqnid

Danke für den Tipp! Wusste nicht von pg_size_pretty. Ich habe meine Antwort aktualisiert, um sie aufzunehmen.
Tommym

3
ersetzen (antworten, 'per say', 'per se')
asjo

23

Der einfachste Trick, um postgresql viel leistungsfähiger zu machen (abgesehen von der Einstellung und Verwendung der richtigen Indizes natürlich), besteht darin, ihm mehr RAM zum Arbeiten zu geben (falls Sie dies noch nicht getan haben). Bei den meisten Standardinstallationen ist der Wert für shared_buffers (meiner Meinung nach) viel zu niedrig. Sie können einstellen

shared_buffers

in postgresql.conf. Teilen Sie diese Zahl durch 128, um eine ungefähre Menge an Speicher (in MB) zu erhalten, die Postgres beanspruchen kann. Wenn Sie genug davon haben, wird dies postgresql zum Fliegen bringen. Vergessen Sie nicht, postgresql neu zu starten.

Wenn postgresql auf Linux-Systemen nicht erneut gestartet werden kann, haben Sie wahrscheinlich das kernel.shmmax-Limit erreicht. Stellen Sie es höher mit

sysctl -w kernel.shmmax=xxxx

Fügen Sie /etc/sysctl.conf einen kernel.shmmax-Eintrag hinzu, damit dies zwischen den Starts bestehen bleibt.

Eine ganze Reihe von Postgresql-Tricks finden Sie hier :


17

Postgres verfügt dank seiner INTERVAL-Unterstützung über eine sehr leistungsstarke Datetime-Handling-Funktion.

Zum Beispiel:

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)

Sie können viele Zeichenfolgen in einen INTERVAL-Typ umwandeln.


15

KOPIEREN

Ich werde anfangen. Wenn ich von SQLite zu Postgres wechsle, habe ich normalerweise einige wirklich große Datenmengen. Der Schlüssel ist, Ihre Tabellen mit COPY FROM zu laden, anstatt INSERTS auszuführen. Siehe Dokumentation:

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

Im folgenden Beispiel wird eine Tabelle mit dem vertikalen Balken (|) als Feldtrennzeichen auf den Client kopiert:

COPY country TO STDOUT WITH DELIMITER '|';

So kopieren Sie Daten aus einer Datei in die Ländertabelle:

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

Siehe auch hier: Schnellere Masseneinsätze in sqlite3?


2
Dies ist auch praktisch für CSV-Importe.
ChristopheD

In neueren Versionen (mindestens 8.3, möglicherweise früher) wird das WAL-Protokoll nicht berührt, wenn Sie die Tabelle, die Sie in derselben Transaktion wie die KOPIE auffüllen, ERSTELLEN oder TRUNCIEREN, und Sie erhalten eine noch schnellere Leistung. postgresql.org/docs/8.3/static/populate.html
TREE

12
  • Mein bei weitem Favorit ist generate_series: Endlich eine saubere Möglichkeit, Dummy-Rowsets zu generieren.
  • Möglichkeit, einen korrelierten Wert in einer LIMITKlausel einer Unterabfrage zu verwenden:

    SELECT  (
            SELECT  exp_word
            FROM    mytable
            OFFSET id
            LIMIT 1
            )
    FROM    othertable
    
  • Möglichkeit, mehrere Parameter in benutzerdefinierten Aggregaten zu verwenden (wird in der Dokumentation nicht behandelt): Ein Beispiel finden Sie im Artikel in meinem Blog .

1
+1, generate_series () ist genau das, was Sie für einige Dinge benötigen (z. B. wenn Sie eine "Dummy-Tabelle" benötigen). Das 2. Snippet sieht ebenfalls faszinierend aus.
j_random_hacker

9

Eines der Dinge, die ich an Postgres wirklich mag, sind einige der in Spalten unterstützten Datentypen. Beispielsweise gibt es Spaltentypen zum Speichern von Netzwerkadressen und Arrays . Mit den entsprechenden Funktionen ( Netzwerkadressen / Arrays ) für diese Spaltentypen können Sie viele komplexe Vorgänge in Abfragen ausführen, die Sie ausführen müssen, indem Sie Ergebnisse über Code in MySQL oder anderen Datenbankmodulen verarbeiten.


2
Und Sie können ganz einfach Ihre eigenen Typen erstellen, wenn die Standardtypen nicht zu Ihnen passen!
Bortzmeyer

8

Arrays sind wirklich cool, wenn man sie kennenlernt. Nehmen wir an, Sie möchten einige Hyperlinks zwischen Seiten speichern. Sie könnten damit beginnen, eine Tabelle wie diese zu erstellen:

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

Wenn Sie indizieren die benötigte Schwanz Spalte, und man hatte, sagen wir 200 Millionen Links-Reihen (wie wikipedia würden Sie geben), findet man sich mit einem großen Tisch und einem großen Index finden würde.

Mit PostgreSQL können Sie jedoch stattdessen dieses Tabellenformat verwenden:

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

Um alle Köpfe für einen Link zu erhalten, können Sie einen Befehl wie diesen senden (unnest () ist seit 8.4 Standard):

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

Diese Abfrage ist überraschend schnell, wenn sie mit der ersten Option verglichen wird (unnest () ist schnell und der Index ist viel kleiner). Darüber hinaus belegen Ihre Tabelle und Ihr Index viel weniger RAM-Speicher und HD-Speicherplatz, insbesondere wenn Ihre Arrays so lang sind, dass sie zu einer Toast-Tabelle komprimiert werden. Arrays sind wirklich mächtig.

Hinweis: Während unnest () Zeilen aus einem Array generiert, aggregiert array_agg () Zeilen zu einem Array.


6

Materialisierte Ansichten sind ziemlich einfach einzurichten:

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;

Dadurch wird eine neue Tabelle, my_matview, mit den Spalten und Werten von my_view erstellt. Trigger oder ein Cron-Skript können dann eingerichtet werden, um die Daten auf dem neuesten Stand zu halten, oder wenn Sie faul sind:

TRUNCATE my_matview;
INSERT INTO my_matview SELECT * FROM my_view;

6
  • Vererbung..infact Mehrfachvererbung (wie bei der Eltern-Kind-Vererbung, nicht bei der 1-zu-1-Beziehungsvererbung, die viele Web-Frameworks bei der Arbeit mit Postgres implementieren).

  • PostGIS (räumliche Erweiterung), ein wunderbares Add-On, das umfassende Geometriefunktionen bietet und die Speicherung sofort koordiniert. Weit verbreitet in vielen Open-Source-Geobibliotheken (z. B. OpenLayers, MapServer, Mapnik usw.) und definitiv viel besser als die räumlichen Erweiterungen von MySQL.

  • Schreibvorgänge in verschiedenen Sprachen, z. B. C, Python, Perl usw. (erleichtert das Codieren in Ihrem Leben, wenn Sie Entwickler und kein Datenbankadministrator sind).

    Alle Prozeduren können auch extern (als Module) gespeichert und zur Laufzeit durch angegebene Argumente aufgerufen oder importiert werden. Auf diese Weise können Sie den Code in der Quellcodeverwaltung steuern und den Code einfach debuggen.

  • Ein umfangreicher Katalog zu allen in Ihrer Datenbank implementierten Objekten (z. B. Tabellen, Einschränkungen, Indizes usw.).

    Ich finde es immer sehr hilfreich, einige Abfragen auszuführen und alle Metainformationen abzurufen, z. B. Einschränkungsnamen und Felder, in denen sie implementiert wurden, Indexnamen usw.

    Für mich ist alles sehr praktisch, wenn ich neue Daten laden oder umfangreiche Aktualisierungen in großen Tabellen durchführen muss (ich würde Trigger automatisch deaktivieren und Indizes löschen) und sie nach Abschluss der Verarbeitung einfach wieder neu erstellen muss. Jemand hat eine Handvoll dieser Fragen hervorragend geschrieben.

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

  • Mehrere Schemas unter einer Datenbank. Sie können sie verwenden, wenn Ihre Datenbank eine große Anzahl von Tabellen enthält. Sie können sich Schemas als Kategorien vorstellen. Alle Tabellen (unabhängig vom Schema) haben Zugriff auf alle anderen Tabellen und Funktionen in der übergeordneten Datenbank.


+1 Ich kann nicht glauben, dass die Mehrfachvererbung so weit unten liegt.
Adam Gent


4
select pg_size_pretty(200 * 1024)

versuchte dies in PostgreSQL 9.3gefangenem Fehler
Vivek S.

@WingedPanther Was ist dein Fehler? Hier 9.3 hat es auch einen Fehler (hat damals noch keinen Fehler), das Update ist, dass Sie die Ganzzahl in eine große Ganzzahl umwandeln müssen:pg_size_pretty((200 * 1024)::bigint)
Michael Buen

Ja, das ist die Sache
Vivek S.

3

pgcrypto : Mehr kryptografische Funktionen als die Kryptomodule vieler Programmiersprachen bieten, auf die alle direkt aus der Datenbank zugegriffen werden kann. Es macht kryptografische Dinge unglaublich einfach, einfach richtig zu machen.


3

Eine Datenbank kann kopiert werden mit:

createdb -T old_db new_db

Die Dokumentation sagt:

Dies ist (noch) nicht als allgemeine "COPY DATABASE" -Funktion gedacht

aber es funktioniert gut für mich und ist viel schneller als

createdb new_db

pg_dump old_db | psql new_db


2

Speicher für Wegwerfdaten / globale Variablen

Sie können einen Tabellenbereich erstellen, der sich im RAM befindet, und Tabellen (möglicherweise nicht protokolliert in 9.1) in diesem Tabellenbereich, um Wegwerfdaten / globale Variablen zu speichern, die Sie für mehrere Sitzungen freigeben möchten.

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

Hinweisschlösser

Diese sind in einem dunklen Bereich des Handbuchs dokumentiert:

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

Es ist gelegentlich schneller als das Erfassen einer Vielzahl von Sperren auf Zeilenebene und kann verwendet werden, um Fälle zu umgehen, in denen FOR UPDATE nicht implementiert ist (z. B. rekursive CTE-Abfragen).


4
Das Erstellen eines Tablespace im RAM ist eine äußerst schlechte Idee. Wenn Sie dies nicht tun, riskieren Sie eine schwere und nicht behebbare Beschädigung der gesamten Datenbank. Verwenden Sie UNLOGGEDTabellen.
Craig Ringer

2

Dies ist meine Favoritenliste mit weniger bekannten Funktionen.

Transaktions-DDL

Nahezu jede SQL-Anweisung ist in Postgres transaktional. Wenn Sie die automatische Festschreibung deaktivieren, ist Folgendes möglich:

drop table customer_orders;
rollback;
select *
from customer_orders;

Bereichstypen und Ausschlussbeschränkungen

Meines Wissens ist Postgres das einzige RDBMS, mit dem Sie eine Einschränkung erstellen können, die prüft, ob sich zwei Bereiche überschneiden. Ein Beispiel ist eine Tabelle, die Produktpreise mit dem Datum "gültig von" und "gültig bis" enthält:

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-Funktionen

Die hstore Erweiterung bietet einen flexiblen und sehr schnellen Schlüssel- / Wertspeicher, der verwendet werden kann, wenn Teile der Datenbank "schemalos" sein müssen. JSON ist eine weitere Option zum Speichern von Daten ohne Schema und

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

Der Ausführungsplan für das oben Gesagte in einer Tabelle mit 700.000 Zeilen:

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

Um das Einfügen von Zeilen mit überlappenden Gültigkeitsbereichen zu vermeiden, kann eine einfache (und effiziente) eindeutige Einschränkung definiert werden:

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

Unendlichkeit

Anstatt weit in der Zukunft ein "echtes" Datum zu verlangen, kann Postgres Daten mit unendlich vergleichen. Wenn Sie beispielsweise keinen Datumsbereich verwenden, können Sie Folgendes tun

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

Beschreibbare allgemeine Tabellenausdrücke

Sie können eine einzelne Anweisung löschen, einfügen und auswählen:

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;

Mit dem obigen Befehl werden alle Bestellungen gelöscht, die älter als 10 Jahre sind, in die archived_ordersTabelle verschoben und dann die verschobenen Zeilen angezeigt.


2

1.) Wenn Sie zum Abfragen eine Benachrichtigung anhängen müssen, können Sie einen verschachtelten Kommentar verwenden

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

2.) Entfernen Sie nachgestellte Leerzeichen aus allen textund- varcharFeldern in einer Datenbank.

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.) Wir können eine Fensterfunktion verwenden, um doppelte Zeilen sehr effektiv zu entfernen:

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

Einige optimierte PostgreSQL-Versionen (mit 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.) Wenn wir den Serverstatus identifizieren müssen, können wir eine Funktion verwenden:

SELECT pg_is_in_recovery();

5.) Holen Sie sich den DDL-Befehl der Funktionen.

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

6.) Sicheres Ändern des Spaltendatentyps in 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

Sie können der obigen Tabelle entnehmen, dass ich den Datentyp "Zeichen variierend" für die
Spalte "ID" verwendet habe . Aber es war ein Fehler, weil ich immer ganze Zahlen als ID gebe. Daher ist die Verwendung von Varchar hier eine schlechte Praxis. Versuchen wir also, den Spaltentyp in eine Ganzzahl zu ändern.

ALTER TABLE test ALTER COLUMN id TYPE integer;

Aber es kehrt zurück:

FEHLER: Die Spalte "id" kann nicht automatisch in einen ganzzahligen SQL-Status umgewandelt werden: 42804 Hinweis: Geben Sie einen USING-Ausdruck an, um die Konvertierung durchzuführen

Das heißt, wir können den Datentyp nicht einfach ändern, da die Daten bereits in der Spalte vorhanden sind. Da die Daten vom Typ 'zeichenvariierend' sind, können Postgres sie nicht als Ganzzahl erwarten, obwohl wir nur Ganzzahlen eingegeben haben. Wie postgres vorgeschlagen hat, können wir jetzt den Ausdruck 'USING' verwenden, um unsere Daten in Ganzzahlen umzuwandeln.

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

Es klappt.

7.) Wissen, wer mit der Datenbank verbunden ist
Dies ist mehr oder weniger ein Überwachungsbefehl. Verwenden Sie die folgende SQL, um festzustellen, welcher Benutzer mit welcher Datenbank verbunden ist, einschließlich IP und Port:

SELECT datname,usename,client_addr,client_port FROM pg_stat_activity ;

8.) PostgreSQL-Konfigurationsdateien neu laden, ohne den Server neu zu starten

PostgreSQL-Konfigurationsparameter befinden sich in speziellen Dateien wie postgresql.conf und pg_hba.conf. Oft müssen Sie diese Parameter ändern. Damit einige Parameter wirksam werden, müssen wir die Konfigurationsdatei häufig neu laden. Ein Neustart des Servers reicht natürlich aus. In einer Produktionsumgebung ist es jedoch nicht bevorzugt, die Datenbank, die von Tausenden verwendet wird, neu zu starten, nur um einige Parameter festzulegen. In solchen Situationen können wir die Konfigurationsdateien neu laden, ohne den Server mit der folgenden Funktion neu zu starten:

select pg_reload_conf();

Denken Sie daran, dass dies nicht für alle Parameter funktioniert. Einige Parameteränderungen erfordern einen vollständigen Neustart des Servers, um wirksam zu werden.

9.) Abrufen des Datenverzeichnispfads des aktuellen Datenbankclusters

Es ist möglich, dass in einem System mehrere Instanzen (Cluster) von PostgreSQL im Allgemeinen an verschiedenen Ports oder so eingerichtet werden. In solchen Fällen ist es eine hektische Aufgabe, herauszufinden, welches Verzeichnis (physisches Speicherverzeichnis) von welcher Instanz verwendet wird. In solchen Fällen können wir den folgenden Befehl in jeder Datenbank in dem Cluster von Interesse verwenden, um den Verzeichnispfad abzurufen:

SHOW data_directory;

Dieselbe Funktion kann zum Ändern des Datenverzeichnisses des Clusters verwendet werden, erfordert jedoch einen Neustart des Servers:

SET data_directory to new_directory_path;

10.) Finden Sie ein CHAR ist DATE oder nicht

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;

Verwendung: Folgendes gibt True zurück

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.) Ändern Sie den Eigentümer in PostgreSQL

REASSIGN OWNED BY sa  TO postgres;

12.) PGADMIN PLPGSQL DEBUGGER

Nun erklärt hier


+1 für 2 , 3 , 6 , 9

0

Es ist praktisch, eine alte Datenbank umzubenennen, anstatt dies mit MySQL zu tun. Verwenden Sie einfach den folgenden Befehl:

ALTER DATABASE name RENAME TO new_name
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.