ЁЯФТ рдзрдбрд╛ 07 тАФ Concurrency рдЖрдгрд┐ isolation: рджреЛрди рдХрд╛рд░рдХреВрди, рдПрдХ рдиреЛрдВрджрд╡рд╣реА
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 18 рдкреИрдХреА рдзрдбрд╛ 07 тАФ рднрд╛рдЧ 2 рд╕реБрд░реВ! ┬╖ рдорд╛рдЧреЗ: lesson-06-indexes ┬╖ рдкреБрдвреЗ: lesson-08-migrations
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ06, рдЖрдгрд┐ рджреЛрди clerks рдирд╛ рдПрдХрд╛рдЪ рд╡реЗрд│реА рддреАрдЪ рдиреЛрдВрджрд╡рд╣реА рд╣рд╡реА рдЕрд╕рддреЗ рддреЗрд╡реНрд╣рд╛ рдХрд╛рдп рд╣реЛрддреЗ: locks, isolation levels, рдЖрдгрд┐ deadlock рдЪреЗ рд╣рд╕реНрддрд╛рдВрджреЛрд▓рди.
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
Clerk 2 students рдиреЛрдВрджрд╡рд╣реА рдЙрдШрдбрддреЗ, pencil рдиреЗ "Katrina тЖТ Katrina K" рд▓рд┐рд╣рд┐рддреЗ
(BEGIN, UPDATE) рдЖрдгрд┐ рд╢рд╛рдИрдиреЗ рдкрдХреНрдХреЗ рдХрд░рдгреНрдпрд╛рдЖрдзреА рдХрд╛рд╣реАрддрд░реА рддрдкрд╛рд╕рд╛рдпрд▓рд╛ рдирд┐рдШреВрди рдЬрд╛рддреЗ.
Clerk 1 рдпреЗрддреЗ:
- Clerk 1 рдХрддрд░рд┐рдирд╛рдЪреА рдУрд│ рд╡рд╛рдЪрддреЗ. рддрд┐рд▓рд╛ рдЬреБрдиреЗ рдирд╛рд╡ рджрд┐рд╕рддреЗ. рд╢рд╛рдИрдиреЗ рдкрдХреНрдХреНрдпрд╛ рд╣реЛрдИрдкрд░реНрдпрдВрдд pencil рдЦреБрдгрд╛ рдЦрд╛рдЬрдЧреА рдЕрд╕рддрд╛рдд тАФ рд╣реЗрдЪ isolation. рдЕрд░реНрдзрд╡рдЯ рдХрд╛рдорд╛рдд рдбреЛрдХрд╛рд╡рдгреЗ рдирд╛рд╣реА.
- Clerk 1 рд▓рд╛ рд╕реБрджреНрдзрд╛ рдХрддрд░рд┐рдирд╛рдЪреА рдУрд│ рд▓рд┐рд╣рд╛рдпрдЪреА рдЖрд╣реЗ. рдиреЛрдВрджрд╡рд╣реА clerk 2 рдЪреНрдпрд╛ pencil рдиреЗ рдзрд░реВрди рдареЗрд╡рд▓реА рдЖрд╣реЗ. Clerk 1 lock рд╕рд╛рдареА рдерд╛рдВрдмрддреЗтАж рдЖрдгрд┐ рдХрд╛рд╣реА рд╡реЗрд│рд╛рдиреЗ рд╣рд╛рд░ рдорд╛рдирддреЗ: "database is locked". lock рдиреЗ рддреНрдпрд╛ рдУрд│реАрд▓рд╛ рдПрдХрд╛рдЪ рд╡реЗрд│реА рджреЛрди pencils рдкрд╛рд╕реВрди рд╡рд╛рдЪрд╡рд▓реЗ.
рд╡рд╛рдЪрдгрд╛рд░реАрд▓рд╛ рдХрд┐рддреА pencil рджрд┐рд╕реВ рд╢рдХрддреЗ? рддреЗ рдмрдЯрдг рдореНрд╣рдгрдЬреЗ isolation level: "рдлрдХреНрдд рд╢рд╛рдИрдиреЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реНрдпрд╛ рдУрд│реА" (read committed, рдиреЗрд╣рдореАрдЪрд╛ default) рдкрд╛рд╕реВрди "рдЬрдгреВ рдПрдХрд╛ рд╡реЗрд│реА рдПрдХрдЪ clerk рдХрд╛рдо рдХрд░рдд рдЖрд╣реЗ" (serializable) рдкрд░реНрдпрдВрдд. рдЬрд╛рд╕реНрдд рдХрдбрдХ рдореНрд╣рдгрдЬреЗ рдЬрд╛рд╕реНрдд рд╕реБрд░рдХреНрд╖рд┐рдд рдЖрдгрд┐ рдЬрд╛рд╕реНрдд рдерд╛рдВрдмрдгреЗ.
рдЖрдгрд┐ рдХреНрд▓рд╛рд╕рд┐рдХ рд╕рд╛рдкрд│рд╛ тАФ deadlock: clerk 1 рдиреЗ students рдиреЛрдВрджрд╡рд╣реА рдзрд░рд▓реА рдЖрд╣реЗ рдЖрдгрд┐ рддрд┐рд▓рд╛ grades рд╣рд╡реА рдЖрд╣реЗ; clerk 2 рдиреЗ grades рдзрд░рд▓реА рдЖрд╣реЗ рдЖрдгрд┐ рддрд┐рд▓рд╛ students рд╣рд╡реА рдЖрд╣реЗ. рдХреЛрдгреАрдЪ рд╣рд▓реВ рд╢рдХрдд рдирд╛рд╣реА. рджрдкреНрддрд░рджрд╛рд░ рдПрдХреАрд▓рд╛ рдирд┐рд╡рдбрддреЛ, рддрд┐рдЪреЗ pencil рдХрд╛рдо рдлрд╛рдбреВрди рдЯрд╛рдХрддреЛ, рдЖрдгрд┐ рддрд┐рд▓рд╛ рдкреБрдиреНрд╣рд╛ рд╕реБрд░реБрд╡рд╛рдд рдХрд░рд╛рдпрд▓рд╛ рд╕рд╛рдВрдЧрддреЛ. рдЙрдкрд╛рдп рдХрдВрдЯрд╛рд│рд╡рд╛рдгрд╛ рдЖрд╣реЗ: transactions рд▓рд╣рд╛рди рдареЗрд╡рд╛, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ рд╡реЗрд│реА рдиреЛрдВрджрд╡рд╣реНрдпрд╛ рдПрдХрд╛рдЪ рдХреНрд░рдорд╛рдиреЗ рдШреНрдпрд╛.
ЁЯЧ║я╕П рдЖрдХреГрддреА
sequenceDiagram
participant A as ЁЯзСтАНЁЯТ╝ clerk 1
participant DB as ЁЯЧДя╕П register
participant B as ЁЯзСтАНЁЯТ╝ clerk 2
B->>DB: 1 BEGIN; UPDATE students SET name='Katrina K' WHERE id=2 (pencil, not committed)
A->>DB: 2 SELECT name WHERE id=2
DB-->>A: 'Katrina' тАФ the old, inked value (isolation)
A->>DB: 3 UPDATE students SET name='Katrina R' WHERE id=2
DB-->>A: тП│ waits for the lockтАж 'database is locked'
B->>DB: 4 ROLLBACK (pencil erased) тАФ the register is free again
тЭУ рдХрд╛рдп
- Locks: рд▓рд┐рд╣рд┐рдгрд╛рд░реЗ рдЬреЗ рдмрджрд▓рддрд╛рдд рддреНрдпрд╛рд╡рд░ lock рдШреЗрддрд╛рдд; рдЗрддрд░ рд▓рд┐рд╣рд┐рдгрд╛рд░реЗ рдерд╛рдВрдмрддрд╛рдд. SQLite writes рд╕рд╛рдареА рд╕рдВрдкреВрд░реНрдг database file lock рдХрд░рддреЗ (рдПрдХрд╛ рд╡реЗрд│реА рдПрдХрдЪ writer, WAL mode рдордзреНрдпреЗ рдЕрдиреЗрдХ readers); Postgres рдЖрдгрд┐ MySQL rows lock рдХрд░рддрд╛рдд, рдореНрд╣рдгреВрди рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреАрдВрд╡рд░ рдХрд╛рдо рдХрд░рдгрд╛рд▒реНрдпрд╛ рджреЛрди clerks рдПрдХрдореЗрдХреАрдВрдЪреА рд╡рд╛рдЯ рдкрд╛рд╣рдд рдирд╛рд╣реАрдд.
- Isolation levels (SQL standard): read uncommitted (pencil рджрд┐рд╕рддреЗ тАФ рдЬрд╡рд│рдЬрд╡рд│ рдХрдзреАрдЪ рд╡рд╛рдкрд░рдд рдирд╛рд╣реАрдд), read committed (Postgres default), repeatable read (MySQL default; рдПрдХрд╛ transaction рдордзреНрдпреЗ рддреЗрдЪ read рддреНрдпрд╛рдЪ rows рдкрд░рдд рджреЗрддреЗ), serializable (рдЬрдгреВ рдХреНрд░рдорд╛рдиреЗ; "рдорд▓рд╛ retry рдХрд░рд╛" рдореНрд╣рдгреВрди abort рд╣реЛрдК рд╢рдХрддреЗ).
- рд╕реИрд▓ levels рдкрд░рд╡рд╛рдирдЧреА рджреЗрддрд╛рдд рддреНрдпрд╛ anomalies: dirty read, non-repeatable
read, phantom rows, lost update (рджреЛрди clerks 5 рд╡рд╛рдЪрддрд╛рдд, рджреЛрдШреА 6 рд▓рд┐рд╣рд┐рддрд╛рдд).
counters рдЖрдгрд┐ рдкреИрд╢рд╛рдВрд╕рд╛рдареА:
SELECT тАж FOR UPDATE, рдХрд┐рдВрд╡рд╛ serializable, рдХрд┐рдВрд╡рд╛ atomicUPDATE тАж SET n = n + 1. - Deadlock: рд╡рд╛рдЯ рдкрд╛рд╣рдгреНрдпрд╛рдЪреЗ рд╡рд░реНрддреБрд│. database рддреЗ рдУрд│рдЦрддреЛ рдЖрдгрд┐ рдПрдХ transaction abort рдХрд░рддреЛ; рддреБрдордЪрд╛ code retry рдХрд░рд╛рдпрд▓рд╛ рддрдпрд╛рд░ рд╣рд╡рд╛.
- Timeouts: рдкреНрд░рддреНрдпреЗрдХ lock wait рд▓рд╛ рдПрдХ рд╣рд╡рд╛ (рдЖрдордЪреНрдпрд╛ connection рдордзреНрдпреЗ
timeout=1.0) тАФ timeout рд╢рд┐рд╡рд╛рдпрдЪреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдореНрд╣рдгрдЬреЗ hang.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг "рд╕рд╛рдорд╛рдпрд┐рдХ" (рдзрдбрд╛ 01 рдЪреЗ рджреБрд╕рд░реЗ рд╡рдЪрди) рд╣реЗ рддрд┐рдиреНрд╣реАрдВрдкреИрдХреА рд╕рд░реНрд╡рд╛рдд рдХрдареАрдг рдЖрд╣реЗ. рдЗрдерд▓реЗ bugs tests рдордзреНрдпреЗ рдЕрджреГрд╢реНрдп (рдПрдХ user) рдЖрдгрд┐ production рдордзреНрдпреЗ рд╡рд┐рдирд╛рд╢рдХрд╛рд░реА рдЕрд╕рддрд╛рдд (sale, ticket drop, payroll run): рджреЛрдирджрд╛ book рдЭрд╛рд▓реЗрд▓реНрдпрд╛ рдЬрд╛рдЧрд╛ рдЖрдгрд┐ рд╣рд░рд╡рд▓реЗрд▓реЗ updates рдореНрд╣рдгрдЬреЗ business рдЪрд╛ рд╡реЗрд╢ рдШрд╛рддрд▓реЗрд▓реНрдпрд╛ isolation рд╕рдорд╕реНрдпрд╛.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
db/demo.py рдордзреАрд▓ locks() рджреБрд╕рд░реЗ connection рдЙрдШрдбрддреЗ,
commit рди рдХреЗрд▓реЗрд▓реНрдпрд╛ UPDATE рд╕рд╣ BEGIN IMMEDIATE (write lock) рд╕реБрд░реВ рдХрд░рддреЗ, рдордЧ
clerk 1 рдЬреБрдиреА value рд╡рд╛рдЪрддрд╛рдирд╛ рдЖрдгрд┐ timeout рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рдгреНрдпрд╛рдд рдЕрдкрдпрд╢реА рд╣реЛрддрд╛рдирд╛ рджрд╛рдЦрд╡рддреЗ.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 db/demo.py locks
python3 - <<'EOF'
import sqlite3, threading
def clerk(name, delta):
c = sqlite3.connect("db/school.db", timeout=5)
with c: # atomic increment: no lost update
c.execute("UPDATE homework SET title = title || ?", (delta,))
print(name, "done")
ts = [threading.Thread(target=clerk, args=(f"clerk {i}", "!")) for i in range(5)]
[t.start() for t in ts]; [t.join() for t in ts]
c = sqlite3.connect("db/school.db"); print(c.execute("SELECT title FROM homework").fetchall())
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
locks clerk 1 рдЪреНрдпрд╛ read рд╕рд╛рдареА рдЬреБрдиреЗ рдирд╛рд╡ рдЫрд╛рдкрддреЛ рдЖрдгрд┐ рдордЧ timeout рдирдВрддрд░ database is locked. рддреБрдордЪреЗ рдкрд╛рдЪ threads рдкреНрд░рддреНрдпреЗрдХреА рдПрдХ ! рдЬреЛрдбрддрд╛рдд тАФ рдкреНрд░рддреНрдпреЗрдХ title рдЕрдЧрджреА рдкрд╛рдЪ ! рдиреЗ рд╕рдВрдкрддреЗ: lock рдиреЗ writers рдирд╛ рдПрдХрд╛рдорд╛рдЧреВрди рдПрдХ рд▓рд╛рд╡рд▓реЗ рдЖрдгрд┐ рдПрдХрд╣реА update рд╣рд░рд╡рд▓рд╛ рдирд╛рд╣реА.
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
Readers рдирд╛ рдХрдзреАрдЪ pencil рджрд┐рд╕рдд рдирд╛рд╣реА, writers lock рд╕рд╛рдареА рдерд╛рдВрдмрддрд╛рдд, рдЖрдгрд┐ рдкрд╛рдЪ рдПрдХрд╛рдЪрд╡реЗрд│рдЪреЗ updates рдкрд╛рдЪ рдореНрд╣рдгреВрдирдЪ рдЙрддрд░рд▓реЗ тАФ рдПрдХрд╛рдиреЗ рдЗрддрд░рд╛рдВрд╡рд░ overwrite рдХрд░реВрди рдирд╡реНрд╣реЗ.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- рджреЛрди statements рдордзреНрдпреЗ read-then-write (
SELECT nтАжUPDATE SET n = 6) тАФ lost update;SET n = n + 1рдХрд┐рдВрд╡рд╛ row lock рд╡рд╛рдкрд░рд╛ - user рдЪреА рд╡рд╛рдЯ рдкрд╛рд╣рдд рдЙрдШрдбреА рд░рд╛рд╣рдгрд╛рд░реА transactions тАФ рдмрд╛рдХреА рд╕рдЧрд│реЗ рд╕реБрджреНрдзрд╛ рдерд╛рдВрдмрддрд╛рдд
- deadlocks/serialization failures рд╕рд╛рдареА retry logic рдирд╕рдгреЗ тАФ database рдХрдзреА рдирд╛ рдХрдзреА рддреБрдореНрд╣рд╛рд▓рд╛ abort рдХрд░рдгрд╛рд░рдЪ
- рддреБрдордЪреНрдпрд╛ ORM рдЪрд╛ default isolation serializable рдЖрд╣реЗ рдЕрд╕реЗ рдЧреГрд╣реАрдд рдзрд░рдгреЗ тАФ рддреЛ рдЬрд╡рд│рдЬрд╡рд│ рдиреЗрд╣рдореА read committed рдЕрд╕рддреЛ
- timeout рд╢рд┐рд╡рд╛рдпрдЪреЗ lock waits
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: "flash sale рдордзреНрдпреЗ 100 рдкреИрдХреА 101 items рд╡рд┐рдХрд▓реЗ" рд╣реА рдЧреЛрд╖реНрдЯ рдореНрд╣рдгрдЬреЗ lost update; "рд░рд╛рддреНрд░реА 9 рд▓рд╛ checkout рдЕрдбрдХрддреЛ" рд╣реА рдЧреЛрд╖реНрдЯ рдореНрд╣рдгрдЬреЗ lock рдзрд░реВрди рдмрд╕рд▓реЗрд▓реЗ рд▓рд╛рдВрдм transaction. рдЗрдереЗ рдПрдХрджрд╛ рдЕрдиреБрднрд╡рд▓реНрдпрд╛рд╡рд░ рджреЛрдиреНрд╣реАрдВрдЪреЗ рдирд┐рджрд╛рди рдзрдбрд╛ 07 рдЪреНрдпрд╛ рдПрдХрд╛ рд╡рд╛рдХреНрдпрд╛рдд рд╣реЛрддреЗ.
тПня╕П рдкреБрдвреЗ
рд╢рд╛рд│рд╛ рдЪрд╛рд▓реВ рдЕрд╕рддрд╛рдирд╛рдЪ рдиреЛрдВрджрд╡рд╣реНрдпрд╛ рдмрджрд▓рд╛рд╡реНрдпрд╛ рд▓рд╛рдЧрддрд╛рдд: migrations тАФ versioned scripts, logbook, рдЖрдгрд┐ expand тЖТ migrate тЖТ contract.
git checkout lesson-08-migrations