ЁЯПл The SchoolтА║ЁЯЧДя╕П DatabasesтА║ЁЯзп рдзрдбрд╛ 09 тАФ Backups рдЖрдгрд┐ recovery: рдЖрдЧреАрдкрд╛рд╕реВрди рд╕реБрд░рдХреНрд╖рд┐рдд рдкреНрд░рдд
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯзп рдзрдбрд╛ 09 тАФ Backups рдЖрдгрд┐ recovery: рдЖрдЧреАрдкрд╛рд╕реВрди рд╕реБрд░рдХреНрд╖рд┐рдд рдкреНрд░рдд

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 18 рдкреИрдХреА рдзрдбрд╛ 09 ┬╖ рдорд╛рдЧреЗ: lesson-08-migrations ┬╖ рдкреБрдвреЗ: lesson-10-scaling


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ08, рдЖрдгрд┐ рдЗрддрд░ рд╕рд░реНрд╡ рд╡рдЪрдирд╛рдВрдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рдЯрд┐рдХрдгрд╛рд░реЗ рд╡рдЪрди: рджреБрд╕рд▒реНрдпрд╛ рдЗрдорд╛рд░рддреАрдд рдЦреЛрд▓реАрдЪреА рдПрдХ рдкреНрд░рдд, рдЖрдЧ рд▓рд╛рдЧрдгреНрдпрд╛рдЖрдзреА рд╢рд╛рд│рд╛ рдард░рд╡рддреЗ рддреЗ рджреЛрди рдЖрдХрдбреЗ (RPO, RTO), point-in-time recovery, рдЖрдгрд┐ рдХреЛрдгрд╛рд▓рд╛рдЪ рдирдХреЛ рдЕрд╕рд▓реЗрд▓реА рдкрдг рд╕рд░реНрд╡рд╛рдВрдирд╛ рд╣рд╡реА рдЕрд╕рд▓реЗрд▓реА restore рдЪреА рд░рдВрдЧреАрдд рддрд╛рд▓реАрдо.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдзрдбрд╛ 04 рдиреЗ рд╕рд┐рджреНрдз рдХреЗрд▓реЗ рдХреА transaction errors рдЦреЛрдбрддреЗ. рдЪреБрдХрд╛рдВрд╕рд╛рдареА рддреЗ рдХрд╛рд╣реАрдЪ рдХрд░рдд рдирд╛рд╣реА: WHERE рд╢рд┐рд╡рд╛рдпрдЪрд╛ DELETE FROM students рдЕрдЧрджреА рд╡реНрдпрд╡рд╕реНрдерд┐рдд commit рд╣реЛрддреЛ. рдЖрдЧ, рдкреВрд░, рдЪреЛрд░реАрд▓рд╛ рдЧреЗрд▓реЗрд▓реЗ laptops рдХрд┐рдВрд╡рд╛ рд░рд╡рд┐рд╡рд╛рд░реА рдмрдВрдж рдкрдбрдгрд╛рд░реА disk рдпрд╛рдВрдЪреНрдпрд╛рд╕рд╛рдареАрд╣реА рдирд╛рд╣реА.

рдореНрд╣рдгреВрди рджрдкреНрддрд░рджрд╛рд░ рджреБрд╕рд▒реНрдпрд╛ рдЗрдорд╛рд░рддреАрдд рдПрдХ рдЖрдЧреАрдкрд╛рд╕реВрди рд╕реБрд░рдХреНрд╖рд┐рдд рдкреНрд░рдд ЁЯзп рдареЗрд╡рддреЛ: рджрд░ рд░рд╛рддреНрд░реА рдкреНрд░рддреНрдпреЗрдХ рдиреЛрдВрджрд╡рд╣реАрдЪреА photocopy (рдПрдХ full backup), рдЖрдгрд┐ рджрд┐рд╡рд╕рднрд░ рд▓рд┐рд╣рд┐рд▓реНрдпрд╛ рдЬрд╛рдгрд╛рд▒реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ pencil-рдЖрдгрд┐-рд╢рд╛рдИрдЪреНрдпрд╛ рдУрд│реАрдЪреА рдкреНрд░рдд (write-ahead log). рджреЛрдиреНрд╣реА рдЕрд╕рд▓реНрдпрд╛рд╕ рд╢рд╛рд│рд╛ рдЦреЛрд▓реА рдХреЛрдгрддреНрдпрд╛рд╣реА рдорд┐рдирд┐рдЯрд╛рд▓рд╛ рд╣реЛрддреА рддрд╢реА рдкреБрдиреНрд╣рд╛ рдмрд╛рдВрдзреВ рд╢рдХрддреЗ тАФ point-in-time recovery.

рдХрд╛рд╣реАрд╣реА рдЬрд│рдгреНрдпрд╛рдЖрдзреА рджреЛрди рдЖрдХрдбреЗ рдард░рд╡рд▓реЗ рдЬрд╛рддрд╛рдд:

рдХрд╛рд░рдг рд░реЗрдХреЙрд░реНрдб рд░реВрдордЪрд╛ рд╕рд░реНрд╡рд╛рдд рдЬреБрдирд╛ рдирд┐рдпрдо рдЖрд╣реЗ: рдХреЛрдгреАрд╣реА restore рди рдХреЗрд▓реЗрд▓рд╛ backup рдореНрд╣рдгрдЬреЗ рдиреБрд╕рддреА рдЕрдлрд╡рд╛.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    room["ЁЯЧДя╕П school.db"]
    copy["ЁЯзп school.backup.db<br/>conn.backup() тАФ the fireproof copy"]
    boom["ЁЯТе DELETE тАж WHERE id = 5<br/>Rohan is gone (a mistake, not an error)"]
    restore["тЩ╗я╕П copy the backup back<br/>SELECT to prove it тАФ Rohan returns"]
    room -->|"1 nightly + the log all day"| copy
    room -->|"2"| boom -->|"3 restore, measured (RTO)"| restore
    n["RPO: how much may we lose ┬╖ RTO: how long may we be shut ┬╖ drill quarterly"]

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдЗрддрд░ рдкреНрд░рддреНрдпреЗрдХ рдзрдбрд╛ рдЦреЛрд▓реА рдЕрд╕реНрддрд┐рддреНрд╡рд╛рдд рдЖрд╣реЗ рдЕрд╕реЗ рдЧреГрд╣реАрдд рдзрд░рддреЛ. Durability (рдзрдбрд╛ 01) crashes рдкрд╛рд╕реВрди рд╡рд╛рдЪрд╡рддреЗ, rm, ransomware, рд╡рд╛рдИрдЯ migration рдХрд┐рдВрд╡рд╛ рдЪрд╛рдВрдЧрд▓реНрдпрд╛ рд╣реЗрддреВрдиреЗ рдХреЗрд▓реЗрд▓реНрдпрд╛ DELETE рдкрд╛рд╕реВрди рдирд╛рд╣реА. "рдЖрдордЪреНрдпрд╛рдХрдбреЗ backups рдЖрд╣реЗрдд" рдпрд╛рд▓рд╛ рдХрд╛рд╣реА рдЕрд░реНрде рдЖрд╣реЗ рдпрд╛рдЪрд╛ рдПрдХрдореЗрд╡ рдкреБрд░рд╛рд╡рд╛ рдореНрд╣рдгрдЬреЗ restore рдЪреА рд░рдВрдЧреАрдд рддрд╛рд▓реАрдо.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

db/demo.py рдордзреАрд▓ backup() conn.backup() рдиреЗ рдкреНрд░рдд рдмрдирд╡рддреЗ, Rohan рд▓рд╛ delete рдХрд░рддреЗ, рдордЧ backup рдкрд░рдд copy рдХрд░рддреЗ рдЖрдгрд┐ рддреЛ рдкрд░рдд рдЖрд▓рд╛ рд╣реЗ рд╕рд┐рджреНрдз рдХрд░рддреЗ. stopwatch рддреБрдореНрд╣реА рдЬреЛрдбрд╛рдпрдЪреЗ рдЖрд╣реЗ.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛ тАФ рд░рдВрдЧреАрдд рддрд╛рд▓реАрдо

python3 db/demo.py backup
python3 - <<'EOF'
import sqlite3, shutil, time, os
src = sqlite3.connect("db/school.db"); dst = sqlite3.connect("db/drill.db"); src.backup(dst); dst.close()   # 1) the copy
before = src.execute("SELECT COUNT(*) FROM grades").fetchone()[0]
src.execute("DELETE FROM grades"); src.commit()                                                              # 2) the mistake
t = time.perf_counter(); src.close(); shutil.copy("db/drill.db", "db/school.db")                             # 3) the restore
after = sqlite3.connect("db/school.db").execute("SELECT COUNT(*) FROM grades").fetchone()[0]
print(f"grades before {before}, after restore {after}, RTO {(time.perf_counter()-t)*1000:.1f} ms"); os.remove("db/drill.db")
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

backup рдЖрдзреА Rohan рд╢рд┐рд╡рд╛рдпрдЪреА рд╡рд┐рджреНрдпрд╛рд░реНрдереНрдпрд╛рдВрдЪреА рдпрд╛рджреА рдЫрд╛рдкрддреЛ, рдордЧ рдкреБрдиреНрд╣рд╛ рддреНрдпрд╛рдЪреНрдпрд╛рд╕рд╣. рддреБрдордЪреА рд░рдВрдЧреАрдд рддрд╛рд▓реАрдо grades before 10, after restore 10 рдЖрдгрд┐ milliseconds рдордзреНрдпреЗ рдПрдХ RTO рдЫрд╛рдкрддреЗ тАФ рддреБрдореНрд╣реА restore рдЧреГрд╣реАрдд рдзрд░рдгреНрдпрд╛рдРрд╡рдЬреА рддреЛ рдореЛрдЬрд▓рд╛.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

transaction рдЙрд▓рдЯрд╡реВ рд╢рдХрдд рдирд╛рд╣реА рдЕрд╢реА рдЪреВрдХ рддреБрдореНрд╣реА рдЪрд╛рдЪрдгреА рдШреЗрддрд▓реЗрд▓реНрдпрд╛ рдкреНрд░рддреАрдиреЗ рдЙрд▓рдЯрд╡рд▓реА тАФ рдЖрдгрд┐ рддреБрдореНрд╣рд╛рд▓рд╛ рддреБрдордЪрд╛ RPO (рдПрдХ рдкреНрд░рдд рдЖрдзреА) рдЖрдгрд┐ RTO (stopwatch) рдорд╛рд╣реАрдд рдЖрд╣реЗ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рдЖрдкрд▓реНрдпрд╛ database рдЪреНрдпрд╛ рд╕рд░реНрд╡рд╛рдд рд╡рд╛рдИрдЯ рджрд┐рд╡рд╕рд╛рддреВрди рд╡рд╛рдЪрд▓реЗрд▓реНрдпрд╛ companies рдореНрд╣рдгрдЬреЗ рдЬреНрдпрд╛рдВрдЪреНрдпрд╛ calendar рд╡рд░ restore рдЪреА рд░рдВрдЧреАрдд рддрд╛рд▓реАрдо рд╣реЛрддреА. Managed services (RDS, рдзрдбрд╛ 10) copy рдХрд░рдгреЗ automate рдХрд░рддрд╛рдд; рд░рдВрдЧреАрдд рддрд╛рд▓реАрдо рдЖрдгрд┐ рддреЗ рджреЛрди рдЖрдХрдбреЗ рдЕрдЬреВрдирд╣реА рддреБрдордЪреЗрдЪ рдЖрд╣реЗрдд.

тПня╕П рдкреБрдвреЗ

рдЬрд╛рд╕реНрдд рд╡рд┐рджреНрдпрд╛рд░реНрдереА, рдЬрд╛рд╕реНрдд clerks, рдЬрд╛рд╕реНрдд рдкреНрд░рд╢реНрди: scaling тАФ pools, caches, replicas, partitions, рдЖрдгрд┐ teams рдкреНрд░рддреНрдпрдХреНрд╖рд╛рдд рдкрд╛рд│рддрд╛рдд рддреЛ рдХреНрд░рдо.

git checkout lesson-10-scaling

ЁЯзп Lesson 09 тАФ Backups & recovery: the fireproof copy

ЁЯУН You are here: Lesson 09 of 18 ┬╖ Previous: lesson-08-migrations ┬╖ Next: lesson-10-scaling


ЁЯУж What's in this branch

Lessons 01тАУ08, plus the promise that outlives every other: a copy of the room in another building, the two numbers the school agrees on before the fire (RPO, RTO), point-in-time recovery, and the restore drill nobody wants and everybody needs.

ЁЯзТ Explain like I'm 5

Lesson 04 proved a transaction erases errors. It does nothing for mistakes: DELETE FROM students without a WHERE commits perfectly. Nor for fires, floods, stolen laptops or a disk that dies on a Sunday.

So the archivist keeps a fireproof copy ЁЯзп in another building: every night a photocopy of every register (a full backup), and all day long a copy of every pencil-and-ink line as it is written (the write-ahead log). With both, the school can rebuild the room as it was at any minute тАФ point-in-time recovery.

Two numbers are agreed before anything burns:

Because the oldest rule of the record room is: a backup nobody has restored is a rumour.

ЁЯЧ║я╕П Diagram

flowchart LR
    room["ЁЯЧДя╕П school.db"]
    copy["ЁЯзп school.backup.db<br/>conn.backup() тАФ the fireproof copy"]
    boom["ЁЯТе DELETE тАж WHERE id = 5<br/>Rohan is gone (a mistake, not an error)"]
    restore["тЩ╗я╕П copy the backup back<br/>SELECT to prove it тАФ Rohan returns"]
    room -->|"1 nightly + the log all day"| copy
    room -->|"2"| boom -->|"3 restore, measured (RTO)"| restore
    n["RPO: how much may we lose ┬╖ RTO: how long may we be shut ┬╖ drill quarterly"]

тЭУ What

ЁЯдФ Why

Because every other lesson assumes the room exists. Durability (lesson 01) protects against crashes, not against rm, ransomware, a bad migration or a well-meaning DELETE. The restore drill is the only proof that "we have backups" means anything.

ЁЯФз How (in this repo)

backup() in db/demo.py makes a copy with conn.backup(), deletes Rohan, then copies the backup back and proves he returned. The stopwatch is yours to add.

ЁЯзк Try it тАФ the drill

python3 db/demo.py backup
python3 - <<'EOF'
import sqlite3, shutil, time, os
src = sqlite3.connect("db/school.db"); dst = sqlite3.connect("db/drill.db"); src.backup(dst); dst.close()   # 1) the copy
before = src.execute("SELECT COUNT(*) FROM grades").fetchone()[0]
src.execute("DELETE FROM grades"); src.commit()                                                              # 2) the mistake
t = time.perf_counter(); src.close(); shutil.copy("db/drill.db", "db/school.db")                             # 3) the restore
after = sqlite3.connect("db/school.db").execute("SELECT COUNT(*) FROM grades").fetchone()[0]
print(f"grades before {before}, after restore {after}, RTO {(time.perf_counter()-t)*1000:.1f} ms"); os.remove("db/drill.db")
EOF

тЬЕ Verify тАФ what you should see

backup prints the student list without Rohan, then with him again. Your drill prints grades before 10, after restore 10 and an RTO in milliseconds тАФ you measured a restore instead of assuming one.

ЁЯПБ What you just proved

A mistake that a transaction cannot undo was undone by a copy you had tested тАФ and you know your RPO (one copy ago) and RTO (the stopwatch).

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: the companies that survived their worst database day are the ones with a restore drill on the calendar. Managed services (RDS, lesson 10) automate the copying; the drill and the two numbers are still yours.

тПня╕П Next

More students, more clerks, more questions: scaling тАФ pools, caches, replicas, partitions, and the order teams actually follow.

git checkout lesson-10-scaling
тЖР PreviousmigrationsNext тЖТscaling

This page is the lesson's README from the lesson-09-backups-recovery branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.