Вставка в таблицу cassandra с составным первичным ключом от hadoop Reduce

1

Я использую Apache Hadoop, MapReduce и Cassandra для запуска задания MapReduce, которое читается из таблицы Cassandra и выводится в другую таблицу Cassandra.

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

    CREATE TABLE word_count(
        word text,
        count int,
        PRIMARY KEY(text)
    ) WITH COMPACT STORAGE;

Связанный класс сокращения выглядит примерно так:

public static class ReducerToCassandra 
    extends Reducer<Text, IntWritable, ByteBuffer, List<Mutation>>
{
    public void reduce(Text word, Iterable<IntWritable> values, Context context) 
        throws IOException, InterruptedException
    {
        int sum = 0;
        for (IntWritable val : values){
            sum += val.get();
        }

        org.apache.cassandra.thrift.Column c 
                = new org.apache.cassandra.thrift.Column();
        c.setName(ByteBufferUtil.bytes("count");
        c.setValue(ByteBufferUtil.bytes(sum));
        c.setTimestamp(System.currentTimeMillis());

        Mutation mutation = new Mutation();
        mutation.setColumn_or_supercolumn(new ColumnOrSuperColumn());
        mutation.column_or_supercolumn.setColumn(c);

        ByteBuffer keyByteBuffer = ByteBufferUtil.bytes(word.toString());
        context.write(keyByteBuffer, Collections.singletonList(mutation));
    }
}

Если я хочу добавить дополнительный столбец, тогда мне просто нужно добавить еще одну мутацию в List<Mutation> уже выводящийся с помощью reduce но я не могу понять, как выводить в таблицу с новым столбцом в составной первичной ключ. Например, эта таблица делает то же самое, что и выше, но также индексирует слова вместе с часом их публикации.

    CREATE TABLE word_count(
        word text,
        publication_hour bigint,
        count int,
        PRIMARY KEY(word, publication_hour)
    ) WITH COMPACT STORAGE;

Я пробовал несколько разных подходов, например, пытаюсь вывести пользовательский WritableComparable (который содержит как слово, так и час), и соответственно обновлять подписи class и method и конфигурацию job, но это делает reduce throw ClassCastException когда оно пытается выполнить обычай WritableComparable к ByteBuffer.

Я попытался создать соответствующее имя столбца в Builder.

public static class ReducerToCassandra 
    //              MappedKey     MappedValue  ReducedKey  ReducedValues
    extends Reducer<WordHourPair, IntWritable, ByteBuffer, List<Mutation>>
{
    //                 MappedKey                  Values with the key wordHourPair
    public void reduce(WordHourPair wordHourPair, Iterable<IntWritable> values, 
    Context context) 
        throws IOException, InterruptedException
    {
        int sum = 0;
        for (IntWritable val : values){
        sum += val.get();
        }
        long hour = wordHourPair.getHourLong();

        org.apache.cassandra.thrift.Column c 
            = new org.apache.cassandra.thrift.Column();
        c.setName(ByteBufferUtil.bytes("count");
        c.setValue(ByteBufferUtil.bytes(sum));
        c.setTimestamp(System.currentTimeMillis());

        Mutation mutation = new Mutation();
        mutation.setColumn_or_supercolumn(new ColumnOrSuperColumn());
        mutation.column_or_supercolumn.setColumn(c);

        //New Code
        List<AbstractType<?>> keyTypes = new ArrayList<AbstractType<?>>(); 
        keyTypes.add(UTF8Type.instance);
        keyTypes.add(LongType.instance);
        CompositeType compositeKey = CompositeType.getInstance(keyTypes);

        Builder builder = new Builder(compositeKey);
        builder.add(ByteBufferUtil.bytes(word.toString());
        builder.add(ByteBufferUtil.bytes(hour));

        ByteBuffer keyByteBuffer = builder.build();
        context.write(keyByteBuffer, Collections.singletonList(mutation));
    }
}

Но это IOException

java.io.IOException: InvalidRequestException(why:String didn't validate.)
    at org.apache.cassandra.hadoop.ColumnFamilyRecordWriter$RangeClient.run(ColumnFamilyRecordWriter.java:204)
Caused by: InvalidRequestException(why:String didn't validate.)
    at org.apache.cassandra.thrift.Cassandra$batch_mutate_result$batch_mutate_resultStandardScheme.read(Cassandra.java:28232)
    at org.apache.cassandra.thrift.Cassandra$batch_mutate_result$batch_mutate_resultStandardScheme.read(Cassandra.java:28218)
    at org.apache.cassandra.thrift.Cassandra$batch_mutate_result.read(Cassandra.java:28152)
    at org.apache.thrift.TServiceClient.receiveBase(TServiceClient.java:78)
    at org.apache.cassandra.thrift.Cassandra$Client.recv_batch_mutate(Cassandra.java:1069)
    at org.apache.cassandra.thrift.Cassandra$Client.batch_mutate(Cassandra.java:1055)
    at org.apache.cassandra.hadoop.ColumnFamilyRecordWriter$RangeClient.run(ColumnFamilyRecordWriter.java:196)

Этот вопрос: композитный ключ Cassandra CQL3, не написанный редуктором Hadoop, кажется, демонстрирует вид кода, который я ищу, но он вызывает context.write с параметрами типа HashMap, ByteBuffer и я не уверен, как бы я сделал context.write принять те параметры.

Как я могу получить данные, которые я хочу (слова-ключи, значения int) в таблице?

Теги:
cassandra
hadoop
mapreduce
composite-key

1 ответ

1
Лучший ответ

Ответ на этот вопрос заключался в использовании интерфейса Cassandra CQL, а не в Thrift API.

Теперь я могу написать таблицу со сложным ключом, объявив классы ввода/значения моего класса сокращения как "Map, List", а затем создаст карту для составного ключа, где ключ (строки типа) является столбцом имя и значение (типа ByteBuffer) - это значение столбцов, преобразованное в ByteBuffer с ByteBufferUtil.

Например, чтобы записать в таблицу, определенную как таковую:

CREATE TABLE foo (
    customer_id uuid,
    time timestamp,
    my_value int,
    PRIMARY KEY (customer_id, time)
)

Я могу написать:

String customerID = "the customer id";
long time = DateTime.now().getMillis();
int myValue = 1;

Map<String, ByteBuffer> key = new Map<String, ByteBuffer>();
key.put("customer_id",ByteBufferUtil.bytes(customerID));
key.put("time",ByteBufferUtil.bytes(time));

List<ByteBuffer> values = Collections.singletonList(ByteBufferUtil.bytes(myValue));

context.write(key, values);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню