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

ЁЯПШя╕П рдзрдбрд╛ 11 тАФ NoSQL рдЖрдгрд┐ рдЗрддрд░ рдЦреЛрд▓реНрдпрд╛: рдкреНрд░рд╢реНрдирд╛рд╕рд╛рдареА рдпреЛрдЧреНрдп рдЦреЛрд▓реА рдирд┐рд╡рдбрдгреЗ

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 18 рдкреИрдХреА рдзрдбрд╛ 11 ┬╖ рдорд╛рдЧреАрд▓: lesson-10-scaling ┬╖ рдкреБрдвреАрд▓: lesson-12-performance-ops


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

рдзрдбреЗ 01тАУ10, рдЖрдгрд┐ рд╢рд╛рд│рд╛ рдмрд╛рдВрдзреВ рд╢рдХреЗрд▓ рдЕрд╢рд╛ рдЗрддрд░ рдкреНрд░рдХрд╛рд░рдЪреНрдпрд╛ рдЦреЛрд▓реНрдпрд╛ тАФ document, key-value, columnar, graph, vector тАФ рдкреНрд░рддреНрдпреЗрдХ рдХрд╢рд╛рдЪреЗ рдЙрддреНрддрд░ рдЪрд╛рдВрдЧрд▓реЗ рджреЗрддреЗ, рдкреНрд░рддреНрдпреЗрдХ рдХрд╛рдп рд╕реЛрдбреВрди рджреЗрддреЗ, рдЖрдгрд┐ рдирд┐рд╡рдбреАрдЪрд╛ рдПрдХрдЪ рдирд┐рдпрдо.

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

рд░реЗрдХреЙрд░реНрдб рд░реВрдо рд╣реА relational рдЦреЛрд▓реА рдЖрд╣реЗ: рдиреЛрдВрджрд╡рд╣реНрдпрд╛, keys, рдирд┐рдпрдо, JOINs, transactions. рддреА рд╢рд╛рд│реЗрдЪреНрдпрд╛ рдЬрд╡рд│рдЬрд╡рд│ рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рд╢реНрдирд╛рдЪреЗ рдЙрддреНрддрд░ рдЪрд╛рдВрдЧрд▓реЗ рджреЗрддреЗ. рдкрдг рдХрд╛рд╣реА рдкреНрд░рд╢реНрди рддрд┐рдереЗ рдЕрд╡рдШрдб рдЬрд╛рддрд╛рдд, рдореНрд╣рдгреВрди рд╢рд╛рд│рд╛ рдЗрддрд░ рдЦреЛрд▓реНрдпрд╛ рдмрд╛рдВрдзрддрд╛рдд:

рдирд┐рдпрдо: рдЦреЛрд▓реА рдкреНрд░рд╢реНрдирд╛рдЪреНрдпрд╛ рдорд╛рдЧреЗ рдпреЗрддреЗ. relational рдЦреЛрд▓реАрддреВрди рд╕реБрд░реБрд╡рд╛рдд рдХрд░рд╛; рддреА рдЬреНрдпрд╛ рдкреНрд░рд╢реНрдирд╛рдЪреЗ рдЙрддреНрддрд░ рд╡рд╛рдИрдЯ рджреЗрддреЗ рддреНрдпрд╛рд╕рд╛рдареАрдЪ рджреБрд╕рд░реА рдЦреЛрд▓реА рдмрд╛рдВрдзрд╛ тАФ рдЖрдгрд┐ рд╕рддреНрдп рдПрдХрд╛рдЪ рдард┐рдХрд╛рдгреА рдареЗрд╡рд╛.

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

flowchart TB
    rel["ЁЯЧДя╕П relational тАФ the default room<br/>rules, JOINs, transactions"]
    doc["ЁЯУД document<br/>one folder per thing, flexible shape"]
    kv["ЁЯФС key-value<br/>one label тЖТ one answer, ┬╡s"]
    col["ЁЯУК columnar<br/>sum a billion rows"]
    gr["ЁЯХ╕я╕П graph<br/>hops between things"]
    vec["ЁЯЧ║я╕П vector<br/>nearest by meaning"]
    q["ЁЯзн the room follows the question"] --> rel
    rel -.->|"a question it answers badly"| doc & kv & col & gr & vec

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

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдореЛрдЬрд▓реЗрд▓реНрдпрд╛ рдкреНрд░рд╢реНрдирд╛рд╢рд┐рд╡рд╛рдп "scale рд╕рд╛рдареА NoSQL" рдХреЗрд▓реНрдпрд╛рдиреЗ рдЕрдиреЗрдХ teams рдиреА рддреНрдпрд╛рдВрдЪреЗ constraints, JOINs рдЖрдгрд┐ transactions рдЧрдорд╛рд╡рд▓реЗ тАФ рдЖрдгрд┐ рддреЗ рдореЛрдареА рдХрд┐рдВрдордд рджреЗрдКрди рдкрд░рдд рд╡рд┐рдХрдд рдШреЗрддрд▓реЗ. рдЖрдгрд┐ рдЙрд▓рдЯреА рдЪреВрдХрд╣реА рдЦрд░реА рдЖрд╣реЗ: analytics рдХрд┐рдВрд╡рд╛ рдЕрд░реНрде-search transactional рдЦреЛрд▓реАрддреВрди рдЬрдмрд░рджрд╕реНрддреАрдиреЗ рдЪрд╛рд▓рд╡рдгреЗ. рдЖрдзреА рдкреНрд░рд╢реНрдирд╛рд▓рд╛ рдирд╛рд╡ рджреНрдпрд╛; рдордЧ рдЦреЛрд▓реАрд▓рд╛.

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

SQLite рдкреНрд░рд╛рдорд╛рдгрд┐рдХрдкрдгреЗ рджреЛрди рдЦреЛрд▓реНрдпрд╛ рдмрдирддреЗ: relational (рдкреНрд░рддреНрдпреЗрдХ рдзрдбреНрдпрд╛рдд) рдЖрдгрд┐, JSON column рд╕рд╣, рдПрдХ рдЫреЛрдЯреА document рдЦреЛрд▓реА тАФ рдЦрд╛рд▓рдЪрд╛ рд╕рд░рд╛рд╡. рд╢реЗрдЬрд╛рд░рдЪреЗ рдЕрд░реНрдерд╛рдЪреЗ рд╕рднрд╛рдЧреГрд╣ рдореНрд╣рдгрдЬреЗ VectorDB рд╢рд╛рд│реЗрдЪреЗ vectordb/vectordb.py.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 - <<'EOF'
import sqlite3, json; c = sqlite3.connect("db/school.db")
c.execute("CREATE TABLE IF NOT EXISTS profiles (student_id INTEGER PRIMARY KEY REFERENCES students(id), doc TEXT NOT NULL)")
c.execute("INSERT OR REPLACE INTO profiles VALUES (2, ?)", (json.dumps({"clubs": ["chess", "robotics"], "guardian": {"name": "R. Rao", "phone": "555-0102"}}),))
c.commit()
print(c.execute("SELECT s.name, json_extract(p.doc, '$.guardian.name'), json_extract(p.doc, '$.clubs[0]') FROM profiles p JOIN students s ON s.id = p.student_id").fetchall())
print(c.execute("SELECT s.name FROM profiles p JOIN students s ON s.id=p.student_id, json_each(p.doc, '$.clubs') j WHERE j.value = 'robotics'").fetchall())
EOF
# paper exercise: for each question, name the room тАФ (a) 'how many students were present each month for 10 years?'
# (b) 'is the session token valid?' (c) 'which students share two clubs with Katrina?' (d) 'find the policy paragraph closest in meaning to this question' (e) 'transfer Aishwarya to 3B and update the counts'

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

Document рд╕рд░рд╛рд╡ relational row рдордзрд▓реНрдпрд╛ JSON рдордзреВрди рдХрддрд░рд┐рдирд╛рдЪреЗ рдкрд╛рд▓рдХ рдЖрдгрд┐ рдкрд╣рд┐рд▓рд╛ club рдЫрд╛рдкрддреЛ, рдЖрдгрд┐ json_each рдиреЗ robotics рдЪреЗ рд╕рджрд╕реНрдп рд╢реЛрдзрддреЛ тАФ рд░реЗрдХреЙрд░реНрдб рд░реВрдордЪреНрдпрд╛ рдЖрдд рдПрдХ document рдЦреЛрд▓реА, key рдорд╛рддреНрд░ рдЕрдЬреВрдирд╣реА relational. рдХрд╛рдЧрджрд╛рд╡рд░: (a) columnar, (b) key-value, (c) graph (рдХрд┐рдВрд╡рд╛ рдпрд╛ рдЖрдХрд╛рд░рд╛рдд self-JOIN), (d) vector, (e) relational тАФ transaction.

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

рдЦреЛрд▓реНрдпрд╛ рдкреНрд░рд╢реНрдирд╛рдиреБрд╕рд╛рд░ рдирд┐рд╡рдбрд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд, рдЖрдгрд┐ relational рдЦреЛрд▓реА рдЖрдкрд▓реНрдпрд╛ keys рди рд╕реЛрдбрддрд╛ рдПрдХ рдЫреЛрдЯреА document рдЦреЛрд▓реА рд╕рд╛рдорд╛рд╡реВ рд╢рдХрддреЗ.

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

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рдмрд╣реБрддреЗрдХ рдЦрд▒реНрдпрд╛ systems рдореНрд╣рдгрдЬреЗ Postgres + Redis + рдПрдХ analytics рдЦреЛрд▓реА, рдЖрдгрд┐ product рд▓рд╛ рдЧрд░рдЬ рдкрдбрд▓реНрдпрд╛рд╡рд░ search рдХрд┐рдВрд╡рд╛ vector рдЦреЛрд▓реА. Interview рдордзрд▓рд╛ рдкреНрд░рд╢реНрди рдХрдзреАрдЪ "рдХреЛрдгрддреА рд╕рд░реНрд╡реЛрддреНрддрдо" рдирд╕рддреЛ тАФ рддреЛ рдЕрд╕рддреЛ "рдХреЛрдгрддрд╛ рдкреНрд░рд╢реНрди, рдЖрдгрд┐ рддреА рдЦреЛрд▓реА рдХрд╛рдп рд╕реЛрдбреВрди рджреЗрддреЗ".

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

рд╢реЗрд╡рдЯрдЪрд╛ рдЯрдкреНрдкрд╛: performance рдЖрдгрд┐ operations тАФ рд╣рд│реВ queries рдЪреА рддрдкрд╛рд╕рдгреА, N+1, рдЖрдгрд┐ рд░реЗрдХреЙрд░реНрдб рд░реВрдорд╕рд╛рдареА on-call рдЪреЗрдХрд▓рд┐рд╕реНрдЯ.

git checkout lesson-12-performance-ops

ЁЯПШя╕П Lesson 11 тАФ NoSQL & other rooms: choosing the room for the question

ЁЯУН You are here: Lesson 11 of 18 ┬╖ Previous: lesson-10-scaling ┬╖ Next: lesson-12-performance-ops


ЁЯУж What's in this branch

Lessons 01тАУ10, plus the other kinds of room a school might build тАФ document, key-value, columnar, graph, vector тАФ what each answers well, what each gives up, and the one rule for choosing.

ЁЯзТ Explain like I'm 5

The record room is the relational room: registers, keys, rules, JOINs, transactions. It answers almost every school question well. But some questions are awkward there, so schools build other rooms:

The rule: the room follows the question. Start in the relational room; build another only for a question it answers badly тАФ and keep the truth in one place.

ЁЯЧ║я╕П Diagram

flowchart TB
    rel["ЁЯЧДя╕П relational тАФ the default room<br/>rules, JOINs, transactions"]
    doc["ЁЯУД document<br/>one folder per thing, flexible shape"]
    kv["ЁЯФС key-value<br/>one label тЖТ one answer, ┬╡s"]
    col["ЁЯУК columnar<br/>sum a billion rows"]
    gr["ЁЯХ╕я╕П graph<br/>hops between things"]
    vec["ЁЯЧ║я╕П vector<br/>nearest by meaning"]
    q["ЁЯзн the room follows the question"] --> rel
    rel -.->|"a question it answers badly"| doc & kv & col & gr & vec

тЭУ What

ЁЯдФ Why

Because "NoSQL because scale" without a measured question cost many teams their constraints, their JOINs and their transactions тАФ and they bought them back at great expense. And because the opposite mistake is real too: forcing analytics or meaning-search through a transactional room. Name the question; then name the room.

ЁЯФз How (in this repo)

SQLite plays two rooms honestly: the relational one (every lesson) and, with a JSON column, a small document one тАФ the exercise below. The meaning hall next door is the VectorDB school's vectordb/vectordb.py.

ЁЯзк Try it

python3 - <<'EOF'
import sqlite3, json; c = sqlite3.connect("db/school.db")
c.execute("CREATE TABLE IF NOT EXISTS profiles (student_id INTEGER PRIMARY KEY REFERENCES students(id), doc TEXT NOT NULL)")
c.execute("INSERT OR REPLACE INTO profiles VALUES (2, ?)", (json.dumps({"clubs": ["chess", "robotics"], "guardian": {"name": "R. Rao", "phone": "555-0102"}}),))
c.commit()
print(c.execute("SELECT s.name, json_extract(p.doc, '$.guardian.name'), json_extract(p.doc, '$.clubs[0]') FROM profiles p JOIN students s ON s.id = p.student_id").fetchall())
print(c.execute("SELECT s.name FROM profiles p JOIN students s ON s.id=p.student_id, json_each(p.doc, '$.clubs') j WHERE j.value = 'robotics'").fetchall())
EOF
# paper exercise: for each question, name the room тАФ (a) 'how many students were present each month for 10 years?'
# (b) 'is the session token valid?' (c) 'which students share two clubs with Katrina?' (d) 'find the policy paragraph closest in meaning to this question' (e) 'transfer Aishwarya to 3B and update the counts'

тЬЕ Verify тАФ what you should see

The document exercise prints Katrina's guardian and first club from JSON inside a relational row, and finds robotics members with json_each тАФ a document room inside the record room, with the key still relational. Paper: (a) columnar, (b) key-value, (c) graph (or a self-JOIN at this size), (d) vector, (e) relational тАФ the transaction.

ЁЯПБ What you just proved

Rooms are chosen per question, and the relational room can host a small document room without giving up its keys.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: most real systems are Postgres + Redis + one analytics room, with a search or vector room when a product needs it. The interview question is never "which is best" тАФ it is "which question, and what does the room give up".

тПня╕П Next

The last mile: performance and operations тАФ slow-query triage, N+1, and the on-call checklist for the record room.

git checkout lesson-12-performance-ops
тЖР PreviousscalingNext тЖТperformance ops

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