„mysqldump: Got error: 145 … when using LOCK TABLES”: jedna uszkodzona tabela MyISAM i tydzień pustych kopii

Skrypt z crona przez 7 nocy zapisywał zamiast ok. 280 MB plik 751 B: jedna tabela MyISAM była oznaczona jako uszkodzona, mysqldump przerywał na LOCK TABLES, skrypt kończył się kodem 0, a cron wyrzucał komunikat. Odtworzyliśmy awarię, naprawiliśmy tabelę, przenieśliśmy bazy na InnoDB i wdrożyliśmy kopię, która co noc próbnie się przywraca i alarmuje.

  • Plik kopii: 751 B zamiast ok. 280 MB, skrypt z kodem 0
  • mysqldump z kodem 2, komunikat wyrzucony przez cron (No MTA installed)
  • Nowa kopia: próbne przywrócenie 48 tabel w 32 do 34 s co noc
Lista dziewięciu plików kopii z tą samą godziną 01:30. Dwa górne wiersze mają długi czarny pasek i rozmiar 285 MB, siedem kolejnych tylko krótki pomarańczowy znacznik i rozmiar 751 B

Serwer Docker z MySQL 5.7.44 i kilkoma bazami WordPress, w tym sklep WooCommerce

Branża: Handel

Wyzwanie

Na serwerze Docker z MySQL 5.7.44 działało kilka stron WordPress, w tym sklep WooCommerce, z tabelami w starym silniku MyISAM. Skrypt bash na hoście co noc zapisywał każdą bazę do osobnego pliku <baza>.sql, bez daty w nazwie. Klient sprawdzał, czy kopie się robią. Tak było przez trzy lata.

Potem trzeba było przywrócić bazę sklepu. Kopie z 7 ostatnich nocy miały po 751 bajtów zamiast 200 do 300 MB: nagłówek, CREATE DATABASE, USE i koniec. Dopiero kopia sprzed 8 dni była pełna. Strona i panel działały przez cały czas, skrypt i cron nie zgłosiły żadnego błędu.

Zadanie: ustalić, dlaczego kopie były puste, choć wszystko wyglądało na sprawne, usunąć przyczynę i zbudować kopię, która sama udowadnia, że da się z niej wrócić.

Rozwiązanie

Sam plik 751 B powiedział najwięcej: zrzut przerwał się przed pierwszą tabelą, a nie zapisał pustej bazy. Odtworzyliśmy serwer klienta z tą samą wersją bazy i tym samym rodzajem skryptu, a awarię wywołaliśmy tak, jak zdarza się naprawdę: pełny dysk bazy w trakcie ruchu w sklepie, potem restart kontenera. Wynik: plik o budowie identycznej z plikiem klienta, co do bajtu.

Naprawiliśmy uszkodzoną tabelę i sprawdziliśmy wszystkie pozostałe, przenieśliśmy bazy na InnoDB i napisaliśmy kopię od nowa: kod wyjścia, znacznik końca zrzutu, rozmiar, a potem próbne przywrócenie każdej bazy do osobnej instancji MySQL 5.7.44 z porównaniem tabel i wierszy. Dopiero sprawdzona kopia dostaje nazwę z datą, każdy błąd to e-mail. Do tego plan odtworzenia po awarii, przećwiczony na czystym serwerze (stan na 2026-11).

Wyniki

Kopia sklepu przy uszkodzonej tabeli
751 B przez 7 nocy z kodem 0, teraz alarm e-mail pierwszej nocy z kodem 1
Ostatnia dobra kopia
14 dni historii, żadna kopia nie jest nadpisywana
Próbne przywrócenie bazy sklepu
48 tabel i 3,2 mln wierszy w 32 do 34 s, co noc
Cała kopia z próbnym przywróceniem
65 s dla trzech baz razem z instancją testową
Naprawa uszkodzonej tabeli
0,47 s REPAIR TABLE, 2,1 s kontrola wszystkich tabel
Odtworzenie sklepu na czystym serwerze
50 s od pustej instancji, ćwiczenie wykazało 3 braki w kopiach

Jak pracowaliśmy

  1. Plik 751 B linia po linii

    Znacznik sandbox klienta MariaDB, brak komentarzy, brak stopki, koniec po USE. Zrzut przerwał się przed pierwszą tabelą, baza nie była pusta.

  2. Log serwera

    Około 70 linii „marked as crashed” i „Checking table:” w jednej sekundzie po restarcie: komunikaty MyISAM i włączone automatyczne sprawdzanie tabel.

  3. Odtworzenie środowiska

    MySQL 5.7.44 w Dockerze z tabelami MyISAM, trzy strony WordPress, sklep WooCommerce z 8 000 produktów i 32 400 zamówieniami, ten sam rodzaj skryptu z crona na hoście z klientem MariaDB z Debiana 12.

  4. Prawdziwa awaria, bez edycji plików

    Pełny wolumen bazy w trakcie ruchu w sklepie, potem docker stop. Trzy przebiegi tego samego scenariusza, w dwóch trwałe uszkodzenie tabeli.

  5. Kopia i strona przy uszkodzonej tabeli

    Stary skrypt z crona, trzy warianty mysqldump, ekrany sklepu i panelu, koszyk gościa przez Store API WooCommerce.

  6. Próba przywrócenia

    Plik 743 B i pełny zrzut do świeżego MySQL 5.7.44 klientami mysql 5.7.44, 8.0.46, 8.4.13 i mariadb 10.11, zrzut ze starszego mariadb-dump 10.11.8, klient z Debiana 13.

  7. Naprawa, nowa kopia i ćwiczenie

    REPAIR TABLE i kontrola wszystkich tabel, konwersja na InnoDB, nowy skrypt na zdrowej i na uszkodzonej bazie, odtworzenie całego sklepu na czystym serwerze z pomiarem czasu.

Co zapisuje mysqldump, gdy jedna z 48 tabel jest uszkodzona (odtworzenie, stan na 2026-11)

Wariant Kod wyjścia Plik Tabele w pliku Koniec pliku
domyślny, --lock-tables 2 743 B 0 z 48 po USE, bez stopki
--force 2 210 083 890 B 47 z 48 normalna stopka
--single-transaction 2 210 078 388 B 42 z 48, przerwany na tabeli sesji urwany w połowie
domyślny po REPAIR TABLE 0 285 934 637 B 48 z 48 normalna stopka

Wyzwania, które pokonaliśmy

Strona i panel działały, skrypt i cron nie zgłaszały błędu. Poniżej komunikaty tak, jak je zapisały MySQL, mysqldump, WordPress i cron. W materiałach klienta nazwa bazy jest zastąpiona <baza>, w odtworzeniu baza sklepu nazywa się sklep.

Dokładne komunikaty

Plik kopii klienta, 751 B, cała zawartość:

/*M!999999\- enable the sandbox mode */

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

CREATE DATABASE /*!32312 IF NOT EXISTS*/ `<baza>` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `<baza>`;

Pierwsza linia to znacznik trybu sandbox, który dopisuje tylko klient MariaDB, więc zrzut robił mysqldump z pakietu mariadb-client na hoście (na Debianie 12 to dowiązanie do mariadb-dump), a nie narzędzie z kontenera MySQL. Brak komentarzy oznacza --skip-comments, które usuwa też końcowy znacznik -- Dump completed on …. Brak stopki oznacza, że zrzut się przerwał: pusta baza daje plik ze stopką (w odtworzeniu 1 173 B).

Log serwera klienta po restarcie, około 70 linii w tej samej sekundzie:

[ERROR] mysqld: Table './<baza>/wp_options' is marked as crashed and should be repaired
[ERROR] mysqld: Table './<baza>/wp_postmeta' is marked as crashed and should be repaired
[Warning] Checking table:   './<baza>/wp_postmeta'
[ERROR] mysqld: Table './<baza>/wp_woocommerce_sessions' is marked as crashed and should be repaired
[Warning] Checking table:   './<baza>/wp_woocommerce_sessions'

Checking table: pojawia się tylko przy włączonym myisam-recover-options. W obrazie mysql:5.7.44 domyślnie jest OFF, więc serwer klienta miał to w konfiguracji.

Odtworzenie: pełny dysk. MyISAM nie zwraca błędu, tylko czeka na miejsce i trzyma blokadę tabeli, a strona główna dalej odpowiada:

[ERROR] Disk is full writing './sklep/wp_woocommerce_sessions.MYD' (Errcode: 15795968 - No space left on device). Waiting for someone to free space...
[ERROR] Retry in 60 secs. Message reprinted in 600 secs

Restart kontenera bazy, czyli typowa reakcja. Docker daje procesowi 10 s, potem SIGKILL:

$ docker stop db        # 10,5 s, ExitCode 137
[Note] Giving 7 client threads a chance to die gracefully
[Warning] mysqld: Forcing close of thread 31  user: 'sklep'
[ERROR] mysqld: Incorrect key file for table './sklep/wp_woocommerce_sessions.MYI'; try to repair it
[ERROR] Got an error from thread_id=31, .../storage/myisam/mi_write.c:232

Sprzątanie sesji WooCommerce kilka godzin później:

WordPress database error Incorrect key file for table './sklep/wp_woocommerce_sessions.MYI'; try to repair it for query DELETE FROM wp_woocommerce_sessions WHERE session_expiry < 1793638328 ORDER BY session_expiry LIMIT 100 made by ... wc_cleanup_session_data, WC_Session_Handler->cleanup_sessions

Od tej chwili każde ponowne otwarcie tabeli kończy się:

ERROR 145 (HY000): Table './sklep/wp_woocommerce_sessions' is marked as crashed and should be repaired

Kopia z crona przy uszkodzonej tabeli:

Kopia bazy blog
Kopia bazy firma
Kopia bazy sklep
mysqldump: Got error: 145: "Table './sklep/wp_woocommerce_sessions' is marked as crashed and should be repaired" when using LOCK TABLES
Kopia zakonczona: Mon Nov  2 16:53:55 UTC 2026
kod wyjscia skryptu: 0
CRON[107]: (root) CMD ([108] /root/skrypty/backup-mysql.sh)
CRON[107]: (CRON) info (No MTA installed, discarding output)
CRON[107]: (root) END ([108] /root/skrypty/backup-mysql.sh)

Plik sklepu skurczył się z 285 757 704 B do 743 B, z bieżącą datą. Po podstawieniu nazwy bazy o długości jak u klienta wychodzi dokładnie 751 B.

Uszkodzenie działa jak zapalnik z opóźnieniem

Po restarcie wszystko działało, a kopia 285 MB się udała. Tabela dostała flagę dopiero, gdy zadanie sprzątania sesji WooCommerce (co 12 godzin) trafiło na niespójny indeks, a otwarcie zablokowało się dopiero po ponownym otwarciu tabeli. Kopie zaczynają padać godziny albo dni po awarii, gdy nikt już jej z kopiami nie łączy. Samo zabicie procesu bazy nie wystarczyło do trwałego uszkodzenia; zrobił to dopiero pełny dysk z docker stop, a trzeci przebieg tego samego scenariusza skończył się tylko ostrzeżeniem. To, czy restart popsuje tabelę, zależy od chwili.

Naprawa tego, co widać, zostawia resztę

W drugim przebiegu padła też wp_options i sklep pokazał „Error establishing a database connection”. Narzędzie WordPressa wp-admin/maint/repair.php sprawdza tylko 12 tabel rdzenia i to, co wtyczki dopiszą filtrem tables_to_repair; WooCommerce nic nie dopisuje. Strona wróciła, a uszkodzona tabela sesji została. Do tego narzędzie zgłosiło „Failed to repair the wp_options table”, choć tabela była naprawiona: czyta tylko pierwszy wiersz wyniku REPAIR TABLE, a status OK jest w ostatnim. Także mysqlcheck --all-databases --check wypisał Corrupt i zakończył się kodem 0, więc jako test w monitoringu nic by nie dał.

Jedna tabela zatrzymuje całą bazę, a „poprawki” dają gorsze pliki

Przy domyślnym --lock-tables zrzut zaczyna od jednego LOCK TABLES na wszystkie tabele bazy, więc jedna tabela z flagą zatrzymuje wszystkie. Plik 743 B rzuca się w oczy, jeśli ktoś patrzy na rozmiar. --force daje plik 210 MB bez jednej tabeli i z normalnym zakończeniem, --single-transaction plik 210 MB urwany w połowie listy tabel. Oba wyglądają jak kopia. Częściowy plik nowego skryptu miał 82 % rozmiaru poprzedniej kopii, czyli przeszedłby popularny próg „alarm poniżej 80 %”.

Data pliku niczego nie dowodzi, a nazwa bez daty niszczy ostatnią dobrą kopię

Powłoka obcina plik > <baza>.sql, zanim mysqldump się połączy. Plik ma świeżą datę każdej nocy, a pierwsza nieudana noc zastępuje ostatnią dobrą kopię. Pełną wersję sprzed 8 dni trzeba było wziąć z innej kopii serwera.

Skrypt i cron raportują sukces

mysqldump zwraca 2, skrypt 0, bo ostatnie polecenie to echo. Komunikat idzie na stderr, cron próbuje wysłać go pocztą, na hoście nie ma serwera pocztowego, więc go wyrzuca i loguje tylko END, bez kodu wyjścia.

Strona i panel nie pokazują nic

Przy uszkodzonej tabeli sesji strona główna, sklep, karta produktu, wyszukiwarka, logowanie i wszystkie sprawdzone ekrany panelu odpowiadały 200 bez komunikatu. Objaw był jeden: koszyk gościa po dodaniu produktu miał 0 pozycji. Jedyny ślad w panelu to WooCommerce, Status, Baza danych, który uszkodzoną tabelę nazywa brakującą: „Brakujące tabele bazy danych: wp_woocommerce_sessions” (po angielsku „Missing base tables”).

Przywrócenie pustej kopii „się udaje”, a kopie potrafią przestać działać bez awarii

Wczytanie pliku 743 B kończy się kodem 0 i pustą bazą w każdym sprawdzonym kliencie: mysql 5.7.44, 8.0.46, 8.4.13 i mariadb 10.11. Znacznik sandbox z pliku klienta (/*M!) klientom MySQL nie przeszkadza, ale zrzuty ze starszego mariadb-dump 10.11.8 zaczynają się od /*! i nie wczytują nic:

ERROR at line 1: Unknown command '\-'.

A po aktualizacji hosta do Debiana 13 (mariadb-client 11.8.6) ten sam skrypt pada już na liście baz, bo nowsze klienty MariaDB domyślnie weryfikują certyfikat serwera, a MySQL 5.7 ma samopodpisany. Zero plików, kod 0, „Kopia zakonczona”:

ERROR 2026 (HY000): TLS/SSL error: Certificate verification failure: The certificate is NOT trusted.
Łańcuch pięciu ogniw połączonych pomarańczowymi łącznikami: dysk z napisem 100 %, plik indeksu z napisem .MYI i pęknięciem, flaga z napisem crashed, kłódka z napisem LOCK TABLES 145 i krótki plik z napisami 751 B i exit 0

Naprawa i kopia, która sama sprawdza, czy da się z niej wrócić

Naprawa i kontrola wszystkich tabel

mysqlcheck -h db -uroot -p --all-databases --check --silent     # 2,1 s, wypisuje tylko problemy
REPAIR TABLE sklep.wp_woocommerce_sessions;
-- info     Found block that points outside data file at 70053148
-- warning  Number of rows changed from 74527 to 74695
-- status   OK                                          (0,47 s)
CHECK TABLE sklep.wp_woocommerce_sessions;               -- status OK

Po naprawie stary skrypt znów zapisał 285 934 637 B, a koszyk gościa miał 1 pozycję. Wynik mysqlcheck trzeba czytać, nie kod wyjścia.

InnoDB zamiast MyISAM

InnoDB po nagłym wyłączeniu odtwarza spójny stan z dziennika i pozwala robić spójny zrzut bez blokowania tabel (--single-transaction). Ten sam scenariusz pełnego dysku po konwersji: InnoDB nie czeka, tylko od razu odrzuca zapis.

[ERROR] InnoDB: posix_fallocate(): Failed to preallocate data for file ./sklep/wp_woocommerce_sessions.ibd, desired size 4194304 bytes. Operating system error number 28. Check that the disk is not full or a disk quota exceeded. ...
[ERROR] mysqld: The table 'wp_woocommerce_sessions' is full

W czasie pełnego dysku 2 752 z 4 777 żądań (58 %) skończyło się kodem 500. Za to docker stop trwał 4,1 s i zakończył się czysto, a po zwolnieniu miejsca mysqlcheck nie zgłosił żadnego problemu. Widoczny błąd na kilka minut zamiast niewidocznego na tydzień.

Pułapka przy konwersji: domyślny sql_mode MySQL 5.7 zawiera NO_ZERO_DATE, NO_ZERO_IN_DATE i STRICT_TRANS_TABLES, a kolumny WordPressa mają DEFAULT '0000-00-00 00:00:00'. Zwykłe ALTER TABLE padło na 19 z 72 tabel:

ERROR 1067 (42000) at line 1: Invalid default value for 'post_date'

WordPress ustawia sobie łagodniejszy tryb w sesji, dlatego na co dzień tego nie widać. Konwersja w oknie serwisowym, bo ALTER TABLE kopiuje tabelę i blokuje zapisy:

SET SESSION sql_mode = 'NO_ENGINE_SUBSTITUTION';
ALTER TABLE sklep.wp_posts ENGINE=InnoDB;   -- i tak dla każdej tabeli MyISAM
BazaCzas konwersjiDane i indeksy przed i po
sklep, 48 tabelok. 95 s393,5 MB i 627,9 MB
blog3,6 s11,3 MB i 15,4 MB
firma2,7 s0,5 MB i 1,2 MB

W konfiguracji default-storage-engine wraca na InnoDB, żeby nowe tabele wtyczek nie powstawały jako MyISAM, a innodb_buffer_pool_size dobieramy do pamięci serwera, bo baza sklepu po konwersji jest większa niż domyślne 128 MB.

Nowa kopia: najważniejsze fragmenty skryptu

set -uo pipefail
# zrzut narzędziem z kontenera bazy: ta sama wersja co serwer, bez niespodzianek klienta MariaDB
docker exec -e MYSQL_PWD="$DB_PASS" "$DB_CONTAINER" mysqldump -uroot --single-transaction --quick \
    --routines --triggers --events --default-character-set=utf8mb4 --databases "$db" 2> "$dir/.stderr" | gzip -6 > "$part"
rc=("${PIPESTATUS[@]}")
[ "${rc[0]}" = 0 ] && [ "${rc[1]}" = 0 ] || fail "$db" "mysqldump kod ${rc[0]}, gzip kod ${rc[1]}"
# kompletność: mysqldump dopisuje ten znacznik tylko po udanym końcu (dlatego bez --skip-comments)
zcat "$part" | tail -n 1 | grep -q '^-- Dump completed' || fail "$db" "brak znacznika '-- Dump completed'"
# rozmiar względem poprzedniej udanej kopii
[ $(( size * 100 / psize )) -ge 80 ] || fail "$db" "kopia ma $ratio% rozmiaru poprzedniej"
# próbne przywrócenie do świeżej instancji mysql:5.7.44 i porównanie z produkcją
zcat "$part" | docker exec -i -e MYSQL_PWD=test "$TEST_CONTAINER" mysql -uroot || fail "$db" "przywrócenie nie powiodło się"
#   lista tabel identyczna, liczba wierszy: tabele > 1000 wierszy co najmniej 90%, mniejsze ±5, wp_options/siteurl czytelne
# dopiero teraz nazwa docelowa z datą; poprzednie kopie nigdy nie są nadpisywane
mv "$part" "$dir/$db-$STAMP.sql.gz"

Do tego eksport kont i uprawnień (SHOW CREATE USER, SHOW GRANTS), bo baza mysql nie wchodzi do zrzutów aplikacji; rotacja 14 dni; przy każdym błędzie e-mail przez SMTP bez lokalnego serwera pocztowego i kod wyjścia 1; po sukcesie sygnał do zewnętrznego monitoringu, który alarmuje, gdy kopia w ogóle się nie uruchomi.

BazaZrzutPlik .sql.gzPróbne przywrócenie
sklep4 do 5 s33,4 MB48 tabel, 3 175 139 wierszy, 32 do 34 s
blogdo 1 s1,9 MB12 tabel, 12 067 wierszy, 2 s
firmaponiżej 1 s88 kB12 tabel, 361 wierszy, 1 s

Cały przebieg trzech baz razem z uruchomieniem instancji testowej: 65 s. Ten sam skrypt na kopii bazy z uszkodzoną tabelą MyISAM:

=== sklep 18:26:33
UWAGA: sklep ma 48 tabel innych niz InnoDB (zrzut bez spojnosci transakcyjnej dla nich)
mysqldump: Couldn't execute 'show create table `wp_woocommerce_sessions`': Table './sklep/wp_woocommerce_sessions' is marked as crashed and should be repaired (145)
BLAD [sklep]: mysqldump kod 2, gzip kod 0: ...
KONIEC Z BLEDAMI: 1

E-mail z treścią błędu dotarł w tej samej minucie. Dwie poprzednie kopie sklepu zostały nietknięte, a częściowy plik został jako .part do diagnozy.

Tor od bazy danych przez cztery bramki z haczykami podpisane exit 0, Dump completed, 80 % i restore, bramka restore pomarańczowa, na końcu plik z datą; od bramek odchodzi w dół linia do koperty alarmu

Plan odtworzenia po awarii i test całego sklepu

Plan mieści się na jednej stronie i leży poza serwerem:

  • Cele: ile danych wolno stracić (przy kopii nocnej do 24 godzin) i jak długo sklep może nie działać.
  • Co jest w kopii: bazy, konta i uprawnienia bazy, pliki stron (wp-content, wp-config.php), pliki Compose i konfiguracja usług, wersje obrazów. Kopia bazy to nie kopia systemu.
  • Gdzie: co najmniej jedna kopia poza serwerem i poza kontem, z którego da się ją skasować.
  • Procedura krok po kroku, z hasłami w osobnym, znanym miejscu, i osoba odpowiedzialna.
  • Test: co noc automatyczne próbne przywrócenie baz, raz na kwartał odtworzenie całości na czystej maszynie z pomiarem czasu.

Test całości: pusta instancja MySQL 5.7.44, najnowsze kopie trzech baz, konta, archiwum plików sklepu, świeży kontener WordPress.

KrokCzas od startu
pusty MySQL 5.7.44 gotowy12,7 s
import bazy sklepu (31,7 MB gz)44,8 s
import baz firmy i bloga, konta i uprawnienia47,9 s
pliki sklepu rozpakowane (204 MB)48,7 s
sklep odpowiada 200, Store API zwraca 8 000 produktów50,2 s

Co ćwiczenie wykazało, każde zjawisko sprawdzone osobno:

  • Bez kont bazy sklep nie wstaje: „Error establishing a database connection”, HTTP 500. Zrzuty --databases nie zawierają kont z bazy mysql, stąd osobny eksport kont w nowym skrypcie.
  • Pod nowym adresem WordPress przekierowuje (301) na adres z siteurl. Do testu potrzebny jest nagłówek Host jak na produkcji albo wpis w hosts.
  • Wtyczki montowane w Compose spoza katalogu strony nie trafiły do archiwum plików. Odtworzony sklep działał, ale bez dwóch wtyczek must-use z logiką sklepu, i nic nie zgłosiło błędu. Kopia plików musi obejmować wszystkie ścieżki z volumes: w pliku Compose.
Pięć pasków na osi czasu od 0 do 50 s, jeden na krok odtworzenia: mysqld do 12,7 s, import do 44,8 s, GRANT do 47,9 s, wp-content do 48,7 s i pomarańczowy pasek HTTP 200 do 50,2 s

Co z tego wynika

  • Kopia, z której nikt nie wrócił, jest tylko plikiem. Plik powstawał co noc przez trzy lata, a jego treść przestała się zgadzać bez żadnego sygnału.
  • Kod wyjścia, znacznik końca zrzutu i rozmiar to minimum, ale nie wystarczają. Częściowy zrzut przeszedłby próg rozmiaru. Rozstrzyga dopiero automatyczne wczytanie kopii do osobnej bazy i porównanie z produkcją.
  • Awaria psuje kopie z opóźnieniem. Po każdym incydencie z dyskiem albo zasilaniem sprawdzamy wszystkie tabele, nie tylko te, które zepsuły stronę.
  • Plan odtworzenia ma sens dopiero po pierwszym ćwiczeniu na czystej maszynie. To ono pokazuje, czego w kopiach brakuje. Takie znaleziska są typowe przy przejęciu i utrzymaniu systemu.

Pracę na samej bazie sklepu, od planu zapytania po indeksy, opisuje projekt indeksów SQL, a zmianę przygotowaną i przeprowadzoną na serwerze produkcyjnym projekt ColdFusion 2025.

Nie mają Państwo pewności, czy z kopii bazy da się wrócić? Sprawdzimy to próbnym przywróceniem na osobnym systemie, bez zmian w produkcyjnej bazie, i pokażemy, czego w kopiach brakuje. Pierwszy krok to rozmowa wstępna.

Źródła

Wersje i wartości domyślne sprawdziliśmy w odtworzeniu; opis opcji pochodzi z dokumentacji producentów (stan: 2026-11):

Przed i po. Po lewej krótki plik z napisami 751 B i exit 0 oraz przekreślona koperta. Po prawej plik z datą, haczyk z napisem restore 48/48 i pomarańczowa koperta alarmu

Pierwszy krok: przegląd bazy danych z testem odtworzenia

Przegląd bazy danych: MySQL i MariaDB

Dla firm, których aplikacja zależy od bazy danych MySQL lub MariaDB i które chcą wiedzieć, co się stanie przy awarii i czy dane z wczoraj naprawdę da się odtworzyć.

  • Kopie zapasowe: co jest zabezpieczane, jak często i gdzie leżą kopie
  • Test odtworzenia na osobnym systemie, ze zmierzonym czasem
  • Replikacja i odporność na awarie: co jest, a czego brakuje
  • Najwolniejsze zapytania i brakujące indeksy
  • Wersja, stan wsparcia i konfiguracja
  • Rekomendacja: kopie zapasowe, replikacja lub klaster, zależnie od potrzeb

Klaster, taki jak MariaDB Galera, chroni przed awarią serwera, ale nie przed usuniętymi danymi. Do tego potrzebne są sprawdzone kopie zapasowe. Dlatego zaczynamy od testu odtworzenia. Na jakim systemie się odbędzie, ustalamy z Państwem. W produkcyjnej bazie danych niczego nie zmieniamy.

Co zawiera wynik

Pisemny raport z wynikiem testu odtworzenia, ryzykami według priorytetu i rekomendacją z nakładem pracy. Raport należy do Państwa. Na życzenie wdrażamy potem wybrane rozwiązanie, także klaster Galera.

od 1 900 € netto

Zwykle 2 dni robocze

Zamów przegląd bazy danych

Powiązane sytuacje

  • Państwa system działa, ale nikt nie zna go dokładnie. Spisujemy aplikację, bazę danych, serwer i pocztę, oceniamy ryzyka, na piśmie rekomendujemy dalszą drogę, a na życzenie przejmujemy utrzymanie systemu.

  • Przenosimy Państwa aplikację w Lucee, ColdFusion lub PHP z klasycznego hostingu do kontenerów. Środowisko można wtedy odtworzyć, zaktualizować i przenieść na inny serwer. Część dostawców hostingu pobiera dopłaty za stare wersje PHP, a dla PHP 8.2 poprawki bezpieczeństwa kończą się 31.12.2026 (stan: 2026-10).

  • Wsparcie Debiana 11 zakończyło się 31.08.2026, a CentOS 7 już 30.06.2024 (stan: 2026-10). Zaktualizujemy Państwa serwer albo zaplanujemy i wykonamy przejście na Debiana. Pracujemy również z Proxmox VE 8 i z serwerami fizycznymi, które przenosimy na Proxmox.

Inne projekty

  • Ramka Connector/J 5.1.44 z dwoma połączeniami: do serwera 5.5.5-10.11.19-MariaDB prosto z haczykiem, do serwera 11.8.8-MariaDB zatrzymane na pomarańczowej kłódce TLSv1.1 z krzyżykiem

    ColdFusion po aktualizacji MariaDB: Connector/J 5.1 i przejście na MariaDB Connector/J

    Po przejściu MariaDB z gałęzi 10.11 na nowszą ColdFusion z ręcznie dodanym MySQL Connector/J 5.1.44 przestał łączyć się z bazą: każde zapytanie kończyło się po 4 s błędem „Could not create connection to database server”. Winny był nowy nagłówek wersji serwera, przez który sterownik sam włączał TLS w wersji blokowanej przez Javę 21. Źródła danych przepiął jeden skrypt na MariaDB Connector/J.

  • Dwa panele: WordPress + Elementor i Craft CMS w pomarańczowej ramce, w każdym trzy paski JS, SQL i pliki PHP; paski Craft są krótsze, pasek JS prawie znika

    WordPress czy Craft CMS: te same trzy strony zbudowane w obu systemach i zmierzone

    Trzy strony firmowe, przebudowane wcześniej na WordPressie z darmowym Elementorem, zbudowaliśmy od zera w Craft CMS 5: ta sama treść, te same pliki WebP, wygląd 1:1, ten sam serwer. Craft wysyła 5 KB zamiast 97 do 129 KB JavaScriptu i robi mniej niż połowę zapytań SQL przy podobnym TTFB. Te same strony jako typowy WordPress z 9 do 21 wtyczkami miały Lighthouse mobile 30 do 72.

W 20 minut ustalimy, jaki pierwszy krok pasuje do Państwa systemu

Państwo opisują cel i obecny stan, my mówimy otwarcie, czy i jak możemy pomóc. Odpowiedź w ciągu jednego dnia roboczego, bez zobowiązań.

Telefon (niemiecki, polski, angielski) +48 501 250 220
E-mail E-mail

Dostępni: poniedziałek–piątek, 9:00–17:00 (czas środkowoeuropejski); wsparcie i awarie całą dobę (24/7)