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 :)
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:
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)
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 :
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.
[~ # ~] 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?
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
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.
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.
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;
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.
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.
Vous n'avez pas besoin d'apprendre à déchiffrer la sortie "expliquer analyser", il existe un outil: http://explain.depesz.com
select pg_size_pretty(200 * 1024)
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.
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
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).
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
Ceci est ma liste préférée de fonctionnalités moins connues.
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;
À 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
);
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 &&)
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');
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.
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