L-ewwel esperjenza tiegħi nirkupra database Postgres wara falliment (paġna invalida fil-blokk 4123007 ta' relaton base/16490)

Nixtieq naqsam miegħek l-ewwel esperjenza ta 'suċċess tiegħi ta' restawr ta 'database Postgres għal funzjonalità sħiħa. Sirt familjari mad-DBMS Postgres nofs sena ilu; qabel ma kelli l-ebda esperjenza fl-amministrazzjoni tad-databases.

L-ewwel esperjenza tiegħi nirkupra database Postgres wara falliment (paġna invalida fil-blokk 4123007 ta' relaton base/16490)

Naħdem bħala inġinier semi-DevOps f'kumpanija kbira tal-IT. Il-kumpanija tagħna tiżviluppa softwer għal servizzi ta 'tagħbija għolja, u jien responsabbli għall-prestazzjoni, il-manutenzjoni u l-iskjerament. Ingħatejt kompitu standard: li taġġorna applikazzjoni fuq server wieħed. L-applikazzjoni hija miktuba f'Django, waqt l-aġġornament isiru migrazzjonijiet (bidliet fl-istruttura tad-database), u qabel dan il-proċess nieħdu dump tad-database sħiħa permezz tal-programm standard pg_dump, fil-każ.

Żball mhux mistenni seħħ waqt li ħadet dump (verżjoni Postgres 9.5):

pg_dump: Oumping the contents of table “ws_log_smevlog” failed: PQgetResult() failed.
pg_dump: Error message from server: ERROR: invalid page in block 4123007 of relatton base/16490/21396989
pg_dump: The command was: COPY public.ws_log_smevlog [...]
pg_dunp: [parallel archtver] a worker process dled unexpectedly

Bug "paġna mhux valida fil-blokk" jitkellem dwar problemi fil-livell tas-sistema tal-fajls, li hija ħażina ħafna. Fuq diversi fora ġie ssuġġerit li tagħmel VAKWU SĦIĦ b'għażla zero_damged_pages biex issolvi din il-problema. Ukoll, ejja nippruvaw...

Tħejjija għall-irkupru

ATTENZJONI Kun żgur li tieħu backup Postgres qabel kwalunkwe tentattiv biex tirrestawra d-database tiegħek. Jekk għandek magna virtwali, waqqaf id-database u ħu snapshot. Jekk ma jkunx possibbli li tieħu snapshot, waqqaf id-database u kkopja l-kontenut tad-direttorju Postgres (inklużi fajls wal) f'post sigur. Il-ħaġa ewlenija fin-negozju tagħna mhix li nagħmlu l-affarijiet agħar. Aqra dan.

Peress li d-database ġeneralment ħadmet għalija, illimitajt ruħi għal dump tad-database regolari, iżda esklujt it-tabella b'data bil-ħsara (għażla -T, --exclude-table=TABELLA fi pg_dump).

Is-server kien fiżiku, kien impossibbli li tieħu snapshot. Il-backup tneħħa, ejja nkomplu.

Kontroll tas-sistema tal-fajls

Qabel ma nippruvaw nirrestawraw id-database, irridu niżguraw li kollox huwa fl-ordni mas-sistema tal-fajls innifsu. U f'każ ta 'żbalji, ikkoreġihom, għax inkella tista' biss tagħmel l-affarijiet agħar.

Fil-każ tiegħi, is-sistema tal-fajls bid-database kienet immuntata fiha "/srv" u t-tip kien ext4.

Twaqqaf id-database: systemctl waqfien [protett bl-email] u iċċekkja li s-sistema tal-fajls ma tkunx qed tintuża minn ħadd u tista' tiġi żmuntata bl-użu tal-kmand lsof:
lsof +D /srv

Kelli wkoll inwaqqaf id-database redis, peress li kienet qed tuża wkoll "/srv". Sussegwentement żmuntajt / srv (umount).

Is-sistema tal-fajls ġiet iċċekkjata bl-użu tal-utilità e2fsck bis-swiċċ -f (Forza l-iċċekkjar anke jekk is-sistema tal-fajls hija mmarkata nadifa):

L-ewwel esperjenza tiegħi nirkupra database Postgres wara falliment (paġna invalida fil-blokk 4123007 ta' relaton base/16490)

Sussegwentement, tuża l-utilità dumpe2fs (sudo dumpe2fs /dev/mapper/gu2—sys-srv | grep iċċekkjat) tista' tivverifika li l-verifika twettqet fil-fatt:

L-ewwel esperjenza tiegħi nirkupra database Postgres wara falliment (paġna invalida fil-blokk 4123007 ta' relaton base/16490)

e2fsck jgħid li ma nstabu l-ebda problemi fil-livell tas-sistema tal-fajls ext4, li jfisser li tista 'tkompli tipprova tirrestawra d-database, jew aħjar terġa' lura għal vakwu mimli (naturalment, għandek bżonn timmonta s-sistema tal-fajls lura u tibda d-database).

Jekk għandek server fiżiku, kun żgur li tiċċekkja l-istatus tad-diski (per smartctl -a /dev/XXX) jew kontrollur RAID biex tiżgura li l-problema mhix fil-livell tal-hardware. Fil-każ tiegħi, ir-RAID irriżulta li kien "ħardwer", għalhekk staqsejt lill-amministratur lokali biex jiċċekkja l-istatus tar-RAID (is-server kien diversi mijiet ta 'kilometri 'l bogħod minni). Qal li ma kienx hemm żbalji, li jfisser li żgur nistgħu nibdew ir-restawr.

Tentattiv 1: zero_damaged_pages

Aħna nikkonnettjaw mad-database permezz ta 'psql ma' kont li għandu drittijiet ta 'superuser. Għandna bżonn superuser, għax... għażla zero_damged_pages hu biss jista’ jinbidel. Fil-każ tiegħi huwa postgres:

psql -h 127.0.0.1 -U postgres -s [database_name]

Għażla zero_damged_pages meħtieġa sabiex jiġu injorati l-iżbalji tal-qari (mill-websajt postgrespro):

Meta PostgreSQL jiskopri header tal-paġna korrotti, tipikament jirrapporta żball u jwaqqaf it-tranżazzjoni attwali. Jekk zero_damaged_pages hija attivata, is-sistema minflok toħroġ twissija, tneħħi żero l-paġna bil-ħsara fil-memorja, u tkompli l-ipproċessar. Din l-imġieba teqred id-dejta, jiġifieri r-ringieli kollha fil-paġna bil-ħsara.

Aħna nippermettu l-għażla u nippruvaw nagħmlu vakwu sħiħ tat-tabelli:

VACUUM FULL VERBOSE

L-ewwel esperjenza tiegħi nirkupra database Postgres wara falliment (paġna invalida fil-blokk 4123007 ta' relaton base/16490)
Sfortunatament, xorti ħażina.

Iltqajna ma' żball simili:

INFO: vacuuming "“public.ws_log_smevlog”
WARNING: invalid page in block 4123007 of relation base/16400/21396989; zeroing out page
ERROR: unexpected chunk number 573 (expected 565) for toast value 21648541 in pg_toast_106070

pg_toast – mekkaniżmu għall-ħażna ta’ “dejta twila” f’Poetgres jekk ma tidħolx f’paġna waħda (8kb b’mod awtomatiku).

Tentattiv 2: indiċi mill-ġdid

L-ewwel parir minn Google ma għenx. Wara ftit minuti ta 'tiftix, sibt it-tieni ponta - biex tagħmel indiċi mill-ġdid mejda bil-ħsara. Rajt dan il-parir f’ħafna postijiet, iżda ma ispirax fiduċja. Ejja nerġgħu indiċjaw:

reindex table ws_log_smevlog

L-ewwel esperjenza tiegħi nirkupra database Postgres wara falliment (paġna invalida fil-blokk 4123007 ta' relaton base/16490)

indiċi mill-ġdid tlesti mingħajr problemi.

Madankollu, dan ma għenx, VACUUM SĦIĦA ġġarraf bi żball simili. Peress li jien imdorri għall-fallimenti, bdejt infittex aktar għal pariri fuq l-Internet u ltqajt ma 'pjuttost interessanti oġġett.

Tentattiv 3: AGĦŻEL, LIMITA, KOMPENS

L-artikolu ta 'hawn fuq issuġġerixxa li tħares lejn it-tabella ringiela b'ringiela u t-tneħħija ta' data problematika. L-ewwel kellna nħarsu lejn il-linji kollha:

for ((i=0; i<"Number_of_rows_in_nodes"; i++ )); do psql -U "Username" "Database Name" -c "SELECT * FROM nodes LIMIT 1 offset $i" >/dev/null || echo $i; done

Fil-każ tiegħi, it-tabella li tinsab 1 628 991 linji! Kien meħtieġ li tieħu ħsieb tajjeb qsim tad-data, iżda dan huwa suġġett għal diskussjoni separata. Kien is-Sibt, gejt dan il-kmand fit-tmux u mort torqod:

for ((i=0; i<1628991; i++ )); do psql -U my_user -d my_database -c "SELECT * FROM ws_log_smevlog LIMIT 1 offset $i" >/dev/null || echo $i; done

Sa filgħodu ddeċidejt li niċċekkja kif kienu sejrin l-affarijiet. B'sorpriża tiegħi, skoprejt li wara 20 siegħa, 2% biss tad-dejta kienet ġiet skennjata! Ma ridtx nistenna 50 jum. Falliment ieħor sħiħ.

Imma jien ma qatgħetx qalbek. Staqsejt għaliex l-iskannjar ħa daqshekk. Mid-dokumentazzjoni (għal darb'oħra fuq postgrespro) sibt:

OFFSET jispeċifika li taqbeż in-numru speċifikat ta' ringieli qabel ma tibda toħroġ ringieli.
Jekk jiġu speċifikati kemm OFFSET kif ukoll LIMIT, is-sistema l-ewwel taqbeż ir-ringieli OFFSET u mbagħad tibda tgħodd ir-ringieli għar-restrizzjoni LIMIT.

Meta tuża LIMIT, huwa importanti li tuża wkoll klawżola ORDER BY sabiex ir-ringieli tar-riżultat jintbagħtu lura f'ordni speċifika. Inkella, se jintbagħtu lura subsettijiet imprevedibbli ta' ringieli.

Ovvjament, il-kmand ta 'hawn fuq kien żbaljat: l-ewwelnett, ma kienx hemm ordni minn, ir-riżultat jista' jkun żbaljat. It-tieni nett, Postgres l-ewwel kellha tiskennja u taqbeż ir-ringieli OFFSET, u maż-żieda JIKKUMPENSAW il-produttività tonqos saħansitra aktar.

Tentattiv 4: ħu dump f'forma ta 'test

Imbagħad ġietni f’moħħi idea li tidher brillanti: ħu dump f’forma ta’ test u tanalizza l-aħħar linja rreġistrata.

Imma l-ewwel, ejja nagħtu ħarsa lejn l-istruttura tat-tabella. ws_log_smevlog:

L-ewwel esperjenza tiegħi nirkupra database Postgres wara falliment (paġna invalida fil-blokk 4123007 ta' relaton base/16490)

Fil-każ tagħna għandna kolonna "Id", li kien fih l-identifikatur uniku (counter) tar-ringiela. Il-pjan kien hekk:

  1. Nibdew nieħdu dump f'forma ta 'test (fil-forma ta' kmandi sql)
  2. F'ċertu punt fiż-żmien, id-dump jiġi interrott minħabba żball, iżda l-fajl tat-test xorta jiġi ssejvjat fuq disk
  3. Inħarsu lejn it-tmiem tal-fajl tat-test, u b'hekk insibu l-identifikatur (id) tal-aħħar linja li tneħħiet b'suċċess

Bdejt nieħu dump f'forma ta' test:

pg_dump -U my_user -d my_database -F p -t ws_log_smevlog -f ./my_dump.dump

Id-dump, kif mistenni, ġie interrott bl-istess żball:

pg_dump: Error message from server: ERROR: invalid page in block 4123007 of relatton base/16490/21396989

Aktar permezz denb Ħarist lejn it-tarf tal-miżbla (denb -5 ./my_dump.dump) skoprew li l-miżbla kienet interrotta fuq il-linja b'id 186 525. "Allura l-problema hija konformi mal-id 186 526, hija miksura, u jeħtieġ li titħassar!" – Ħsibt. Iżda, tagħmel mistoqsija lid-database:
«agħżel * minn ws_log_smevlog fejn id=186529“Irriżulta li kollox kien tajjeb b’din il-linja... Ringieli b’indiċi 186 - 530 ħadmu wkoll mingħajr problemi. "Idea brillanti" oħra falliet. Aktar tard fhimt għaliex ġara dan: meta tħassar u tbiddel id-dejta minn tabella, ma jitħassrux fiżikament, iżda huma mmarkati bħala "tuples mejta", imbagħad jiġi awtovakwu u jimmarka dawn il-linji bħala mħassra u jippermetti li dawn il-linji jerġgħu jintużaw. Biex tifhem, jekk id-dejta fit-tabella tinbidel u l-awtovakwu huwa attivat, allura ma tkunx maħżuna b'mod sekwenzjali.

Tentattiv 5: AGĦŻEL, MINN, FEJN id=

Il-fallimenti jagħmluna aktar b'saħħithom. Qatt m'għandek taqta' qalbek, trid tmur sal-aħħar u temmen fik innifsek u l-kapaċitajiet tiegħek. Għalhekk iddeċidejt li nipprova għażla oħra: ara r-rekords kollha fid-database wieħed wieħed. Billi nafu l-istruttura tat-tabella tiegħi (ara hawn fuq), għandna qasam id li huwa uniku (ċavetta primarja). Għandna 1 ringieli fit-tabella u id huma fl-ordni, li jfisser li nistgħu ngħaddu minnhom wieħed wieħed:

for ((i=1; i<1628991; i=$((i+1)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id=$i" >/dev/null || echo $i; done

Jekk xi ħadd ma jifhimx, il-kmand jaħdem kif ġej: jiskenja t-tabella ringiela b’ringiela u jibgħat stdout lil / dev / null, iżda jekk il-kmand SELECT jonqos, allura t-test tal-iżball jiġi stampat (stderr jintbagħat lill-console) u tiġi stampata linja li fiha l-iżball (grazzi għal ||, li jfisser li l-għażla kellha problemi (il-kodiċi tar-ritorn tal-kmand) mhux 0)).

Kont xortik tajba, kelli indiċi maħluqa fuq il-qasam id:

L-ewwel esperjenza tiegħi nirkupra database Postgres wara falliment (paġna invalida fil-blokk 4123007 ta' relaton base/16490)

Dan ifisser li s-sejba ta 'linja bl-id mixtieqa m'għandhiex tieħu ħafna ħin. Fit-teorija għandu jaħdem. Ukoll, ejja tmexxi l-kmand fi tmux u ejja torqod.

Sa filgħodu sibt li kienu dehru madwar 90 dħul, li huwa ftit aktar minn 000%. Riżultat eċċellenti meta mqabbel mal-metodu preċedenti (5%)! Imma ma ridtx nistenna 2 jum...

Tentattiv 6: AGĦŻEL, MINN, FEJN id >= u id

Il-klijent kellu server eċċellenti ddedikat għad-database: dual-processor Intel Xeon E5-2697 v2, kien hemm daqs 48 ħajta fil-post tagħna! It-tagħbija fuq is-server kienet medja; stajna tniżżel madwar 20 ħajt mingħajr problemi. Kien hemm ukoll biżżejjed RAM: daqs 384 gigabytes!

Għalhekk, il-kmand kellu jiġi parallelizzat:

for ((i=1; i<1628991; i=$((i+1)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id=$i" >/dev/null || echo $i; done

Hawnhekk kien possibbli li tikteb kitba sabiħa u eleganti, imma għażilt il-metodu ta 'parallelizzazzjoni l-aktar mgħaġġel: qassam manwalment il-firxa 0-1628991 f'intervalli ta' 100 rekord u mexxi separatament 000-il kmand tal-forma:

for ((i=N; i<M; i=$((i+1)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id=$i" >/dev/null || echo $i; done

Imma dan mhux kollox. Fit-teorija, il-konnessjoni ma 'database tieħu wkoll ftit ħin u riżorsi tas-sistema. Il-konnessjoni ta '1 ma kinitx intelliġenti ħafna, int taqbel. Għalhekk, ejja nġibu 628 ringieli minflok konnessjoni waħda fuq waħda. Bħala riżultat, it-tim inbidel f'dan:

for ((i=N; i<M; i=$((i+1000)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id>=$i and id<$((i+1000))" >/dev/null || echo $i; done

Iftaħ 16-il tieqa f'sessjoni tmux u mexxi l-kmandi:

1) for ((i=0; i<100000; i=$((i+1000)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id>=$i and id<$((i+1000))" >/dev/null || echo $i; done
2) for ((i=100000; i<200000; i=$((i+1000)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id>=$i and id<$((i+1000))" >/dev/null || echo $i; done
…
15) for ((i=1400000; i<1500000; i=$((i+1000)) )); do psql -U my_user -d my_database -c "SELECT * FROM ws_log_smevlog where id>=$i and id<$((i+1000))" >/dev/null || echo $i; done
16) for ((i=1500000; i<1628991; i=$((i+1000)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id>=$i and id<$((i+1000))" >/dev/null || echo $i; done

Ġurnata wara rċevejt l-ewwel riżultati! Jiġifieri (il-valuri XXX u ZZZ m'għadhomx ippreservati):

ERROR:  missing chunk number 0 for toast value 37837571 in pg_toast_106070
829000
ERROR:  missing chunk number 0 for toast value XXX in pg_toast_106070
829000
ERROR:  missing chunk number 0 for toast value ZZZ in pg_toast_106070
146000

Dan ifisser li tliet linji fihom żball. L-ids tal-ewwel u t-tieni rekords tal-problema kienu bejn 829 u 000, l-ids tat-tielet kienu bejn 830 u 000. Sussegwentement, kellna sempliċement insibu l-valur id eżatt tar-rekords tal-problema. Biex tagħmel dan, inħarsu mill-firxa tagħna b'rekords problematiċi b'pass ta '146 u nidentifikaw l-id:

for ((i=829000; i<830000; i=$((i+1)) )); do psql -U my_user -d my_database -c "SELECT * FROM ws_log_smevlog where id=$i" >/dev/null || echo $i; done
829417
ERROR:  unexpected chunk number 2 (expected 0) for toast value 37837843 in pg_toast_106070
829449
for ((i=146000; i<147000; i=$((i+1)) )); do psql -U my_user -d my_database -c "SELECT * FROM ws_log_smevlog where id=$i" >/dev/null || echo $i; done
829417
ERROR:  unexpected chunk number ZZZ (expected 0) for toast value XXX in pg_toast_106070
146911

Tmiem hieni

Sibna l-linji problematiċi. Aħna nidħlu fid-database permezz ta' psql u nippruvaw inħassruhom:

my_database=# delete from ws_log_smevlog where id=829417;
DELETE 1
my_database=# delete from ws_log_smevlog where id=829449;
DELETE 1
my_database=# delete from ws_log_smevlog where id=146911;
DELETE 1

B'sorpriża tiegħi, l-iskrizzjonijiet tħassru mingħajr problemi anke mingħajr l-għażla zero_damged_pages.

Imbagħad I konnessi mad-database, ma VACUUM SĦIĦA (Naħseb li ma kienx meħtieġ li tagħmel dan), u finalment neħħi b'suċċess il-backup bl-użu pg_dump. Il-miżbla ttieħdet mingħajr ebda żball! Il-problema ġiet solvuta b'tali mod stupid. Il-ferħ ma kienx jaf limiti, wara tant fallimenti rnexxielna nsibu soluzzjoni!

Rikonoxximenti u Konklużjoni

Dan huwa kif irriżulta l-ewwel esperjenza tiegħi ta 'restawr ta' database Postgres reali. Din l-esperjenza se niftakar għal żmien twil.

U fl-aħħarnett, nixtieq ngħid grazzi lil PostgresPro għat-traduzzjoni tad-dokumentazzjoni bir-Russu u għal korsijiet onlajn kompletament bla ħlas, li għenet ħafna waqt l-analiżi tal-problema.

Sors: www.habr.com

Żid kumment