Le blog de Jean David TECHER, un Réunionnais à Saint-Priest/Lyon

Aller au contenu | Aller au menu | Aller à la recherche


< 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 >

dimanche 13 avril 2008

Samedi soir à l'Australian Café de Montpellier

Après la semaine de m...passé sous la pluie sur Sophia, il a commencé à faire beau vendredi aprem' sur Juan les Pins. Samedi matin, suite à la rédaction de mon précédent billet, j'ai finalement décidé décoller pour rentrer sur Montpellier sur les coups de 15h00~15h15. Sur la route, vents et chaleurs mais il faisait un super temps.

Arrivé sur place un petit wiski-coca au bar de la plage avec un couple d'amis, ensuite retour à l'appart. Mon coloc' avait réorganisé la disposition des meubles. Il s'y est bien pris. Sur les coups de 22h30, on a tracé à l'Australian Café de Montpellier...Y'avait du monde! Au bout de 4 p'ti punchs j'étais assez entamé mais bien réveillé...J'ai pu tapé la discut' avec un jeune mexicain de passage et d'autres personnes! Bonne soirée, en tout cas y'avait la zikmu que je kiffe!

Ce matin dimanche, c'est acheter de la bouffe pour le chat au Super U de la pompignane, lessive et courrier! Retour programmé vers 17h00~18h00... Levée vers 10h00, après avoir fait les courses, j'en ai profité pour m'arrêter à la terrasse du bar de Castelnau pour prendre un petit café et me dorer au soleil...Il faut beau ces temps-ci.

samedi 12 avril 2008

Intérêt du VACUUM FULL lors d'UPDATE fréquents - Exemple concret

Souvent on me pose la question à quoi sert l'option FULL du VACUUM? Je ne vais pas décortiquer ici tout ce que fais le FULL mais juste montrer un exemple pratique d'un UPDATE sur toute une table.

Pour se faire, je vais partir d'un exemple pris du bouquin de référence (que j'avais déjà introduis dans un précédent billet)...

CHARGEMENT DES DONNEES

On va commencer par télécharger les sources en faisant
wget http://www.conjectrix.com/pgbook/source2/bookdata.tar.gz
On les décompresse ensuite
tar xvzf bookdata.tar.gz
Pour rester en conformité avec les données, on va se créer une base performance et un utilisateur korry
postgres@bremko:~$ createdb performance
CREATE DATABASE
postgres@bremko:~$ createuser -s korry
CREATE USER
On se rend dans le sous-répertoire chapter04 qui contient un fichier sql recalls.sql contenant la structure de la table recalls qui va nous servir. On en profite par la même occasion de charger le fichier. Au passage, on notera que le fichier fait 236Mo
postgres@bremko:~/chapter04$ du -hs recalls.sql
236M    recalls.sql
postgres@bremko:~/chapter04$ psql -d performance -f recalls.sql
You are now connected to database "performance" as user "korry".
CREATE TABLE
CREATE INDEX
CREATE INDEX
postgres@bremko:~/chapter04$
Au passage, on se connecte à la base performance pour la suite, on obtient le descriptif suivant
performance=# \d recalls
              Table "public.recalls"
   Column    |          Type           | Modifiers
-------------+-------------------------+-----------
 record_id   | numeric(9,0)            |
 campno      | character(9)            |
 maketxt     | character(25)           |
 modeltxt    | character(25)           |
 yeartxt     | character(4)            |
 mfgcampno   | character(10)           |
 compdesc    | character(75)           |
 mgftxt      | character(30)           |
 bgman       | character(8)            |
 endman      | character(8)            |
 vet         | character(1)            |
 potaff      | numeric(9,0)            |
 ndate       | character(8)            |
 odate       | character(8)            |
 influenced  | character(4)            |
 mfgname     | character(30)           |
 rcdate      | character(8)            |
 datea       | character(8)            |
 rpno        | character(3)            |
 fmvss       | character(3)            |
 desc_defect | character varying(2000) |
 con_defect  | character varying(2000) |
 cor_action  | character varying(2000) |
Indexes:
    "recall_potaff" btree (potaff)
    "recall_record_id" btree (record_id)

CAS D'UN SEUL UPDATE ET ETUDE

Examinons maintenant la taille de la table
performance=# SELECT relname,reltuples,relpages,pg_relation_size(relname)  FROM pg_class WHERE relname='recalls';
 relname | reltuples | relpages | pg_relation_size
---------+-----------+----------+------------------
 recalls |     39241 |     4400 |         36044800
(1 row)
Une page pour PostgreSQL est de 8192 bytes. Ce que nous confirme la commande suivante
performance=# show block_size ;
 block_size
------------
 8192
(1 row)
La table a 39241 tuples et 4400 pages. Ce qui donne donc 4400x8192=36044800.Ce que confirme la fonctionnalité pg_relation_size(). Ici bien sûr, je n'ai pas tenu compte de la place qu'occupait les index. On peut aussi le vérifier sur le disque mais pour celà, il faut remonter l'OID associé à la table recalls. Ce qui s'obtient en faisant
performance=# SELECT relname,relfilenode  FROM pg_class WHERE relname='recalls';
 relname | relfilenode
---------+-------------
 recalls |      188438
(1 row)
Par la suite, j'ai
postgres@bremko:~/chapter04$ ls -al /mnt/pgdata/base/188437/188438
-rw------- 1 postgres postgres 36044800 2008-04-12 10:50 /mnt/pgdata/base/188437/188438
Maintenant si l'option stats_row_level est active depuis le démarrage du serveur (à ne pas faire dans un environnement de prod...), on a aussi
performance=# \x
Expanded display is on.
performance=# Select * from pg_stat_user_tables ;
-[ RECORD 1 ]----+--------
relid            | 188438
schemaname       | public
relname          | recalls
seq_scan         | 2
seq_tup_read     | 78482
idx_scan         | 0
idx_tup_fetch    | 0
n_tup_ins        | 39241
n_tup_upd        | 0
n_tup_del        | 0
last_vacuum      |
last_autovacuum  |
last_analyze     |
last_autoanalyze |
Le champs n_tup_ins indique le nombre de tuples inséré depuis le démarrage du serveur soit 39241 tuples. On va maintenant modifier la table en faisant un simple UPDATE sur le champs potaff
performance=# START TRANSACTION ;
START TRANSACTION
performance=# UPDATE recalls SET potaff = potaff +1;
UPDATE 39241
performance=# END TRANSACTION ;
COMMIT
Revenons à une de nos précédentes requêtes
performance=# SELECT relname,reltuples,relpages,pg_relation_size(relname)  FROM pg_class WHERE relname='recalls';
 relname | reltuples | relpages | pg_relation_size
---------+-----------+----------+------------------
 recalls |     39241 |     4400 |         71868416
(1 row)
On s'apercoit qu'un simple UPDATE sur la table entière à suffit pour doubler la taille de la table! Ce que confirme aussi la commande suivante
postgres@bremko:~/chapter04$ ls -al /mnt/pgdata/base/188437/188438
-rw------- 1 postgres postgres 71868416 2008-04-12 10:50 /mnt/pgdata/base/188437/188438
En revanche le nombre de pages n'est pas à jour. Un simple ANALYZE sur la table suffira pour le mettre à jour
performance=# ANALYZE recalls ;
ANALYZE
performance=# SELECT relname,reltuples,relpages,pg_relation_size(relname)  FROM pg_class WHERE relname='recalls';
 relname | reltuples | relpages | pg_relation_size
---------+-----------+----------+------------------
 recalls |     39493 |     8773 |         71868416
(1 row)
Le nombre de pages a aussi quasiment doublé! C'est maintenant qu'intervient le FULL! Bien sûr, on supposera aussi qu'aucune autre activité n'a lieu pendant ce temps sur la base. PostgreSQL en fait garde une trace des lignes qui sont sujet à modification et le rend non accessibles. Tout celà repose en grande partie sur le principe du MVCC et isolation de transaction qu'à choisi PostgreSQL qui par défaut est en REAL COMMITTED. D'ailleurs PostgreSQL ne supporte que deux niveaux d'isolation
performance=# show transaction_isolation ;
 transaction_isolation
-----------------------
 read committed
(1 row)
So let's go now for notre FULL de la mort qui tue le diable
performance=# VACUUM FULL recalls ;
VACUUM
performance=# SELECT relname,reltuples,relpages,pg_relation_size(relname)  FROM pg_class WHERE relname='recalls';
 relname | reltuples | relpages | pg_relation_size
---------+-----------+----------+------------------
 recalls |     39241 |     4248 |         34799616
(1 row)
Or mon remarquera que par rapport à la taille initiale on a 34799616 < 36044800 ce qui est tout à fait normal car le FULL essait de "compresser/réagencer au mieux" l'espace disque sur les blocks de la table (déplace etc...), un peu comme le principe de la défragmentation sous Windaube. Dès fois le gain n'est pas important mais parfois il peut le devenir en fonction de ce qui a dans la base.

Donc des updates fréquents sur une table font grossir la table . Voilà une des raisons pour laquelle un FULL s'avère efficace pour récupérer l'espace disque lorsque ce dernier arrive à moins de 10 à 20 % de sa capacité.

Actuellement j'ai donc en utilisant pg_size_pretty () et pg_total_relation_size()
performance=# SELECT relname,reltuples,relpages,pg_size_pretty(pg_total_relation_size(relname))  FROM pg_class WHERE relname='recalls';
 relname | reltuples | relpages | pg_size_pretty
---------+-----------+----------+----------------
 recalls |     39241 |     4248 | 39 MB
(1 row)
Ma table occupe donc en y incluant (les index etc...) 39 MB sur le disque. Imaginons maintenant 10 UPDATES successifs comme suit

...ET POUR 10 UPDATES IDENTIQUES???

START TRANSACTION;
UPDATE recalls SET potaff=potaff+1;
UPDATE recalls SET potaff=potaff+1;
UPDATE recalls SET potaff=potaff+1;
UPDATE recalls SET potaff=potaff+1;
UPDATE recalls SET potaff=potaff+1;
UPDATE recalls SET potaff=potaff+1;
UPDATE recalls SET potaff=potaff+1;
UPDATE recalls SET potaff=potaff+1;
UPDATE recalls SET potaff=potaff+1;
UPDATE recalls SET potaff=potaff+1;
END TRANSACTION;
Dans un environnement de production, si le serveur tient la charge, on peut imaginer que celà correspond à 10 connexions au serveur de manière séquentiels (ce qui est encore l'un des meilleurs cas par rapport à ce accès concurrents faisant monté en charge le CPU) répétant la même requête...Essayez d'imaginer la taille de votre table par la suite! Sans faire de FULL au bout de 6 UPDATES, on est passé de 39 M à plus de 260M. :(. Et encore on en se plaindra pas car il s'agit ici d'un table de taille très raisonnable.

Faisons donc les tests! On en profitera pour créer une table qui stockera au fur et à mesure l'avancée suite à un requête de mise jour

START TRANSACTION;
CREATE TABLE stats_size(id serial,dtsize numeric);
INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
UPDATE recalls SET potaff=potaff+1;
INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
UPDATE recalls SET potaff=potaff+1;
INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
UPDATE recalls SET potaff=potaff+1;
INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
UPDATE recalls SET potaff=potaff+1;
INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
UPDATE recalls SET potaff=potaff+1;
INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
UPDATE recalls SET potaff=potaff+1;
INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
UPDATE recalls SET potaff=potaff+1;
INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
UPDATE recalls SET potaff=potaff+1;
INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
UPDATE recalls SET potaff=potaff+1;
INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
UPDATE recalls SET potaff=potaff+1;
INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
END TRANSACTION;
Ce qui donne donc
performance=# select * from stats_size;
 id | dtsize
----+--------
  1 |     36
  2 |     74
  3 |    111
  4 |    147
  5 |    185
  6 |    222
  7 |    258
  8 |    295
  9 |    333
 10 |    369
 11 |    407
(11 rows)
En effectuant 10 fois de suite la même requête on est passé de 36 à 407 soit un facteur de 10 pour la totalité de la table qui augmente de manière quasi linéraire comme le montre le graphique suivant

Taille de la table suite à 10 UPDATE successifs
Faisons maintenant un VACUUM FULL ANALYZE et insérons la nouvelle valeur dans la table stats_size en faisant;
performance=# VACUUM FULL ANALYZE recalls ;
VACUUM
performance=# INSERT INTO stats_size(dtsize) VALUES ((SELECT trim(both ' MB' from pg_size_pretty(pg_total_relation_size('recalls')))::numeric));
INSERT 0 1
Et on obtient

Taille de la table suite à 10 UPDATE successifs + VACUUM FULL ANALYZE
Et hop! On ne s'affolera pas ici mais ce simple exemple montre au combien il est nécessaire de penser à faire un FULL sur sa base lorsque des mises à jours sont fréquents sur une ou plusieurs tables de la base en production. On voit donc que lorsqu'on a des mises à jour fréquentes sur sa/ses tables(s), il est nécessaire de planifier un FULL. En revanche, il ne faut pas non plus oublier qu'un FULL est bien plus long qu'un simple VACUUM, que dans l'usage on ne le fait pas souvent non plus....Pour de plus amples informations, on pourra toujours se reporter à la documentation de PostgreSQLFR sur le VACUUM

vendredi 11 avril 2008

Fond d'écran d'un DBA PostgreSQL/PostGIS et révisions sur les BLOBS

Cette aprem', comme je dois me remettre à flot pour PostgreSQL, je devais réviser les BLOBS (Binary Large OBject)...Pour se faire, 'j'ai récupérer une image sur le Web d'Ewa Sonnet dont j'ai profité pour mettre en fond d'écran (ce qui m'a bien sûr value une remarque des plus .... d'une de mes collègues Pourquoi vous les mecs vous ne mettez que des photos de femme à poil...! )

No comment! :)


My WallPaper used for test on Binary Large OBject :)

Let's go!...On commence par télécharger l'image

 wget http://img.wallpapers-zone.com/wallpapers/stars_et_top_models_femmes/ewa_sonnet/ewa_sonnet_001.jpg

Ensuite on crée la table

postgres@bremko:~$ psql testgis
Welcome to psql 8.2.3, the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help with psql commands
       \g or terminate with semicolon to execute query
       \q to quit

testgis=# create table testblob(numero serial,nom text,source oid)with oids;
NOTICE:  CREATE TABLE will create implicit sequence "testblob_numero_seq" for serial column "testblob.numero"
CREATE TABLE
testgis=#
Ensuite on insère grâce à lo_import()
testgis=# START TRANSACTION;
START TRANSACTION
testgis=# INSERT INTO testblob(nom,source) VALUES ('Ewa Sonnet',lo_import('/home/postgres/ewa_sonnet_001.jpg'));
INSERT 188428 1
testgis=# END TRANSACTION;
COMMIT
testgis=#
Pour l'exportation, on utilise tout simplement lo_export()
testgis=# START TRANSACTION ;
START TRANSACTION
testgis=# SELECT lo_export(source,'/home/postgres/ewa_sonnet.jpg') from testblob;
 lo_export
-----------
         1
(1 row)

testgis=# END TRANSACTION;
COMMIT
testgis=#
On peut vérifier que le fichier à bien été créé sans sortir de psql
testgis=# \! ls -al '/home/postgres/ewa_sonnet.jpg'
-rw-r--r-- 1 postgres postgres 104073 2008-04-11 22:53 /home/postgres/ewa_sonnet.jpg

Ca va je n'ai pas trop rouillé au niveau des fonctionnolités.

Remarque: ici les début et fin de transaction ne sont pas obligatoire mais celà vient de mon habitude avec les BLOB.

Mes deux premières semaines chez F.T. et départ de mon pote

Aya j'ai commencé depuis lundi 31 mars chez F.T et intégré ma nouvelle équipe de travail...Les horaires sont les heures de bureaux classique. Le travail est plaisant, bonne ambiance dans le bureau actuel!

Cette semaine j'ai préféré ramené ma gamelle de bouffe le midi au taf, histoire de remmettre à zéro mes finances après mes deux mois de fête...

Cette semaine j'ai appris qu'un de mes coloc's allait s'installé avec dulciné sur Nîmes..La vie continue...Lui et moi, on est sorti une dernière fois ensemble la semaine dernière (histoire de marquer le coup)...Je sentais bien qu'il allait partir :)...C'est comme ça les destinées se croisent et se décroisent! Très très bonne semaine!

En tout cas Ludo mon pote ce soir-là, tu m'as sorti une phrase à mon retour quand tu m'attendais aors que je courais couru après les 3 suédoises (avec qui j'ai dansé et ??? plus que dansé...)...Tu m'as dis

"Moi je regarde sur la droite et mercenaire qui arrive sur ma gauche! LOL LOL"

Allez bandit! Bon courage à toi dans ta nouvelle vie...Mercenaire sera toujours là pour toi :)

P.S: je te ramène une partie de tes affaires comme prévu ce week-end en remontant sur Montpellier