Использование Aerospike для обработки данных, уникальность которых определяется сочетанием клавиш - PullRequest
2 голосов
/ 07 февраля 2020

Я пытаюсь понять, как лучше структурировать мою схему Aerospike. Играя с этим, я осознаю, что часть проблемы заключается в том, что я не совсем понимаю, как Aerospike обрабатывает данные, которые, похоже, отличаются как от RDBMS, так и от Cassandra.

Мой набор данных - это коллекция записей, так что уникальный «первичный ключ» определяется комбинацией нескольких полей (извиняюсь, если я неправильно использую термин primary key в смысле Aerospike - изначально я планировал просто объединить эти поля с разделителем). Мне нужна возможность как извлекать отдельную запись, указав все эти поля, так и получить пакеты, которые указывают подмножество. Например, допустим, я храню демографические данные, где мой «первичный ключ» представляет собой комбинацию столбцов year, location и source, из которых я получил данные. Указав все 3, я бы получил точную запись и набор записей, если бы я указал 2 или просто 1.

В СУБД я бы достиг этого с помощью индексов. В Cassandra лучший подход состоит в том, чтобы добавить все 3 к первичному ключу и изменить их порядок в материализованных представлениях, если мне не всегда гарантирован доступ к ключу раздела при выполнении поиска.

Как я играя больше с Aerospike, я понимаю, что PK здесь не рассматриваются как ни один из 2 случаев выше. Более того, я начинаю думать, что, возможно, ПК Aerospike вообще не должны быть частью пользовательских данных, поскольку они не возвращаются по умолчанию (если sendKeys не установлено перед записью, в этом случае они просто дублируется в ячейки).

Из прочтения документации звучит так, что я действительно хочу вместо этого secondary indexes (поскольку они обеспечивают большую гибкость при запросе данных)? Являются ли индексы правильным подходом или их не поощряют, как в Кассандре? Я могу запутать себя, пытаясь сравнить концепции Aerospike с другими БД.

Ответы [ 2 ]

3 голосов
/ 07 февраля 2020

Отличный вопрос - нужен подробный ответ, но позвольте мне кратко изложить концепцию за счет полной точности.

1 - Первичный ключ в Aersopike - это строка / int / bytes, что бы вы ни выбрали -> хэшируется в 20 байтов клиентской библиотекой, к которой привязано ваше приложение. Эти 20 байт га sh являются «ключом», отправляемым на сервер и используемым сервером для обработки ваших данных записей. Таким образом, вы можете создать строковый ключ: «2020: san_jose: web», и любые данные, связанные с этим ключом, будут сохранены в Aerospike в виде записи. Вы можете сделать sendKey или даже сохранить свой ключ как другой строковый бин в записи. Но Aerospike использует для отслеживания вашей записи 20 байтов га sh из «2020: san_jose: web». Этот вид составного ключа не привязан неявно к бинам данных - вы явно создаете его в своем приложении. Этот метод может использоваться для чтения пакета записей, если вы можете «сгенерировать» эту строку (в своем приложении) для набора записей, которые вас интересуют, а затем использовать API пакетного чтения. Но вы не можете использовать данные в корзинах и сказать Aerospike «сгенерировать» этот ключ для вас, найти подходящие записи и вернуть их.

2 - Можно ли использовать вторичные индексы? В Aerospike вы можете создать до 256 SI, но использовать только один в данном запросе. (Я бы не советовал собирать больше, чем несколько, для оперативной памяти, а также для других эксплуатационных соображений.) Чем выше количество элементов в корзине, тем больше оперативной памяти вам потребуется. Индексы встроены в оперативную память - что имеет свои собственные операционные последствия - и t ie запрашивает хешированные ключи и, следовательно, записи-кандидаты. Итак, допустим, вы используете SI для city == "san_jose" - это даст подмножество записей. (Выберите SI, который отбирает данные до 15% от общего количества, в идеале - предложение.) Теперь это будет извлекать все записи, где мусорная корзина города соответствует san_jose. Это все в оперативной памяти - так что это быстро. После этого он будет читать все эти записи с диска и отправлять их обратно клиенту.

3 - На данном этапе у вас есть дополнительная возможность написать фильтры предикатов значительной сложности. Таким образом, вы можете сказать, что из этого найденного набора пришлите мне записи, где год = 2020 и источник = сеть ... все логические условия ИЛИ НЕ, которые вам нужны, regexes et c. (Я жертвую точностью, чтобы управлять большей точкой. Вы также можете запускать предопределенные фильтры для метаданных записи, которые происходят на уровне ОЗУ до того, как запись извлекается с диска.)

4 - Наконец, почему СИ не рекомендуется в распределенных базы данных? Они отлично работают, если кластер стабилен. Если узлы входят или выходят, данные переносятся для создания копий реплик - запрос SI выполняется параллельно с переносом данных - вы можете пропустить или получить дубликаты. Считайте запрос SI относительно «длительной» выполняемой операцией. В Aerospike, если вы убедитесь, что данные не мигрируют перед запуском запроса SI, есть необязательный флаг, который вы можете установить - failOnClusterChange - так что ваш запрос не будет выполнен (клиент получит информацию), если узел выпадет или присоединится во время активного Запрос СИ. В зависимости от вашей модели данных, вы можете или не можете заботиться о точности запроса SI на 100%.

1 голос
/ 07 февраля 2020

Этот пример кода может помочь понять. Это сработало бы, даже если бы я не использовал вторичный индекс для «страны», он будет просто менее эффективен - то есть он будет извлекать каждую запись в этом пространстве / наборе имен и применять PredEx.

public void read () {
                Record record = null;
                Statement stmt = new Statement();
                stmt.setSetName("testset");
                stmt.setNamespace("test");
                stmt.setIndexName("country_idx");
                stmt.setFilter(Filter.equal("country","USA")); 
                stmt.setPredExp(
                        PredExp.stringBin("city"),
                        PredExp.stringValue(".*Diego"),  //prefix-suffix match: prefix.*suffix, ignore case or newline
                        PredExp.stringRegex(RegexFlag.ICASE | RegexFlag.NEWLINE)
                );

                RecordSet recordSet = this.client.query(queryPolicy, stmt);
            while (recordSet.next()) {
                record = recordSet.getRecord();
                System.out.println(record.toString());
            }

         }

BTW клиентские библиотеки имеют открытый исходный код и содержат примеры всех API. Для java см .: https://github.com/aerospike/aerospike-client-java/tree/master/examples

...