Таҷрибаи аввалини ман дар барқарор кардани пойгоҳи додаҳои Postgres пас аз нокомӣ (саҳифаи беэътибор дар блоки 4123007 базаи relatton/16490)

Ман мехоҳам бо шумо таҷрибаи аввалини муваффақи худро дар бораи барқарор кардани пойгоҳи додаҳои Postgres ба пуррагӣ мубодила кунам. Ман бо DBMS Postgres ним сол пеш шинос шудам; пеш аз он ман умуман дар идоракунии пойгоҳи додаҳо таҷриба надоштам.

Таҷрибаи аввалини ман дар барқарор кардани пойгоҳи додаҳои Postgres пас аз нокомӣ (саҳифаи беэътибор дар блоки 4123007 базаи relatton/16490)

Ман ба ҳайси муҳандиси ним-DevOps дар як ширкати бузурги IT кор мекунам. Ширкати мо нармафзорро барои хидматҳои сербор таҳия мекунад ва ман барои иҷроиш, нигоҳдорӣ ва ҷойгиркунӣ масъул ҳастам. Ба ман вазифаи стандартӣ дода шуд: навсозии барнома дар як сервер. Ариза дар Django навишта шудааст, ҳангоми навсозӣ муҳоҷир (тағйирот дар сохтори пойгоҳи додаҳо) анҷом дода мешавад ва пеш аз ин раванд, мо ба ҳар сурат, тавассути барномаи стандартии pg_dump партови пурраи пойгоҳи додаҳоро мегирем.

Ҳангоми гирифтани партов хатогии ғайричашмдошт рух дод (версияи 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

хатогӣ "саҳифаи беэътибор дар блок" дар бораи мушкилот дар сатҳи системаи файлӣ сухан меронад, ки ин хеле бад аст. Дар форумҳои гуногун пешниҳод карда шуд, ки анҷом дода шавад ВАКУУМИ ПУРРА бо вариант саҳифаҳои_зарарнок барои халли ин масъала. Хуб, биёед кӯшиш кунем ...

Омодагӣ ба барқароршавӣ

ДАСТГИРӢ! Боварӣ ҳосил кунед, ки пеш аз ҳама кӯшиши барқарор кардани пойгоҳи додаи худ нусхаи эҳтиётии Postgres гиред. Агар шумо мошини виртуалӣ дошта бошед, пойгоҳи додаҳоро қатъ кунед ва аксбардорӣ кунед. Агар имкони гирифтани аксбардорӣ набошад, пойгоҳи додаҳоро қатъ кунед ва мундариҷаи директорияи Postgres (аз ҷумла файлҳои wal) ба ҷои амн нусхабардорӣ кунед. Дар кори мо чизи асосй он аст, ки корро бедтар накунем. Хондан ин.

Азбаски пойгоҳи додаҳо умуман барои ман кор мекард, ман худамро бо партовгоҳи муқаррарии пойгоҳи додаҳо маҳдуд кардам, аммо ҷадвалро бо маълумоти вайроншуда хориҷ кардам (ихтиёрӣ -T, --exclude-table = TABLE дар pg_dump).

Сервер физикӣ буд, гирифтани аксбардорӣ ғайриимкон буд. Нусхаи эҳтиётӣ хориҷ карда шуд, биёед идома диҳем.

Санҷиши системаи файлӣ

Пеш аз кӯшиши барқарор кардани пойгоҳи додаҳо, мо бояд боварӣ ҳосил кунем, ки ҳама чиз бо худи системаи файлӣ мувофиқ аст. Ва дар сурати хатогиҳо онҳоро ислоҳ кунед, зеро дар акси ҳол шумо метавонед корро танҳо бадтар кунед.

Дар ҳолати ман, системаи файлӣ бо пойгоҳи додаҳо насб карда шудааст "/srv" ва навъи ext4 буд.

Қатъи пойгоҳи додаҳо: systemctl қатъ [почтаи электронӣ ҳифз карда шудааст] ва санҷед, ки системаи файлӣ аз ҷониби касе истифода намешавад ва онро бо ёрии фармон ҷудо кардан мумкин аст лоф:
lsof +D /srv

Ман инчунин маҷбур шудам, ки пойгоҳи додаи redis-ро қатъ кунам, зеро он низ истифода мешуд "/srv". Баъд ман ҷудо кардам / srv (микдор).

Системаи файлӣ бо истифода аз утилита тафтиш карда шуд e2fsck бо тугмаи -f (Тафтиши маҷбурӣ ҳатто агар системаи файлӣ тоза бошад):

Таҷрибаи аввалини ман дар барқарор кардани пойгоҳи додаҳои Postgres пас аз нокомӣ (саҳифаи беэътибор дар блоки 4123007 базаи relatton/16490)

Баъдан, бо истифода аз утилита dumpe2fs (sudo dumpe2fs /dev/mapper/gu2—sys-srv | grep тафтиш кард) шумо метавонед тафтиш кунед, ки санҷиш воқеан иҷро шудааст:

Таҷрибаи аввалини ман дар барқарор кардани пойгоҳи додаҳои Postgres пас аз нокомӣ (саҳифаи беэътибор дар блоки 4123007 базаи relatton/16490)

e2fsck мегӯяд, ки дар сатҳи системаи файлии ext4 ҳеҷ мушкиле пайдо нашудааст, ки ин маънои онро дорад, ки шумо метавонед кӯшиши барқарор кардани пойгоҳи додаҳоро идома диҳед ё баргардед. вакуум пур (албатта, ба шумо лозим аст, ки системаи файлиро дубора насб кунед ва пойгоҳи додаҳоро оғоз кунед).

Агар шумо сервери ҷисмонӣ дошта бошед, ҳатман ҳолати дискҳоро тафтиш кунед (тавассути smartctl -a /dev/XXX) ё контролери RAID барои боварӣ ҳосил кунед, ки мушкилот дар сатҳи сахтафзор нест. Дар ҳолати ман, RAID "сахтафзор" буд, аз ин рӯ ман аз маъмури маҳаллӣ хоҳиш кардам, ки ҳолати RAID-ро тафтиш кунад (сервер аз ман чандсад километр дур буд). Вай гуфт, ки ҳеҷ гуна хатогӣ вуҷуд надорад, яъне мо бешубҳа ба барқарорсозӣ шурӯъ карда метавонем.

Кӯшиши 1: zero_damaged_pages

Мо ба пойгоҳи додаҳо тавассути psql бо ҳисобе пайваст мешавем, ки дорои ҳуқуқи суперкорбар аст. Мо ба суперкорбар ниёз дорем, зеро... вариант саҳифаҳои_зарарнок танҳо ӯ метавонад тағйир ёбад. Дар ҳолати ман ин postgres аст:

psql -h 127.0.0.1 -U postgres -s [номи_маълумот]

Интихоб саҳифаҳои_зарарнок барои нодида гирифтани хатогиҳои хондан лозим аст (аз вебсайти postgrespro):

Вақте ки PostgreSQL сарлавҳаи вайроншудаи саҳифаро ошкор мекунад, он одатан дар бораи хато хабар медиҳад ва амалиёти ҷорӣро қатъ мекунад. Агар zero_damaged_pages фаъол бошад, система ба ҷои он огоҳӣ медиҳад, саҳифаи вайроншударо дар хотира сифр мекунад ва коркардро идома медиҳад. Ин рафтор маълумотро, яъне ҳамаи сатрҳои саҳифаи вайроншударо нест мекунад.

Мо вариантро фаъол мекунем ва кӯшиш мекунем, ки холигии пурраи ҷадвалҳоро анҷом диҳем:

VACUUM FULL VERBOSE

Таҷрибаи аввалини ман дар барқарор кардани пойгоҳи додаҳои Postgres пас аз нокомӣ (саҳифаи беэътибор дар блоки 4123007 базаи relatton/16490)
Мутаассифона, бадбахтӣ.

Мо бо хатогии шабеҳ дучор шудем:

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 - механизми нигоҳдории "маълумоти дароз" дар Poetgres, агар он дар як саҳифа мувофиқат накунад (бо пешфарз 8кб).

Кӯшиши 2: дубора индексатсия

Аввалин маслиҳати Google кӯмак накард. Пас аз чанд дақиқа ҷустуҷӯ, ман маслиҳати дуюмро пайдо кардам - ​​барои сохтани реиндекс мизи вайроншуда. Ман ин маслиҳатро дар бисёр ҷойҳо дидам, аммо ин бовариро ба вуҷуд наовард. Биёед дубора индексатсия кунем:

reindex table ws_log_smevlog

Таҷрибаи аввалини ман дар барқарор кардани пойгоҳи додаҳои Postgres пас аз нокомӣ (саҳифаи беэътибор дар блоки 4123007 базаи relatton/16490)

реиндекс бе мушкилот анҷом ёфт.

Бо вуҷуди ин, ин кӯмак накард, ВАКУУМ ПУР бо хатои шабеҳ афтод. Азбаски ман ба нокомиҳо одат кардаам, ман ба ҷустуҷӯи маслиҳат дар Интернет шурӯъ кардам ва ба як чизи ҷолибе дучор шудам мақола.

Кӯшиши 3: ИНТИХОБ, ЛИМИТ, ОФСЕТ

Дар мақолаи боло тавсия дода шудааст, ки ба сатр ба сатр нигоҳ карда, маълумотҳои мушкилотро нест кунед. Аввал ба мо лозим буд, ки ҳамаи сатрҳоро бубинем:

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

Дар ҳолати ман, ҷадвал мавҷуд аст 1 628 991 хатҳо! Нигохубини хуб кардан лозим буд тақсимоти маълумот, вале ин мавзуъ барои мухокимаи алохида аст. Рӯзи шанбе буд, ман ин фармонро дар tmux иҷро кардам ва ба хоб рафтам:

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

Субҳ ман қарор додам, ки тафтиш кунам, ки кор чӣ гуна аст. Тааҷҷубовар буд, ки ман фаҳмидам, ки пас аз 20 соат танҳо 2% маълумот скан карда шудааст! Ман 50 рӯз интизор шудан намехостам. Боз як нокомии комил.

Аммо ман таслим нашудам. Ман ҳайрон будам, ки чаро сканер ин қадар тӯл кашид. Аз ҳуҷҷатҳо (боз дар postgrespro) ман фаҳмидам:

OFFSET муайян мекунад, ки пеш аз оғози сатрҳои баромад гузаштан аз шумораи муайяни сатрҳо.
Агар ҳам OFFSET ва ҳам LIMIT муайян карда шуда бошанд, система аввал сатрҳои OFFSET-ро мегузаронад ва сипас ба ҳисобкунии сатрҳо барои маҳдудияти LIMIT оғоз мекунад.

Ҳангоми истифодаи LIMIT, муҳим аст, ки банди ORDER BY низ истифода шавад, то сатрҳои натиҷа бо тартиби мушаххас баргардонида шаванд. Дар акси ҳол, зермаҷмӯаҳои пешгӯинашавандаи сатрҳо баргардонида мешаванд.

Аён аст, ки фармони боло нодуруст буд: аввалан, набуд фармоиш аз ҷониби, натиҷа метавонад хато бошад. Дуюм, Postgres аввал бояд сатрҳои OFFSET-ро скан ва гузаред ва бо афзоиши он OFFSET хосилнокии мехнат боз хам паст мешуд.

Кӯшиши 4: партовро дар шакли матн гиред

Пас аз он як идеяи ба назар олиҷаноб ба сарам омад: як партовро дар шакли матн гиред ва хати охирини сабтшударо таҳлил кунед.

Аммо аввал, биёед ба сохтори ҷадвал назар андозем. ws_log_smevlog:

Таҷрибаи аввалини ман дар барқарор кардани пойгоҳи додаҳои Postgres пас аз нокомӣ (саҳифаи беэътибор дар блоки 4123007 базаи relatton/16490)

Дар мавриди мо сутун дорем "ID", ки идентификатори ягонаи (ҳисобкунаки) сатрро дар бар мегирад. Нақша чунин буд:

  1. Мо ба гирифтани партов дар шакли матн шурӯъ мекунем (дар шакли фармонҳои sql)
  2. Дар лаҳзаи муайяни вақт, партов бо сабаби хато қатъ карда мешавад, аммо файли матнӣ дар диск нигоҳ дошта мешавад.
  3. Мо ба охири файли матнӣ менигарем ва ба ин васила идентификатори (id) сатри охиринро, ки бомуваффақият хориҷ карда шудааст, пайдо мекунем.

Ман ба гирифтани партов дар шакли матн шурӯъ кардам:

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

Партов, тавре интизор мерафт, бо ҳамон хато қатъ карда шуд:

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

Минбаъд ба воситаи думи Ман ба охири партовгоҳ нигоҳ кардам (дум -5 ./my_dump.dump) муайян кард, ки партов дар хатти бо идо-ра катъ гардидааст 186 525. "Пас, мушкилот дар хати id 186 526 аст, он шикастааст ва бояд нест карда шавад!" — фикр кардам. Аммо, як дархост ба базаи маълумот:
«интихоб кунед * аз ws_log_smevlog, ки дар он id = 186529“Маълум шуд, ки бо ин хат ҳама чиз хуб будааст... Қаторҳои индекси 186 - 530 низ бе мушкилот кор мекарданд. Боз як «идеяи оличаноб» барбод рафт. Баъдтар ман фаҳмидам, ки чаро ин тавр шуд: ҳангоми нест кардан ва тағир додани маълумот аз ҷадвал, онҳо аз ҷиҳати ҷисмонӣ нест карда намешаванд, балки ҳамчун "кортаҳои мурда" қайд карда мешаванд. автовакуум ва ин сатрҳоро ҳамчун ҳазфшуда қайд мекунад ва имкон медиҳад, ки ин сатрҳо дубора истифода шаванд. Барои фаҳмидани он, ки агар маълумот дар ҷадвал тағир ёбад ва автовакуум фаъол бошад, он гоҳ пайдарпай нигоҳ дошта намешавад.

Кӯшиши 5: SELECT, FROM, КУҶО id=

Нокомиҳо моро қавитар мекунанд. Шумо набояд ҳеҷ гоҳ таслим шавед, шумо бояд то ба охир биравед ва ба худ ва имкониятҳои худ бовар кунед. Аз ин рӯ, ман қарор додам, ки як варианти дигарро санҷам: танҳо ҳамаи сабтҳои пойгоҳи додаҳоро як-як аз назар гузаронам. Бо донистани сохтори ҷадвали ман (ба боло нигаред), мо майдони id дорем, ки беназир аст (калиди аввалия). Мо дар ҷадвал 1 сатр дорем ва id ба тартиб оварда шудаанд, ин маънои онро дорад, ки мо метавонем онҳоро як-як аз сар гузаронем:

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

Агар касе нафаҳмад, фармон чунин кор мекунад: он ҷадвалро сатр ба сатр скан мекунад ва ба stdout мефиристад. / dev / null, аммо агар фармони SELECT ноком шавад, пас матни хато чоп карда мешавад (stderr ба консол фиристода мешавад) ва сатри дорои хато чоп карда мешавад (ба шарофати ||, ки маънои онро дорад, ки интихоб мушкилот дошт (коди баргардонидани фармон). 0 нест)).

Ман бахти ман буд, ман индексҳои дар саҳро офарида id:

Таҷрибаи аввалини ман дар барқарор кардани пойгоҳи додаҳои Postgres пас аз нокомӣ (саҳифаи беэътибор дар блоки 4123007 базаи relatton/16490)

Ин маънои онро дорад, ки ёфтани сатр бо ID-и дилхоҳ набояд вақти зиёдро талаб кунад. Дар назария он бояд кор кунад. Хуб, биёед фармонро иҷро кунем tmux ва биёед ба хоб равем.

То субҳ ман фаҳмидам, ки тақрибан 90 сабтҳо дида шудаанд, ки каме бештар аз 000% аст. Натиҷаи аъло дар муқоиса бо усули қаблӣ (5%)! Аммо ман намехостам 2 рӯз интизор шавам...

Кӯшиши 6: SELECT, FROM, WHERE id >= ва id

Мизоҷ сервери аълои ба пойгоҳи додаҳо бахшидашуда дошт: протсессори дугона Intel Xeon E5-2697 v2, дар макони мо то 48 ришта мавҷуд буд! Сарборӣ дар сервер миёна буд; мо метавонем тақрибан 20 риштаро бидуни мушкилот зеркашӣ кунем. Инчунин RAM кофӣ буд: то 384 гигабайт!

Аз ин рӯ, фармон бояд параллелизатсия карда шавад:

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

Дар ин ҷо метавон як скрипти зебо ва шево навишт, аммо ман зудтарин усули параллелизатсияро интихоб кардам: диапазони 0-1628991-ро ба фосилаи 100 сабтҳо тақсим кунед ва 000 фармони формаро алоҳида иҷро кунед:

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

Аммо ин ҳама нест. Дар назария, пайвастшавӣ ба пойгоҳи додаҳо низ каме вақт ва захираҳои системаро талаб мекунад. Пайваст кардани 1 чандон оқилона набуд, шумо розӣ мешавед. Аз ин рӯ, биёед ба ҷои як пайвастшавӣ 628 сатрро гирем. Дар натиҷа, даста ба ин табдил ёфт:

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

Дар сессияи tmux 16 тирезаро кушоед ва фармонҳоро иҷро кунед:

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

Пас аз як рӯз ман натиҷаҳои аввалро гирифтам! Яъне (қиматҳои XXX ва 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

Ин маънои онро дорад, ки се сатр хато дорад. Идентификатсияҳои сабтҳои мушкилоти якум ва дуюм аз 829 000 то 830 000, идентификаторҳои сеюм аз 146 000 то 147 000 буданд. Баъдан, мо танҳо бояд арзиши дақиқи id-и сабтҳои мушкилотро пайдо кунем. Барои ин, мо диапазони худро бо сабтҳои мушкилот бо қадами 1 дида мебароем ва 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

Анҷоми хуш

Мо хатҳои мушкилотро пайдо кардем. Мо тавассути psql ба пойгоҳи додаҳо ворид мешавем ва кӯшиш мекунем, ки онҳоро нест кунем:

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

Тааҷҷубовар аст, ки сабтҳо бе ягон мушкилот ҳатто бидуни интихоб нест карда шуданд саҳифаҳои_зарарнок.

Пас аз он ман ба базаи маълумот пайваст шудам, кард ВАКУУМ ПУР (Ман фикр мекунам, ки ин кор лозим набуд) ва дар ниҳоят ман бомуваффақият нусхабардориро бо истифода аз хориҷ кардам pg_dump. Партов бе ягон хато гирифта шуд! Масъ-аларо хамин тавр беаклона хал карданд. Хурсандӣ ҳадду канор надошт, пас аз ин қадар нокомиҳо мо тавонистем роҳи ҳалли худро пайдо кунем!

Эътибор ва хулоса

Ин аст, ки таҷрибаи аввалини ман дар барқарор кардани пойгоҳи додаҳои воқеии Postgres. Ман ин таҷрибаро муддати тӯлонӣ дар хотир дорам.

Ва дар охир, ман мехоҳам ташаккур ба PostgresPro барои тарҷумаи ҳуҷҷатҳо ба забони русӣ ва барои курсҳои онлайни комилан ройгон, ки дар вакти тахлили масъала ёрии калон расонд.

Манбаъ: will.com

Илова Эзоҳ