2015-01-28 4 views
4

Моя Cassandra таблица имеет следующие схемыCassandra Чтение/Получить Производительность

CREATE TABLE cachetable1 (
id text, 
lsn text, 
lst timestamp, 
PRIMARY KEY ((id)) 
) WITH 
bloom_filter_fp_chance=0.010000 AND 
caching='{"keys":"ALL", "rows_per_partition":"ALL"}' AND 
comment='' AND 
dclocal_read_repair_chance=0.100000 AND 
gc_grace_seconds=864000 AND 
read_repair_chance=0.000000 AND 
default_time_to_live=0 AND 
speculative_retry='99.0PERCENTILE' AND 
memtable_flush_period_in_ms=0 AND 
compaction={'class': 'SizeTieredCompactionStrategy'} AND 
compression={'sstable_compression': 'LZ4Compressor'}; 

Над таблицей содержит 221 миллионов строк (ок. 16 ГБ) данных. CassandraDaemon работает с 4-гигабайтным пространством кучи, и я сконфигурировал 4 ГБ памяти для кеша строк. Я пытаюсь запустить запросы на выборку из моего кода Java, как этот

for(int i = 0; i < 1000; i ++) 
    { 
     int id = random.nextInt(20000000 - 0) + 0; 
     for(j = id; j <= id + 100; j++) 
     { 
      ls.add(j+""); 
     } 

      Statement s = QueryBuilder.select("lst","lsn").from("ks1" , "cachetable1").where(QueryBuilder.in("id",ls.toArray())); 
      s.setFetchSize(100); 

       ResultSet rs=sess.execute(s); 
       List<Row> lsr=rs.all(); 
       for(Row rw:lsr) 
       { 
        //System.out.println(rw.toString()); 
        count++; 
       } 

     ls.clear(); 
    } 

В коде выше, я пытаюсь принести 0,1 миллиона записей. Но производительность чтения/получения очень плохая. Для получения 0,1 миллиона строк требуется 400-500 секунд. Есть ли лучший способ читать/получать записи из Cassandra через Java? Требуется ли какая-либо настройка, кроме размера кеша строки и размера кучи Cassandra?

ответ

2

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

Измените свою схему, чтобы использовать идентификатор в качестве ключа раздела и индекс куска в качестве столбца кластеризации, то есть PRIMARY KEY ((id), chunk_idx). Когда вы вставляете данные, вам нужно будет выяснить, как сопоставить свои индексы в id и chunk_idx (например, возможно, по модулю 100 на одном из ваших значений для генерации chunk_idx).

Теперь, когда вы запрашиваете идентификатор и не указываете chunk_idx, Cassandra может эффективно возвращать все 100 строк с одним диском, читаемым в разделе.И вы все еще можете задавать запросы и результаты поиска отдельных строк в разделе, указав chunk_idx, если вы не всегда хотите читать целый кусок строк.

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

Вторая ошибка, которую вы делаете, заключается в том, что вы выполняете запрос синхронно (т. Е. Вы отправляете запрос и ожидаете завершения запроса до того, как вы выполните еще какие-либо запросы). То, что вы хотите сделать, это использовать пул потоков, чтобы вы могли запускать много запросов параллельно или использовать метод executeAsync в одном потоке. Поскольку ваш запрос неэффективен, ожидая, что 100 случайных разделов будут завершены, будет долго ждать, и большая часть конвейерной способности Cassandra будет сидеть там, покачивая большими пальцами, ожидая чего-то сделать. Если вы пытаетесь максимизировать производительность, вы хотите, чтобы все узлы были максимально заняты.

Еще одна вещь, которую нужно изучить, - это использование TokenAwarePolicy при подключении к кластеру. Это позволяет каждому запросу перейти непосредственно к узлу с репликой раздела, а не к случайному узлу, который, возможно, должен действовать как координатор и получать данные через дополнительный прыжок. И, конечно же, уровень согласованности ONE при чтении быстрее, чем более высокий уровень согласованности.

Размер кеша строки и размер кучи не являются источником вашей проблемы, поэтому это неправильный путь.

1

Я собираюсь предположить, что это ваш виновник:

.where(QueryBuilder.in("id",ls.toArray())) 

Использование IN связи в предложении WHERE широко известно, что не-производительным. В некоторых случаях выполнение множества параллельных запросов может быть быстрее, чем использование одного запроса IN. Из DataStax SELECT documentation:

Когда не использовать IN

... Использование IN может привести к снижению производительности, так как, как правило, многие узлы должны быть опрашивается. Например, в одном локальном кластере центра обработки данных с 30 узлами, коэффициентом репликации 3 и уровнем согласованности LOCAL_QUORUM, один ключевой запрос выходит на два узла, но если запрос использует условие IN, количество запрашиваемых узлов: , скорее всего, даже выше, до 20 узлов в зависимости от того, где ключи попадают в диапазон токенов.

Так у вас есть два варианта (при условии, что жить с этим неэффективные запроса не один из них):

  1. Перепишите свой код, чтобы сделать несколько параллельных запросов для каждого id.

  2. Обновите свою модель данных, чтобы узнать, есть ли у вас другое значение, которое имеет смысл для ввода ваших данных. Например, если все ваши id s в ls имеют общее значение столбца, которое является уникальным для них, это хороший кандидат для первичного ключа. В принципе, найдите другой способ запросить все id s, которые вы ищете, и создать определенную таблицу запросов для ее поддержки.

+0

Я уже пробовал то, что вы предлагаете. Я изменил свой код как: для (int c = 0; c

+0

Тогда следующий подход к попытке (то есть, где вы, вероятно, увидите самый большой баг для вашего доллара), является моим вторым предложением, и посмотрите, есть ли способ смоделировать таблицу запросов для этих идентификаторов. Я полагаю, вы могли бы также попытаться «СЧИТАТЬ» ваши id-запросы и отправить их в Cassandra вместе, но «BATCH» также не известен как исполнитель. – Aaron