- Метрики качества
- Проблемы нагрузок
- Тестирование производительности
- Индексы
- Репликация
- Шардирование
- Кеширование
- Брокеры сообщений
- Транзакции в РСУБД
- In-memory СУБД
rps
,rpm
- количество запросов в единицу времениpps
(packets per second),mps
(megabytes ...) - количество данных в единицу времениsimultaneous connections
,concurrency
- количество одновременных соединенийlatency
- задержка ответаthroughput
- пропускная способность системы (в хайлоад жертвуют latency ради этого)
Статистические величины (95, 99) - перцентили
- Вертикальное - увеличение мощности сервера
- Горизонтальное - использование большего количества серверов
В операционных системах с планировщиком задач существует проблема переключения контекста между задачами и потоками, так как необходимо пройти несколько этапов
- Сохранение регистров процессора
- Сохранение общей информации pid, tid, uid, gid, euid, egid и т. д.
- Сохранение состояния процесса/потока
- Сохранение прав доступа
- Сохранение используемых потоком ресурсов и блокировок
- Сохранение счетчики использования ресурсов (например, таймеры использованного процессорного времени)
- Сохранение регионов памяти, выделенных процессу
- Очистка конвейера команд и данных процессора
- Очистка TLB, отвечающий за страничное отображение линейных адресов на физические
В системе linux процессы и потоки отличаются доступом к памяти. У потоков она общая
- создание процесса
clone(
child_stack=0,
ags=CLONE_CHILD_CLEARTID |
CLONE_CHILD_SETTID |
SIGCHLD,
child_tidptr=0x7fa001a93a10) = 6916
- создание потока
clone(
child_stack=0,
ags=CLONE_VM|
CLONE_FS|
CLONE_FILES|
CLONE_SYSVSEM|
CLONE_SIGHAND|
CLONE_THREAD|
CLONE_SETTLS|
CLONE_PARENT_S
child_tidptr=0x7fa001a93a10) = 6916
- worker (многопоточный)
- prefork (многопроцессный)
- синхронный (select)
- async (асинхронный epol)
- Комбинированные варианты
- переключение контекста между потоками менее затратное
- меньше потребление памяти
- потоки сложнее синхронизировать
Блокирующие системные вызовы блокируют процесс (или поток) до того, как будут получены данные (часть данных). Во время блокировки процесс не потребляет процессорное время, но потребляет память. Чтение из сети может быть неблокирующим. В неблокирующем режиме чтение из сокета возвращает или ответ (хотя бы его часть), или сообщение, что данные еще не готовы. Чтение с диска может быть только блокирующим(*)
- Apache - синхронный многопроцессный
- Nginx - асинхронный многопоточный
- Асинхронный код - быстрый, потребляет мало памяти.
- Асинхронный код - сложно писать.
- Синхронный код - медленее, потребляет больше памяти.
- Синхронный код - просто писать.
-
Perl
- Обычно работает внутри apache.
- Синхнронный язык с поддержкой тредов (редко).
- Есть библиотека AnyEvent (еще реже тредов)
- Есть библиотека для fiber
-
Php
- CLI SAPI - в качестве консольной команды php для запуска наших кронов и других cli-программ (Command Line Interface)
- apxs2 SAPI - в качестве модуля к apache2
- CGI SAPI - в качестве запускаемого на каждом запросе CGI (сейчас так почти никто не делает)
- FPM SAPI - Fast Process Manager, написанный для PHP разработчиками из комании Badoo и теперь поддерживаемый сообществом
- Можно делать потоки, но редко используется.
-
Python
- Есть потоки.
- Есть fiber.
- Процесс с потоками в Python может утилизировать только одно ядро процессора
-
Node.js
- Асинхронный код.
- Строго однопоточный.
-
Go
- Концепция зеленых тредов.
- Умеет использовать столько ядер ЦПУ, сколько нужно.
- Ориентирован на микросервисы.
- Быстрый.
Frontend, Backend, Storage
- Задачи frontend (reverse proxy)
- Терминировать ssl-соединения.
- Обработка медленных клиентов.
- Отдача статики.
- Keep-Alive.
- Кэширование.
- Балансировка.
- Роутинг по бэкендам
- Задачи backend
- Бизнес-логика
- Ожидание ответов от хранилищ
- Задачи хранилищ
- Хранение информации
- Быстрый поиск (индексы)
- Обеспечение транзакционности (ACID)
-
Node.js multithreading: What are Worker Threads and why do they matter? - LogRocket Blog https://blog.logrocket.com/node-js-multithreading-what-are-worker-threads-and-why-do-they-matter-48ab102f8b10/
-
Влияние Transparent Huge Pages на производительность системы https://habr.com/ru/company/tinkoff/blog/446342/
-
Is this explanation about VSS/RSS/PSS/USS accurate? https://stackoverflow.com/questions/22372960/is-this-explanation-about-vss-rss-pss-uss-accurate
- Администраторы, backend
- Быстрые ответы сервера
- Эффективное использование оборудования
- Проектировщики интерфейсов, Frontend, Android, iOs
- Клиентская оптимизация, быстрая отрисовка
- Энергоэффективность
- Аналитики
- Удобство использования
- Понятность бизнес-процесса, запоминаемость
- нагрузка на диск
- fio - flexible i/o tester https://github.com/axboe/fio
- The following example benchmarks maximum write throughput:
fio --ioengine=sync --direct=0 \ --fsync_on_close=1 --randrepeat=0 --nrfiles=1 --name=seqwrite --rw=write \ --bs=1m --size=20G --end_fsync=1 --fallocate=none --overwrite=0 --numjobs=1 \ --directory=/mnt/gcfs --loops=10
- The following example benchmarks maximum write IOPS:
fio --ioengine=sync --direct=0 \ --fsync_on_close=1 --randrepeat=0 --nrfiles=1 --name=randwrite --rw=randwrite \ --bs=4K --size=1G --end_fsync=1 --fallocate=none --overwrite=0 --numjobs=80 \ --sync=1 --directory=/mnt/standard --loops=10
- fio - flexible i/o tester https://github.com/axboe/fio
- нагрузка на процессор
- Phoronix https://phoronix-test-suite.com/
phoronix-test-suite benchmark smallpt Test Results: 57.683207988739 57.237819910049 57.684161901474 Average: 57.54 Seconds
- Phoronix https://phoronix-test-suite.com/
- нагрузка на сеть
- iperf3 https://software.es.net/iperf/
iperf3 -s iperf3 -c 192.168.1.66 [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-10.00 sec 42.9 GBytes 36.8 Gbits/sec 0 sender [ 4] 0.00-10.00 sec 42.9 GBytes 36.8 Gbits/sec
- iperf3 https://software.es.net/iperf/
- Подать нагрузку на балансировщик
httpperf
,ab
- Подать нагрузку на сервер баз данных
sysbench
(MySQL),pgbench
(PostgreeSQL)
- Подать нагрузку на сервер приложений
curl
,wget
,ab
- на уровне TCP -
tcpreplay
Производятся динамически связанные запросы к системе, связанные со сценариями пользовательской активности, с высокой интенсивностью
- Самописные утилиты "Бешенный пользователь"
- Функциональный тест без пауз в несколько потоков
Производится точная нагрузка, в подходящее время, в нужном месте, с нужной силой, с анализом происходящего
- Appache.JMeter - простые сценарии (Groovy, Java, JavaScript)
- Gatling - сложные сценарии с десятками зпросов (Scala)
- wrk - автоматизация на Lua
- Yandex.Tank - единый отчет, масштабирование (Go, Pyhon)
- Смоделировать нагрузку
- Отладить мониторинг (для сравнения результатов теста с показателями мониторинга систем)
TIG
- Telegraph, InfluxDB, GrafanaPrometheus
- Telegraf, Prometheus, GrafanaZabbix
- Zabbix, Grafana.
- Сравнить со стандартами для оборудования
-
Нагрузочное (load testing) - выполнение программы с повышением нагрузки от среднего профиля до максимального и выше
- подаем нагрузку ступенями, наращиваем количество операций в единицу времени ( выдержит ли система нагрузку, есть ли узкие места)
- выше максимума, чтобы знать есть ли запас
- ищем точку деградации и доступности (timeout, 504)
- сравниваем с профилем нагрузки (min, med, max)
- ищем узкое место, лимиты, в.т.ч конфигурационные
- необходимо понимание работы системы
- ищем точку появления ошибок под нагрузкой
- стараемся избавиться от ошибок и предупреждений
- их их отношения к профилю нагрузки (med, max)
- выбираем профиль для теста стабильности (80% от точки деградации)
-
Стабильности (stability testing) - длительное тестирование со средней нагрузкой, поиском отклонений и проверками корректности работы (что будет при эксплуатации 24/7, стабильна ли система под нагрузкой, как быстро растет размер базы данных, есть ли утечка соединений, нет ли потерь данных)
-
Стрессовое (stress testing) - тестирование за пределами рабочих нагрузок в ограниченных ресурсах (если нагрузка ненадолго превысит максимум, освобождаются ли неиспользуемые ресурсы, восстановится ли система после ошибок, какие ошибки проявляются под нагрузкой)
- быстро и ненадолго повысим нагрузку и повторим такой процесс несколько раз
- Выявим утечку ресурсов, проверим корректность обработки исключений
- выявим утечку ресурсов, возможно, что ресурсы не освобождаются полностью
- возможно, что ресурсы не освобождаются частично
- быстро ли разбираются внутренние очереди системы
-
Обьемное (volume testing) - тестирование при увеличении обьемов данных (что будет через 5 лет работы, а если увеличить размер документов)
- нужен генератор баз данных и лучше его писать на SQL
-
Масштабируемости (scalability testing) - серия нагрузочных тестов для разных профилей оборудования, количества серверов или узлов системы, настроек - размеров очередей, лимитов (поможет ли увеличение памяти в 2 раза, какой предел на другом железе, какая производительность на двух серверах)
- Наращивание мощьности оборудования вертикальное масштабирование
- Тестирование на разных конфигурациях для прогноза затрат
- Наращивание мощьности оборудования имеет технический предел
- Наращивание количества узлов горизонтальное масштабирование
- Наращивание количества узлов закладывается в архитектуру
- Наращивание количества узлов - дает гибкость
- Лучше всего использовать SQL для генерации данных
- высокая скорость генерации
- можно сгенерировать 100Гбайт
- не нужны интеграции
- SQL - может многое
- API - гибкость и надежность
- генерация на активной системе
- быстрый отклик по корректировке
Большинство проблем с производительностью баз данных решается индексами
- Ускоряют запросы
- Позволяют делать constaints (UNIQUE, FOREIGN KEY)
- BTREE
- RTREE
- Hash
- FULLTEXT
-
умеет
- Поиск по равенству (а=5)
- Поиск по открытым диапазонам (а > 5 b a < 3)
- Поиск по закрытому диапазону ( 2 < f < 8)
-
не умеет
- Искать четные числа
- Искать суффиксы
-
для строк те же правила (Like то же работает с индексами)
- хорошо
LIKE "a%"
, плохоLIKE "%c"
- хорошо
- InnoDB - указателем на данные для вторичного ключа будет значение первичного ключа (у любого индекса есть скрытый элемент индекса)
- MyIsam - указателем на данные для ключа будет физическое смещение в файле
- Могут быть многоколоночными key(a,b,c)
- сравниваются по элементам массива
- Индексы дорогая штука
- при записи данных происходит обновление индексов
- при чтении приходится анализировать больше индексов
- Кластерный ключ - InnoDB - создает скрытый кластерный ключ, из PRIMARY_KEY или UNIQUE_KEY или создает самостоятельно
- если правильно задавать кластерный ключ,то можно достичь высокой степени локализации записей ( к примеру идентификатор комментария к посту, является конкатенацией идентификатора поста и своей части) это позволяет достичь хранения всех комменатиев рядом друг с другом, что ускоряет чтение
- Поиск данных
- Сортировка
- Избежание чтения из таблицы (покрывающие индексы - covering indexes)
- Специальные оптимизации
USE db;
CREATE TABLE tbl (
id bigint(20) NOT NULL AUTO_INCREMENT,
a bigint(20) NOT NULL,
b bigint(20) NOT NULL,
c bigint(20) NOT NULL,
d bigint(20) NOT NULL,
e bigint(20) NOT NULL,
str VARCHAR(54) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
- контейнер - data/docker-compose.yml
- данные - mock-generate.py
EXPLAIN SELECT a, b from tbl where a = 1;
- type: all - полное сканирование
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | tbl | NULL | ALL | NULL | NULL | NULL | NULL | 21460 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
- создаем индекс
CREATE INDEX a ON tbl(a);
EXPLAIN SELECT a, b from tbl where a = 1;
+----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------+
| 1 | SIMPLE | tbl | NULL | ref | a | a | 8 | const | 10730 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------+
- type: ref
EXPLAIN SELECT a from tbl where a = 1;
- Extra: Using index - все данные берутся из индекса
+----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------------+
| 1 | SIMPLE | tbl | NULL | ref | a | a | 8 | const | 10730 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------------+
EXPLAIN SELECT a, b from tbl where a = 1 and b = 3
- запрос по индеску а, с дополнительным сканированием по b
+----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------------+
| 1 | SIMPLE | tbl | NULL | ref | a | a | 8 | const | 10730 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------------+
CREATE INDEX a_b ON tbl(a,b);
EXPLAIN SELECT a, b from tbl where a = 1 and b = 3
- составной индекс и запрос по соответствующим ключам позволяет получать данные покрывающим индексом
+----+-------------+-------+------------+------+---------------+------+---------+-------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-------------+------+----------+-------------+
| 1 | SIMPLE | tbl | NULL | ref | a_b | a_b | 16 | const,const | 1000 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+------+---------+-------------+------+----------+-------------+
CREATE INDEX a_b_c ON tbl(a,b,c)
- Хорошие запросы
- where a = 0
- where a > 0
- where a = 0 and b > 4
- where a = 0 and b = 2
- where a = 0 and b = 2 and c > 2
- where a = 0 and b in (2,4) and c > 3
- Плохие запросы (индекс не работает,так как префикс а не используется)
- where b > 3
- where b = 6
- where b = 2
- Частичное использование - префикс индекса работает с лева на право, до первого условия неравенства включительно
- a > 0 and b = 4
- a = 0 and b < 3 and c = 3
- a = 0 and b > 3 and c < 3
- Хороший запрос
- SELECT * FROM tbl ORDER BY a LIMIT 10
- SELECT * FROM tbl WHERE a = 1 ORDER BY b LIMIT 10
- SELECT * FROM tbl WHERE a > 1 ORDER BY a LIMIT 10 - те же правила к сортировке что и к неравенству (это же сравнение)
- SELECT * FROM tbl ORDER BY a DESC, b DESC LIMIT 10 - сортировка должна быть однонаправленной
- SELECT * FROM tbl ORDER BY a ASC, b ASC LIMIT 10
- Плохие запросы
- SELECT * FROM tbl ORDER BY b LIMIT 10
- SELECT * FROM tbl WHERE a > 1 ORDER BY b LIMIT 10
- SELECT * FROM tbl WHERE a in (1,2,3) ORDER BY b LIMIT 10 - в сортировке in не работает!!!
- SELECT * FROM tbl ORDER BY a ASC, d DESC LIMIT 10 - в новых версиях появились разнонаправленные индексы
- min и max - очень быстро работают по индексам, другие функции не ускоряются
- CREATE INDEX a_str ON tbl(a, str)
- SELECT a, max(str) from tbl GROUP BY a
- В MySQL используется метод nested loops, но с оптимизациями
- JOIN должны быть по индексам
SELECT * FROM posts WHERE author='Peter'
JOIN comments ON posts.id = comments.post_id
- Индекс по post_id - бесполезен
- Индекс по comments.post_id - необходим
- OR это плохая конструкция в запросе
- Можно использовать два индекса, вместо составного
- можно составить индекс по префиксу строки, важна селективность
- between
- нельзя -
SELECT * FROM tbl WHERE a BETWEEN 0 AND 5 AND b = 5
- можно
SELECT * FROM tbl WHERE a IN (0,1,2,3,4,5) AND b = 5
- нельзя -
- фейковые фильтры - если надо использовать одно значение из составного индекса, можно включить весь первый
- SELECT * from tbl WHERE gender in('m', 'f') and city = 'Moscow' (index - gender_city)
- сортировка
- нельзя
SELECT * FROM tbl WHERE a IN(0,1) ORDER BY b LIMIT 10
- можно
к оглавлению >>(SELECT * FROM tbl WHERE a = 0 ORDER BY b LIMIT 10) UNION ALL (SELECT * FROM tbl WHERE a = 1 ORDER BY b LIMIT 10) ORDER BY b LIMIT 10
- нельзя
Репликация - это копирование данных
- один из способов масштабирования
- можно использовать множество серверов для обработки запросов
- не является backup
- Не помогает ускорить запись
- помогает ускорить чтение
- Помогает при падениях
- один источник данных
- наиболее распространенный подход
- относительно просто и понятно
- отключить/включить реплику
- мастер иногда крашится...
- нет единой точки отказа
- постоянное время работы
- легкий failover
- нет консистентности (все обязательно поломается)
- group replication?
- sync
- закомитили локально, закомитили удаленно (Postgres)
- сделанное изменения видные всем и везде
- async
- закомитили локально, все (MySQL, Postgres))
- никаких гарантий на другом конце
- semi-sync
- закомитили локально, получили ack (MySQL)
- доступно здесь, уже скопировано на другой конец
- Master:
- обрабатывает запросы
- делает транзакции
- пишет binary logs
- binlog dump thread (SHOW PROCESSLIST на мастере)
- Slave:
- стягивает изменения с мастера, кладет в relay log
- читает данные из relay log и применяет изменения
- slave I/O thread (SHOW SLAVE STATUS на слейве)
- slave SQL thread
https://dev.mysql.com/doc/refman/5.7/en/replication-implementation-details.html
- statement based
- передаются сами запросы
- гоняется небольшое количество данных
- все запросы в логе
- UPDATE items SET enabled=1 WHERE time < UNIX_TIMESTAMP(NOW())-60 и все поломалось каждый запрос считается на каждой ноде https://dev.mysql.com/doc/refman/8.0/en/replication-rbr-safe-unsafe.html
- row based
- передаются измененные строчки
- бинарный формат
- непонятны границы statementа
- его трудно читать
- before/after image
- Посмотрите на binlog_row_image: full, minimal, blob https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html#sysvar_binlog_row_image
- mixed
https://dev.mysql.com/doc/refman/5.7/en/binary-log-setting.html
- можно реплицировать данные частично
- можно обогощать данные слейва
- использовать осторожно!
Опции:
- replicate_do_db, replicate_ignore_db, replicate_do_table... https://dev.mysql.com/doc/refman/5.7/en/change-replication-filter.html
- binary log position (FILE NAME + OFFSET)
- mysql-bin.00078:44
- локальный для сервера
- обязательно разъедется!
- GTID (SOURCE_ID:TRANSACTION_ID)
- 7F33BC78-56CA-44B3-5E33-B34CC7689333:44
- глобален, генерируется автоматически при коммите
- бесплатная трассировка
- простой slave promotion
- используйте его!
- https://dev.mysql.com/doc/refman/5.7/en/replication-gtids.html
- обычно используется однопоточная репликация всех данных
- с MySQL5.6 можно реплицировать параллельно несколько баз данных
- с MySQL5.7 можно реплицировать параллельно одни и те же таблицы
Полезные опции:
- slave-parallel-workers
- slave-parallel-type (DATABASE|LOGICAL_CLOCK)
https://dev.mysql.com/doc/refman/5.7/en/replication-options-slave.html
- 1 мастер, 3 слейва
- первый реплицирует в 1 поток
- второй реплицирует в 20 потоков
- третий реплицирует в 100 потоков
- вставка в 25 различных таблиц внутри одной базы в 100 потоков (sysbench)
https://www.percona.com/blog/2016/02/10/estimating-potential-for-mysql-5-7-parallel-replication/
- физические изменения страниц (значение в блоке 2564 равно 154)
- сюда попадают абсолютно все операции
- один журная на все
https://wiki.postgresql.org/images/a/af/FOSDEM-2015-New-WAL-format.pdf
- есть binlog
- у некоторых движков есть аналог WAL (InnoDB Undo/Redo Log)
- с точки зрения MySQL - это разные логи!
- нарушение абстракций (логгирование реализовано на разных уровнях программы)
- MySQL пишет больше данных для репликации (~1.5 раза)
https://dev.mysql.com/doc/refman/5.7/en/innodb-undo-logs.html
- тот самый row based формат
- работает с кортежами данных
- не знает, как они хранятся на диске
- CPU-bound, можно параллелить по процессорам
- В Postgres10+ тоже есть - https://www.postgresql.org/docs/10/logical-replication.html
- работает со страницами
- slave = master байт в байт
- Postgres WAL, InnoDB Undo/RedoLog - как примеры работающих со страницами
- IO-bound, нет смысла параллелить
- плагин начиная с версии MySQL5.7
- по сути - синхронная репликация
- нет концепта master-slave, скорее master-master
- репликация между всеми нодами
- single primary (по умолчанию)
- кворум, умеет automatic failover
- flow control (я что-то очень отстал)
- лимит в 9 node
Можно попробовать групповую репликацю в докере:
- https://mysqlhighavailability.com/setting-up-mysql-group-replication-with-mysql-docker-images/ Документация:
- https://dev.mysql.com/doc/refman/5.7/en/group-replication.html Обзор, бенчмарки, сравнения:
- http://mysqlhighavailability.com/performance-evaluation-mysql-5-7-group-replication/
- подготовить транзакию движком базы данных
- записать транзакию в binary log
- завершить транзакию в движке базы
- вернуть ответ клиенту
- В нормальной ситуации отставание достигает секунды.
- Плохие запросы (и по мастеру, и по реплике) могут вызвать отставание репликации.
- Рекомендация:
- Убивайте медленные запросы.
- Держите отдельную реплику для медленных запросов.
- Думайте о кросс-СУБД репликации.
- Старайтесь избегать паттерна запись-чтение.
- свои данные читаем с мастера, чуижие со слейва
- Монотонное чтение
- Ожидается, что пользователь не будет видеть пропадающие комментарии.
- Вариант - привязать пользователя к реплике.
- Согласованное префиксное чтение
- Характерно для шардированных баз данных.
- Важно для сохранения причинно-следственных связей.
- Избегание конфликтов.
- Last wins.
- Ранг реплик. Выйгрывает запись от старшей реплики.
- Слияние.
- Решение конфликтов на клиенте.
- Conflict-free replicated data types (CRDT).
- Mergeable persistent data structures.
- Предотвращение энтропии данных - Формула для расчета кворума: w + r > number of replicas
- https://www.youtube.com/watch?v=ppI74hTuXO0
- https://www.youtube.com/watch?v=_r8vLPTl0PE
- https://m.habr.com/ru/company/oleg-bunin/blog/414111/
- https://severalnines.com/database-blog/overview-logical-replication-postgresql
- документация MySQL
- документация Postgres
- блог Percona
- берем данные и разделяем по какому-то признаку
- разделенные данные физически лежат отдельно
- все в пределах одного сервера!
- бывает разных типов
- в теории, должно работать быстрее (?)
- key
- range
- list
- hash
CREATE TABLE members (
firstname VARCHAR(25) NOT NULL,
lastname VARCHAR(25) NOT NULL,
username VARCHAR(16) NOT NULL,
email VARCHAR(35),
joined DATE NOT NULL
)
PARTITION BY RANGE( YEAR(joined) ) (
PARTITION p0 VALUES LESS THAN (1960),
PARTITION p1 VALUES LESS THAN (1970),
PARTITION p2 VALUES LESS THAN (1980),
PARTITION p3 VALUES LESS THAN (1990),
PARTITION p4 VALUES LESS THAN MAXVALUE
);
CREATE TABLE measurement (
city_id
int not null,
logdate
date not null,
peaktemp
int,
unitsales
int
) PARTITION BY RANGE (logdate);
CREATE TABLE measurement_y2006m02 PARTITION OF measurement
FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
CREATE TABLE measurement_y2006m03 PARTITION OF measurement
FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
- один из вариантов масштабирования
- разбиение данных на куски
- данные живут физически на разных серверах (ну обычно так)
- не репликация - !
- не партиционирование - !
- горизонтальное масштабирование
- ускорить обработку запросов (особенно запись!)
- повысить отказоустойчивость(*)
- Key Based Sharding
- еще называют hash based
- формула примерно такая: F(key) -> shard_id
- F и key очень важны
- наиболее распространенный способ
- плюсы
- просто и понятно (+)
- равномерное распределение (+)
- алгоритмическое распределение (+)
- минусы
- добавление/удаление шарда всегда боль (-)
- Range Based Sharding
- Directory Based Sharding
- похож на range based
- отображение key -> shard_id
- low cardinality для key - наш случай
- плюсы
- гибкость (+)
- минусы
- обновление (-)
- SPOF (-)
- из приложения (умный клиент)
- простой метод
- нет лишних хопов
- как обновлять/решардить?
- прокси
- приложение вообще не знает о шардинге
- код не меняется
- лишний хоп (*)
- могут падать
- им нужно общаться
- https://www.proxysql.com https://shardingsphere.apache.org/document/current/en/manual/sharding-proxy
- координатор
- приложение вообще не знает о шардинге
- код не меняется
- лишний хоп
- нагрузка
- SPOF
- Плохо распределили данные
- подобрать лучший ключ/функцию шардирования
- решардинг (если имеет смысл)
- JOIN
- держать нужные данные на одной машине
- делать вычисления на одной машине
- В лоб:
- в худшем случае нужно перемешать все данные
- если степени 2ки - уже неплохо (переместить ~50%)
- Пример:
- формула shard_id % count
- 16 записей на 8 шардов => 2 записи на шард
- 16 записей на 16 шардов => 1 запись на шард
- Consistent hashing
- используем одну hash-функцию для нод и данных
- выбирается ближайший по часовой стрелке узел
- при добавлении/удалении затрагивается только часть
- формально не больше K/N (K-ключи, N-сервера)
- Данные могут быть распределены неравномерно.
- Consistent hashing - Virtual Nodes
- помогает разделить данные более равномерно
- количество физических серверов не меняется
- запросы по ключу шардирования вероятно ускорятся
- запросы не по ключу обойдут все шарды
- запросы по диапазону ключей могут обойти все шарды
- aggregating, joinы - отдельная тема
Было:
- Total = Serial + Parallel
Стало:
- Total = Serial + Parallel/N + Xserial
https://en.wikipedia.org/wiki/Amdahl%27s_law
- MySQL не умеет автошардинг (NDB ?)
- Postgres умеет автошардинг (FDW)
- Cassandra умеет автошардинг
- MongoDB умеет автошардинг
CREATE TABLE temperature (
id BIGSERIAL PRIMARY KEY NOT NULL,
city_id INT NOT NULL,
timestamp TIMESTAMP NOT NULL,
temp DECIMAL(5,2) NOT NULL
);
CREATE TABLE temperature_201904 (
id BIGSERIAL NOT NULL,
city_id INT NOT NULL,
timestamp TIMESTAMP NOT NULL,
temp DECIMAL(5,2) NOT NULL
);
CREATE EXTENSION postgres_fdw;
GRANT USAGE ON FOREIGN DATA WRAPPER postgres_fdw to app_user;
CREATE SERVER shard02 FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (dbname 'postgres', host 'shard02', port
'5432');
CREATE USER MAPPING for app_user SERVER shard02
OPTIONS (user 'fdw_user', password 'secret');
CREATE FOREIGN TABLE temperature_201904 PARTITION OF temperature
FOR VALUES FROM ('2019-04-01') TO ('2019-05-01')
SERVER remoteserver01;
- https://www.percona.com/blog/2018/08/21/foreign-data-wrappers-postgresql-postgres_fdw
- https://www.percona.com/blog/2019/05/24/an-overview-of-sharding-in-postgresql-and-how-it-relates-to-mongodbs
- https://www.youtube.com/watch?v=MhGO7BBqSBU
- https://www.youtube.com/watch?v=xx_Lv1P_X_I
- https://www.youtube.com/watch?v=ihrPoHlDFkk
- https://www.mysql.com/products/cluster/scalability.html
- https://www.postgresql.org/docs/9.5/postgres-fdw.html
- https://clickhouse.yandex/docs/en/operations/table_engines/distributed/
- Система для горизонтального масштабирования MySQL
- Обеспечивает read-tolerance и переключение мастер-баз в случае выхода мастера из строя
- Умеет
- горизонтальное масштабирование(шардирование)
- пуллинг соединений
- кеширование запросов(удаление дублей)
- 2хфазный коммит(c недавнего времени)
- топология(при помощи consul, etcd, ZooKeeper)
- ACL на запросы
- Не умеет
- last_inserted_id на шардированном кейспейсе: SELECT last_inserted_id() FROM user;
- distinct & функции агреггирования: SELECT DISTINCT a, COUNT(*) FROM user
- если используется группировка по полю, то поле должно обязательно быть написано в SELECT: SELECT a FROM user GROUP BY b;
- удаление по шардам с лимитом: DELETE FROM user LIMIT 10;
- Полный список: https://github.com/vitessio/vitess/blob/master/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt
- Cell — группа серверов и сетевой инфраструктуры, изолированной от аварий
в других cell(датацентр)
- Обычно, это или ДЦ или часть ДЦ, иногда называется “зона” или “зона
доступности”.
- Keyspace — логическая база данных
- Если используется шардирование, то кейспейс указывает на несколько
- MySQL баз данных, иначе на одну базу
- Чтение данных из кейспейса представляет собой чтение из MySQL
- Keyspace graph позволяет Vitess решать, какой шард использовать для
конкретного запроса
- Shard — логическая часть внутри кейспейса. Обычно содержит один мастер
и много MySQL слейвов
- В конфигурации имеет следующий вид
- SHARD=-80, где -80 — это ключи [0x0, 0x80)
- Или же SHARD=80-, где 80- — это ключи [0x80, 0xff]
- Таблетка — это комбинация процесса mysql и vttablet, запущенных на одной
и той же машине.
- master — мастер-база
- replica — слейв, который может быть продвинут до мастера
- rdonly — слейв, который не может быть продвинут до мастера. Обычно, используется в качестве обработки background задач, дампа данных, тяжелых запросов
- backup — база с остановленной репликацией
- restore — база, в которую наливаются данные из бекапа.
- drained — разезервированная база для background задач Vitess’a
- Сервера, которые хранят данные о топологии и предоставляют
- распределенный сервис блокировок
- Например, etcd, ZooKeeper или Consul
- Роутит запросы
- Координирует таблеты между собой
- Хранит конфигурацию серверов
Описание того, как данные лежат внутри кейспейсов и шардов. Чаще всего описывается JSONом, например
{
"sharded": true,
"tables": {
"dinosaurs": {
"column_vindexes": [
{"column": "id", "name": "hash"}
]
}
}
- hash — 3DES null-key hash
- consistent_lookup_unique — хеш по уникальным значениям в таблице(primary key)
- unicode_loose_md5 — md5 по ключу в таблице
https://vitess.io/docs/reference/vschema/ — полный список
- Прокси-сервер между пользователем и vttablet.
- Общается с клиентом по MySQL протоколу
- Общается с vttablet по gRPC
- vtctl — CLI для администрирования vitess-кластера
- Занимается долгими задачами, например, решардингом
Кэш или кеш(англ. cache, от фр. cacher — «прятать»; произносится [kæʃ] — «кэш») — промежуточный буфер с быстрым доступом к нему, содержащий информацию, которая может быть запрошена с наибольшей вероятностью. Доступ к данным в кэше осуществляется быстрее, чем выборка исходных данных из более медленной памяти или удалённого источника, однако её объём существенно ограничен по сравнению с хранилищем исходных данных.
В локальные моменты времени используется лишь небольшое подмножество данных.
- Для чатов - последние сообщения
- Для новостей - последние новости
- Для сервиса такси - активные заказы.
- Алгоритм Белади (идеальный алгоритм)
- Least recently used (LRU)
- Most Recently Used (MRU)
- Псевдо-LRU
- Least-Frequently Used
- Adaptive Replacement Cache
Вы должны держать нагрузку без кеша. Задача кеша - ускорить ответ, а не держать нагрузку. Проводите учения. Убедитесь, что вы работаете без кеша.
- Кеширование в браузере
- Кеширование страниц (или блоков)
- Кеширование данных базы
Кешируем только GET, ожидается, что GET - идемпотентен.
- ETAG
- If-Modified-Since
- Cache-Control
- LocalStorage
Самый простой вариант - указывать версию в гет-параметрах. Еще более правильный вариант - делать название файла md5-суммой от содержимого.
- кеширование статичского контента
- https://habr.com/ru/post/428127/
proxy_cache_path /data/nginx/cache keys_zone=cache_zone:10m;
map $request_method $purge_method {
PURGE
1;
default 0;
}
server {
...
location / {
proxy_pass http://backend;
proxy_cache cache_zone;
proxy_cache_key $uri;
proxy_cache_purge $purge_method;
}
}
Походы в базу данных могут быть достаточно дорогими. В этом случае, результаты запросов имеет смысл сохранять в кеш. Самыми распространенными вариантами являются:
- Храниние данных в ОЗУ/shared memory
- memcache
- redis
- Redis:
- Большое колличество команд
- Есть встроенный lua
- Сложный типы данных
- Есть персистентный режим
- Кластерный
- Сложный в настройках
- Memcache:
- Простой
- Многопоточный
- Для кластерных конфигураций есть moxy.
- Tarantool поддерживает протокол memcache
AverageTime = DbAccessTime * CacheMissRate + CacheAccessTime
Пусть:
- DbAccessTime = 100ms CacheAccessTime = 20ms
- Тогда при CacheMissRate > 0.8 - кеш вреден!
При отсутствии ключа есть большой риск перегрузить базу. Для избежания проблем с перегрузкой базы необходимо ставить локи. Хорошие пример - новая запись в блоге под высокой нагрузкой.
- Получаем доступ к кэшу cache, его срок жизни истёк.
- Пытаемся заблокироваться по ключу user_cache_lock.
- Не удалось получить блокировку:
- ждём снятия блокировки;
- Не дождались
- возвращаем старые данные кэша;
- Дождались
- выбираем значения ключа заново, возвращаем новые данные (построенный кэш другим процессом).
- Удалось получить блокировку:
- строим кэш самостоятельно.
После аварии кеш, скорее всего будет инвалидирован. А в случае неперсистентных хранилищ кеша не будет точно при пропадании питания. Подняться с непрогретым кешом сложная задача. Общий рецепт:
- Заранее напишите скрипт прогрева кешей
- Возвращайте нагрузку плавно
- Помните о правиле No1
-
Инвалидация по времени
- Указываем TTL
- После устаревание, автоматическое обновление
- Данные могут быть не консистентны при большом TTL.
- При малом TTL будет высокий cache miss.
-
Инвалидация по событию
- Удаляем данные из кеша при изменении в базе
- Опасно из-за риска мгновенной инвалидации.
-
Инвалидация по ключу
- Меняем данные в базе
- В ключ включаем версию
- Версию храним отдельно
- Старые версии протухают со временем
- Плюс - высокая консистентность.
Версии тэгов:
tag1 -> 25
tag2 -> 63
Кэш выборки:
[
срок годности: 2008-11-07 21:00
данные кэша: [
...
]
тэги: [
tag1: 25
tag2: 63
]
]
- Асинхронные ответы на долгие запросы
- построение отчетов
- аналитические, бухгалтерские запросы
- Асинхронная обработка очереди запросов
- Передача данных между приложениями, микросервисами
- использование базы данных
- медленно, неэффективно
- использование IPC, shared memory
- сложная реализация, блокировки
- отсутствие кластеризации
- использование систем доставки сообщений
- Rabbitmq
- Kafka
- NATS
- Redis ( pub/sub, streams)
- Гарантия доставки сообщений
- at least once delivery
- at most once delivery
- exactly once
- Порядок передачи сообщений
- Управлению размером очереди (ограничение на передачу)
- Зеркалирование
- Масштабирование
AMQP (Advanced Message Queuing Protocol) обеспечивает взаимодействие между клиентами и брокерами (промежуточным ПО для обмена сообщениями).
- Протокол обеспечивает:
- Надежность доставки сообщений
- Высокую скорость доставки сообщений
- Подтверждение приема сообщений
- Брокер – это приложение, реализующее модель AMQP, которое принимает соединения клиентов для маршрутизации сообщений и т.п.
- Сообщение (message) – это единица передаваемых данных (включая полезные данные и атрибуты сообщения).
- Потребитель (consumer) – приложение, которое получает сообщения из очередей.
- Производитель (producer)– приложение, которое отправляет сообщения в очередь через обмен.
- гарантия доставки сообщений
- at least once delivery
- at most once delivery
- Гарантирует порядок передачи сообщений (FIFO)
- Возможность сохранения на диск
- Подтверждение отправки, подтверждение получения
- Ограничение кол-ва отправляемых сообщение
- Управление поведением неполученных сообщений
- Кластеризация
- Зеркалирование
- Producer, consumer, message - (рассмотрели ранее)
- Queue: Буфер который хранит сообщения
- Connection: TCP соединение между приложениями и менеджером очередей
- Channel: Виртуальное соединение внутри соединения. Когда вы публикуете или получаете сообщения через очередь – это все делается в канале.
- Exchange: Получает сообщение от поставщика и отправляет его в очередь. Зависит от типа.
- Binding: Связь между очередью и обработчиком сообщений
- Routing key: Ключ на который смотрит обработчик и решает в какую очередь перенаправить сообщение
- Users: Возможность подключится к брокеру сообщений с помощью имени пользователя и пароля.
- Vhost, virtual host: Способ разделения приложений используя один и тот же экземпляр RabbitMQ.
Сообщения публикуются в очередь не на прямую, вместо этого, поставщик шлет сообщение обработчику который отвечает за перенаправление сообщения в нужную очередь с помощью биндинга ключей роутинга.
- Поставщик публикует сообщение в exchange: direct, fanout, topics, headers Exchange. Типы
- Обработчик получает сообщение и отвечает за его перенаправление. Обработчик берет различные атрибуты, такие как, ключ роутинга, зависимость на тип обмена и другие.
- Создается связь между обработчиком и очередью
- Сообщение остается в очереди до тех пор пока не будет обработано получателем.
- Получатель обрабатывает сообщение
Ключ маршрутизации — значение, которое указывается при публикации сообщения в точку обмена, служит для определения очередей в которые попадет сообщение.
Заголовок сообщения — набор аргументов вида ключ- значение связанных с сообщением.
-
direct — сообщения попавшие в эту точку обмена будут скопированы только в те очереди, которые связаны с точкой обмена строгим ключом маршрутизации.
-
fanout — сообщение поступившее в точку обмена копируется во все привязанные очереди, без проверки ключа маршрутизации или заголовка сообщения.
-
topic — ключ маршрутизации может быть составным, и задаваться в виде паттерна, для чего существуют два специальных символа: * — обозначает одно слово, # — одно или несколько слов. Слова разделяются точкой. Пример: routingKey = "*.database"
-
headers — очередь связывается с обработчиком по заголовку сообщения, указывается условие, какие аргументы и их значения ожидаются
- auto_delete — если очередь пустая и к ней нет активных подключений, очередь автоматически удаляется
- durable — устойчивая очередь, сообщения не теряются при рестарте rabbitMQ (или внезапной перезагрузке), при публикации и до окончания отдачи хранятся в базе данных
- exclusive — очередь предназначена для не более чем одного подключения единовременно
- passive — при объявлении очереди пассивной, при обращении клиента сервер будет считать что очередь уже создана, т.е. не будет автоматически создавать ее в случае отсутствия, этот вариант нужен если вы хотите обратиться к серверу не изменяя его состояние.
- internal - очередь между exchanges
- Синхронизировать /var/lib/rabbitmq/.erlang.cookie
- rabbitmqctl stop_app
- rabbitmqctl join_cluster --ram rabbit@master
- rabbitmqctl start_app
- По умолчанию очереди не синхронизированы!
- Включение зеркалирования очередей
- # rabbitmqctl set_policy ha-all ".*" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
- Включение зеркалирования очередей
По-умолчанию, в случае если RabbitMQ начинает использовать больше 40%
от общего объема памяти, то все соединения блокируются.
При высвобождении памяти до требуемого уровня нормальный процесс
работы RabbitMQ возобновляется. Порог используемой памяти можно
переопределить:
rabbitmqctl set_vm_memory_high_watermark 0.5
или перманентно в /etc/rabbitmq/rabbitmq.config
[{rabbit, [{vm_memory_high_watermark, 0.5}]}].
- Kafka — это
- распределенный
- реплицированный
- журнал фиксации изменений (commit log).
- Гарантия доставки
- Репликация журнала и синхронизация реплик
- Уведомление о смещении(offset) получателя
- Уведомление о получении сообщения для продюсера
- хранит свои записи на диске и ничего не держит в
-оперативной памяти
- Операции считывания и записи выполняются за постоянное время O(1)
- Последовательное чтение и запись на диск
- Использует кэш диска
- API для producers/consumers
- поддержка KSQL
- Consumer через pull запрашивает о появлении новых сообщений
- Журнал очищается на основе настроек времени и размера
- Есть возможность сжатия журнала
- Есть возможность сдвига смещения для переигрывания приема сообщений
- Для хранения метаданных используется zookeeper, в последних версиях отдельные топики kafka
- Record – Запись, состоящая из ключа и значения
- Topic – категория или имя потока куда публикуются записи
- Producer – процесс публикующий данные в топик
- Consumer – процесс читающий данные из топика
- Offset – позиция записи
- Partition – шард топика
- Zookeeper – это распределенное хранилище ключей и значений. Оно сильно
оптимизировано для считывания, но записи в нем происходят медленнее.
Применяется для хранения метаданных и обработки механизмов
кластеризации (hearbeat, распределенные операции обновления,
конфигурации, т.д.).
- Offset групп потребителей в рамках секции
- ACL — используются для ограничения доступа /авторизации
- Квоты — максимальные предельные количества сообщений в секунду
- Partition Leaders и уровень их работоспособности
- ПО или аппартное обеспечение могут отказать в любое время
- В любой момент может произойти фатальный сбой ПО
- Может случиться разрыв сети
- Клиенты работают с базой параллельно, могут перезаписывать друг друга
- Клиенты могут читать частично обновленные состояния
Транзакция — способ группировки приложением нескольких операций записи и чтения в одну логическую единицу. По сути, все операции записи и чтения в ней выполняются как одна: вся транзакция или целиком выполняется успешно (с фиксацией изменений), или целиком завершается неудачно (с прерыванием и откатом). Появились в середине 1970-х, с тех пор не сильно менялись. В NoSQL от них часто отказываются.
- Atomicity: Атомарность означает "все или ничего". Гарантирует, что либо все операции транзакции будут успешно применены, либо не будет применена ни одна из них. Благодаря этому появляется возможность повторять прерванные транзакции, не опасаясь что часть операций уже была выполнена.
- Consistency: Определяются инварианты системы: ( Дебит должен сходится с кредитом, Цена проданных билетов должна соответствовать выручке за сеанс.)
- В реальность означает только то, что constaints будут выполнятся.
- По сути, поддержание согласованности - задача приложения, а не базы.
- Isolation: Изоляция - свойство, которое позволяет выполнять параллельные транзакции как последовательные. Существует несколько уровней изоляции, которые дают различные гарантии. В полном смысле изоляцию обеспечивает только уровень SERIALIZABLE.
- Durability: Под этим свойством подразумевают только одно - данные при отдаче ответа зафиксированны в энергонезависимой памяти (fsync). Полностью это свойство обеспечить невозможно. Диски сбоят. Но можно повышать (репликация) гарантии.
Многие NoSQL базы, хоть и не поддерживают транзакции, обладают возможностью атомарных операций. Для memcached это:
- inc
- dec
- Чтение данных, добавленных или изменённых транзакцией, которая впоследствии не подтвердится (откатится).
- при одновременном изменении одного блока данных разными транзакциями теряются все изменения, кроме последнего;
Ситуация, когда при повторном чтении в рамках одной транзакции ранее прочитанные данные оказываются изменёнными.
- Ситуация, когда при повторном чтении в рамках одной транзакции одна и та же выборка дает разные множества строк.
Под «уровнем изоляции транзакций» понимается степень обеспечиваемой внутренними механизмами СУБД (то есть не требующей специального программирования) защиты от всех или некоторых видов вышеперечисленных несогласованности данных, возникающих при параллельном выполнении транзакций. Стандарт SQL-92 определяет шкалу из четырёх уровней изоляции: Read uncommitted, Read committed, Repeatable read, Serializable. Первый из них является самым слабым, последний — самым сильным, каждый последующий включает в себя все предыдущие.
Низший (первый) уровень изоляции. Он гарантирует только отсутствие потерянных обновлений. Если несколько параллельных транзакций пытаются изменять одну и ту же строку таблицы, то в окончательном варианте строка будет иметь значение, определенное всем набором успешно выполненных транзакций. При этом возможно считывание не только логически несогласованных данных, но и данных, изменения которых ещё не зафиксированы.
Большинство промышленных СУБД, в частности, Microsoft SQL Server, PostgreSQL и Oracle, по умолчанию используют именно этот уровень. На этом уровне обеспечивается защита от чернового, «грязного» чтения, тем не менее, в процессе работы одной транзакции другая может быть успешно завершена и сделанные ею изменения зафиксированы. В итоге первая транзакция будет работать с другим набором данных.
- Покрывает аномалии
- dirty reads
- dirty writes
- Реализация:
- Блокировка строк при записи
- Сохраняем после записи старые значения строк до коммита изменений.
Уровень, при котором читающая транзакция «не видит» изменения данных, которые были ею ранее прочитаны. При этом никакая другая транзакция не может изменять данные, читаемые текущей транзакцией, пока та не окончена.
- покрывает
- unrepeatable reads
- реализация
- Блокировки (чтение не блокирует запись, а запись не блокирует чтение)
- MVCC
- В начале каждой транзакции база данных создает список всех остальных выполняемых на текущий момент транзакций (но еще не зафиксированных или прерванных). Все выполненные этими транзакциями изменения игнорируются, даже если впоследствии они будут зафиксированы.
- Все операции записи, выполненные прерванными транзакциями, игнорируются.
- Все операции записи, выполненные транзакциями с более поздним идентификатором транзакции (то есть начавшиеся после запуска текущей транзакции), игнорируются независимо от того, были ли они зафиксированы.
- Результаты всех остальных операций записи видны запросам приложения.
Самый высокий уровень изолированности; транзакции полностью изолируются друг от друга, каждая выполняется так, как будто параллельных транзакций не существует. Только на этом уровне параллельные транзакции не подвержены эффекту «фантомного чтения».
- покрывает
- фантомное чтение
- реализация
- последовательное выполнение
- двухфазная блокировка (2-Phase Lock)
- методы оптимистического управления конкурентным доступом
- Пример - tarantool, redis.
- Хорошо подходит для In-Memory баз данных. Крайне рекомендуется использовать шардинг (но кросс-шард запросы надо синхронизировать).
- Есть комбинированное решение. OLAP - выносится в отдельные потоки выполнения с MVCC.
- Ускорение работы
- Избегаем проблем кешей
- Экономия денег
- Горизонтальное масштабирование
- Полноценный lua application server
- Хранение логов
- Аналитические запросы
- Тяжелые запросы
- space - таблица
- tuple - строка таблицы
- box.schema.space.create('myspace', {if_not_exists=true})
box.space.myspace:create_index(
'primary', {
type="TREE",
unique=true,
parts={
1, 'unsigned', 2, 'string'
},
if_not_exists=true
})
- type - тип индекса: TREE, HASH, RTREE, BITMAP
- unique - уникальность индекса
- parts - индексные поля. Важно - можно использовать префикс индекса
> box.space.myspace:insert({"test", 3, 6})
---
- error: 'Tuple field 1 type does not
match one required by operation: expected unsigned'
...
> box.space.myspace:insert({1, 'qwerty', 6})
---
- [1, 'qwerty', 6]
...
> box.space.myspace:insert({1, 10, 6})
---
- error: Duplicate key exists in unique index
'primary' in space 'myspace'
...
> box.space.myspace:insert({2, 10, 6})
---
- [2, 10, 6]
...
> box.space.myspace:insert({3, 10, 6, 98})
---
- [3, 10, 6, 98]
...
- В кортеже может быть переменное число столбцов
- В кортеж можно вставлять сложные типы данных
- Типы данных в неиндексных полях могут несовпадать.
> box.space.myspace:update({2}, {{'+', 3, 1}})
---
- [2, 10, 7]
...
> box.space.myspace:update({2}, {{'+', 3, 1}})
---
- [2, 10, 8]
...
> box.space.myspace:update({2}, {{'+', 3, 10}})
---
- [2, 10, 18]
...
> box.space.myspace:update({2}, {{'-', 3, 3}})
---
- [2, 10, 15]
...
> box.space.myspace:update({2}, {{'=', 3, 148}})
---
- [2, 10, 148]
...
- Важно: менять можно только по уникальному индексу.
box.space.myspace:create_index('tindex', {type="TREE", parts={3, 'unsigned'}, unique=false})
box.space.myspace:create_index('hindex', {type="HASH", parts={1, 'unsigned'}})
box.space.myspace:select({}) box.space.myspace.index.tindex:select({}) box.space.myspace.index.tindex:select({148})
box.space.myspace.index.tindex:select({148}, {iterator='GT'}) box.space.myspace.index.tindex:select({148}, {iterator='GE'})
box.space.myspace.index.hindex:select({3}, {iterator='GE'})
- replace
- delete
- upsert
function myproc(id)
local tup = box.space.myspace.index.tindex:select({id}, {iterator='GE'})
return tup[2]
end
- Не забывайте, что тарантул однопоточен.
- Тарантул передает управление сам при:
- Модифицирующих операциях
- Блокирующих системных вызовах
- select не передает управление
- Во время долгих циклов пользуйтесь
fiber.yield()
- Во время долгих циклов пользуйтесь
- Начало транзакции - box.begin()
- Откат транзакции - box.rollback()
- Подтверждение транзакции - box.commit() Важно: блокирующие операции откатывают транзакцию автоматически
Изучаем сервис профилей Почта@Mail.ru