web-dev-qa-db-fra.com

Fonctionnalités cachées de PostgreSQL

Je suis surpris que cela n'ait pas encore été publié. Des astuces intéressantes que vous connaissez dans Postgres? Les options de configuration obscures et les astuces de mise à l'échelle/perf sont particulièrement les bienvenues.

Je suis sûr que nous pouvons battre les 9 commentaires sur le correspondant fil MySQL :)

79
ramanujan

Depuis postgres est beaucoup plus sain d'esprit que MySQL, il n'y a pas beaucoup de "trucs" à signaler ;-)

Le manuel a quelques astuces de Nice performance .

Quelques autres éléments liés aux performances à garder à l'esprit:

  • Assurez-vous que le vide automatique est activé
  • Assurez-vous d'avoir parcouru votre postgres.conf (taille effective du cache, tampons partagés, mémoire de travail ... beaucoup d'options à régler).
  • Utilisez pgpool ou pgbouncer pour garder vos "vraies" connexions à la base de données au minimum
  • Découvrez comment EXPLIQUE et EXPLAIN ANALYZE fonctionne. Apprenez à lire la sortie.
  • CLUSTER trie les données sur le disque selon un index. Peut considérablement améliorer les performances des grandes tables (principalement) en lecture seule. Le clustering est une opération unique: lorsque la table est mise à jour par la suite, les modifications ne sont pas groupées.

Voici quelques éléments que j'ai trouvés utiles qui ne sont pas liés à la configuration ou aux performances en soi.

Pour voir ce qui se passe actuellement:

select * from pg_stat_activity;

Recherche de fonctions diverses:

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

Trouver la taille de la base de données:

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

Trouvez la taille de toutes les bases de données:

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

Trouver la taille des tables et des index:

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

Ou, pour répertorier toutes les tables et tous les index (probablement plus faciles à visualiser):

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, et vous pouvez imbriquer des transactions, annuler des transactions partielles ++

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)
75
tommym

L'astuce le plus simple pour permettre à postgresql de fonctionner beaucoup mieux (à part la définition et l'utilisation d'index appropriés bien sûr) consiste simplement à lui donner plus RAM pour travailler avec ( si vous ne l'avez pas déjà fait). Sur la plupart des installations par défaut, la valeur de shared_buffers est beaucoup trop faible (à mon avis). Vous pouvez définir

shared_buffers

dans postgresql.conf. Divisez ce nombre par 128 pour obtenir une approximation de la quantité de mémoire (en Mo) que les postgres peuvent réclamer. Si vous le montez suffisamment, cela fera voler postgresql. N'oubliez pas de redémarrer postgresql.

Sur les systèmes Linux, lorsque postgresql ne redémarrera pas, vous aurez probablement atteint la limite kernel.shmmax. Réglez-le plus haut avec

sysctl -w kernel.shmmax=xxxx

Pour que cela persiste entre les démarrages, ajoutez une entrée kernel.shmmax à /etc/sysctl.conf.

Un tas d'astuces Postgresql peuvent être trouvées ici :

23
ChristopheD

Postgres dispose d'une installation de gestion de datetime très puissante grâce à son support INTERVAL.

Par exemple:

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)

Vous pouvez convertir de nombreuses chaînes en un type INTERVAL.

17
Yann Ramin

[~ # ~] copier [~ # ~]

Je vais commencer. Chaque fois que je passe à Postgres à partir de SQLite, j'ai généralement de très gros ensembles de données. La clé est de charger vos tables avec COPY FROM plutôt que de faire des INSERTS. Voir documentation:

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

L'exemple suivant copie une table sur le client en utilisant la barre verticale (|) comme délimiteur de champ:

COPY country TO STDOUT WITH DELIMITER '|';

Pour copier des données d'un fichier dans la table des pays:

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

Voir aussi ici: insertions en vrac plus rapides dans sqlite3?

15
ramanujan
  • Mon préféré est de loin generate_series: enfin un moyen propre de générer des ensembles de lignes factices.
  • Possibilité d'utiliser une valeur corrélée dans une clause LIMIT d'une sous-requête:

    SELECT  (
            SELECT  exp_Word
            FROM    mytable
            OFFSET id
            LIMIT 1
            )
    FROM    othertable
    
  • Possibilité d'utiliser plusieurs paramètres dans des agrégats personnalisés (non couverts par la documentation): voir l'article de mon blog pour un exemple .
12
Quassnoi

L'une des choses que j'aime vraiment à propos de Postgres est certains des types de données pris en charge dans les colonnes. Par exemple, il existe des types de colonnes conçus pour stocker Adresses résea et Tableaux . Les fonctions correspondantes ( Adresses résea / Tableaux ) pour ces types de colonnes vous permettent d'effectuer de nombreuses opérations complexes dans les requêtes que vous auriez à faire en traitant les résultats via le code dans MySQL ou autres moteurs de base de données.

9
Chad Birch

Les tableaux sont vraiment cool une fois que vous les connaissez. Disons que vous souhaitez stocker des hyperliens entre les pages. Vous pourriez commencer par penser à créer un tableau un peu comme ceci:

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

Si vous aviez besoin d'indexer la colonne tail, et que vous aviez, disons, 200 000 000 de lignes-liens (comme wikipedia vous donnerait), vous vous retrouveriez avec un énorme tableau et un énorme index.

Cependant, avec PostgreSQL, vous pouvez utiliser ce format de table à la place:

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

Pour obtenir toutes les têtes pour un lien, vous pouvez envoyer une commande comme celle-ci (unnest () est standard depuis 8.4):

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

Cette requête est étonnamment rapide lorsqu'elle est comparée à la première option (unnest () est rapide et l'index est beaucoup plus petit). De plus, votre table et votre index occuperont beaucoup moins de mémoire RAM et d'espace HD, en particulier lorsque vos baies sont si longues qu'elles sont compressées en une table Toast. Les tableaux sont vraiment puissants.

Remarque: alors que unnest () générera des lignes à partir d'un tableau, array_agg () agrégera les lignes dans un tableau.

8
Nicholas Leonard

Les vues matérialisées sont assez faciles à configurer:

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;

Cela crée une nouvelle table, my_matview, avec les colonnes et les valeurs de my_view. Des déclencheurs ou un script cron peuvent ensuite être configurés pour maintenir les données à jour, ou si vous êtes paresseux:

TRUNCATE my_matview;
INSERT INTO my_matview SELECT * FROM my_view;
6
Cameron
  • Inheritance..infact Multiple Inheritance (comme dans le cas de l'héritage parent-enfant et non de l'héritage de relations 1 à 1 que de nombreux frameworks Web implémentent lorsqu'ils travaillent avec postgres).

  • PostGIS (extension spatiale), un merveilleux complément qui offre un ensemble complet de fonctions de géométrie et coordonne le stockage hors de la boîte. Largement utilisé dans de nombreuses bibliothèques géographiques open source (par exemple OpenLayers, MapServer, Mapnik, etc.) et nettement mieux que les extensions spatiales de MySQL.

  • Rédaction de procédures dans différentes langues, par ex. C, Python, Perl, etc. (rend votre vie plus facile à coder si vous êtes un développeur et non un administrateur db).

    De plus, toutes les procédures peuvent être stockées en externe (sous forme de modules) et peuvent être appelées ou importées lors de l'exécution par des arguments spécifiés. De cette façon, vous pouvez contrôler le code source et déboguer le code facilement.

  • Un catalogue énorme et complet sur tous les objets implémentés dans votre base de données (ex: tables, contraintes, index, etc.).

    Je trouve toujours extrêmement utile d'exécuter quelques requêtes et d'obtenir toutes les méta-informations, par exemple , noms de contraintes et champs sur lesquels ils ont été implémentés, noms d'index, etc.

    Pour moi, tout devient extrêmement pratique lorsque je dois charger de nouvelles données ou effectuer des mises à jour massives dans de grandes tables (je désactiverais automatiquement les déclencheurs et supprimer les index), puis les recréer facilement une fois le traitement terminé. Quelqu'un a fait un excellent travail d'écriture d'une poignée de ces requêtes.

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

  • Plusieurs schémas sous une seule base de données, vous pouvez l'utiliser si votre base de données contient un grand nombre de tables, vous pouvez considérer les schémas comme des catégories. Toutes les tables (quel que soit son schéma) ont accès à toutes les autres tables et fonctions présentes dans la base de données parent.

6
Nakh

Vous n'avez pas besoin d'apprendre à déchiffrer la sortie "expliquer analyser", il existe un outil: http://explain.depesz.com

5
AAS
select pg_size_pretty(200 * 1024)
4
Michael Buen

pgcrypto : plus de fonctions cryptographiques que les modules cryptographiques de nombreux langages de programmation fournissent, tous accessibles directement depuis la base de données. Cela rend les choses cryptographiques incroyablement faciles à obtenir.

3
kquinn

Une base de données peut être copiée avec:

createdb -T old_db new_db

La documentation dit:

il ne s'agit pas (encore) d'une installation polyvalente "COPY DATABASE"

mais cela fonctionne bien pour moi et est beaucoup plus rapide que

createdb new_db

pg_dump old_db | psql new_db

3
Kim Rutherford

Stockage mémoire pour données jetables/variables globales

Vous pouvez créer un espace de table qui réside dans la RAM et des tables (éventuellement non enregistrées, dans 9.1) dans cet espace de table pour stocker les données jetables/variables globales que vous souhaitez partager entre les sessions.

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

Verrous consultatifs

Ceux-ci sont documentés dans une zone obscure du manuel:

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

C'est parfois plus rapide que d'acquérir une multitude de verrous au niveau des lignes, et ils peuvent être utilisés pour contourner les cas où FOR UPDATE n'est pas implémenté (comme les requêtes CTE récursives).

2

1.) Lorsque vous devez ajouter un avis pour interroger, vous pouvez utiliser un commentaire imbriqué

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

2.) Supprimer les espaces de fin de tous les champs text et varchar dans une base de données.

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.) Nous pouvons utiliser une fonction de fenêtre pour supprimer très efficacement les lignes en double:

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

Quelques versions optimisées de PostgreSQL (avec 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.) Lorsque nous devons identifier l'état du serveur, nous pouvons utiliser une fonction:

SELECT pg_is_in_recovery();

5.) Récupère la commande DDL des fonctions.

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

6.) Modification sécurisée du type de données de colonne dans 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

Vous pouvez voir dans le tableau ci-dessus que j'ai utilisé le type de données - "caractère variant" pour "id"
colonne. Mais c'était une erreur, car je donne toujours des entiers comme id. Donc, utiliser varchar ici est une mauvaise pratique. Essayons donc de changer le type de colonne en entier.

ALTER TABLE test ALTER COLUMN id TYPE integer;

Mais ça revient:

ERREUR: la colonne "id" ne peut pas être convertie automatiquement pour saisir un état SQL entier: 42804 Conseil: spécifiez une expression USING pour effectuer la conversion

Cela signifie que nous ne pouvons pas simplement changer le type de données, car les données sont déjà présentes dans la colonne. Étant donné que les données sont de type "variant selon les caractères", les postgres ne peuvent pas s’attendre à ce qu’elles soient entières, bien que nous ayons uniquement entré des entiers. Alors maintenant, comme l’a suggéré postgres, nous pouvons utiliser l’expression ‘USING’ pour convertir nos données en nombres entiers.

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

Ça marche.

7.) Sachez qui est connecté à la base de données
Il s'agit plus ou moins d'une commande de surveillance. Pour savoir quel utilisateur connecté à quelle base de données, y compris son IP et son port, utilisez le SQL suivant:

SELECT datname,usename,client_addr,client_port FROM pg_stat_activity ;

8.) Rechargement des fichiers de configuration PostgreSQL sans redémarrer le serveur

Les paramètres de configuration de PostgreSQL se trouvent dans des fichiers spéciaux tels que postgresql.conf et pg_hba.conf. Souvent, vous devrez peut-être modifier ces paramètres. Mais pour que certains paramètres prennent effet, nous devons souvent recharger le fichier de configuration. Bien sûr, le redémarrage du serveur le fera. Mais dans un environnement de production, il n'est pas préférable de redémarrer la base de données, qui est utilisée par des milliers, juste pour définir certains paramètres. Dans de telles situations, nous pouvons recharger les fichiers de configuration sans redémarrer le serveur en utilisant la fonction suivante:

select pg_reload_conf();

N'oubliez pas que cela ne fonctionnera pas pour tous les paramètres, certaines modifications de paramètres nécessitent un redémarrage complet du serveur pour être prises en compte.

9.) Obtention du chemin du répertoire de données du cluster de base de données actuel

Il est possible que dans un système, plusieurs instances (cluster) de PostgreSQL soient configurées, généralement, dans différents ports ou plus. Dans de tels cas, trouver quel répertoire (répertoire de stockage physique) est utilisé par quelle instance est une tâche mouvementée. Dans de tels cas, nous pouvons utiliser la commande suivante dans n'importe quelle base de données du cluster qui nous intéresse pour obtenir le chemin du répertoire:

SHOW data_directory;

La même fonction peut être utilisée pour changer le répertoire de données du cluster, mais elle nécessite un redémarrage du serveur:

SET data_directory to new_directory_path;

10.) Trouver un CHAR est DATE ou pas

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;

Utilisation: ce qui suit renverra Vrai

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.) Changer le propriétaire dans PostgreSQL

REASSIGN OWNED BY sa  TO postgres;

12.) DÉBOGUEUR PGADMIN PLPGSQL

Bien expliqué ici

2
Vivek S.

Ceci est ma liste préférée de fonctionnalités moins connues.

DDL transactionnel

Presque chaque instruction SQL est transactionnelle dans Postgres. Si vous désactivez la validation automatique, les opérations suivantes sont possibles:

drop table customer_orders;
rollback;
select *
from customer_orders;

Types de plages et contrainte d'exclusion

À ma connaissance, Postgres est le seul SGBDR qui vous permet de créer une contrainte qui vérifie si deux plages se chevauchent. Un exemple est un tableau qui contient les prix des produits avec une date "valable du" et "valable jusqu'au":

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

Fonctionnalités NoSQL

L'extension hstore offre un magasin de clés/valeurs flexible et très rapide qui peut être utilisé lorsque des parties de la base de données doivent être "sans schéma". JSON est une autre option pour stocker des données de manière sans schéma et

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

Le plan d'exécution pour ce qui précède sur une table avec 700.000 lignes:

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

Pour éviter d'insérer des lignes avec des plages de validité qui se chevauchent, une contrainte unique simple (et efficace) peut être définie:

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

Infini

Au lieu d'exiger une date "réelle" dans le futur, Postgres peut comparer les dates à l'infini. Par exemple. lorsque vous n'utilisez pas de plage de dates, vous pouvez effectuer les opérations suivantes

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

Expressions de table communes inscriptibles

Vous pouvez supprimer, insérer et sélectionner dans une seule instruction:

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;

Ce qui précède supprimera toutes les commandes de plus de 10 ans, déplacez-les dans le archived_orders table, puis affichez les lignes qui ont été déplacées.

2

Il est pratique de renommer une ancienne base de données plutôt que mysql ne peut le faire. Il suffit d'utiliser la commande suivante:

ALTER DATABASE name RENAME TO new_name
0
Moon_of_father