Ny traikefako voalohany namerina ny angon-drakitra Postgres taorian'ny tsy fahombiazana (pejy tsy mety amin'ny sakana 4123007 amin'ny base relatton/16490)

Tiako ny hizara aminareo ny traikefako voalohany nahomby tamin'ny famerenana ny angon-drakitra Postgres ho amin'ny fiasa feno. Nahafantatra ny Postgres DBMS aho tamin'ny antsasaky ny taona lasa izay; talohan'izay dia tsy nanana traikefa tamin'ny fitantanana angon-drakitra mihitsy aho.

Ny traikefako voalohany namerina ny angon-drakitra Postgres taorian'ny tsy fahombiazana (pejy tsy mety amin'ny sakana 4123007 amin'ny base relatton/16490)

Miasa amin'ny maha injeniera semi-DevOps aho amin'ny orinasa IT lehibe iray. Ny orinasanay dia mamolavola rindrambaiko ho an'ny serivisy be entana, ary izaho no tompon'andraikitra amin'ny fampisehoana, fikojakojana ary fametrahana. Nomena asa mahazatra aho: manavao ny fampiharana amin'ny mpizara iray. Ny fampiharana dia nosoratana tamin'ny Django, mandritra ny fanavaozana ny fifindra-monina dia atao (fanovana amin'ny rafitry ny angon-drakitra), ary alohan'ity dingana ity dia maka fanariam-baovao feno amin'ny alàlan'ny programa pg_dump mahazatra, raha tsy izany.

Nisy hadisoana tsy nampoizina nitranga nandritra ny fanariam-pako (Postgres version 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

fahadisoana "pejy tsy mety amin'ny sakana" miresaka momba ny olana eo amin'ny rafi-drakitra, izay tena ratsy. Ao amin'ny forum isan-karazany no nanolorana azy ho manao VACUUM FENO miaraka amin'ny safidy zero_simba_pejy hamahana ity olana ity. Eny ary, andeha isika ...

Miomana amin'ny fanarenana

FAMPITANDREMANA! Aza hadino ny maka backup Postgres alohan'ny hanandrana hamerina ny angon-drakitrao. Raha manana milina virtoaly ianao dia atsaharo ny angon-drakitra ary alaivo sary. Raha tsy azo atao ny maka sary dia ajanony ny angon-drakitra ary adika any amin'ny toerana azo antoka ny votoatin'ny lahatahiry Postgres (anisan'izany ny rakitra wal). Ny zava-dehibe indrindra amin'ny asantsika dia ny tsy hanaratsy ny raharaha. HAMAKY izany.

Satria niasa ho ahy ny angon-drakitra tamin'ny ankapobeny, dia nametra ny tenako ho fanariam-baovao mahazatra aho, saingy nesoriko ny latabatra misy angona simba (safidy -T, --exclude-table=TABLE ao amin'ny pg_dump).

Ny mpizara dia ara-batana, tsy azo atao ny maka sary. Nesorina ny backup, andao hiroso.

Fanamarinana ny rafitra fisie

Alohan'ny hanandramana hamerina ny angon-drakitra dia mila mahazo antoka isika fa ny zava-drehetra dia milamina miaraka amin'ny rafitra fichier. Ary raha misy hadisoana dia ahitsio izy ireo, satria raha tsy izany dia mety ho ratsy kokoa ny zava-drehetra.

Raha ny ahy, ny rafitra fichier miaraka amin'ny angon-drakitra dia napetraka ao "/srv" ary ny karazany dia ext4.

Atsaharo ny angon-drakitra: systemctl mijanona [email voaaro] ary jereo fa tsy ampiasain'olona ny rafitra fichier ary azo esorina amin'ny alàlan'ny baiko lsof:
lsof +D /srv

Tsy maintsy najanoko ihany koa ny angon-drakitra redis, satria nampiasa koa izy io "/srv". Avy eo dia nesoriko / srv (ohatra).

Ny rafitra fichier dia nozahana tamin'ny alalan'ny utility e2fsck miaraka amin'ny switch -f (Manery ny fisavana na dia voamarika madio aza ny rafi-drakitra):

Ny traikefako voalohany namerina ny angon-drakitra Postgres taorian'ny tsy fahombiazana (pejy tsy mety amin'ny sakana 4123007 amin'ny base relatton/16490)

Manaraka, mampiasa ny utility dumpe2fs (sudo dumpe2fs /dev/mapper/gu2—sys-srv | grep voamarina) azonao atao ny manamarina fa tena natao ny fanamarinana:

Ny traikefako voalohany namerina ny angon-drakitra Postgres taorian'ny tsy fahombiazana (pejy tsy mety amin'ny sakana 4123007 amin'ny base relatton/16490)

e2fsck milaza fa tsy nisy olana hita tao amin'ny haavon'ny rafitra rakitra ext4, izay midika fa afaka manohy manandrana mamerina ny angon-drakitra ianao, na miverina any feno banga (Mazava ho azy fa mila mametaka ny rafitra rakitra ianao ary manomboka ny angon-drakitra).

Raha manana mpizara ara-batana ianao dia jereo ny satan'ny disks (amin'ny alàlan'ny smartctl -a /dev/XXX) na RAID controller mba hahazoana antoka fa tsy eo amin'ny sehatry ny hardware ny olana. Raha ny fahitako azy dia nivadika ho “hardware” ilay RAID, ka nangataka ny admin teo an-toerana aho mba hijery ny satan'ny RAID (misy kilometatra an-jatony miala amiko ny server). Nilaza izy fa tsy nisy fahadisoana, izay midika fa afaka manomboka amin'ny laoniny isika.

Andrana 1: zero_damaged_pages

Mifandray amin'ny angon-drakitra amin'ny alàlan'ny psql miaraka amin'ny kaonty manana zo superuser izahay. Mila mpampiasa super izahay, satria... SAFIDY zero_simba_pejy izy ihany no afaka miova. Raha ny ahy dia postgres:

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

Option zero_simba_pejy ilaina mba tsy hiraharahana ny fahadisoana mamaky (avy amin'ny tranokala postgrespro):

Rehefa mahita lohapejy simba ny PostgreSQL dia mitatitra lesoka matetika izy ary manafoana ny fifampiraharahana ankehitriny. Raha alefa ny zero_damaged_pages, dia mamoaka fampitandremana kosa ny rafitra, manafoana ny pejy simba ao anaty fitadidiana, ary manohy ny fanodinana. Ity fihetsika ity dia manimba ny angona, izany hoe ny andalana rehetra ao amin'ny pejy simba.

Avelantsika ny safidy ary manandrana manao banga feno amin'ny latabatra:

VACUUM FULL VERBOSE

Ny traikefako voalohany namerina ny angon-drakitra Postgres taorian'ny tsy fahombiazana (pejy tsy mety amin'ny sakana 4123007 amin'ny base relatton/16490)
Indrisy fa ratsy vintana.

Nisedra hadisoana mitovy amin'izany izahay:

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 – mekanika fitehirizana “data lava” ao amin'ny Poetgres raha toa ka tsy mifanaraka amin'ny pejy iray (8kb raha default).

Andrana 2: reindex

Tsy nanampy ny torohevitra voalohany avy amin'ny Google. Rehefa avy nikaroka minitra vitsivitsy aho dia nahita ny tendro faharoa - ny hanao reindex latabatra simba. Hitako tany amin’ny toerana maro izany torohevitra izany, saingy tsy nampisy fahatokiana izany. Andeha hojerentsika indray:

reindex table ws_log_smevlog

Ny traikefako voalohany namerina ny angon-drakitra Postgres taorian'ny tsy fahombiazana (pejy tsy mety amin'ny sakana 4123007 amin'ny base relatton/16490)

reindex vita tsy misy olana.

Tsy nanampy anefa izany, FENO FENO nianjera tamin'ny fahadisoana mitovy. Satria efa zatra ny tsy fahombiazana aho, dia nanomboka nitady torohevitra bebe kokoa tao amin'ny Internet aho ary nahita zavatra mahaliana lahatsoratra.

Andrana 3: SELECT, LIMIT, OFFSET

Ny lahatsoratra etsy ambony dia nanoro hevitra ny mijery ny andalana isaky ny andalana ary manaisotra ny angona misy olana. Voalohany dia mila mijery ny andalana rehetra isika:

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

Raha ny ahy dia nisy ny latabatra 1 628 991 andalana! nilaina ny nikarakara tsara data partitioning, fa lohahevitra ho an'ny dinika manokana ity. Sabotsy tamin'izay, nandefa ity baiko ity tamin'ny tmux aho ary natory:

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

Ny maraina dia nanapa-kevitra ny hijery ny fandehan-javatra aho. Nahagaga ahy fa 20% monja tamin'ny angon-drakitra no voazaha rehefa afaka 2 ora! Tsy te hiandry 50 andro aho. Tsy fahombiazana tanteraka iray hafa.

Tsy nilavo lefona anefa aho. Nanontany tena aho hoe nahoana no naharitra ela ny scanning. Avy amin'ny antontan-taratasy (indray amin'ny postgrespro) dia hitako hoe:

Ny OFFSET dia mamaritra ny handingana ny isan'ny laharana voatondro alohan'ny hanombohan'ny famoahana andalana.
Raha samy OFFSET sy LIMIT no voafaritra, ny rafitra dia mitsambikina aloha ny laharana OFFSET ary avy eo dia manomboka manisa ny andalana ho an'ny fetra LIMIT.

Rehefa mampiasa LIMIT dia zava-dehibe ihany koa ny mampiasa fehezanteny ORDER BY mba hamerenana ny laharan'ny valiny amin'ny filaharana manokana. Raha tsy izany dia haverina ireo andalana tsy ampoizina.

Mazava ho azy fa diso ny baiko etsy ambony: voalohany, tsy nisy baiko avy amin'ny, mety ho diso ny vokany. Faharoa, ny Postgres aloha dia tsy maintsy nijery sy nitsambikina andalana OFFSET, ary nitombo offset vao mainka hihena ny vokatra.

Andrana 4: maka fanariam-pako amin'ny endrika lahatsoratra

Avy eo dia tonga tao an-tsaiko ny hevitra toa mamirapiratra: manaova fanariam-pako amin'ny endrika lahatsoratra ary diniho ny andalana voarakitra farany.

Fa aloha, andeha hojerentsika ny firafitry ny latabatra. ws_log_smevlog:

Ny traikefako voalohany namerina ny angon-drakitra Postgres taorian'ny tsy fahombiazana (pejy tsy mety amin'ny sakana 4123007 amin'ny base relatton/16490)

Amin'ny tranga misy antsika dia manana tsanganana isika "Id", izay nahitana ny famantarana tokana (counter) ny andalana. Toy izao ny drafitra:

  1. Manomboka maka fanariam-pako amin'ny endrika lahatsoratra isika (amin'ny endrika baiko sql)
  2. Amin'ny fotoana iray dia tapaka ny fanariam-pako noho ny hadisoana, fa ny rakitra lahatsoratra dia mbola voatahiry ao anaty kapila.
  3. Mijery ny faran'ny rakitra an-tsoratra isika, ka mahita ny famantarana (id) an'ny andalana farany izay nesorina soa aman-tsara.

Nanomboka nanary fanariam-bava aho tamin'ny endrika lahatsoratra:

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

Ny fanariam-pako, araka ny efa nampoizina, dia tapaka tamin'ny fahadisoana mitovy:

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

Mbola lavitra rambony Nijery ny faran'ny fanariam-pako aho (rambo -5 ./my_dump.dump) dia nahita fa tapaka ny fanariam-pako tamin'ny tsipika misy id 186 525. "Ka ny olana dia eo amin'ny laharana miaraka amin'ny id 186 526, tapaka izany ary mila esorina!" - Noheveriko. Saingy, mametraka fanontaniana amin'ny database:
«safidio * avy amin'ny ws_log_smevlog izay id=186529"Hita fa tsara daholo ny zava-drehetra tamin'ity tsipika ity ... Niasa tsy nisy olana koa ny andalana misy indices 186 - 530. “Hevitra mamiratra” iray hafa tsy nahomby. Taty aoriana dia azoko ny antony nitrangan'izany: rehefa mamafa sy manova ny angona avy amin'ny latabatra, dia tsy voafafa ara-batana izy ireo, fa voamarika ho "tuples maty", avy eo tonga. autovacuum ary manamarika ireo andalana ireo ho voafafa ary mamela ireo andalana ireo hampiasaina indray. Mba hahatakarana, raha miova ny angon-drakitra ao amin'ny latabatra ary alefa ny autovacuum, dia tsy voatahiry manaraka izany.

Andrana 5: SELECT, FROM, WHERE id=

Mampahery antsika ny tsy fahombiazana. Tsy tokony ho kivy na oviana na oviana ianao, mila mandeha hatramin'ny farany ianao ary mino ny tenanao sy ny fahaiza-manaonao. Noho izany dia nanapa-kevitra ny hanandrana safidy hafa aho: jereo tsirairay ny rakitra rehetra ao amin'ny tahiry. Ny fahafantarana ny firafitry ny latabatro (jereo etsy ambony), dia manana saha id izahay izay tokana (fanalahidy voalohany). Manana andalana 1 izahay ao amin'ny latabatra ary id dia mirindra, izay midika fa afaka mamaky azy ireo tsirairay avy isika:

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

Raha misy tsy mahazo, dia toy izao manaraka izao ny baiko: mijery ny andalana isaky ny andalana ary mandefa stdout mankany / Dev / tohivakana foana, fa raha tsy mahomby ny baiko SELECT, dia atao pirinty ny lahatsoratra diso (nalefa any amin'ny console ny stderr) ary misy tsipika misy ny hadisoana atao pirinty (noho ny ||, midika izany fa nisy olana ny voafantina (ny code miverina amin'ny baiko). tsy 0)).

Tsara vintana aho, nanana indeksa noforonina teny an-kianja id:

Ny traikefako voalohany namerina ny angon-drakitra Postgres taorian'ny tsy fahombiazana (pejy tsy mety amin'ny sakana 4123007 amin'ny base relatton/16490)

Midika izany fa tsy mila fotoana be ny fitadiavana tsipika misy ny id irina. Amin'ny teoria dia tokony hiasa izany. Eny ary, andao hataontsika ny baiko tmux ary andeha hatory.

Tamin'ny maraina aho dia nahita fa 90 eo ho eo ny fidirana nojerena, izany hoe mihoatra ny 000%. Vokatra tena tsara raha ampitahaina amin'ny fomba teo aloha (5%)! Saingy tsy te hiandry 2 andro aho ...

Andrana 6: SELECT, FROM, WHERE id >= ary id

Ny mpanjifa dia nanana mpizara tena tsara natokana ho an'ny angon-drakitra: dual-processor Intel Xeon E5-2697 v2, nisy kofehy 48 teo ho eo tao aminay! Ny enta-mavesatra teo amin'ny mpizara dia antonony; afaka misintona kofehy 20 eo ho eo izahay tsy misy olana. Ampy ihany koa ny RAM: hatramin'ny 384 gigabytes!

Noho izany, ny baiko dia tsy maintsy ampifanarahana:

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

Eto aho dia azo atao ny manoratra script tsara tarehy sy kanto, saingy nisafidy ny fomba fampifanarahana haingana indrindra aho: mizara amin'ny tanana ny laharana 0-1628991 amin'ny elanelan'ny firaketana 100 ary mihazakazaka baiko 000 amin'ny endrika:

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

Tsy izay ihany anefa. Amin'ny teoria, ny fifandraisana amin'ny angon-drakitra dia mitaky fotoana sy loharanon'ny rafitra ihany koa. Ny fampifandraisana 1 dia tsy dia hendry loatra, hanaiky ianao. Noho izany, andao haka andalana 628 fa tsy fifandraisana iray. Vokatr'izany, niova ho toy izao ny ekipa:

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

Sokafy 16 windows amin'ny tmux session ary araho ny baiko:

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

Indray andro taty aoriana dia nahazo ny valiny voalohany aho! Izany hoe (tsy voatahiry intsony ny soatoavina XXX sy ZZZ):

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

Midika izany fa misy lesoka ny andalana telo. Ny id amin'ny firaketana olana voalohany sy faharoa dia eo anelanelan'ny 829 sy 000, ny id an'ny fahatelo dia eo anelanelan'ny 830 sy 000. Avy eo, tsy maintsy nitady ny sandan'ny id marina amin'ny firaketana olana izahay. Mba hanaovana izany, mijery ny faritra misy anay miaraka amin'ny firaketana olana miaraka amin'ny dingana 146 izahay ary fantaro ny 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

Fotoana mahafaly

Hitanay ireo tsipika misy olana. Miditra ao amin'ny angon-drakitra amin'ny alàlan'ny psql izahay ary manandrana mamafa azy ireo:

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

Nahagaga ahy fa nofafana tsy nisy olana ny fidirana na dia tsy nisy safidy aza zero_simba_pejy.

Dia nifandray tamin'ny angon-drakitra aho, nanao FENO FENO (Heveriko fa tsy ilaina ny manao izany), ary tamin'ny farany dia nesoriko soa aman-tsara ny backup mampiasa pg_dump. Nalaina tsy nisy hadisoana ny fanariam-pako! Voavaha tamin'ny fomba hadalana toy izany ny olana. Tsy nisy fetra ny hafaliana, taorian'ny tsy fahombiazana maro dia nahita vahaolana izahay!

Fisaorana sy famaranana

Toy izany no nivoahan'ny traikefako voalohany namerina ny angon-drakitra Postgres tena izy. Hotadidiako hatry ny ela io traikefa io.

Ary farany, te-hisaotra an'i PostgresPro aho tamin'ny fandikana ny antontan-taratasy ho amin'ny teny Rosiana sy ho an'ny maimaim-poana tanteraka fampianarana an-tserasera, izay nanampy betsaka nandritra ny famakafakana ny olana.

Source: www.habr.com

Add a comment