Статьи

Составные ключи: соединение точек между CQL3, Astyanax и Hector


Я до сих пор не знаю, где я нахожусь в дебатах по
терминологии , но я знаю, что вещи могут запутаться, если вы обращаетесь к своей базе данных как из CQL, так и из Java. У них очень разные взгляды на мир. Надеемся, что эта статья сможет восполнить этот пробел и объяснить различные взгляды на составные ключи.

Под капотом модель хранения Cassandra не изменилась, чтобы вместить композитные ключи. Составные ключи на самом деле представляют собой просто причудливое побитовое сцепление ключевых компонентов перед хранением и / или извлечением. Модель хранения остается, rowkey -> columnname -> value.

Таким образом, вам придется немного напрячь свой мозг при просмотре Cassandra через CQL. Поскольку несколько «столбцов», как их видит CQL, станет меньше количества столбцов в физическом хранилище. А поскольку API-интерфейсы Java предоставляют прямой доступ к модели хранения, он будет отображаться в виде одного столбца через API-интерфейсы Java (по крайней мере, на начальном этапе).

Давайте рассмотрим следующий CQL в качестве нашего примера:

CREATE TABLE fishblogs (
  userid varchar,
  when timestamp,
  fishtype varchar,
  blog varchar,
  image blob,
  PRIMARY KEY (userid, when, fishtype)
);

Хотя это не очевидно из CQL, оператор PRIMARY KEY использует порядок параметров для сопоставления схемы с моделью хранения Cassandra. В частности, пять объявленных выше столбцов приведут к двум столбцам при выполнении оператора вставки. Вот как Cassandra отображает схему в хранилище.

row_key => идентификатор пользователя (поскольку это первый объявленный ключ)

column_name_1 => когда + fishtype + blog  
(так как когда и fishtype — это ключи, а blog — это значение, которое нужно хранить)

column_name_1 => когда + fishtype + image (с тех пор, когда и fishtype — это ключи, а image — это значение, которое нужно хранить)

Если я могу перефразировать сопоставление … первый первичный ключ становится ключом строки. Последующие первичные ключи содержат префиксные компоненты имени составного столбца. Один столбец создается для каждого столбца не первичного ключа. Составной ключ объединяет общий префикс (включающий первичные ключи) и имя столбца для значения.

Если мы выполним следующую вставку:

cqlsh:hms_data> insert into fishblogs (userid, when, fishtype, blog, image) values ('boneill42', 1343925155443, 'CATFISH', 'Caught the big one.', '632a726f636b73');
cqlsh:hms_data> select * from fishblogs;
 userid    | when                     | fishtype | blog                | image
-----------+--------------------------+----------+---------------------+----------------
 boneill42 | 2012-08-02 12:32:35-0400 |  CATFISH | Caught the big one. | 632a726f636b73

CQL представляет это как пять столбцов, но под капотом на самом деле есть только два (имя столбца -> значение):

[8?.vsCATFISHblog]->[Caught the big one.]
[8?.vsCATFISHimage]->[c*rocks]


Выше вы можете увидеть сцепленные ключи в имени столбца.
Это необработанный вывод, который вы получаете из запроса с использованием Astyanax, когда у вас нет составных ключей. Это сокращенный код:
ColumnFamily columnFamily = new ColumnFamily (columnFamilyName, 
StringSerializer.get (), StringSerializer.get ());
OperationResult> результат =  это . пространство ключей .prepareQuery (ColumnFamily) .getKey (RowKey) .Execute ();
result.getResult (); 


Чтобы иметь то же представление, что и CQL, нам нужно сопоставить составной ключ с классом и сопоставить поля этого класса.
Например,

 public class FishBlog {
    @Component(ordinal = 0)
    public long when;
    @Component(ordinal = 1)
    public String type;
    @Component(ordinal = 2)
    public String field;
    
    public FishBlog() {
    }
}

Что-то неестественное в этом отображении — это переменная-член field.
В идеале, мы бы предпочли две переменные-члены: «изображение» и «блог». Но поскольку данные распределены по нескольким столбцам, каждый из которых содержит значение для отдельного поля, класс должен быть достаточно гибким, чтобы переносить либо (но не оба).  

Затем мы можем использовать следующий код Astyanax для извлечения данных:

 AnnotatedCompositeSerializer entitySerializer = new AnnotatedCompositeSerializer(FishBlog.class);
ColumnFamily columnFamily = new ColumnFamily(columnFamilyName, StringSerializer.get(), entitySerializer); OperationResult> result = this.keyspace.prepareQuery(columnFamily).getKey(rowKey).execute();
return result.getResult();


Это приводит к следующему выводу при доступе к набору результатов, который содержит FishBlog.

 fishBlog.when=>[Thu Aug 02 12:32:35 EDT 2012]
fishBlog.type=>[CATFISH]
fishBlog.field=>[image]
fishBlog.value=>[c*rocks]

Ясно как грязь?
Теперь, чтобы завершить картину, я покажу тот же код для Гектора. Вот выборка:

 ColumnFamilyTemplate template = new ThriftColumnFamilyTemplate(this.keyspace, columnFamilyName, new StringSerializer(), new CompositeSerializer());
return template.queryColumns(rowKey);

Затем, что может показаться немного странным, это то, что объект Composite, который содержит ключи компонентов для вашего объекта, происходит от имени столбца:

 for (Composite columnName : columns.getColumnNames()){
   FishBlog fishBlog = new FishBlog(columnName);
   LOG.debug("fishBlog.when=>[" + new Date(fishBlog.getWhen()) + "]");
   LOG.debug("fishBlog.type=>[" + fishBlog.getType() + "]");
   LOG.debug("fishBlog.field=>[" + fishBlog.getField() + "]");
   LOG.debug("fishBlog.value=>[" + columns.getString(columnName) + "]");
}

Как видно из вышесказанного, я рекомендую обернуть композитный объект в объект, обеспечивающий удобные средства доступа.
(что напоминает Astyanax) Вот мой объект FishBlog для Гектора:

 public class FishBlog {
    private Composite composite;
    public FishBlog(Composite composite) {
        this.composite = composite;
    }
    public long getWhen() {
        return composite.get(0, new LongSerializer());
    }
    public String getType() {
        return composite.get(1, new StringSerializer());
    }
    public String getField() {
        return composite.get(2, new StringSerializer());
    }
}


Хорошо — надеюсь, это демонстрирует, как пять столбцов с точки зрения CQL преобразуются в два физических столбца, к которым можно получить доступ через Гектор или Astyanax, в результате чего в наборе результатов отображаются две записи, сопоставленные с объектами Java, которые можно использовать для получения значений.
Я постараюсь получить код для этих примеров на github.