ბიბლიოთეკა
00/08 · ~40 წთ
GUIDEDECK · ყველა აპის მონაცემებისთვის

მონაცემთა ბაზები & SQL
მოდელი, შეკითხვები
და რატომ ნელდებიან.

40-წუთიანი სამუშაო სესია რელაციურ მონაცემთა ბაზებზე — იმით დაწყებული, თუ რატომ ვმოდელირებთ მონაცემებს ცხრილებად, გავლით SQL-ზე, რომელსაც ყოველდღე წერთ, ტრანზაქციებსა და ACID-ზე, ინდექსებსა და შეკითხვის გეგმებზე, იმაზე, თუ რომელი ძრავა აირჩიოთ, და იმაზე, როგორ შექმნათ სქემა, რომელიც მოგვიანებით არ შეგებრძოლებათ.

~40 წთშერეული გუნდიSQL · ვენდორ-ნეიტრალური
გადაახვიეთ
01 · რელაციური 4 წთ

მონაცემთა ბაზა ერთადერთი ადგილია,
სადაც მონაცემები ჭეშმარიტი უნდა დარჩეს.

კოდი მუდმივად იწერება თავიდან; მონაცემები კი ყველა მათგანს გადაურჩება. რელაციური მოდელი იმიტომ იმარჯვებს, რომ ერთ დაპირებას იძლევა, რომელსაც სხვა ვერავინ: თქვენი მონაცემების ერთი, სანდო ასლი, რომელსაც მრავალი პროგრამა ერთდროულად კითხულობს და წერს ერთმანეთისთვის ხელის შეშლის გარეშე. წარმოიდგინეთ ბანკის წიგნი — ყველა ოპერატორი და ბანკომატი ერთსა და იმავე ბალანსებს ეხება და არავის აქვს უფლება, ის ნახევრად განახლებული დატოვოს.

რელაციური მონაცემთა ბაზა — მონაცემები, რომლებიც ინახება სტრიქონებისა და სვეტების ცხრილებში, სადაც დაკავშირებული ცხრილები ერთმანეთს უკავშირდება (სიტყვა "რელაციური" სწორედ ამ კავშირებიდან მოდის). ის მისდევს ფიქსირებულ სქემას — შეთანხმებულ სიას ცხრილების, მათი სვეტებისა და თითოეულ სვეტში დაშვებული მნიშვნელობის ტიპისა — და შეკითხვებს მას SQL-ით უსვამთ. თავად ძრავა (Postgres, MySQL, SQLite) იცავს წესებს — სწორი ტიპები, დუბლიკატების გარეშე, ვალიდური კავშირები — ასე რომ ყველა მიერთებული პროგრამა ერთსა და იმავე თანმიმდევრულ სურათს ხედავს.

რას აკეთებს ძრავა თქვენთვის

  • მდგრადობა — გადარჩება გადატვირთვას, ავარიასა და დენის გათიშვას.
  • კონკურენტულობა — ასობით მომხმარებელი ერთდროულად კითხულობს და წერს ერთმანეთის დაზიანების გარეშე.
  • მთლიანობა — შეზღუდვები უარყოფს ცუდ მონაცემებს მანამ, სანამ ისინი ჩაიწერება, და არა შემდეგ.
  • დეკლარაციული შეკითხვები — თქვენ ამბობთ, რა გინდათ; ძრავა კი თავად არკვევს, როგორ მოიტანოს ის.

ბევრი პროგრამა, ერთი საერთო ჭეშმარიტება — მონაცემებს ფლობს ბაზა და არა რომელიმე ცალკეული აპი.

რატომ არ შევინახოთ ის უბრალოდ აპში?

მონაცემები კოდსა და ფაილებში
// a file each service writes to its own way [ { "id": 1, "total": "42.0", "user": "Mara" }, { "id": 1, "total": "oops" } // dup id, bad type ] // no rules · two writers race · who is "Mara"?
მონაცემები რელაციურ ბაზაში
CREATE TABLE orders ( id BIGINT PRIMARY KEY, total NUMERIC(10,2) NOT NULL, user_id BIGINT REFERENCES users(id) ); // types, uniqueness & references enforced for every writer

სქემა "გთხოვთ, ფრთხილად იყავით"-ს აქცევს წესებად, რომელთა დარღვევასაც ძრავა არ დაგანებებთ.

02 · ცხრილები და გასაღებები 6 წთ

სტრიქონები ინახავს ფაქტებს; გასაღებები აკავშირებს
მათ მონაცემების კოპირების გარეშე.

კარგი სქემა ყოველ ფაქტს ერთხელ ინახავს და ფაქტებს ერთმანეთს გასაღებებით უკავშირებს (გასაღები უბრალოდ სვეტია, რომლის საქმეც სტრიქონის იდენტიფიცირება ან მასზე მითითებაა). სწორად შერჩეული გასაღებებით მოდელი თავად აიხსნება — ხედავთ, როგორ უკავშირდება ყველაფერი ერთმანეთს უბრალოდ ცხრილების კითხვით, რადგან კავშირები მონაცემებში ცხოვრობს და არა მხოლოდ თქვენს თავში.

პირველადი გასაღები — სვეტი, რომელიც სტრიქონს უნიკალურად იდენტიფიცირებს. უცხო გასაღები კი სვეტია, რომელიც სხვა ცხრილის პირველად გასაღებზე მიუთითებს. ერთად ისინი საშუალებას აძლევენ ერთ სტრიქონს, მიმართოს მეორეს იმის ნაცვლად, რომ მისი მონაცემები დაიდუბლიროს — და ძრავა გარანტიას იძლევა, რომ ეს მიმართვა ყოველთვის ვალიდურია.
პირველადი

ერთი სტრიქონი, ერთი იდენტობა

უნიკალური და არასოდეს null. აირჩიეთ სუროგატული გასაღები (ავტომატური id) და არა ბუნებრივი, როგორიცაა email — ბუნებრივი მნიშვნელობები იცვლება, id-ები კი არ უნდა იცვლებოდეს.

უცხო

მაჩვენებელი გარანტიით

order.user_id → users.id. ძრავა უარყოფს შეკვეთას იმ მომხმარებლისთვის, რომელიც არ არსებობს — ობოლი სტრიქონები არ ჩნდება.

შეზღუდვები

წესები მონაცემთა შრეზე

NOT NULL, UNIQUE, CHECK, DEFAULT — ინვარიანტები, რომლებიც ყველა მწერლისთვის მოქმედებს და არა მხოლოდ ფრთხილებისთვის.

კავშირის სამი ფორმა

  • ერთი-მრავალთან — მომხმარებელს ბევრი შეკვეთა აქვს. უცხო გასაღები მრავლის მხარეს ცხოვრობს (orders.user_id).
  • ერთი-ერთთან — მომხმარებელს ერთი პროფილი აქვს. უცხო გასაღები პლუს UNIQUE შეზღუდვა.
  • მრავალი-მრავალთან — შეკვეთები ბევრ პროდუქტს შეიცავს; პროდუქტები ბევრ შეკვეთაში ჩნდება. საჭიროა საკავშირო ცხრილი.

ჰგავს  ბიბლიოთეკას: ერთი მკითხველის ბარათი ბევრ გატანას უკავშირდება; თითოეული გატანა კი ერთ წიგნსა და ერთ მკითხველზე მიუთითებს.

order_items არის საკავშირო ცხრილი: ის შეკვეთები↔პროდუქტებს (მრავალი-მრავალთან) ორ სუფთა ერთი-მრავალთან კავშირად აქცევს.

CREATE TABLE users ( id BIGSERIAL PRIMARY KEY, email TEXT UNIQUE NOT NULL ); CREATE TABLE products ( id BIGSERIAL PRIMARY KEY, price NUMERIC(10,2) CHECK (price >= 0) ); CREATE TABLE orders ( id BIGSERIAL PRIMARY KEY, user_id BIGINT REFERENCES users(id) ); CREATE TABLE order_items ( order_id BIGINT REFERENCES orders(id), product_id BIGINT REFERENCES products(id), qty INT NOT NULL CHECK (qty > 0), PRIMARY KEY (order_id, product_id) // კომპოზიტური გასაღები );
03 · თქვენი SQL 7 წთ

SELECT — რა, WHERE — გაფილტრული,
JOIN — დაკავშირებული, GROUP — შესაჯამებლად.

SQL დეკლარაციულია: თქვენ აღწერთ სასურველ შედეგს და არა იმ ციკლებს, რომლებიც მას ააგებს. ოთხი კონსტრუქცია კითხვების აბსოლუტურ უმრავლესობას ფარავს — და ისინი ისეთი თანმიმდევრობით სრულდება, რომელიც ახალბედების უმეტესობას აოცებს.

SQL — Structured Query Language — რელაციური მონაცემების სტანდარტული ენა. SELECT კითხულობს სტრიქონებს; WHERE ფილტრავს მათ; JOIN აერთიანებს ცხრილებს გასაღებების დამთხვევით; GROUP BY კი ბევრ სტრიქონს ჯგუფების მიხედვით შეჯამებად კრავს.

დაწერის თანმიმდევრობა ≠ შესრულების თანმიმდევრობა

თქვენ წერთ SELECT … FROM … WHERE … GROUP BY …, მაგრამ ძრავა ჯერ FROM-ს ამუშავებს და SELECT-ს თითქმის ბოლოს. სწორედ ამიტომ ვერ გამოიყენებთ SELECT-ის ალიასს WHERE-ის შიგნით — ის ჯერ არ არსებობს.

ლოგიკური შეფასების თანმიმდევრობა — გაითავისეთ და დამაბნეველი შეცდომები აზრს შეიძენს.

წაიკითხეთ და გაფილტრეთ სასურველი სტრიქონები

SELECT id, email, created_at FROM users WHERE country = 'PT' AND created_at >= '2026-01-01' ORDER BY created_at DESC LIMIT 50; // დიდი კითხვისთვის არასოდეს SELECT *
აზროვნების მოდელი
აიღეთ ცხრილი, დატოვეთ WHERE-ს შესაბამისი სტრიქონები, დაალაგეთ და შემდეგ ერთ გვერდამდე შემოკვეცეთ.
ყურადღება
NULL არაფრის ტოლი არ არის — გამოიყენეთ IS NULL და არასოდეს = NULL.

გააერთიანეთ ცხრილები გასაღებების დამთხვევით

SELECT u.name, o.id, o.placed_at FROM users u JOIN orders o ON o.user_id = u.id // inner: მხოლოდ დამთხვევები WHERE o.placed_at >= '2026-06-01'; // LEFT JOIN ინახავს ყველა მომხმარებელს, NULL სადაც შეკვეთა არ არის
INNER თუ LEFT
INNER = სტრიქონები, რომლებიც ორივეშია. LEFT = მარცხენას ყველა სტრიქონი, შევსებული NULL-ებით.
ყურადღება
დაკარგული ან არასწორი ON join-ს დეკარტულ ნამრავლად აქცევს — სტრიქონების რაოდენობა აფეთქდება.

შეკარით სტრიქონები ჯგუფების მიხედვით პასუხებად

SELECT u.country, COUNT(*) AS orders, SUM(o.total) AS revenue FROM orders o JOIN users u ON u.id = o.user_id GROUP BY u.country HAVING SUM(o.total) > 10000; // ფილტრავს ჯგუფებს
WHERE თუ HAVING
WHERE ფილტრავს სტრიქონებს დაჯგუფებამდე; HAVING ფილტრავს ჯგუფებს დაჯგუფების შემდეგ.
წესი
ყოველი არააგრეგირებული SELECT სვეტი GROUP BY-შიც უნდა იყოს.

დაარქვით ნაბიჯს სახელი და შემდეგ მასზე ააშენეთ

WITH big_spenders AS ( SELECT user_id, SUM(total) AS spent FROM orders GROUP BY user_id HAVING SUM(total) > 5000 ) SELECT u.email, b.spent FROM big_spenders b JOIN users u ON u.id = b.user_id;
CTE
WITH ბლოკი დასახელებული, ადვილად წასაკითხი ქვეშეკითხვაა — დააჯაჭვეთ ისინი იმის ნაცვლად, რომ ხუთ დონეზე ჩალაგოთ.
სინამდვილეში ეს არის
რეფაქტორინგი SQL-ისთვის: პატარა დასახელებული ნაბიჯები სჯობს ერთ წაუკითხავ მონსტრ-შეკითხვას.
04 · ჩაწერა · ტრანზაქციები · ACID 6 წთ

ტრანზაქცია ბევრ ჩაწერას აქცევს
ერთ „ყველაფერი ან არაფერი“ ნაბიჯად.

წაკითხვა მიმტევებელია; მონაცემები ჩაწერისას ფუჭდება. გამოსავალი ტრანზაქციაა — საზღვარი, რომელიც ამბობს: "ან ყველა მათგანი წარმატდება, ან არცერთი". ACID არის ის ოთხი გარანტია, რომელსაც ის გაძლევთ.

ტრანზაქცია — ინსტრუქციების ჯგუფი, რომელიც ერთ ერთეულად კომიტდება. მოაქციეთ დაკავშირებული ჩაწერები BEGIN … COMMIT-ში; თუ რამე ჩავარდება, ROLLBACK მთელ პარტიას ისე გააუქმებს, თითქოს ის არასოდეს შესრულებულა.
A
Atomicity
ყველა ჩაწერა ან არცერთი
C
Consistency
შეზღუდვები ყოველთვის მოქმედებს
I
Isolation
პარალელური ტრანზაქციები ერთმანეთს არ ეჯახება
D
Durability
დაკომიტებული = ავარიას გადარჩება

კლასიკური მაგალითი: ფულის გადარიცხვა

ორი ცალკე ჩაწერა
UPDATE accounts SET bal = bal - 100 WHERE id = 1; // ── crash here ── UPDATE accounts SET bal = bal + 100 WHERE id = 2; // $100 vanished. account 1 charged, account 2 never paid.
ატომური ტრანზაქცია
BEGIN; UPDATE accounts SET bal = bal - 100 WHERE id = 1; UPDATE accounts SET bal = bal + 100 WHERE id = 2; COMMIT; // crash before COMMIT → both roll back

ატომურობა ერთ სურათში: ტრანზაქცია ან მთლიანად ჯდება, ან მთლიანად ქრება.

იზოლაცია მოკლედ

როცა ტრანზაქციები ერთდროულად სრულდება, იზოლაციის დონე წყვეტს, თითოეულმა მათგანმა სხვების მიმდინარე სამუშაოდან რა დაინახოს. უფრო თავისუფალი = უფრო სწრაფი, მაგრამ ანომალიებს უშვებს; უფრო მკაცრი = უფრო უსაფრთხო, მაგრამ მეტი კონკურენციით.

  • Read Committed — ხედავთ მხოლოდ დაკომიტებულ მონაცემებს (გავრცელებული ნაგულისხმევი).
  • Repeatable Read — ერთი და იმავე სტრიქონის ხელახალი წაკითხვა ტრანზაქციის შიგნით ერთსა და იმავე პასუხს აბრუნებს.
  • Serializable — ისე, თითქოს ტრანზაქციები სათითაოდ სრულდებოდა. ყველაზე უსაფრთხო, ყველაზე ნელი.

ჰგავს  საერთო დოკუმენტის რედაქტირებას: იზოლაცია განსაზღვრავს, სხვისი ნახევრად დასრულებული ცვლილებიდან რამდენის დანახვის უფლება გაქვთ.

05 · ინდექსები და გეგმები 6 წთ

ნელი შეკითხვა ჩვეულებრივ
სრული სკანირებაა, რომელსაც ინდექსი სჭირდება.

ძრავა თქვენს SQL-ს პირდაპირ არ ასრულებს — ის შეკითხვის გეგმას აგებს. წარმადობაზე მუშაობის უდიდესი ნაწილი ამ გეგმის წაკითხვა და მისთვის უფრო სწრაფი გზის მიცემაა. ყველაზე დიდი ბერკეტი ინდექსია.

ინდექსი — დახარისხებული საძებნი სტრუქტურა (ჩვეულებრივ B-ხე) ერთ ან რამდენიმე სვეტზე. ის ძრავას აძლევს საშუალებას, პირდაპირ დამთხვეულ სტრიქონებზე გადახტეს, მთელი ცხრილის წაკითხვის ნაცვლად — და O(n) სკანირებას O(log n) ძებნად აქცევს.

სკანირება თუ ძებნა

  • თანმიმდევრული სკანირება — წაიკითხე ყოველი სტრიქონი და უმეტესობა გადააგდე. პატარა ცხრილებისთვის კარგია; მილიონებზე სასტიკია.
  • ინდექსით ძებნა — გაიარე დახარისხებული ხე სასურველ სტრიქონებამდე და მხოლოდ რამდენიმე გვერდს შეეხე.
  • დააინდექსეთ ის სვეტები, რომლებზეც WHERE, JOIN და ORDER BY გაქვთ — და არა ყველა სვეტი.
ძირი
< key
≥ key
row ✓
SEQ SCAN
ყველა სტრიქონი
INDEX SEEK
რამდენიმე ნაბიჯი

მარცხნივ: ძრავა ყველაფერს კითხულობს. მარჯვნივ: B-ხე რამდენიმე კვანძს გაივლის პირდაპირ დამთხვევამდე.

წაიკითხეთ გეგმა და შემდეგ გაასწორეთ

ინდექსის გარეშე
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 42; Seq Scan on orders (rows=2,000,000) Filter: user_id = 42 actual time: 480 ms // reads every row
ინდექსით — Index Scan
CREATE INDEX ON orders (user_id); EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 42; Index Scan using orders_user_id_idx actual time: 0.3 ms // ~1500x faster
  • ყოველი ინდექსი ყოველ ჩაწერაზე უნდა განახლდეს — მეტი ინდექსი = უფრო ნელი INSERT/UPDATE.
  • ინდექსები დისკსა და მეხსიერებას ხარჯავს; გამოუყენებელი ინდექსი მხოლოდ ხარჯია.
  • კომპოზიტურ ინდექსში თანმიმდევრობას მნიშვნელობა აქვს: (user_id, placed_at) ეხმარება მხოლოდ user_id-ზე ფილტრს, მაგრამ არა მხოლოდ placed_at-ზე.
  • დააინდექსეთ თქვენი რეალური შეკითხვის პატერნები — გაზომეთ EXPLAIN-ით და ნუ გამოიცნობთ.
06 · ნორმალიზაცია და დენორმალიზაცია 4 წთ

შეინახეთ ყოველი ფაქტი ერთხელ —
შემდეგ კი წესი შეგნებულად დაარღვიეთ.

ნაგულისხმევად ნორმალიზება: ერთი ფაქტი, ერთი ადგილი, წინააღმდეგობების გარეშე. დენორმალიზება კი შეგნებულად — და მხოლოდ მაშინ, როცა გაზომილი წაკითხვის პრობლემა ამართლებს იმ დუბლირებას, რომელსაც იღებთ.

ნორმალიზაცია — ცხრილების ისე აგება, რომ ყოველი ფაქტი ზუსტად ერთხელ ინახებოდეს, ზედმეტობის მოცილებით. დენორმალიზაცია კი შეგნებული საპირისპიროა: მონაცემების კოპირება ან წინასწარი შეერთება, რომ წაკითხვა უფრო სწრაფი იყოს — იმის მიღებით, რომ ასლების სინქრონში შენარჩუნება ახლა უკვე თქვენი საქმეა.
უნებლიე დენორმალიზაცია
// user details copied into every order row orders(id, user_name, user_email, user_city, total) // Mara changes her email → update 4,000 rows. // miss one → the same user has two emails. truth forks.
ნორმალიზებული — ერთი ადგილი
users(id, name, email, city) // the fact lives here orders(id, user_id, total) // just a reference // email changes in ONE place. every order sees it.
ნორმალიზება

ჩაწერა & ჭეშმარიტება მთავარია

ტრანზაქციული სისტემები — შეკვეთები, ანგარიშები, მარაგები. სისწორე და იაფი განახლებები სჯობს წაკითხვის სუფთა სიჩქარეს. ეს ნაგულისხმევი არჩევანია.

დენორმალიზება

წაკითხვა ჭარბობს & ტკივა

ცხელი შეკითხვა ხუთ ცხრილს აერთებს ყოველი გვერდის ჩატვირთვისას. წინასწარ გამოთვალეთ ან დააქეშირეთ ეს ფორმა — მას შემდეგ, რაც დაამტკიცებთ, რომ join არის ბოთლის ყელი.

ფასი

თანმიმდევრულობა ახლა თქვენზეა

ყოველი დუბლირებული მნიშვნელობა მომავალი ბაგია, რომელიც ელოდება, რომ განახლება ერთ ასლს გამოტოვებს. დენორმალიზაცია ჩაწერის ტკივილს წაკითხვის სიჩქარეზე ცვლის — არასოდეს უფასოდ.

პრაქტიკული წესი: ანორმალიზეთ, სანამ არ ატკივდება, შემდეგ კი დენორმალიზეთ, სანამ არ იმუშავებს — ციფრებით და არა შეგრძნებით.

07 · ინსტრუმენტთა სამყარო 4 წთ

რომელ ძრავას უნდა
მიმართოთ სინამდვილეში?

აქამდე თითქმის ყველაფერი ვენდორ-ნეიტრალური იყო — ის ერთნაირად მუშაობს პოპულარულ მონაცემთა ბაზებში. მთავარი გზაგასაყარია რელაციური თუ დოკუმენტური: მონაცემებს დაკავშირებულ ცხრილებად ჰყოფთ თუ ყოველ ჩანაწერს ერთ თვითკმარ ბლობად ინახავთ? აი წამყვანი ძრავები, თითოეულის ძლიერი და სუსტი მხარეები და არჩევის წესი.

რელაციური თუ დოკუმენტური — რელაციური მონაცემთა ბაზა მონაცემებს დაკავშირებულ ცხრილებად ჰყოფს ფიქსირებული სქემით (PostgreSQL, MySQL, SQLite). დოკუმენტური ბაზა ყოველ ჩანაწერს ერთ თვითკმარ, JSON-ის მსგავს დოკუმენტად ინახავს, ფიქსირებული ფორმის გარეშე (MongoDB). რელაციური კავშირებსა და წესებს თქვენ მაგივრად უვლის; დოკუმენტური მოქნილობას გაძლევთ, მაგრამ თანმიმდევრულობის შენარჩუნებას თქვენს აპლიკაციას აკისრებს.

ორი ფორმა გვერდიგვერდ

  • რელაციური — მომხმარებელი users-ში ცხოვრობს; მისი შეკვეთები orders-ში და გასაღებით უკან მიუთითებს. არაფერი დუბლირდება და join მათ ერთმანეთს აკერებს, როცა დაგჭირდებათ.
  • დოკუმენტური — მომხმარებელი და მისი შეკვეთები ერთ დოკუმენტშია ჩალაგებული, ამიტომ ერთი წაკითხვა მთელს აბრუნებს — მაგრამ ერთი და იმავე მომხმარებლის მონაცემები სინქრონიდან შეიძლება გავიდეს, თუ ისინი ირგვლივ კოპირდება.
  • არცერთი არაა "უკეთესი". რელაციური კავშირებით სავსე მონაცემებს ერგება; დოკუმენტური — თვითკმარ ჩანაწერებს, რომლებსაც ძირითადად მთლიანად კითხულობთ.
users
id PK · name
{ name: "Mara", orders: [ { id: 1, total: 42 }, { id: 2, total: 19 } ] }
ყველაფერი ერთ ჯერზე
orders
id PK · user_id FK · total
რელაციური · ცხრილები
დოკუმენტი · ერთი ჩანაწერი

მარცხნივ: ფაქტები ერთხელ ინახება და გასაღებით უკავშირდება. მარჯვნივ: იგივე ფაქტები ერთ დოკუმენტშია ჩალაგებული.

ძრავები, რომლებიც უნდა იცოდეთ

PostgreSQL

შესაძლებლობებით სავსე ნაგულისხმევი

დადებითი — უკიდურესად მდიდარი ფუნქციონალით და მკაცრი სისწორის მიმართ; JSON-ს, სრულტექსტურ ძებნას და სხვას დანამატების გარეშე უმკლავდება.
უარყოფითი — მეტი ფუნქციონალი მეტ სასწავლს ნიშნავს, ხოლო ყუთიდან ამოღებული პარამეტრები ფრთხილადაა აწყობილი.

MySQL / MariaDB

ყველგან & მარტივი

დადებითი — მუშაობს პრაქტიკულად ყველა ჰოსტზე, ადვილად დასაწყებია და სწრაფია მარტივი, წაკითხვაზე ორიენტირებული საიტებისთვის.
უარყოფითი — ისტორიულად უფრო თავისუფალია სისწორეში და PostgreSQL-ზე ღარიბი ფუნქციონალი აქვს.

MongoDB · JSON

მოქნილი JSON საცავი

დადებითი — ინახავს ჩალაგებულ, JSON-ის ფორმის ჩანაწერებს ფიქსირებული სქემის გარეშე; სწრაფად საწყებია, სანამ მონაცემები ფორმას ჯერ კიდევ იცვლის.
უარყოფითი — რეალური join-ები არ არსებობს და ძრავა თქვენს კავშირებს არ იცავს, ამიტომ თანმიმდევრულობა ახლა თქვენს აპს ეკისრება.

SQLite

მონაცემთა ბაზა ერთ ფაილში

დადებითი — ნულოვანი დაყენება, პირდაპირ თქვენს აპში მუშაობს და იდეალურია ლოკალური აპებისთვის, პროტოტიპებისა და ტესტებისთვის.
უარყოფითი — ერთდროულად მხოლოდ ერთი მწერალი, ამიტომ დატვირთული მრავალმომხმარებლიანი სერვერისთვის ეს არასწორი ინსტრუმენტია.

როგორ ავირჩიოთ: მიმართეთ PostgreSQL-ს თითქმის ყველა აპისთვის, სადაც რეალური კავშირებია; აირჩიეთ MySQL, თუ თქვენი პლატფორმა უკვე მასზეა სტანდარტიზებული; აირჩიეთ MongoDB მხოლოდ მაშინ, როცა მონაცემები ნამდვილად დოკუმენტის ფორმისაა და იშვიათად ერთდება; ხოლო SQLite გამოიყენეთ ლოკალური აპებისთვის, პროტოტიპებისა და ტესტების ნაკრებისთვის.

როცა საქმე ანალიტიკაა — OLAP & სვეტური საცავები

ყველაფერი ზემოთ არის OLTP — online transaction processing: ბევრი მცირე წაკითხვა და ჩაწერა, სათითაოდ სტრიქონებზე (აპის მუშაობა). ანალიტიკა საპირისპიროა — OLAP, online analytical processing: მილიონობით და მილიარდობით სტრიქონის სკანირება და აგრეგირება რამდენიმე სვეტზე ("შემოსავალი ქვეყნების მიხედვით გასულ კვარტალში"). ამისთვის გჭირდებათ სვეტური ბაზა, რომელიც თითოეულ სვეტს ერთად ინახავს და მხოლოდ იმას კითხულობს, რასაც შეკითხვა ნამდვილად ეხება.
id · name · total
id
name
total
id · name · total
id · name · total
სტრიქონი · OLTP
სწრაფად ერთი სტრიქონი
სვეტური · OLAP
ერთი სვეტის ჯამი მილიარდებზე

სტრიქონული საცავი ჩანაწერს ერთად ინახავს (შესანიშნავია "მომეცი ეს შეკვეთა"-სთვის); სვეტური საცავი თითოეულ ველს ინახავს ერთად, ამიტომ აგრეგატი მხოლოდ საჭირო სვეტს კითხულობს.

ანალიტიკური ძრავები

  • ClickHouse — ელვისებურად სწრაფი ღიაკოდიანი სვეტური OLAP; იღებს მოვლენებისა და ლოგების უზარმაზარ ნაკადებს და მილიწამებში აგრეგირებს. მთავარი არჩევანი თვითჰოსტინგიანი, რეალურ დროში ანალიტიკისთვის.
  • ღრუბლოვანი საწყობები — BigQuery, Snowflake, Redshift: სრულად მართული, საცავი გამოთვლისგან გამოყოფილი, მასშტაბირდება პეტაბაიტებამდე.
  • DuckDB — "SQLite ანალიტიკისთვის": პროცესშივე მომუშავე სვეტური ძრავა მონაცემთა ფაილების ლოკალურად დასამუშავებლად.
  • ვექტორული ბაზები — pgvector, Qdrant, Pinecone, Weaviate, Chroma: ინახავს ემბედინგებს (რიცხვებში გამოხატულ მნიშვნელობას) სემანტიკური და მსგავსების ძებნისთვის — AI-ით მოძიების ხერხემალი (იხილეთ დეკი RAG & Vector Search).

პრაქტიკული წესი: OLTP (Postgres / MySQL) აპს ამუშავებს; OLAP (ClickHouse ან საწყობი) კი მასზე კითხვებს პასუხობს. გუნდები ხშირად ორივეს უშვებენ — საწყობს აპის ბაზიდან კვებავენ (ეს უკვე ETL/ELT დეკია).

08 · რეალური სქემა · შეჯამება 3 წთ

ელცხრილის სუნიდან
სქემამდე, რომელიც მასშტაბირდება.

შევკრათ ერთად: აიღეთ ბრტყელი "ყველაფერი ერთ ცხრილში" დიზაინი და აქციეთ ის გასაღებებად, კავშირებად და შეზღუდვებად — შემდეგ კი წესებით ხელში გახვიდეთ.

ერთი ფართო ცხრილი — ხაფანგი
CREATE TABLE signups ( email TEXT, // no key — dups allowed course TEXT, // "SQL", "sql ", "Sql" teacher TEXT, // repeated on every row price TEXT // "42", "free", "$42" ); // rename a course → hunt every row. no integrity at all.
მოდელირებული — გასაღებები, ტიპები
CREATE TABLE students ( id BIGSERIAL PRIMARY KEY, email TEXT UNIQUE ); CREATE TABLE courses ( id BIGSERIAL PRIMARY KEY, name TEXT, price NUMERIC(8,2) ); CREATE TABLE enrollments ( student_id BIGINT REFERENCES students(id), course_id BIGINT REFERENCES courses(id), PRIMARY KEY (student_id, course_id) // no double-enroll );

ხუთი წესი, რომლითაც უნდა გახვიდეთ

1სქემა არის კონტრაქტი. გადაიტანეთ წესები — გასაღებები, ტიპები, NOT NULL, CHECK — ძრავაში და არა მხოლოდ აპში.
2ერთი ფაქტი, ერთი ადგილი. ნაგულისხმევად ნორმალიზეთ; დაკავშირება უცხო გასაღებებს მიანდეთ.
3დაკავშირებული ჩაწერები ტრანზაქციაში მოაქციეთ. „ყველაფერი ან არაფერი“ არის სხვაობა ბაგსა და დაკარგულ ფულს შორის.
4ნელი შეკითხვა? წაიკითხეთ გეგმა. ჯერ EXPLAIN; შემდეგ დააინდექსეთ ის სვეტები, რომლებზეც ფილტრავთ და აერთებთ.
5დენორმალიზეთ ციფრებით და არა შეგრძნებით. ჩაწერის ტკივილი წაკითხვის სიჩქარეზე მხოლოდ მაშინ გაცვალეთ, როცა გაზომვა ამას მოითხოვს.

გააგრძელეთ

  • Use The Index, Luke! — Markus Winand (ინდექსირება, უფასოდ ონლაინ)
  • Designing Data-Intensive Applications — Martin Kleppmann
  • SQL Performance Explained — Markus Winand
  • თქვენი ძრავის დოკუმენტაცია EXPLAIN-ზე — Postgres-საც და MySQL-საც შესანიშნავი აქვთ

ერთი წინადადება დასამახსოვრებლად

"დაამოდელირეთ ჭეშმარიტება ერთხელ, დაცვა ძრავას მიანდეთ და კითხვები SQL-ით დაუსვით."

— მთელი მოხსენება, შეკუმშული

ცოდნის შემოწმება

დაგამახსოვრდათ?

ხუთი სწრაფი კითხვა სქემის დიზაინზე, SQL-ზე, ტრანზაქციებსა და ინდექსებზე — მყისიერი უკუკავშირი, ავტორიზაციის გარეშე.

შეაფასეთ ეს დასტა
იყავით პირველი

ნავიგაცია ← → ისრებით ან სქროლით · ნაწილი 2: გაღრმავებული SQL → · უკან ბიბლიოთეკაში