От папок с CSV к общей базе группы: SQL на SQLite, индексы и планы, транзакции, Python и PostgreSQL, эксплуатация
Вячеслав Федоров
Лекция 13
Разработка и применение ПО
в физических исследованиях
SQL
Содержание
От папок с файлами к общей базе группы
01
Зачем СУБД
02
Модель и SQL
03
Индексы и планы
04
Транзакции
05
Python и базы
06
Эксплуатация и выбор
Цель лекции
Перейти от папок с файлами к базе данных, которой можно доверять
Тот, кто понимает модель данных, индексы и транзакции, проектирует схему журнала измерений, пишет запросы, которые отвечают за миллисекунды, не теряет записанное при сбоях и одновременной работе, восстанавливает базу из резервной копии и выбирает СУБД по задаче, а не по моде.
2 кампании, 41 заход (40 с измерениями), 5 каналов по 5000 точек, 100 Гц: 1 000 000 измерений
Для физика
В синтетическом журнале сигнал BPM задан пропорциональным энергии пучка: 0,02 на МэВ с шумом.
Пояснение
Тот же журнал проходит через SQLite, PostgreSQL, SQLAlchemy и pandas.
Зачем СУБД
Пределы файлов, OLTP, OLAP и временные ряды, различия систем, встраиваемая и серверная архитектура
01
Зачем СУБД
Модель и SQL
Индексы и планы
Транзакции
Python и базы
Эксплуатация и выбор
Файлы
и СУБД
01
Одновременный доступ
скрипт дописывает файл, анализ читает недописанную строку
02
Целостность
в столбце температуры «error», ссылка на удалённый заход
03
Поиск
ради одного канала одного захода читаются все файлы
04
Восстановление
сбой посреди записи оставляет файл испорченным
Задача
Средство СУБД
одновременный доступ
транзакции, блокировки, версии строк
целостность
типы, ключи, NOT NULL, UNIQUE
поиск
язык SQL и индексы
восстановление
журнал WAL и резервные копии
Для физика
Папка 2026-08-20/ с двадцатью файлами и result_final_v2.csv — знакомая история данных установки.
OLTP, OLAP
и временные ряды
Нагрузка
Запросы
Пример
Типичные СУБД
OLTP
много коротких транзакций, по нескольку строк
запись захода, оплата в магазине
PostgreSQL, MySQL, SQLite
OLAP
редкие тяжёлые запросы по миллиардам строк
статистика архива пучка за сезон
ClickHouse, DuckDB
временные ряды
поток точек «время — значение», агрегаты по окнам
вакуум, температуры, токи магнитов
InfluxDB, TimescaleDB
Пояснение
Нагрузка задаёт устройство системы: строки или столбцы на диске, транзакции, способ вставки.
Для физика
Журнал запусков — OLTP, анализ архива за сезон — OLAP, медленный контроль ускорителя — временные ряды.
Различия
СУБД
SQLite
PostgreSQL
ClickHouse
Valkey, Redis
Объём
файл до сотен ГБ
ТБ на сервер
ПБ на кластере
объём памяти
Нагрузка
встроенная OLTP
OLTP
OLAP
ключ — значение
Язык
SQL
SQL
диалект SQL
команды GET, SET, SCAN
Транзакции
ACID, сериализуемые
ACID, уровни изоляции
атомарная вставка блока
MULTI/EXEC без отката
Репликация
нет встроенной
физическая и логическая
таблицы Replicated, Keeper
асинхронная на реплики
Пояснение
SQLite не заменит кластер, а кластер ради сотни байт не разворачивают: систему выбирают по нагрузке и объёму.
Встраиваемая
и клиент-серверная
SQLite — библиотека
процесс python3 daq.py
код программы и модуль sqlite3
lab.db, lab.db-wal
файлы на локальном диске
PostgreSQL — сервер, процесс на соединение
psql
сервис FastAPI
скрипт анализа
процесс 1
процесс 2
процесс 3
общая память
буферы страниц, блокировки, снимки
файлы таблиц и журнал WAL
Пояснение
Новое соединение с PostgreSQL — новый процесс; пул соединений, например PgBouncer, переиспользует их.
Для физика
SQLite — база одного исследователя и его скриптов; PostgreSQL — общая база группы с правами доступа.
Модель и SQL
Таблицы и ключи, нормализация, CREATE TABLE, выборка, соединение и агрегация на SQLite
02
Зачем СУБД
Модель и SQL
Индексы и планы
Транзакции
Python и базы
Эксплуатация и выбор
Реляционная
модель
Таблица runs: строка — заход, столбец — атрибут
id
experiment_id
run_number
energy_mev
1
1
1
5,0
2
1
2
5,5
31
2
31
20,0
41
2
41
25,0
Пояснение
Столбец id — первичный ключ, его выдаёт база; experiment_id — внешний ключ на experiments.id.
01
Таблица
отношение: строки — объекты, столбцы — атрибуты с типом
02
Первичный ключ
однозначно задаёт строку; обычно суррогатный id
03
Внешний ключ
ссылка на ключ другой таблицы, которую проверяет СУБД
04
Многие ко многим
промежуточная таблица с двумя внешними ключами
Нормализация
Одна широкая таблица: кампания, энергия и оператор в каждой точке
run
кампания
E
оператор
t
value
31
Ресурсные испытания
20,0
Петрова
0,00
0,406
31
Ресурсные испытания
20,0
Петрова
0,01
0,403
31
Ресурсные испытания
20,0
Петрова
0,02
0,402
Важно
Опечатка в фамилии оператора — правка миллиона строк, а сбой на середине оставляет противоречие.
01
1НФ
атомарные значения, без повторяющихся групп
02
2НФ
неключевые столбцы зависят от всего ключа
03
3НФ
и только от ключа: энергия — свойство захода, а не точки
Пояснение
Денормализация — сознательное дублирование ради быстрой аналитики; начинают с нормализованной схемы.
CREATE TABLE
и ограничения
CREATE TABLE runs ( id INTEGER PRIMARY KEY, experiment_id INTEGER NOT NULL REFERENCES experiments(id), run_number INTEGER NOT NULL, energy_mev REAL NOT NULL, operator TEXT, started_at TEXT NOT NULL, UNIQUE (experiment_id, run_number) );
PRAGMA foreign_keys = ON; -- в каждом соединении
01
PRIMARY KEY
уникален; INTEGER PRIMARY KEY — номер строки, ключ другого типа в SQLite допускает NULL
02
NOT NULL, UNIQUE
пропуск или повтор — ошибка записи
03
REFERENCES
внешний ключ; SQLite проверяет его после PRAGMA
04
STRICT
строгая проверка типов в SQLite с версии 3.37
Важно
Без PRAGMA foreign_keys = ON ссылка на несуществующую кампанию проходит без ошибки.
SELECT
и JOIN
SELECT run_number, energy_mev FROM runs WHERE energy_mev > 10 ORDER BY started_at DESC LIMIT 10;
SELECT e.name, r.run_number, m.t, m.value FROM measurements AS m JOIN runs AS r ON r.id = m.run_id JOIN experiments AS e ON e.id = r.experiment_id WHERE m.channel = 'BPM01';
Логический порядок выполнения:
01
FROM, JOIN
источники строк и условие соединения
02
WHERE
отбор строк
03
GROUP BY, HAVING
группы и отбор групп
04
SELECT
столбцы результата
05
ORDER BY, LIMIT
сортировка и ограничение
Пояснение
LEFT JOIN сохраняет строки левой таблицы без пары: так находят заходы без измерений.
GROUP BY
и HAVING
SELECT r.run_number, r.energy_mev, COUNT(*) AS n, AVG(m.value) AS mean FROM measurements AS m JOIN runs AS r ON r.id = m.run_id WHERE m.channel = 'BPM01' GROUP BY r.id HAVING COUNT(*) >= 100 ORDER BY r.energy_mev;
01
Агрегаты
COUNT, SUM, AVG, MIN, MAX сворачивают группу в одно значение
02
GROUP BY
признак группы: заход, канал, час
03
WHERE и HAVING
до группировки — по строкам, после — по агрегатам
Для физика
Сводка по заходам считается в базе: в программу приходят 40 строк, а не миллион.
Демонстрация:
журнал измерений
01
make_lab.py: схема и миллион строк одной транзакцией
02
JOIN трёх таблиц: заход 31 с названием кампании
03
GROUP BY: средний сигнал BPM01 по заходам
04
LEFT JOIN: заход без измерений
Что наблюдается
Миллион строк — за 0,9 с, файл 33,4 МиБ; средний сигнал растёт с энергией: 0,47, 0,48, 0,49 при 23,5–24,5 МэВ; без измерений — заход 41.
root@lab — запись со стенда
Индексы и планы
B-дерево, план и время запроса, цена индекса, планировщик и статистика, EXPLAIN ANALYZE
Индекс (run_id, channel): ключи упорядочены по run_id, затем по channel
Пояснение
Поиск — спуск от корня к листу: три-четыре страницы вместо просмотра миллиона строк.
Для физика
Индекс (run_id, channel) ускоряет поиск по run_id и по паре, по channel — лишь перебором run_id.
Демонстрация:
индекс и план
01
Запрос: средний сигнал BPM01 захода 7
02
План и время без индекса
03
Индекс (run_id, channel) и размер файла
04
План и время с индексом; сортировка по t
Что наблюдается
Без индекса — SCAN за 19,5 мс, с индексом — SEARCH за 0,39 мс, в 50 раз быстрее; файл вырос с 34 до 50 МиБ; ORDER BY t требует временного B-дерева.
root@lab — запись со стенда
Цена и выбор
индекса
01
Место
индекс (run_id, channel) — около 16 МиБ к 34 МиБ таблицы
02
Запись
каждая вставка обновляет все индексы таблицы
03
Селективность
условие на большую долю строк быстрее выполнить полным просмотром
04
Порядок столбцов
(run_id, channel, t) убирает и сортировку по t
-- точки канала сразу по порядку времени CREATE INDEX idx_m_run_channel_t ON measurements (run_id, channel, t);
-- он же покрывает внешний ключ run_id; -- runs (experiment_id) покрыт индексом UNIQUE
Важно
Индекс на каждый столбец замедляет запись и раздувает базу: индексы строят под частые запросы.
Планировщик
и статистика
01
Варианты
полный просмотр или индекс, способ и порядок соединений
02
Стоимость
страница подряд — 1, вразброс — 4
03
Селективность
доля строк под условием — по статистике таблицы
04
ANALYZE
собирает статистику; в PostgreSQL его запускает автоочистка
-- SQLite: строк в таблице и на значение SELECT idx, stat FROM sqlite_stat1; -- idx_city | 400001 200
-- PostgreSQL: частые значения столбца SELECT most_common_vals, n_distinct FROM pg_stats WHERE tablename = 'users' AND attname = 'city';
Важно
После массовой загрузки статистика устаревает: ANALYZE выполняют сразу после неё.
Демонстрация:
статистика ANALYZE
01
200 000 пользователей из Москвы, индекс по городу
02
Запрос без статистики и после ANALYZE
03
Ещё 200 000 жителей 2000 городов без ANALYZE
04
ANALYZE в том же и в новом соединении
Что наблюдается
Без статистики индекс медленнее просмотра: 11,7 мс против 8,1 мс; устаревшая статистика ведёт к SCAN ради одной строки — 13,7 мс; новое соединение — SEARCH за 0,01 мс.
root@lab — запись со стенда
EXPLAIN ANALYZE
в PostgreSQL
01
Дерево узлов
Seq Scan, Index Scan, Bitmap Heap Scan, Sort, Hash Join
02
Оценка и факт
rows против actual rows: многократное расхождение — устаревшая статистика
03
Buffers
страницы по 8 КиБ из кеша (hit) и с диска (read)
04
Execution Time
время на сервере без передачи результата
EXPLAIN (ANALYZE, BUFFERS) SELECT avg(value) FROM measurements WHERE run_id = 7 AND channel = 'BPM01';
Важно
EXPLAIN ANALYZE выполняет запрос: UPDATE и DELETE проверяют внутри BEGIN … ROLLBACK.
Пояснение
Sort Method: external merge Disk — сортировке не хватило work_mem (4 МБ на узел).
Демонстрация:
EXPLAIN ANALYZE
01
Таблица на миллион строк в PostgreSQL
02
План без индекса: параллельный просмотр
03
CREATE INDEX по (run_id, channel)
04
План с индексом: Index Scan
Что наблюдается
Без индекса — Parallel Seq Scan по 7353 страницам за 20,2 мс; с индексом — Index Scan по 45 страницам за 0,52 мс, почти в 40 раз быстрее.
root@lab — запись со стенда
Транзакции
ACID и цена фиксации, уровни изоляции, блокировки и WAL в SQLite, MVCC в PostgreSQL
04
Зачем СУБД
Модель и SQL
Индексы и планы
Транзакции
Python и базы
Эксплуатация и выбор
Транзакция
и ACID
def save_run(con, run_row, points): cur = con.cursor() try: cur.execute('INSERT INTO runs … ' 'VALUES (?, ?, ?, ?)', run_row) run_id = cur.lastrowid cur.executemany('INSERT INTO measurements … ' 'VALUES (?, ?, ?, ?)', [(run_id, *p) for p in points]) con.commit() # всё except sqlite3.Error: con.rollback() # или ничего raise
01
Atomicity
всё или ничего: сбой откатывает сделанное
02
Consistency
ограничения соблюдены до и после
03
Isolation
чужие промежуточные состояния не видны
04
Durability
после COMMIT данные переживут отключение питания
Пояснение
Конструкция with con: фиксирует при выходе из блока и откатывает при исключении, но соединение не закрывает.
Демонстрация:
откат и фиксация
01
save_run: заход 42 с двумя точками
02
Заход 43 с пустым значением во второй точке
03
with con: и повтор номера захода
04
1000 вставок: фиксация каждой и одна транзакция
Что наблюдается
Ошибка во второй точке откатывает и сам заход 43; фиксация каждой вставки — 776 мс, одна транзакция — 1,8 мс, в 430 раз быстрее.
root@lab — запись со стенда
Уровни
изоляции
Уровень
Грязное чтение
Неповторяемое чтение
Фантомы
Аномалия сериализации
READ UNCOMMITTED
возможно
возможно
возможны
возможна
READ COMMITTED
нет
возможно
возможны
возможна
REPEATABLE READ
нет
нет
возможны
возможна
SERIALIZABLE
нет
нет
нет
нет
Пояснение
По умолчанию: PostgreSQL — READ COMMITTED, MySQL InnoDB — REPEATABLE READ, SQLite — SERIALIZABLE.
Важно
На строгих уровнях PostgreSQL может прервать транзакцию ошибкой сериализации: её повторяют.
Блокировки
и WAL в SQLite
журнал DELETE
старые страницы — в lab.db-journal, новые — в lab.db при фиксации
COMMIT ждёт ухода всех читателей
читатели и писатель блокируют друг друга
журнал WAL
новые страницы дописываются в lab.db-wal
читатели видят снимок, писатель не ждёт
контрольная точка переносит страницы в lab.db
Пояснение
Писатель всегда один: второй ждёт timeout (в sqlite3 — 5 с) и получает database is locked.
Важно
WAL не работает на сетевом диске (NFS, SMB): все процессы должны быть на одной машине.
Демонстрация:
блокировки и WAL
01
Два соединения: писатель A и читатель B
02
Запись B во время транзакции A
03
COMMIT A при открытом чтении B
04
Режим DELETE и режим WAL
Что наблюдается
Запись B ждёт 0,5 с и получает database is locked в обоих режимах; COMMIT при открытом чтении проходит только в WAL, а B до конца своей транзакции видит 2 строки.
root@lab — запись со стенда
MVCC
в PostgreSQL
страница таблицы runs
версия 1: energy = 5.0
xmin = 745, xmax = 746 (удалена в 746)
версия 2: energy = 5.5
xmin = 746, xmax = 0 (действующая)
снимок, взятый до фиксации 746
видит версию 1
снимок, взятый после фиксации 746
видит версию 2
Пояснение
UPDATE — новая версия и отметка xmax у старой; мёртвую версию убирает VACUUM, когда её не видит ни один снимок.
Важно
Долгая транзакция удерживает старый снимок: мёртвые версии накапливаются, таблица и индексы разрастаются.
Демонстрация:
MVCC и изоляция
01
Сеанс A: строка energy = 5.0, затем UPDATE
02
Сеанс B на уровне READ COMMITTED
03
Сеанс B на уровне REPEATABLE READ
04
Свой UPDATE в B после UPDATE в A
Что наблюдается
READ COMMITTED: B видит новую версию, energy=5.5, и его UPDATE даёт 6,5; REPEATABLE READ: B видит старую версию с xmax=746, а его UPDATE — could not serialize access.
root@lab — запись со стенда
Python и базы
DB-API: sqlite3 и psycopg, SQL-инъекции и параметры, ORM SQLAlchemy, pandas и форматы файлов
05
Зачем СУБД
Модель и SQL
Индексы и планы
Транзакции
Python и базы
Эксплуатация и выбор
DB-API:
sqlite3 и psycopg
import sqlite3
con = sqlite3.connect('lab.db') with con: # транзакция con.executemany( 'INSERT INTO measurements' ' (run_id, channel, t, value)' ' VALUES (?, ?, ?, ?)', points) rows = con.execute( 'SELECT t, value FROM measurements' ' WHERE run_id = ?', (7,)).fetchall()
import psycopg
with psycopg.connect(dsn) as conn: rows = conn.execute( 'SELECT t, value FROM measurements' ' WHERE run_id = %s', (7,)).fetchall() with conn.cursor().copy( 'COPY measurements (run_id,' ' channel, t, value) FROM STDIN' ) as copy: for row in points: copy.write_row(row)
Пояснение
PEP 249: connect, execute, fetchall, commit одинаковы; различаются знаки параметров — ? и %s.
Важно
Соединение с PostgreSQL дорогое: сервис берёт его из пула (psycopg_pool), а не открывает на запрос.
SQL-инъекция
и параметры
channel = input('Канал: ')
# так нельзя: запрос склеен с чужим текстом cur.execute('SELECT t, value FROM measurements ' f"WHERE channel = '{channel}'") # ввод: ' OR '1'='1 # условие: channel = '' OR '1'='1' — все строки
# правильно: текст запроса и данные раздельно cur.execute('SELECT t, value FROM measurements ' 'WHERE channel = ?', (channel,))
01
Всегда параметры
и для своих данных, и для чисел
02
Имена — не параметры
таблицу и столбец берут из белого списка
03
executescript
выполнит все операторы склеенной строки
04
Права
сервис работает от роли без права на DROP
Демонстрация:
SQL-инъекция
01
Обычный ввод BPM01
02
Ввод ' OR '1'='1
03
Ввод с DROP TABLE через execute
04
Тот же ввод через executescript
Что наблюдается
' OR '1'='1 — склейка отдаёт все 1 000 000 строк, параметр — 0; execute отказывается выполнять второй оператор, а executescript удаляет measurements.
связь в цикле — запрос на объект; selectinload — два запроса
04
Миграции
изменения схемы оформляет Alembic
Пояснение
PostgreSQL: create_engine('postgresql+psycopg://…') и установленный драйвер psycopg.
Демонстрация:
ORM и запрос N+1
01
Модели Run и Measurement, три захода
02
SQL, построенный select(), и результат
03
Ленивая загрузка и selectinload
04
Тот же скрипт с адресом PostgreSQL
Что наблюдается
Один код для SQLite и PostgreSQL: SQL различается только записью параметра, ? и %(energy_mev_1)s; ленивая загрузка — 4 запроса, selectinload — 2.
root@lab — запись со стенда
pandas:
read_sql и to_sql
df = pd.read_sql( '''SELECT r.energy_mev, AVG(m.value) AS mean_signal FROM measurements AS m JOIN runs AS r ON r.id = m.run_id WHERE m.channel = 'BPM01' GROUP BY r.id''', con) k, b = np.polyfit(df.energy_mev, df.mean_signal, 1) df.to_sql('run_summary', con, if_exists='replace', index=False)
Формат
Для чего
CSV
обмен с любыми программами
SQLite
журнал: запись по строке, выборка по индексу
Parquet
архив для анализа: столбцы, сжатие
Важно
Таблицу to_sql создаёт без ключей и ограничений: схему постоянных данных пишут вручную.
Демонстрация:
pandas и Parquet
01
read_sql: сводка по заходам в DataFrame
02
Прямая через точки, to_sql и схема
03
Миллион строк в CSV, SQLite и Parquet
04
Чтение целиком и одного канала
Что наблюдается
Наклон 0,0200 на МэВ восстановлен; Parquet — 4,5 МиБ и 16 мс на чтение всего, SQLite — 725 мс на всё, но 3,6 мс на один канал по индексу.
root@lab — запись со стенда
Эксплуатация и выбор
Резервные копии и PITR, репликация, масштабирование, мониторинг, нереляционные хранилища, выбор СУБД
06
Зачем СУБД
Модель и SQL
Индексы и планы
Транзакции
Python и базы
Эксплуатация и выбор
Резервные
копии
Способ
Что получается
Ограничение
cp файла базы
копия, если к базе никто не подключён
в работе и в WAL — неполная копия
backup(), VACUUM INTO
согласованная копия SQLite в работе
одна машина, одна база
pg_dump
логическая копия базы
на момент начала, без PITR
pg_basebackup и архив WAL
физическая копия и журнал: PITR
весь кластер, та же версия
Важно
Копия без проверенного восстановления копией не считается: восстановление репетируют.
Для физика
Правило 3-2-1: три копии на двух видах носителей, одна из них — вне здания.
Демонстрация:
копия базы в WAL
01
writer.py: 500 строк в базе WAL, соединение открыто
02
cp основного файла
03
backup() и VACUUM INTO
04
Копия после завершения писателя
Что наблюдается
Пока писатель работает, данные в wal.db-wal (28 872 байта), а копия cp — без таблицы; backup() и VACUUM INTO — по 500 строк; после закрытия журнал перенесён в wal.db.
root@lab — запись со стенда
Демонстрация:
pg_dump и pg_restore
01
DROP TABLE в транзакции и ROLLBACK
02
pg_dump всей базы в архив
03
DROP TABLE без транзакции
04
pg_restore в новую базу и проверка
Что наблюдается
DROP TABLE внутри транзакции откатывается вместе с ней; архив pg_dump занимает 8,0 МиБ; после необратимого DROP TABLE восстановленная база снова содержит 1 000 000 строк.
root@lab — запись со стенда
WAL:
PITR и реплики
основной сервер
пишет WAL раньше, чем файлы таблиц
WAL
архив WAL и базовая копия
восстановление на момент (PITR)
физическая реплика
поток WAL; только чтение; та же версия
логическая репликация
изменения строк; другая версия или СУБД
Для физика
Ошибочный DROP TABLE в 18:03: из ночной копии и архива WAL база восстанавливается на 18:02:59.
Важно
Реплика не заменяет копию: ошибочная команда доходит до неё за доли секунды.
Масштабирование
01
Пул соединений
PgBouncer: тысячи клиентов на десятки процессов сервера
02
Реплики для чтения
отчёты и графики — с реплики, запись — на основной
03
Секционирование
PARTITION BY RANGE (t): месяц — секция
04
Шардирование
данные по ключу на нескольких серверах
Для физика
Временной ряд датчиков секционируют по месяцам: архив старше срока хранения удаляется одной командой.
Пояснение
Встроенное шардирование — у ClickHouse, MongoDB, YDB; в PostgreSQL — расширение Citus или своё в приложении.
Важно
Шардирование планируют заранее: переделка работающей базы в десятки терабайт занимает месяцы.
Мониторинг
базы
01
Ресурсы
процессор, память, место и inode, операции диска в секунду
02
Запросы
число в секунду, задержка p50 и p99, доля по типам
03
Ошибки
тревога по порогу, а не по каждой записи журнала
04
Признаки СУБД
медленные запросы, мёртвые версии, отставание реплик
-- самые дорогие запросы SELECT calls, mean_exec_time, query FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-- мёртвые версии строк SELECT relname, n_dead_tup FROM pg_stat_user_tables;
Важно
Нижние пороги: нагрузка, упавшая до нуля, — тоже авария.
Нереляционные
хранилища
Тип
Пример
Роль
Ограничение
ключ — значение в памяти
Valkey, Redis
кеш, очереди, счётчики
потеря записей после снимка RDB; блокировка сервера командой KEYS *
документы
MongoDB
разнородные метаданные
схема — в коде или в валидаторе $jsonSchema
столбцы
ClickHouse
аналитика миллиардов строк
вставка партиями от тысяч строк
временные ряды
InfluxDB, TimescaleDB
датчики, медленный контроль
сроки хранения и прореживание
Для физика
Данные ускорительного комплекса: PostgreSQL — строгие метаданные, MongoDB — разнородные измерения, Redis — текущие состояния устройств.
Выбор
СУБД
01
Скучная технология
десятилетия в работе, известные ошибки, специалисты
02
Копии и PITR
восстановление на момент и его проверка
03
Объём и рост
поместится ли на один сервер
04
Нагрузки
транзакции и аналитика — в разных системах
05
Истории неудач
не только успехи в блоге разработчиков
06
Безопасность
аутентификация, TLS, права по умолчанию
Пояснение
Один исследователь и его скрипты — SQLite; общая база группы — PostgreSQL; вторая СУБД — когда первая измеримо не справляется.
Для физика
Тип JSONB в PostgreSQL покрывает потребность в документах для большинства задач лаборатории.
Итоги
лекции
01
СУБД берёт на себя одновременный доступ, целостность, поиск и восстановление — то, с чем не справляются файлы.
02
Схема с ключами и ограничениями — документация, которую база проверяет сама; SQL описывает результат, а не перебор.
03
Индекс превращает просмотр миллиона строк в спуск по B-дереву; план выбирается по статистике.
04
Транзакция — всё или ничего; изоляцию дают блокировки в SQLite и версии строк в PostgreSQL.
05
Значения — только параметрами; копия базы — только с проверенным восстановлением.
Упражнения
и задание
01
Схема своей установки
три-четыре таблицы с ключами и импорт накопленных CSV
02
План запроса
поиск медленного запроса, индекс и проверка плана
03
Изоляция
повторение опыта MVCC на уровне SERIALIZABLE
04
Копия и восстановление
pg_dump, восстановление в новую базу, сверка строк
Пояснение
Задание курса: «Деплой стартапа „Котики в мир“» — приложение, контейнеры, сеть и база.
Для физика
Журнал своей установки в SQLite — первый шаг от папок с CSV к общей базе группы.
Источники
1
Книга курса: главы «Базы данных» и «Работа с базами данных»