HOT-uppdateringar och heap-only-tuple-kedjor
Utforma scheman och index så att uppdateringar förblir heap-only och undviker skrivförstärkning i index.
HOT-uppdateringar och heap-only-tuple-kedjor är en gratis lektion i Prestandaoptimering och frågeoptimering i PostgreSQL på CoddyKit. Detta är lektion 2 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Prestandaoptimering och frågeoptimering i PostgreSQL, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Prestandaoptimering och frågeoptimering i PostgreSQL innehåller totalt 4 lektioner.
Kostnaden för en uppdatering i PostgreSQL
Eftersom PostgreSQL använder MVCC skriver en UPDATE inte över en rad på plats. I stället skrivs en helt ny tuppel (den nya radversionen), och den gamla markeras som död. Den gamla versionen finns kvar tills VACUUM återtar den.
Naivt sett behöver varje ny tuppel en ny pekare i varje index på tabellen, även i index vars kolumner inte ändrades. Med 8 index innebär en logisk uppdatering 8 indexinsättningar plus index-bloat. Detta kallas skrivförstärkning i index.
- Mer WAL skrivs (varje indexändring loggas)
- Mer index-bloat (döda pekare samlas på hög)
- Mer CPU och I/O per uppdatering
Den här lektionen handlar om en mekanism som gör att PostgreSQL kan hoppa över det indexarbetet: HOT-uppdateringar.
Vad är en HOT-uppdatering?
HOT står för Heap-Only Tuple. En HOT-uppdatering skapar den nya tuppelversionen på samma heap-sida som den gamla och uppdaterar inga index alls.
Två villkor måste båda vara uppfyllda för att en uppdatering ska räknas som HOT:
- Ingen indexerad kolumn ändrades. Om ni ändrar någon kolumn som används av ett index är HOT omöjligt.
- Det finns plats på samma sida för den nya tuppelversionen.
När båda villkoren är uppfyllda omdirigeras den gamla tuppelns radpekare så att den pekar på den nya tuppeln, vilket bildar en HOT-kedja. Indexen fortsätter att peka på den ursprungliga radpekaren och behöver aldrig ändras.
Så fungerar HOT-kedjan
På en sida har varje tuppel en radpekare (item ID). Vid en HOT-uppdatering gäller följande:
- Den nya tuppeln får en ny radpekare och flaggan
HEAP_ONLY_TUPLE. - Den gamla tuppeln får flaggan
HEAP_HOT_UPDATED, och desst_ctidpekar framåt mot den nya tuppeln. - Indexposter refererar fortfarande till den ursprungliga radpekaren, så en sökning följer kedjan framåt för att hitta den levande versionen.
När VACUUM senare körs kan det rensa kedjan: döda mellanversioner tas bort och rotens radpekare omvandlas till en redirect-pekare direkt till den tuppel som överlevt. Detta kallas HOT-rensning och kan till och med ske opportunistiskt under en normal sidläsning (heap_page_prune).
Inspektera HOT-aktivitet med pg_stat
Ni kan mäta hur många av era uppdateringar som går HOT-vägen. Vyn pg_stat_user_tables visar både det totala antalet uppdaterade tupler och den delmängd som var HOT.
En frisk, skrivintensiv tabell bör ha n_tup_hot_upd nära n_tup_upd. En låg kvot tyder på att uppdateringarna berör indexerade kolumner eller att sidorna är fulla.
SELECT relname,
n_tup_upd,
n_tup_hot_upd,
round(100.0 * n_tup_hot_upd / NULLIF(n_tup_upd, 0), 1) AS hot_pct
FROM pg_stat_user_tables
ORDER BY n_tup_upd DESC
LIMIT 20;Regel 1: indexera inte flyktiga kolumner
Det effektivaste sättet att möjliggöra HOT är att undvika att indexera kolumner som ändras ofta. Varje index över en kolumn i den aktiva skrivvägen tvingar fram icke-HOT-uppdateringar när kolumnen skrivs.
Tänk på en sessionstabell där last_seen_at uppdateras vid varje begäran. Om ni indexerar den blir varje heartbeat en icke-HOT-uppdatering med fullständig indexaktivitet.
- Fråga er: används indexet av en verklig fråga, eller är det spekulativt?
- Kolumner som ändras ofta och har låg selektivitet har sällan någon större nytta av ett btree-index ändå.
- Om ett sådant index tas bort kan en arbetsbelastning omedelbart gå från 0 % HOT till nästan 100 %.
-- Anti-pattern: indexing a column updated on every request
CREATE INDEX idx_sessions_last_seen ON sessions (last_seen_at);
-- Each heartbeat now forces a non-HOT update + index insert:
UPDATE sessions SET last_seen_at = now() WHERE id = 42;Regel 2: lämna ledigt utrymme med fillfactor
HOT behöver plats på samma sida för den nya tuppeln. Om sidan är fullpackad hamnar den nya versionen på en annan sida och uppdateringen kan inte bli HOT.
fillfactor anger för PostgreSQL hur stor andel av varje sida som ska lämnas tom vid inläsning, så att utrymme reserveras för uppdateringar på samma sida. Standardvärdet för tabeller är 100 (tätt packat), vilket är utmärkt för data som bara läggs till men ogynnsamt för HOT i tabeller som uppdateras ofta.
För tabeller som uppdateras mycket skapar en fillfactor på 70–90 det utrymme som HOT behöver.
ALTER TABLE sessions SET (fillfactor = 85);
-- Rewrite existing pages so the new fillfactor takes effect:
VACUUM FULL sessions; -- or CLUSTER / pg_repack for online rewriteSätt ihop båda reglerna
Det vinnande receptet för en tabell som uppdateras ofta är att kombinera båda reglerna: håll indexerade kolumner oförändrade och reservera utrymme på sidan.
Här uppdateras en räknartabell hela tiden. Vi indexerar bara den stabila uppslagsnyckeln, aldrig räknaren, och anger en generös fillfactor så att de upprepade uppdateringarna stannar på samma sida.
- Indexet gäller
key, som aldrig ändras → villkor 1 är uppfyllt. fillfactor = 80lämnar plats → villkor 2 är uppfyllt.- Resultat: räknarökningarna blir HOT-uppdateringar utan några indexskrivningar.
CREATE TABLE counters (
key text PRIMARY KEY,
hits bigint NOT NULL DEFAULT 0
) WITH (fillfactor = 80);
-- The hot path: bumps only a non-indexed column
UPDATE counters SET hits = hits + 1 WHERE key = 'page:/home';Se upp: uttrycks- och partiella index räknas också
HOT-behörighet avgörs av om värdet i någon indexerad kolumn ändrades, inte bara av vanliga btree-kolumner. Detta orsakar ofta problem med:
- Uttrycksindex: ett index på
lower(email)innebär att en skrivning tillemailblockerar HOT även om värdet med gemener logiskt sett är identiskt. - Partiella index: den indexerade kolumnen deltar fortfarande; en uppdatering av den kan förhindra HOT oavsett WHERE-predikatet.
- Inkluderade kolumner (INCLUDE): i PostgreSQL är kolumner i INCLUDE-satsen också en del av indexet, så ändringar i dem blockerar HOT.
PostgreSQL jämför gamla och nya värden för varje kolumn som refereras av varje index. Om alla är oförändrade tillåts HOT.
-- Both of these put `email` into the indexed-column set,
-- so any UPDATE that writes email becomes non-HOT:
CREATE INDEX idx_users_email_ci ON users (lower(email));
CREATE INDEX idx_users_email_inc ON users (id) INCLUDE (email);Verifiera HOT vid en verklig uppdatering
Räknarna i pg_stat_user_tables är kumulativa, så Ni kan ta en ögonblicksbild före och efter en känd arbetsbelastning för att bevisa om Er optimering fungerade.
Kör en uppdateringsbatch och jämför sedan förändringen i n_tup_hot_upd med förändringen i n_tup_upd. Om de ökar tillsammans är uppdateringarna helt HOT; om bara n_tup_upd ökar tvingar något fortfarande fram indexuppdateringar.
SELECT n_tup_upd, n_tup_hot_upd
FROM pg_stat_user_tables
WHERE relname = 'counters';
-- ... run UPDATE counters SET hits = hits + 1 WHERE key = 'page:/home'; x1000 ...
SELECT n_tup_upd, n_tup_hot_upd
FROM pg_stat_user_tables
WHERE relname = 'counters';
-- Expect both deltas to be ~1000 for a healthy HOT workload.HOT och WAL-volym
Minskade indexskrivningar minskar direkt mängden WAL. En uppdatering som inte är HOT loggar heapförändringen och varje indexinsättning; en HOT-uppdatering loggar endast heapförändringen (samt periodvis en post för rensning).
I en belastad tabell med flera index kan HOT-uppdateringar minska WAL-genereringen avsevärt, vilket i sin tur:
- minskar replikeringsfördröjningen på standbys.
- minskar belastningen på checkpoints och bakgrundsskrivaren.
- minskar arkivlagringen för PITR.
Ni kan kvantifiera WAL per sats med pg_stat_statements (wal_bytes) för att se effekten före och efter ändringar av fillfactor och index.
SELECT query, calls, wal_bytes,
round(wal_bytes / NULLIF(calls, 0)) AS wal_per_call
FROM pg_stat_statements
WHERE query ILIKE 'UPDATE counters%'
ORDER BY wal_bytes DESC;När HOT inte kan hjälpa
HOT är kraftfullt men fungerar inte överallt. Det hjälper inte när:
- Ni faktiskt behöver uppdatera en indexerad kolumn (till exempel ett statusfält som också används som söknyckel) – indexskrivningen kan inte undvikas, även om Ni ibland kan utforma åtkomstvägen på ett annat sätt.
- Sidorna förblir fulla trots fillfactor eftersom rader växer (till exempel när text med variabel längd eller JSONB expanderar), så att nya versioner hamnar på andra sidor.
- Långvariga transaktioner håller tillbaka xmin-horisonten och förhindrar HOT-rensning, så att kedjor och bloat ändå byggs upp.
Den praktiska strategin är att endast indexera stabila kolumner, ange fillfactor för tabeller med många uppdateringar, hålla transaktionerna korta så att rensningen kan återta utrymme och mäta med n_tup_hot_upd samt WAL-statistik.
Snabbkontroll: aktivera HOT
Testa Er förståelse av vad som krävs för att en uppdatering ska kunna bli HOT.
Sammanfattning: håll uppdateringar i heapen
Ni vet nu hur Ni utformar scheman och index så att uppdateringar förblir heap-only:
- HOT update = en ny tupel på samma sida, noll indexskrivningar och en HOT-kedja som VACUUM eller rensning senare komprimerar.
- Två villkor: ingen indexerad kolumn får ändras och det måste finnas ledigt utrymme på sidan.
- Regel 1: indexera inte kolumner som ändras ofta eller ligger i den heta körvägen; uttryck, partiella index och INCLUDE-kolumner räknas alla som indexerade.
- Regel 2: ange
fillfactor(70–90) för tabeller med många uppdateringar för att reservera utrymme på sidan. - Mät: sträva efter ett högt förhållande mellan
n_tup_hot_updochn_tup_updoch kontrollera attwal_bytesminskar. - Begränsningar: växande rader, uppdateringar av genuint indexerade kolumner och långvariga transaktioner kan fortfarande motverka HOT.
Att maximera HOT är en av de mest effektiva och minst riskfyllda förbättringarna för skrivintensiva PostgreSQL-arbetsbelastningar.
Lär dig SQL med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 22
- Lektioner
- 88
Vanliga frågor
Är lektionen ”HOT-uppdateringar och heap-only-tuple-kedjor” gratis?
Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Prestandaoptimering och frågeoptimering i PostgreSQL, inklusive ”HOT-uppdateringar och heap-only-tuple-kedjor”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Prestandaoptimering och frågeoptimering i PostgreSQL innehåller totalt 4 lektioner.
Vad lär jag mig i ”HOT-uppdateringar och heap-only-tuple-kedjor”?
Utforma scheman och index så att uppdateringar förblir heap-only och undviker skrivförstärkning i index. Ni övar på Prestandaoptimering och frågeoptimering i PostgreSQL med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Prestandaoptimering och frågeoptimering i PostgreSQL?
Du behöver inga förkunskaper. Utbildningen i Prestandaoptimering och frågeoptimering i PostgreSQL på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.
Hur lång tid tar lektionen ”HOT-uppdateringar och heap-only-tuple-kedjor”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Prestandaoptimering och frågeoptimering i PostgreSQL-lektionen?
Ja. Varje Prestandaoptimering och frågeoptimering i PostgreSQL-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Tuplesynlighet, xmin och xmax
- HOT-uppdateringar och heap-only-tuple-kedjor
- Synlighetskartan och index-only-genomsökningar
- WAL-generering och skrivförstärkning