Навроцкий Артем
Допустим, мы хотим написать приложение wishlist, которое будет хранить список понравившихся товаров.
У нас есть простые операции: просмотр списка, добавление и удаление товаров.
Все данные загружаем в память из файла в JSON.
При изменении данных пишем JSON на диск.

Для прототипа такое решение может сгодиться, но не более.
| Стоимость операции () | нс (ns) | мкс (µs) | мс (ms) |
|---|---|---|---|
| Чтение 1Мб из RAM (последовательный доступ) | 50 000 | 50 | |
| Round trip внутри одного датацентра | 100 000 | 100 | |
| Чтение 1Мб из SSD (последовательный доступ) | 200 000 | 200 | |
| Чтение 1Мб из RAM (случайный доступ, 64б) | 1 000 000 | 1 000 | 1 |
| Сжатие 1Mb (zstd_fast) | 2 000 000 | 2 000 | 2 |
| Чтение 1Мб из HDD (последовательный доступ) | 2 000 000 | 2 000 | 2 |
| Позиционирование HDD | 10 000 000 | 10 000 | 10 |
| Чтение 1Мб из SSD (случайный доступ, 8Кб) | 15 000 000 | 15 000 | 15 |
| Отправка 1Mb через 1Гбит/сек сеть | 10 000 000 | 10 000 | 10 |
| Round trip NA Central ⇔ West | 40 000 000 | 40 000 | 40 |
| Чтение 1Мб из HDD (случайный доступ, 8Кб) | 2 000 000 000 | 2 000 000 | 2 000 |
Размер блока должен быть выровнен и его нельзя уменьшать. Иначе получаем чтение перед записью.


Основная идея — данные временно сохраняются в специальном буфере (буфере обмена) перед записью.
Это позволяет накапливать изменения и уменьшать общее количество записи.
Порядок записи блоков из буфера в общем случае не детерминирован.
Журнал транзакций — специальный журнал, который содержит информацию о всех транзакциях, которые были выполнены в базе данных.
Batching — алгоритм, который позволяет сгруппировать несколько операций в одну пачку.
Является средством повышения эффективности работы сетей TCP/IP, позволяющим уменьшить количество пакетов, которые должны быть отправлены по сети.
Пока существует отправленный пакет, для которого отправитель ещё не получил никакого подтверждения о доставке, отправитель должен держать в буфере следующие данные для отправки, до тех пор, пока не наберётся достаточно данных на полный пакет, который можно отправить единожды.

B-Tree+ – это сбалансированное дерево поиска, в котором каждый узел содержит множество ключей и имеет более двух потомков.
`O(B)`
`O(log_B (N/B))`

LSM-дерево – структура данных, предоставляющая быстрый доступ по индексу в условиях частых запросов на вставку.
`O(k*log_k (N/B))`
`O((log^2 (N/B))/log k)`

TOAST_TUPLE_THRESHOLD (2Кб) сжимаются;TOAST_TUPLE_TARGET (2Кб), то он перемещается в отдельное
хранилище,
где хранится
чанками.>| SQL | MongoDB |
|---|---|
| |
| |
| |
| |
| |
SQL обычно подразумевает реляционную модель данных.
Переменная отношения находится в первой нормальной форме (1НФ) тогда и только тогда, когда в любом допустимом значении отношения каждый его кортеж содержит только одно значение для каждого из атрибутов.
| Пароль | Имя | Дата | Привилегии | Разделы | |
|---|---|---|---|---|---|
| tolstoy@mail.ru | ***** | Толстой Л.Н. | 09.09.1828 | Админ, Модератор | Новости, Флуд |
| saltykov@inbox.ru | ***** | Николай Щедрин | 27.01.1826 | Модератор | Флуд, Юмор |
| fmd@mail.ru | ***** | Федор Михайлович | 11.11.1821 | Игрок | |
| sukin_syn@mail.ru | ***** | Пушкин А.С. | 06.06.1799 | Админ | Поэзия |
Переменная отношения находится в первой нормальной форме (1НФ) тогда и только тогда, когда в любом допустимом значении отношения каждый его кортеж содержит только одно значение для каждого из атрибутов.
| Пароль | Имя | Дата | Привилегии | Разделы | |
|---|---|---|---|---|---|
| tolstoy@mail.ru | ***** | Толстой Л.Н. | 09.09.1828 | Админ | Новости |
| tolstoy@mail.ru | ***** | Толстой Л.Н. | 09.09.1828 | Админ | Флуд |
| tolstoy@mail.ru | ***** | Толстой Л.Н. | 09.09.1828 | Модератор | Новости |
| tolstoy@mail.ru | ***** | Толстой Л.Н. | 09.09.1828 | Модератор | Флуд |
| saltykov@inbox.ru | ***** | Николай Щедрин | 27.01.1826 | Модератор | Флуд |
| saltykov@inbox.ru | ***** | Николай Щедрин | 27.01.1826 | Модератор | Юмор |
| fmd@mail.ru | ***** | Федор Михайлович | 11.11.1821 | Игрок | |
| sukin_syn@mail.ru | ***** | Пушкин А.С. | 06.06.1799 | Админ | Поэзия |
Переменная отношения находится во второй нормальной форме тогда и только тогда, когда она находится в первой нормальной форме, и каждый неключевой атрибут неприводимо зависит от ее потенциального ключа.
| Пароль | Имя | Дата | Привилегии | Разделы | |
|---|---|---|---|---|---|
| tolstoy@mail.ru | ***** | Толстой Л.Н. | 09.09.1828 | Админ | Новости |
| tolstoy@mail.ru | ***** | Толстой Л.Н. | 09.09.1828 | Админ | Флуд |
| tolstoy@mail.ru | ***** | Толстой Л.Н. | 09.09.1828 | Модератор | Новости |
| tolstoy@mail.ru | ***** | Толстой Л.Н. | 09.09.1828 | Модератор | Флуд |
| saltykov@inbox.ru | ***** | Николай Щедрин | 27.01.1826 | Модератор | Флуд |
| saltykov@inbox.ru | ***** | Николай Щедрин | 27.01.1826 | Модератор | Юмор |
| fmd@mail.ru | ***** | Федор Михайлович | 11.11.1821 | Игрок | |
| sukin_syn@mail.ru | ***** | Пушкин А.С. | 06.06.1799 | Админ | Поэзия |
Переменная отношения находится во второй нормальной форме тогда и только тогда, когда она находится в первой нормальной форме, и каждый неключевой атрибут неприводимо зависит от ее потенциального ключа.
| Пароль | Имя | Дата | |
|---|---|---|---|
| tolstoy@mail.ru | ***** | Толстой Л.Н. | 09.09.1828 |
| saltykov@inbox.ru | ***** | Николай Щедрин | 27.01.1826 |
| fmd@mail.ru | ***** | Федор Михайлович | 11.11.1821 |
| sukin_syn@mail.ru | ***** | Пушкин А.С. | 06.06.1799 |
| Привилегии | Разделы | |
|---|---|---|
| tolstoy@mail.ru | Админ | Новости |
| tolstoy@mail.ru | Админ | Флуд |
| tolstoy@mail.ru | Модератор | Новости |
| tolstoy@mail.ru | Модератор | Флуд |
| saltykov@inbox.ru | Модератор | Флуд |
| saltykov@inbox.ru | Модератор | Юмор |
| fmd@mail.ru | Игрок | |
| sukin_syn@mail.ru | Админ | Поэзия |
Переменная отношения находится в третьей нормальной форме, когда она находится во второй нормальной форме, и отсутствуют транзитивные функциональные зависимости неключевых атрибутов от ключевых.
| TopicID | Заголовок | Дата | Автор | Текст | Оценка | Ответы | |
|---|---|---|---|---|---|---|---|
| 5 | Руслан и Людмила | 01.01.1820 | Пушкин А.С. | sukin_syn@mail.ru | … | 500 | 120 |
| 8 | Война и Мир | 01.03.1869 | Толстой Л.Н. | tolstoy@inbox.ru | … | 100 | 345 |
| 12 | Отцы и Дети | 15.08.1862 | Иван Тургенев | nedobobov@mail.ru | … | 256 | 18 |
| 43 | Повести Белкина | 12.06.1830 | Пушкин А.С. | sukin_syn@mail.ru | … | 400 | 96 |
| 16 | Муму | 10.11.1852 | Иван Тургенев | nedobobov@mail.ru | … | 312 | 46 |
Переменная отношения находится в третьей нормальной форме, когда она находится во второй нормальной форме, и отсутствуют транзитивные функциональные зависимости неключевых атрибутов от ключевых.
| Автор | |
|---|---|
| sukin_syn@mail.ru | Пушкин А.С. |
| tolstoy@inbox.ru | Толстой Л.Н. |
| nedobobov@mail.ru | Иван Тургенев |
| TopicID | Заголовок | Дата | Текст | Оценка | Ответы | |
|---|---|---|---|---|---|---|
| 5 | Руслан и Людмила | 01.01.1820 | sukin_syn@mail.ru | … | 500 | 120 |
| 8 | Война и Мир | 01.03.1869 | tolstoy@inbox.ru | … | 100 | 345 |
| 12 | Отцы и Дети | 15.08.1862 | nedobobov@mail.ru | … | 256 | 18 |
| 43 | Повести Белкина | 12.06.1830 | sukin_syn@mail.ru | … | 400 | 96 |
| 16 | Муму | 10.11.1852 | nedobobov@mail.ru | … | 312 | 46 |
Переменная отношения находится в четвёртой нормальной форме, если она находится в третьей нормальной форме и не содержит нетривиальных многозначных зависимостей.
| Привилегии | Разделы | |
|---|---|---|
| tolstoy@mail.ru | Админ | Новости |
| tolstoy@mail.ru | Админ | Флуд |
| tolstoy@mail.ru | Модератор | Новости |
| tolstoy@mail.ru | Модератор | Флуд |
| saltykov@inbox.ru | Модератор | Флуд |
| saltykov@inbox.ru | Модератор | Юмор |
| fmd@mail.ru | Игрок | |
| sukin_syn@mail.ru | Админ | Поэзия |
Переменная отношения находится в четвёртой нормальной форме, если она находится в третьей нормальной форме и не содержит нетривиальных многозначных зависимостей.
| Привилегии | |
|---|---|
| tolstoy@mail.ru | Админ |
| tolstoy@mail.ru | Модератор |
| saltykov@inbox.ru | Модератор |
| fmd@mail.ru | Игрок |
| sukin_syn@mail.ru | Админ |
| Разделы | |
|---|---|
| tolstoy@mail.ru | Новости |
| tolstoy@mail.ru | Флуд |
| saltykov@inbox.ru | Флуд |
| saltykov@inbox.ru | Юмор |
| fmd@mail.ru | |
| sukin_syn@mail.ru | Поэзия |
Чтобы поиграться можно взять готовые базы даных:
Для импорта данных MovieLens есть скприт: https://github.com/bozaro/presentations/tree/master/scripts/movielens.
SELECT * FROM movies WHERE title = 'Titanic'; id | title | year
--------+---------+------
1721 | Titanic | 1997
3404 | Titanic | 1953
118916 | Titanic | 1996
181653 | Titanic | 1943
(4 rows)EXPLAIN ANALYZE
SELECT * FROM movies WHERE title = 'Titanic'; QUERY PLAN
--------------------------------------------------------
Seq Scan on movies (cost=0.00..1707.71 rows=1 width=27)
Filter: (title = 'Titanic'::text)
Rows Removed by Filter: 86533
Planning Time: 0.077 ms
Execution Time: 7.331 msCREATE INDEX idx_movies_title ON movies (title);EXPLAIN ANALYZE
SELECT * FROM movies WHERE title = 'Titanic'; QUERY PLAN
-----------------------------------------------------------------------------
Index Scan using idx_movies_title on movies (cost=0.42..8.44 rows=1 width=27)
Index Cond: (title = 'Titanic'::text)
Planning Time: 0.325 ms
Execution Time: 0.089 msДля выбора стратегии планировщик использует ранее собранную статистику.
SQL - декларативный язык.
Для написания эффективных запросов надо мыслить категориями множеств.

ACID описывает требования к транзакционной системе, обеспечивающие наиболее надёжную и предсказуемую её работу. Требования ACID были в основном сформулированы в конце 70-х годов Джимом Греем.

Общий алгоритм:
Минимальное время транзакции не меньше времени сброса данных на диск.
| Тип | Устройство | IOPS | Интерфейс |
|---|---|---|---|
| HDD | 7,200 об/мин SATA-диски | ~75-100 | SATA 3 Гбит/с |
| HDD | 10,000 об/мин SATA-диски | ~125-150 | SATA 3 Гбит/с |
| HDD | 10,000 об/мин SAS-диски | ~140 | SAS |
| HDD | 15,000 об/мин SAS-диски | ~175-210 | SAS |
| SSD | Intel X25-M G2 MLC | ~8 600 | SATA 3 Гбит/с |
| SSD | OCZ Vertex 3 | ~60 000 (4K) | SATA 6 Гбит/с |
| SSD | OCZ Vertex 3 MAX IOPS | ~75 000 (4K) | SATA 6 Гбит/с |
| SSD | OCZ Vertex 4 | ~120 000 IOPS (4K) | SATA 6 Гбит/с |
| SSD | OCZ RevoDrive 3 X2 | ~200 000 IOPS (4K) | PCIe |
| SSD | OCZ Z-Drive R4 CloudServ | ~500 000 IOPS | PCIe |

На базе журнала транзакций так же реализуется ряд дополнительных возможностей:
| Уровень изоляции | Потерянное обновление | «Грязное» чтение | Неповторяющееся чтение | Фантомное чтение | Аномалии сериализации |
|---|---|---|---|---|---|
| Read uncommited | не возможно | допускается | возможно | возможно | возможно |
| Read commited | не возможно | не возможно | возможно | возможно | возможно |
| Repeatable read | не возможно | не возможно | не возможно | допускается | возможно |
| Serializable | не возможно | не возможно | не возможно | не возможно | не возможно |
Потерянное обновление происходит в случае перезатирания изменений другой транзакцией до заврешнения транзакции, сделавшей изменения.
| Транзакция 1 | Транзакция 2 |
|---|---|
| |
|
Чтение данных, добавленных или изменённых еще не завершенной транзакцией.
| Транзакция 1 | Транзакция 2 |
|---|---|
| |
| |
| |
|
При повторном чтении в рамках одной транзакции ранее прочитанные данные оказываются изменёнными.
| Транзакция 1 | Транзакция 2 |
|---|---|
| |
| |
| |
|
Ситуация, когда при повторном чтении в рамках одной транзакции одна и та же выборка дает разные множества строк.
От неповторяющегося чтения оно отличается тем, что результат повторного обращения к данным изменился не из-за изменения/удаления самих этих данных, а из-за появления новых (фантомных) данных.
| Транзакция 1 | Транзакция 2 |
|---|---|
| |
| |
| |
|
Ситуация, когда параллельное выполнение транзакций приводит к результату, невозможному при последовательном выполнении тех же транзакций.
| Транзакция 1 | Транзакция 2 |
|---|---|
| |
| |
| |
| Уровень изоляции | Потерянное обновление | «Грязное» чтение | Неповторяющееся чтение | Фантомное чтение | Аномалии сериализации |
|---|---|---|---|---|---|
| Read uncommited | не возможно | допускается | возможно | возможно | возможно |
| Read commited | не возможно | не возможно | возможно | возможно | возможно |
| Repeatable read | не возможно | не возможно | не возможно | допускается | возможно |
| Serializable | не возможно | не возможно | не возможно | не возможно | не возможно |
Более выской уровень изоляции транзакций уменьшает количество аномалий за счет увеличения количества блокировок и вероятности отката транзакции.
SELECT id, money FROM account WHERE name = 'Romeo';
SELECT id, money FROM account WHERE name = 'Giulietta';
UPDATE account SET money = 500 WHERE id = 13 AND money = 600;
UPDATE account SET money = 200 WHERE id = 42 AND money = 100;
COMMIT;
SELECT id, money FROM account WHERE name = 'Romeo';
COMMIT;
---
SELECT id, money FROM account WHERE name = 'Giulietta';
COMMIT;
---
UPDATE account SET money = 500 WHERE id = 13 AND money = 600;
UPDATE account SET money = 200 WHERE id = 42 AND money = 100;
COMMIT;
Для обеспечения изоляции на данные делают блокировки.
Блокировки могут быть разных уровней:
Проблема такого подхода в том, что пишущие транзакции могут заблокировать читающие транзакции.
| id | name | notes |
|---|---|---|
| 1 | Alice | Great at programming |
| 2 | Bob | Always talking to alice |
| 3 | Eve | Listens to everyone's conversations |
| id | name | notes |
|---|---|---|
| 1 | Alice | read Great at programming |
| 2 | Bob | Always talking to alice |
| 3 | Eve | Listens to everyone's conversations |
| id | name | notes |
|---|---|---|
| 1 | Alice | Great at programming |
~ update 2 | Bob | Always talking to alice |
| 3 | Eve | Listens to everyone's conversations |
| id | name | notes |
|---|---|---|
| 1 | Alice | Great at programming |
~ update 2 | Bob | Working very hard |
| 3 | Eve | Listens to everyone's conversations |
| id | name | notes |
|---|---|---|
| 1 | Alice | Great at programming |
| 2 | Bob | Working very hard |
| 3 | Eve | Listens to everyone's conversations |
+ insert 4 | Dave | Very promising new-hire |
| id | name | notes |
|---|---|---|
| 1 | Alice | Great at programming |
| 2 | Bob | Working very hard |
- delete 3 | Eve | Listens to everyone's conversations |
| 4 | Dave | Very promising new-hire |
| id | name | notes |
|---|---|---|
| 1 | Alice | Great at programming |
| 2 | Bob | Working very hard |
| 4 | Dave | Very promising new-hire |
| id | name | notes |
|---|---|---|
| 1 | Alice | read Great at programming |
+ update 2 | Bob | Working very hard |
- delete 3 | Eve | Listens to everyone's conversations |
+ insert 4 | Dave | Very promising new-hire |
TXID: 103 xmin | xmax | id | name | notes |
|---|---|---|---|---|
| 100 | 0 | 1 | Alice | Great at programming |
| 101 | 0 | 2 | Bob | Always talk to alice |
| 102 | 0 | 3 | Eve | Listens to everyone's conversations |
TXID: 103 xmin | xmax | id | name | notes |
|---|---|---|---|---|
| 100 | 0 | 1 | Alice | Great at programming |
- update 101 | 0 | 2 | Bob | Always talk to alice |
| 102 | 0 | 3 | Eve | Listens to everyone's conversations |
TXID: 103 xmin | xmax | id | name | notes |
|---|---|---|---|---|
| 100 | 0 | 1 | Alice | Great at programming |
- update 101 | 103 | 2 | Bob | Always talk to alice |
| 102 | 0 | 3 | Eve | Listens to everyone's conversations |
TXID: 103 xmin | xmax | id | name | notes |
|---|---|---|---|---|
| 100 | 0 | 1 | Alice | Great at programming |
- update 101 | 103 | 2 | Bob | Always talk to alice |
| 102 | 0 | 3 | Eve | Listens to everyone's conversations |
+ update 103 | 0 | 2 | Bob | Working very hard |
TXID: 104 xmin | xmax | id | name | notes |
|---|---|---|---|---|
| 100 | 0 | 1 | Alice | Great at programming |
| 101 | 103 | 2 | Bob | Always talk to alice |
| 102 | 0 | 3 | Eve | Listens to everyone's conversations |
| 103 | 0 | 2 | Bob | Working very hard |
TXID: 104 xmin | xmax | id | name | notes |
|---|---|---|---|---|
| 100 | 0 | 1 | Alice | Great at programming |
| 101 | 103 | 2 | Bob | Always talk to alice |
| 102 | 0 | 3 | Eve | Listens to everyone's conversations |
| 103 | 0 | 2 | Bob | Working very hard |
+ insert 104 | 0 | 4 | Dave | Very promising new-hire |
TXID: 105 xmin | xmax | id | name | notes |
|---|---|---|---|---|
| 100 | 0 | 1 | Alice | Great at programming |
| 101 | 103 | 2 | Bob | Always talk to alice |
| 102 | 0 | 3 | Eve | Listens to everyone's conversations |
| 103 | 0 | 2 | Bob | Working very hard |
| 104 | 0 | 4 | Dave | Very promising new-hire |
TXID: 105 xmin | xmax | id | name | notes |
|---|---|---|---|---|
| 100 | 0 | 1 | Alice | Great at programming |
| 101 | 103 | 2 | Bob | Always talk to alice |
- delete 102 | 105 | 3 | Eve | Listens to everyone's conversations |
| 103 | 0 | 2 | Bob | Working very hard |
| 104 | 0 | 4 | Dave | Very promising new-hire |
TXID: 106 xmin | xmax | id | name | notes |
|---|---|---|---|---|
| 100 | 0 | 1 | Alice | Great at programming |
| 101 | 103 | 2 | Bob | Always talk to alice |
| 102 | 105 | 3 | Eve | Listens to everyone's conversations |
| 103 | 0 | 2 | Bob | Working very hard |
| 104 | 0 | 4 | Dave | Very promising new-hire |
test=# BEGIN TRANSACTION
test-# ISOLATION LEVEL REPEATABLE READ;
BEGIN
test=# SELECT balance
test-# FROM accounts WHERE name = 'Alice';
balance
---------
400
(1 row)
test=# UPDATE accounts
test-# SET balance = balance + 100
test-# WHERE name = 'Alice';
UPDATE 1
test=# COMMIT;
COMMIT
test=# BEGIN TRANSACTION
test-# ISOLATION LEVEL REPEATABLE READ;
BEGIN
test=# SELECT balance
test-# FROM accounts WHERE name = 'Alice';
balance
---------
400
(1 row)
test=# UPDATE accounts
test-# SET balance = balance + 100
test-# WHERE name = 'Alice';
ERROR: could not serialize access
due to concurrent update
test=# BEGIN TRANSACTION
test-# ISOLATION LEVEL REPEATABLE READ;
BEGIN
test=# SELECT balance
test-# FROM accounts WHERE name = 'Alice';
balance
---------
500
(1 row)
test=# UPDATE accounts
test-# SET balance = balance + 100
test-# WHERE name = 'Alice';
UPDATE 1
test=# ROLLBACK;
ROLLBACK
test=# BEGIN TRANSACTION
test-# ISOLATION LEVEL REPEATABLE READ;
BEGIN
test=# SELECT balance
test-# FROM accounts WHERE name = 'Alice';
balance
---------
500
(1 row)
test=# UPDATE accounts
test-# SET balance = balance + 100
test-# WHERE name = 'Alice';
UPDATE 1
test=# COMMIT;
COMMIT
lock_timeout).SEQUENCETRUNCATE TABLECREATE INDEX CONCURRENTLY

Идея: каким-то образом на реплике поддерживать данные в состоянии как на мастере, тогда в случае выхода из строя мастера, можно сделать реплику мастером и продолжить работу.

Подчиненный сервер повторяет состояние главного и не может изменять данные самостоятельно.
Возможен так же вариант, когда данные на подчинённом сервере повторяются с задержкой.
Есть несколько вариантов организации кластера:
Кластер представляется как одна система (Single-System Image, SSI), то есть эквивалент операционной системы для кластера в целом.
В результате нет необходимости в модификации существующих приложений — все это осуществляется автоматически, прозрачно для приложений пообно SMP.
Узлы кластера используют единую файловую систему.
Операционная система берет на себя координацию работы с файловой системой и ряд сервисных функций.
Приложение должно явно поддерживать работу в кластере.
Функции кластера целиком реализуются внутри приложения.
В распределенной системе возможно выбрать только 2 из 3-х свойств:
На самом деле, если у нас больше одного сервера обрабатывающего запросы, то мы уже выбрали P.
Поэтому выбирать можем только между A и C.
Необходимо выбрать новый мастер и реплики могут сделать это самостоятельно (RAFT). Для достижения консенсуса требуется `N/2+1` доступных реплик.
Защитный механизм защищающий от split-brain. Мастер потеряв связанность с остальным кластером, должен через заданное время перестать считать себя мастером.
С другой стороны, возможно он действительно остался один и перестав обрабатывать запросы произошел полный отказ СУБД.
И кто же нас спасёт тогда?
Если данных больше чем может поместиться на одном сервере или недостаточно производительности, то нужно шардировать базу.
Если данных становится так много, что они не помещаются на имеющиеся шарды, то добавляют еще - это называется решардингом.
Для упрощения этого процесса есть приемы, например, виртуальное шардирование.

Универсального ответа нет, надо смотреть на< характер данных и профиль нагрузкиЖ
Все рекомендации субъективны и в основном обусловлены личным опытом.