Pull to refresh
151
231
Сергей Ю. Каменев @inetstar

Алгоритмист. Автор. Поставщик SSD, RAID, серверов.

Send message
Как вы думаете: насколько сложно ваше приложение заставить работать на Apache без Node.JS?
Хорошая возможность. Плюс для PostgreSQL.

Ради точности скажу, что в статье по вашей ссылке идёт речь о JIT-компиляции с кешированием результата, а COS/M однократно компилируется в опкоды или вообще в машинные коды (Caché и GT.M делают это немного по-разному).
При таком раскладе:
1) когда каждая транзакция принципиальна важна
2) их очень много

место для буфера остаётся только в плате рейд-контроллера с батарейкой и Write-back cache.

Видимо для таких случаев и оставлена возможность установки буфера в ноль.

Хотя с другой стороны, если мы для надёжности пишем сразу на 2 сервера, то можем спокойно буферизировать запись. Если один из них сломается — запишется на втором.
Для тех кто будет читать этот тред. В этом комменте можно увидеть результаты Redis vs GlobalsDB
Так и есть. Вы правы. Но всё-равно их называют in-memory. Так принято.
Вообще-то я дал ссылку на статью. А в статье говорилось о тестировании.

Есть проблема. Человек писавший ту статью умер. Я не могу попросить его выложить результаты тестов.

Однако в недрах wayback machine я нашёл инфу о другом тестировании GlobalsDB vs Redis.

Его проводил Rob Tweed. У него можно узнать детали.
http://web.archive.org/web/20120126105627/http://globalsdb.org/forum/topic/70
OK here's a comparison with Redis on the exact same machine — latest version of Redis (2.2.13), standard install.

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$
Чтобы данные не пропали. Ведь далеко невсегда они используются только для кеширования чтения.

redis.io/topics/persistence
Loading and Saving In-Memory Databases www.sqlite.org/backup.html
Вообще-то я ссылался на статью, где GlobalsDB превзошла Redis, а не была равной.

Чудес не бывает. Хорошая оптимизация кода, алгоритмов кеширования, продуманное и экономичное использование памяти. Преимущества долгого развития.

При гигантском размере базы никакое самое мудрое кеширование не спасёт и упрёмся в IOPS. Кстати говоря, не все БД умеют работать со скоростью большей чем IOPSы SSD.
Ну так это только замедляет базы на глобалах. Так что HDD — это фора. Я, кстати, могу представить как делали такое тестирование — поставили время синхронизации с диском такое же как и у in-memory и прогнали тесты.

Ведь и in-memory БД тоже скидывают на диск когда-то.
А если данные в in-memory БД чисто на чтение, то можно в базе на глобалах выставить время синхронизации с диском раз в миллион лет.
Ну а как вы думаете обеспечивают D — Oracle, MySql и MSSQL? У них у всех есть буфера.
D — обеспечивается тем, что при отключении питания не должны повредиться уже проведённые транзакции.

Когда выдаётся команда COMMIT происходит запись на диск, а всё что до COMMIT может храниться в буфере.
При желании кеш всегда можно забить. Понятно, что в этом случае производительность упадёт. На объёмах данных, когда забьётся ОЗУ, забьётся кеш сравнивать нужно уже с БД, которые хранят данные на диске.

Конечно у СУБД на глобалах задачи несколько другие. Более универсальные.
Я не пытаюсь доказать, что диск быстрее памяти )))
Это по определению не «честное» тестирование, потому что разные задачи и требования.


А я думаю честное. И с чего вы взяли, что задачи разные? Может быть задача одна и та же, раз сравнивали с in-memory БД — скорость. Если на одинаковой ОЗУ, одинаковом размере памяти и одинаковом размере памяти база на глобалах скоростнее, то значит она скоростнее. В чём тут нечестность?

Твоя задача скорость — вот и получаешь скорость.
Буферизация? Т.е., пока буфер не наберется (ну или таймаут не случится), записи на диск не будет?


Да.
Однако насколько я знаю: буферизация записи вещь настраиваемая. Можно её вообще выставить в ноль. А можно раздуть, если нужно для задачи.

Причём она может происходить автоматически как на уровне ОС, так и на уровне БД. Можно на уровне БД вообще отключить.
Можно изложить вот так. Если мы тестируем in-memory БД и БД на глобалах на одинаковом объёме ОЗУ, то ничего не мешает той же GlobalsDB держать всё в памяти. Это честное тестирование. На одной и тоже машине: одинаковый объём ОЗУ и винта для двух БД.

Если мы тестируем базу на 10ТБ на глобалах и in-memory базу на 20GB в ОЗУ — это нечестное тестирование. Разные объёмы.

При равных объёмах ОЗУ она выигрывает в скорости за счёт оптимизаций, которые десятками лет выдумывались. Делая синхронизацию когда нужно. Если на машине стоит рейд-контроллер с Write-back и батарейкой, то БД на глобалах может скидывать данные на диск хоть каждую десятую секунды. Write-back кэш будет проглатывать их мгновенно.

При последнем подходе иногда может получиться даже, что база 10ТБ на глобалах будет быстрее чем in-memory база на 20GB в ОЗУ.
Есть 3 вопроса в вашем: при вставке, обновлении, выборке.

Я изложу свои догадки, поскольку я не разработчик.

При случайной выборке из объёмов многократно превосходящих ОЗУ мы упираемся в IOPS диска (тестировал сам), но сама структура хранения глобалов такова, что дерево хранится очень компактно, и поэтому за один IOPS считывается кусок дерева.

То же при обновлении: я подозреваю, что за 1 IOPS обновляется сразу несколько значений узлов.

При записи происходит сортировка и определённая буферизация, очень оттюнингованная. За один IOPS записывается сразу много значений узлов.

Также нужно учитывать, что современные in-memory БД появились недавно и поэтому не до конца оптимизированы. Авторы вот этой статьи утверждают, что GlobalsDB рвёт по тестам Redis. Можно у них попросить данные тестирований.

Также, я думаю, если скидывать значения на диск раз в несколько секунд, то одних ассемблерных и алгоритмических оптимизаций будет достаточно, чтобы выигрывать у тех in-memory продуктов, которые полностью написаны на высокоуровневых языках.
Если быть точнее, то можно ответить так:
Скорость глобалов = Простая архитектура * Ассемблер * Отточенность алгоритмов * Компиляция COS/M * Продуманные алгоритмы кеширования

Каждый множитель важен. И, наверное, есть ещё какие-то множители, которые только разработчики знают.

Меня лично изумила скорость вставки при первом знакомстве с глобалами.
Когда я смотрел исходный код GT.M я был поражён — не менее 50% кода написано на ассемблере (причём под все архитектуры), а остальное на Си. Исходный код Caché закрыт, но я думаю там таже история. Они недавно писали, что оптимизируют всё даже под конкретные модели процессоров.
Концепция глобалов используется в InterSystems Caché (IMHO самый крутой продукт на глобалах), бесплатная GlobalsDB (сделана на ядре Caché), также есть полностью open-source GT.M и в некоторых других БД.

Ещё на глобалы можно посмотреть как на многомерный key->value. Многомерный ключ, скалярное value. На след. неделе, надеюсь, напишу статью об этом.

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