Aerospike - Храните *небольшое количество* больших ценностей

Сценарий
Допустим, я храню до 5 байтовых массивов, каждый по 50 КБ, для каждого пользователя.

Возможные реализации:

1) Один байтовый массив на запись, индексированный вторичным ключом.
Плюсы: Быстрое чтение/запись.
Минусы: Запрос с высокой кардинальностью (до 5 результатов на запрос). Плохо для горизонтального масштабирования, если к байтовым массивам часто обращаются.

2) Все байтовые массивы в одной записи в отдельных ячейках
Плюсы: Быстрое чтение
Нейтрально: размер блока должен быть больше 250 КБ
Минусы: медленная запись (одно изменение означает перезапись всех массивов байтов).

3) Сохранение массивов байтов в LLIST LDT
Плюсы: избегайте минусы решения (1) и (2)
Минусы: LDT обычно медленные

4) Сохраняйте каждый массив байтов в отдельной записи, связанной с UUID. Сохраните список UUID в другой записи.
Плюсы: запись в каждый массив байтов не требует перезаписи всех массивов. Нет проблем с низким количеством элементов вторичных индексов. Избегает использования LDT.
Минусы: Чтение клиентом выполняется в два этапа: получение списка UUID из метазаписи, затем многократное получение для каждого UUID (очень медленно?)

5) Сохраняйте каждый массив байтов как отдельную запись, используя предопределенную схему первичного ключа (например, userid_index, например, 123_0, 123_1, 123_2, 123_3, 123_4) Плюсы: избегайте двухэтапного чтения Минусы: Теоретическая вероятность конфликта с другим пользователем (например, один и тот же хэш продукта user1_index1 и user2_index2). Я знаю, что это (очень, очень) маловероятно, но избегание по-прежнему предпочтительнее (представьте, что один пользователь может прочитать массив байтов другого пользователя из-за коллизии).

Моя оценка
Для сбалансированного чтения/записи ИЛИ ситуаций с большим количеством операций чтения/записи используйте #2 (одна запись, несколько бинов). Перезапись более затратна, но позволяет избежать других недостатков (штраф за LDT, двухэтапное чтение).

Для ситуации с высокой (пере) записью/низкой скоростью чтения используйте #3 (LDT). Это позволяет избежать перезаписи всех байтовых массивов при обновлении одного из них, поскольку записи копируются при записи.

Вопрос
Какая реализация предпочтительнее, учитывая текущую структуру данных (небольшое количество, большие объекты)? Согласны ли вы с моей оценкой (выше)?


person Aaron    schedule 09.11.2015    source источник


Ответы (1)


Вот некоторые данные. (Хочу сообщить, что работаю в Aerospike).

Избегайте # 3. Не используйте LDT, так как эта функция определенно не такая зрелая, как остальная часть платформы, особенно когда речь идет о производительности/надежности во время перебалансировки кластера (миграции), когда узлы покидают/присоединяются к кластеру.

Я бы постарался как можно больше придерживаться базовых транзакций Key/Value. Это всегда должно быть самым быстрым и масштабируемым. Как вы указали, вариант № 1 не будет масштабироваться. Вторичные индексы также имеют накладные расходы в памяти и в настоящее время не допускают быстрого запуска (в любом случае, только для корпоративной версии).

Вы также правы в № 2 для высоких нагрузок записи, особенно если вы собираетесь всегда обновлять 1 корзину ...

Таким образом, остаются варианты №4 и №5. Для варианта №5 коллизии на практике не произойдет. Вы можете пройтись по математике, этого просто не произойдет. Если это произойдет, вы станете знаменитым и сможете опубликовать статью :) (может даже быть цена за обнаружение коллизии). Кроме того, обратите внимание, что у вас есть возможность хранить ключ вместе с записью, что обеспечит вам «проверку ключа» при записи, которая должна быть очень дешевой (поскольку записи все равно считываются перед записью). Вариант № 4 также будет работать, он просто выполнит дополнительное чтение (которое должно быть очень быстрым).

Все зависит от того, где вы хотите получить дополнительную сложность. Таким образом, вы можете провести простой сравнительный анализ между двумя вариантами, если у вас есть такая роскошь, прежде чем принять решение.

person Meher    schedule 11.11.2015
comment
Спасибо за четкое объяснение. Я почти пошел по маршруту LDT. Теперь я реализую № 4, который имеет одно неупомянутое преимущество перед № 5: массивы байтов можно переупорядочить, просто переписав список ссылок UUID (например, [uuid1, uuid2, uuid3, uuid4] -> переписать как [uuid4, uuid1 , uuid2, uuid3]) - person Aaron; 11.11.2015
comment
Можете ли вы также сказать, при какой кардинальности вторичный индекс начинает иметь смысл? Что-то вроде (# результатов) › 2*(# узлов)? - person Aaron; 11.11.2015
comment
Вы имеете в виду, когда вторичный индекс имеет смысл, а не пакетное чтение с точки зрения производительности? На этом этапе вторичные индексы имеют некоторые операционные издержки (нет быстрого старта) и имеют небольшие окна, не обеспечивающие точных правильных результатов во время миграции (подробности можно найти на нашем форуме по адресу обсуждение.aerospike.com). Поэтому я бы, по возможности, придерживался одиночного чтения или пакетного чтения. - person Meher; 16.11.2015