Сергей Ю. Каменев @inetstar
Алгоритмист. Автор. Поставщик SSD, RAID, серверов.
Information
- Rating
- 15-th
- Location
- Москва, Москва и Московская обл., Россия
- Works in
- Date of birth
- Registered
- Activity
Specialization
Backend Developer, Software Architect
Lead
From 500,000 ₽
SQL
Python
Linux
MySQL
Database
Golang
High-loaded systems
OOP
Docker
PostgreSQL
Ради точности скажу, что в статье по вашей ссылке идёт речь о JIT-компиляции с кешированием результата, а COS/M однократно компилируется в опкоды или вообще в машинные коды (Caché и GT.M делают это немного по-разному).
1) когда каждая транзакция принципиальна важна
2) их очень много
место для буфера остаётся только в плате рейд-контроллера с батарейкой и Write-back cache.
Видимо для таких случаев и оставлена возможность установки буфера в ноль.
Хотя с другой стороны, если мы для надёжности пишем сразу на 2 сервера, то можем спокойно буферизировать запись. Если один из них сломается — запишется на втором.
Есть проблема. Человек писавший ту статью умер. Я не могу попросить его выложить результаты тестов.
Однако в недрах wayback machine я нашёл инфу о другом тестировании GlobalsDB vs Redis.
Его проводил Rob Tweed. У него можно узнать детали.
Started up the Redis server and then ran src/redis-benchmark -n 100000.
In summary:
SET: 88,105 /sec
GET: 88,573 /sec
Bear in mind that this benchmark runs a bunch of SETs, then a bunch of GETs, rather than a combination together, and is presumably optimised to show off Redis in as good a light as possible.
Compare with the results for Node.js + Globals, where both Sets and Gets were happening together:
read: 92,000 / sec
write: 50,571 /sec
Or, in terms of total database hits: 142,571 /sec
** Looks like Globals is giving Redis (supposedly the fastest thing on the planet) a serious spanking! **
=======================
Here's the full Redis-benchmark trace for anyone interested:
rob@rob-ProLiant-ML115-G5:~/redis-2.2.13$ src/redis-benchmark -n 100000
====== PING (inline) ======
100000 requests completed in 1.22 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.87% <= 1 milliseconds
100.00% <= 1 milliseconds
82169.27 requests per second
====== PING ======
100000 requests completed in 1.22 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.81% <= 1 milliseconds
100.00% <= 1 milliseconds
82034.45 requests per second
====== MSET (10 keys) ======
100000 requests completed in 2.21 seconds
50 parallel clients
3 bytes payload
keep alive: 1
67.53% <= 1 milliseconds
99.93% <= 2 milliseconds
99.99% <= 3 milliseconds
99.99% <= 4 milliseconds
100.00% <= 5 milliseconds
100.00% <= 6 milliseconds
100.00% <= 7 milliseconds
100.00% <= 8 milliseconds
45248.87 requests per second
====== SET ======
100000 requests completed in 1.13 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.90% <= 1 milliseconds
100.00% <= 1 milliseconds
88105.73 requests per second
====== GET ======
100000 requests completed in 1.14 seconds
50 parallel clients
3 bytes payload
keep alive: 1
100.00% <= 0 milliseconds
87412.59 requests per second
====== INCR ======
100000 requests completed in 1.12 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.98% <= 1 milliseconds
100.00% <= 1 milliseconds
88967.98 requests per second
====== LPUSH ======
100000 requests completed in 1.13 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.98% <= 1 milliseconds
99.99% <= 2 milliseconds
100.00% <= 3 milliseconds
100.00% <= 3 milliseconds
88573.96 requests per second
====== LPOP ======
100000 requests completed in 1.14 seconds
50 parallel clients
3 bytes payload
keep alive: 1
100.00% <= 0 milliseconds
87796.30 requests per second
====== SADD ======
100000 requests completed in 1.20 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.95% <= 10 milliseconds
99.96% <= 11 milliseconds
99.96% <= 12 milliseconds
99.97% <= 13 milliseconds
99.97% <= 14 milliseconds
99.98% <= 15 milliseconds
99.98% <= 16 milliseconds
99.99% <= 17 milliseconds
99.99% <= 18 milliseconds
100.00% <= 18 milliseconds
83612.04 requests per second
====== SPOP ======
100000 requests completed in 1.17 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.97% <= 1 milliseconds
99.99% <= 2 milliseconds
100.00% <= 3 milliseconds
100.00% <= 4 milliseconds
100.00% <= 4 milliseconds
85763.29 requests per second
====== LPUSH (again, in order to bench LRANGE) ======
100000 requests completed in 1.12 seconds
50 parallel clients
3 bytes payload
keep alive: 1
100.00% <= 0 milliseconds
88888.89 requests per second
====== LRANGE (first 100 elements) ======
100000 requests completed in 2.41 seconds
50 parallel clients
3 bytes payload
keep alive: 1
48.07% <= 1 milliseconds
99.31% <= 2 milliseconds
99.99% <= 3 milliseconds
100.00% <= 3 milliseconds
41476.57 requests per second
====== LRANGE (first 300 elements) ======
100000 requests completed in 4.80 seconds
50 parallel clients
3 bytes payload
keep alive: 1
1.26% <= 1 milliseconds
66.13% <= 2 milliseconds
95.34% <= 3 milliseconds
99.88% <= 4 milliseconds
99.95% <= 5 milliseconds
99.98% <= 7 milliseconds
99.98% <= 8 milliseconds
99.98% <= 10 milliseconds
99.99% <= 11 milliseconds
99.99% <= 13 milliseconds
99.99% <= 14 milliseconds
99.99% <= 16 milliseconds
99.99% <= 17 milliseconds
99.99% <= 19 milliseconds
99.99% <= 20 milliseconds
99.99% <= 23 milliseconds
99.99% <= 24 milliseconds
99.99% <= 26 milliseconds
100.00% <= 28 milliseconds
100.00% <= 30 milliseconds
100.00% <= 32 milliseconds
100.00% <= 34 milliseconds
100.00% <= 36 milliseconds
100.00% <= 38 milliseconds
20842.02 requests per second
====== LRANGE (first 450 elements) ======
100000 requests completed in 6.68 seconds
50 parallel clients
3 bytes payload
keep alive: 1
0.64% <= 1 milliseconds
29.63% <= 2 milliseconds
74.15% <= 3 milliseconds
91.33% <= 4 milliseconds
99.79% <= 5 milliseconds
99.89% <= 6 milliseconds
99.92% <= 7 milliseconds
99.96% <= 8 milliseconds
100.00% <= 8 milliseconds
14974.54 requests per second
====== LRANGE (first 600 elements) ======
100000 requests completed in 8.34 seconds
50 parallel clients
3 bytes payload
keep alive: 1
1.31% <= 1 milliseconds
29.83% <= 2 milliseconds
59.73% <= 3 milliseconds
83.49% <= 4 milliseconds
95.88% <= 5 milliseconds
99.78% <= 6 milliseconds
99.91% <= 7 milliseconds
99.95% <= 8 milliseconds
99.95% <= 9 milliseconds
99.98% <= 10 milliseconds
100.00% <= 10 milliseconds
11996.16 requests per second
rob@rob-ProLiant-ML115-G5:~/redis-2.2.13$
habrahabr.ru/company/intersystems/blog/194818
redis.io/topics/persistence
Loading and Saving In-Memory Databases www.sqlite.org/backup.html
Чудес не бывает. Хорошая оптимизация кода, алгоритмов кеширования, продуманное и экономичное использование памяти. Преимущества долгого развития.
При гигантском размере базы никакое самое мудрое кеширование не спасёт и упрёмся в IOPS. Кстати говоря, не все БД умеют работать со скоростью большей чем IOPSы SSD.
Ведь и in-memory БД тоже скидывают на диск когда-то.
А если данные в in-memory БД чисто на чтение, то можно в базе на глобалах выставить время синхронизации с диском раз в миллион лет.
D — обеспечивается тем, что при отключении питания не должны повредиться уже проведённые транзакции.
Когда выдаётся команда COMMIT происходит запись на диск, а всё что до COMMIT может храниться в буфере.
Конечно у СУБД на глобалах задачи несколько другие. Более универсальные.
Я не пытаюсь доказать, что диск быстрее памяти )))
А я думаю честное. И с чего вы взяли, что задачи разные? Может быть задача одна и та же, раз сравнивали с in-memory БД — скорость. Если на одинаковой ОЗУ, одинаковом размере памяти и одинаковом размере памяти база на глобалах скоростнее, то значит она скоростнее. В чём тут нечестность?
Твоя задача скорость — вот и получаешь скорость.
Да.
Однако насколько я знаю: буферизация записи вещь настраиваемая. Можно её вообще выставить в ноль. А можно раздуть, если нужно для задачи.
Причём она может происходить автоматически как на уровне ОС, так и на уровне БД. Можно на уровне БД вообще отключить.
Если мы тестируем базу на 10ТБ на глобалах и in-memory базу на 20GB в ОЗУ — это нечестное тестирование. Разные объёмы.
При равных объёмах ОЗУ она выигрывает в скорости за счёт оптимизаций, которые десятками лет выдумывались. Делая синхронизацию когда нужно. Если на машине стоит рейд-контроллер с Write-back и батарейкой, то БД на глобалах может скидывать данные на диск хоть каждую десятую секунды. Write-back кэш будет проглатывать их мгновенно.
При последнем подходе иногда может получиться даже, что база 10ТБ на глобалах будет быстрее чем in-memory база на 20GB в ОЗУ.
Я изложу свои догадки, поскольку я не разработчик.
При случайной выборке из объёмов многократно превосходящих ОЗУ мы упираемся в IOPS диска (тестировал сам), но сама структура хранения глобалов такова, что дерево хранится очень компактно, и поэтому за один IOPS считывается кусок дерева.
То же при обновлении: я подозреваю, что за 1 IOPS обновляется сразу несколько значений узлов.
При записи происходит сортировка и определённая буферизация, очень оттюнингованная. За один IOPS записывается сразу много значений узлов.
Также нужно учитывать, что современные in-memory БД появились недавно и поэтому не до конца оптимизированы. Авторы вот этой статьи утверждают, что GlobalsDB рвёт по тестам Redis. Можно у них попросить данные тестирований.
Также, я думаю, если скидывать значения на диск раз в несколько секунд, то одних ассемблерных и алгоритмических оптимизаций будет достаточно, чтобы выигрывать у тех in-memory продуктов, которые полностью написаны на высокоуровневых языках.
Скорость глобалов = Простая архитектура * Ассемблер * Отточенность алгоритмов * Компиляция COS/M * Продуманные алгоритмы кеширования
Каждый множитель важен. И, наверное, есть ещё какие-то множители, которые только разработчики знают.
Меня лично изумила скорость вставки при первом знакомстве с глобалами.