web-dev-qa-db-fra.com

Nombre élevé de nuplets vivants/morts dans postgresql/le vide ne fonctionne pas

Il y a une table, qui a 200 lignes. Mais le nombre de nuplets vivants montrant qu’il ya plus que cela (environ 60K).

select count(*) from subscriber_offset_manager;
 count 
-------
   200
(1 row)


 SELECT schemaname,relname,n_live_tup,n_dead_tup FROM pg_stat_user_tables  where relname='subscriber_offset_manager' ORDER BY n_dead_tup
;
 schemaname |          relname          | n_live_tup | n_dead_tup 
------------+---------------------------+------------+------------
 public     | subscriber_offset_manager |      61453 |          5
(1 row)

Mais comme vu de pg_stat_activity et de pg_locks, nous ne sommes pas en mesure de suivre une connexion ouverte.

SELECT query, state,locktype,mode
FROM pg_locks
JOIN pg_stat_activity
  USING (pid)
WHERE relation::regclass = 'subscriber_offset_manager'::regclass
  ;
 query | state | locktype | mode 
-------+-------+----------+------
(0 rows)

J'ai aussi essayé le vide complet sur cette table. Voici les résultats: 

  • Toutes les fois où aucune ligne n'est supprimée 
  • parfois, tous les tuples vivants deviennent des tuples morts. 

Voici la sortie.

vacuum FULL VERBOSE ANALYZE subscriber_offset_manager;
INFO:  vacuuming "public.subscriber_offset_manager"
INFO:  "subscriber_offset_manager": found 0 removable, 67920 nonremovable row versions in 714 pages
DETAIL:  67720 dead row versions cannot be removed yet.
CPU 0.01s/0.06u sec elapsed 0.13 sec.
INFO:  analyzing "public.subscriber_offset_manager"
INFO:  "subscriber_offset_manager": scanned 710 of 710 pages, containing 200 live rows and 67720 dead rows; 200 rows in sample, 200 estimated total rows
VACUUM

 SELECT schemaname,relname,n_live_tup,n_dead_tup FROM pg_stat_user_tables  where relname='subscriber_offset_manager' ORDER BY n_dead_tup
;
 schemaname |          relname          | n_live_tup | n_dead_tup 
------------+---------------------------+------------+------------
 public     | subscriber_offset_manager |        200 |      67749

et après 10 secondes 

SELECT schemaname,relname,n_live_tup,n_dead_tup FROM pg_stat_user_tables  where relname='subscriber_offset_manager' ORDER BY n_dead_tup
;
 schemaname |          relname          | n_live_tup | n_dead_tup 
------------+---------------------------+------------+------------
 public     | subscriber_offset_manager |      68325 |        132

Comment notre application demande à cette table.  

  • Notre application sélectionne généralement certaines lignes et, sur la base de certains calculs, met à jour la ligne. 

    sélection de requête - sélection basée sur un identifiant 

    select * from subscriber_offset_manager où shard_id = 1;

    requête de mise à jour - met à jour une autre colonne pour cet identifiant de fragment sélectionné

  • environ 20 threads le font en parallèle et un thread ne fonctionne que sur une seule ligne.

  • app est écrit en Java et nous utilisons hibernate pour effectuer des opérations de base de données. 
  • La version de Postgresql est 9.3.24

Encore une observation intéressante: - Lorsque j'arrête mon application Java et que je fais le vide complet, tout fonctionne correctement (le nombre de lignes et le nombre de lignes deviennent égaux). Il y a donc quelque chose qui ne va pas si nous sélectionnons et mettons à jour continuellement à partir de l'application Java. - 

Problème/Problème  

Ces tuples vivants vont parfois à des tuples morts et reviennent vivre après quelques fois. 

En raison du comportement ci-dessus, sélectionnez dans le tableau le temps et l’augmentation de la charge sur le serveur, car de nombreux live/deadtuples sont présents.

12
Sahil Aggarwal

Je connais trois choses qui empêchent VACUUM de faire son travail:

  • Longues transactions.

  • Transactions préparées qui n'ont pas été validées.

  • Slots de réplication obsolètes.

Voir mon article de blog pour plus de détails.

2
Laurenz Albe

J'ai eu le problème. 

Pour comprendre le problème, considérez le flux suivant: 

Fil 1 -  

  • Ouvre une session d'hibernation
  • Faites des requêtes sur Table-A  
  • Sélectionnez parmi subscriber_offset_manager
  • Mettre à jour subscriber_offset_manager
  • Ferme la session. 

De nombreux threads de type Thread-1 s'exécutant en parallèle. 

Fil 2 -  

  • Ce type de threads s'exécute en parallèle. 
  • Ouvre une session d'hibernation
  • Faites des requêtes sur Table-A  
  • Ne ferme pas la session. (Fuite de session.) 

Solution temporaire - Si je ferme toutes les connexions établies par Thread-2 en utilisant pg_cancel_backend, alors l'aspiration commence à fonctionner. 

Nous avons également recréé le problème plusieurs fois et avons essayé cette solution et cela a fonctionné. 

Maintenant, il y a des doutes suivants auxquels on ne répond toujours pas. 

  1. Pourquoi postgres n’affiche aucune donnée liée à la table " subscriber_offset_manager ". 
  2. Ce problème ne se reproduit pas lorsque, au lieu d’exécuter Thread-2 , si nous exécutons select sur Table-A , avec psql. 
  3. pourquoi postgres fonctionne comme ça avec jdbc. 

Quelques observations époustouflantes:  

  1. événement si nous exécutons des requêtes sur " subscriber_offset_manager " dans une session différente, puis émettons également problème; 
  2. nous avons trouvé de nombreux cas ici où Thread 2 travaille sur une troisième table " Table-C " et que le problème arrive 
  3. toutes ces transactions de type od dans pg_stat_activity sont " idle_in_transaction ." 

@Erwin Brandstetter et @Laurenz Albe, si vous savez qu'il y a un bogue lié à postgres/jdbc. 

4
Sahil Aggarwal

Après tout, il peut y avoir des verrous, votre requête peut être trompeuse:

SELECT query, state,locktype,mode
FROM pg_locks
JOIN pg_stat_activity USING (pid)
WHERE relation = 'subscriber_offset_manager'::regclass

pg_locks.pid peut être NULL, la jointure éliminerait alors les lignes. Le manuel pour Postgres 9.3:

Identificateur de processus du processus serveur détenant ou en attente de ce verrou, ou null si le verrou est conservé par une transaction préparée

Gras accent mien. (Toujours le même à la page 10.)

Avez-vous quelque chose pour la requête simple?

SELECT * FROM pg_locks
WHERE relation = 'subscriber_offset_manager'::regclass;

Cela pourrait expliquer pourquoi VACUUM se plaint:

DETAIL:  67720 dead row versions cannot be removed yet.

Ceci, à son tour, indiquerait des problèmes dans la logique/les requêtes de votre application, bloquant plus de lignes que nécessaire.

Ma première idée serait longues transactions, où même une simple SELECT (acquisition d'un verrou ACCESS SHARE peu élevé) peut empêcher VACUUM de faire son travail. 20 threads en parallèle pourraient chaîner et verrouiller VACUUM indéfiniment. Gardez vos transactions (et leurs serrures) aussi brèves que possible. Et assurez-vous que vos requêtes sont optimisées et ne verrouillez pas plus de lignes que nécessaire.

Une dernière chose à noter: l'isolation de transaction les niveaux SERIALIZABLE ou REPEATABLE READ rendent beaucoup plus difficile le nettoyage de VACUUM. Le mode READ COMMITTED par défaut est moins restrictif, mais VACUUM peut toujours être bloqué comme indiqué.

En relation:

2
Erwin Brandstetter