Большие записи вызывают нестабильность в кольце Cassandra

Я пытаюсь загрузить большой объем данных в кольцо Cassandra с 10 узлами.

Сценарий, выполняющий вставки, получает около 4000 вставок в секунду, предположительно заблокированных на сетевом вводе-выводе. Я запускаю 8 из них на одной машине, и пропускная способность увеличивается почти линейно. (Индивидуальная производительность немного снижается, но это более чем компенсируется дополнительными процессами.)

Это работает прилично, однако мне все еще не хватает пропускной способности, поэтому я запустил ту же настройку еще на 3 ВМ. (Таким образом, 8 процессов * 4 ВМ) После первой дополнительной ВМ и с увеличением частоты и серьезности по мере добавления дополнительных ВМ происходит следующее:

  • Клиенты начинают получать ошибки тайм-аута. Они могут повторить попытку записи, но, поскольку они делают это партиями, их дальнейшее продвижение почти полностью исключается.
  • Кольцо становится нестабильным, и узлы начинают маркировать себя как «неработающие». Кроме того, разные узлы, как правило, имеют разные представления о том, кто не работает. Кольцо не восстанавливается после прерывания сценариев. (Я даже не смог исправить это, просто перезапустив отдельные узлы: мне пришлось перезапустить все кольцо.)

«Вниз» варьируется. В моем последнем прогоне:

  • 4 узла умерли полностью. (Кассандра вообще не работала.) При проверке журналов не было обнаружено ничего, что могло бы объяснить причину ее смерти.
  • На пятом бежала Кассандра. nodetool status на этом узле висит. Кажется, что два потока находятся в бесконечных циклах. (Они стабильно используют 100% ЦП.) В журналах есть java.lang.OutOfMemoryError: Java heap space.

Код по существу:

def prepped_batch_insert(session, items, insert_query, silent=False):

    # A mapping of number of inserts -> a prepared query for that number of
    # inserts.
    prepped_statements = {}

    def get_prepped_statement(inserts):
        if inserts in prepped:
            # We already created a prepared query for this many inserts, use
            # it:
            return prepped_statements[inserts]
        else:
            # We haven't yet created a prepared query for this many inserts, so
            # do so now:
            query = ['BEGIN UNLOGGED BATCH']
            for idx in xrange(inserts):
                query.append(insert_query.query)
            query.append('APPLY BATCH;')
            query = '\n'.join(query)
            ps = session.prepare(query)
            prepped_statements[inserts] = ps
            return ps

    def do_prepped_batch_insert(batch)
        ps = get_prepped_statement(len(batch))

        # Generate the list of params to the prepared query:
        params = []
        for idx, item in enumerate(batch):
            for k in insert_query.keyorder:
                params.append(item[k])
        # Do it.
        session.execute(ps, params)

    return inserter.insert_and_time(
        items,  # data generator
        do_prepped_batch_insert,  # The above function
        _WHAT_APPEARS_TO_BE_THE_OPTIMAL_CASSANDRA_BATCH_SIZE,  # = 200
        silent=silent,
    )

Функция insert_and_time разбивает items на пакеты по 200 штук, вызывает указанную выше функцию и измеряет время для всего комплекта и kaboodle. (Этот код токсичен для кольца.)

Мы предприняли больше чтений, потому что (мне сказали) 20 000 вставок в секунду было медленным (потребуется некоторое время, чтобы вставить данные, которые я хотел бы вставить с такой скоростью…), и что Cassandra способна работать с высокой пропускной способностью.

Мои вопросы:

  1. Есть ли что-то необычное в том, что я делаю? Ничего плохого?
  2. Я просто DDoS-атак на кольцо?
  3. Как я могу отладить, что не так?
  4. Ошибочный клиент, ИМХО, никогда не должен убить сервер. (И вышеизложенное не так уж ошибочно.) Что я могу сделать, чтобы предотвратить это?

¹Похоже, что клиент также медленно пропускает файловые дескрипторы. Я не думаю, что это связано. (Я вызываю .shutdown как для кластера, так и для соединения.) Глядя на источник драйвера, кажется, что существует множество путей, где исключение может вызвать утечку.


person Thanatos    schedule 11.02.2014    source источник


Ответы (2)


Очень похоже, что вы DDoS-атакуете свою установку.

Сценарий, выполняющий вставки, получает около 4000 вставок в секунду, предположительно заблокированных на сетевом вводе-выводе. Я запускаю 8 из них на одной машине, и пропускная способность увеличивается почти линейно.

Вы не были заблокированы в сетевом вводе-выводе, если запуск дополнительных экземпляров сценария, которые обращаются к Cassandra через ту же сетевую карту, дает почти линейное увеличение пропускной способности.

Заблудший клиент, ИМХО, никогда не сможет убить сервер.

Бросьте достаточную нагрузку на любой сервер, и он начнет глючить. Некоторые серверы пытаются минимизировать это, например. веб-серверы обычно принимают максимальное количество одновременных HTTP-запросов, после чего отвечают, что заняты. Однако даже обработка соединения и сообщение клиенту «уходи на время» занимает несколько циклов. Надлежащая защита от DoS требует выделенного сетевого оборудования.

(Они полностью используют ЦП на 100%.) Существует java.lang.OutOfMemoryError: пространство кучи Java

Еще одно доказательство того, что вы просто загружаете свое кольцо сверх его возможностей.

Чтобы повысить пропускную способность, взгляните на аппаратное обеспечение и конфигурацию Cassandra.

  • IO перегружен? Это типичное узкое место для многих систем сохраняемости. У меня нет особого опыта работы с Cassandra, но производительность ввода-вывода Mongo DB чрезвычайно важна, поскольку рабочий набор не может поместиться в памяти. Если он перегружен, рассмотрите более быстрые подсистемы ввода-вывода (если это, например, AWS, существуют различные средства для повышения производительности ввода-вывода).
  • У вас достаточно оперативной памяти? Вы упомянули, что у Java заканчивается куча. Вы установили максимально возможный размер кучи на каждом узле, учитывая, что ОС и, возможно, другим процессам в системе требуется немного оперативной памяти?
  • ЦП перегружен? Кажется, да. Однако я бы посмотрел на ЦП в последнюю очередь, после рассмотрения соответствующей производительности дискового и сетевого ввода-вывода и просмотра конфигурации Cassandra. Высокая загрузка ЦП иногда может быть признаком других вещей.
  • Проверьте конфигурацию Cassandra. Я не эксперт в этом, но взгляните на конфигурацию, которую Netflix использовал при тестировании производительности Cassandra http://techblog.netflix.com/2011/11/benchmarking-cassandra-scalability-on.html.
person Eric J.    schedule 11.02.2014
comment
Обратите внимание, что я не ожидаю, что служба DDoS будет обслуживать; он просто не должен умирать (особенно без регистрации). 100% ЦП также ожидается во время DDoS. Но сбой, не так уж и много. - person Thanatos; 12.02.2014
comment
Хм. Я предположил, что клиент привязан к сети, потому что они не были связаны с процессором или памятью. Но вы правы: он не масштабировался бы, если бы был привязан к сети. Возможно, тогда задержка. - person Thanatos; 12.02.2014
comment
@Thanatos: я подозреваю, что Кассандра умирает, когда у вас заканчивается место в куче. Просто предположение с моей стороны. Я ожидаю, что исключение нехватки памяти будет где-то зарегистрировано, но точно не знаю, как Cassandra справляется с этим. - person Eric J.; 12.02.2014

Ваша ситуация звучит необычно, но без каких-либо подробностей об оборудовании, на котором вы работаете, у меня есть некоторые предположения. Скорее всего, проблема связана с размером кучи, а затем с узким местом ввода-вывода. Если вы не используете SSD, использование ЦП не должно быть проблемой.

1) Если вы ищете однократную загрузку данных, за которой следует меньший последовательный поток данных, рассмотрите возможность использования массива инструмент загрузки.

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

3) Вы должны заглянуть в системные журналы Cassandra на наличие сообщений типа «попытка сбросить memtable для восстановления пространства», которые являются симптомами нехватки кучи, в них будет информация о памяти GC и других текущих задачах. Для мониторинга в режиме реального времени вы также можете подключиться через JMX с помощью jconsole или visualvm к своим экземплярам Cassandra. Глядя на них, должно быть очевидно, что куча начинает заполняться и система начинает выполнять резервное копирование. Большинство производственных экземпляров Cassandra имеют размер кучи 8 ГБ, причем объемы, превышающие этот размер, дают убывающую отдачу, поскольку события остановки мирового сбора мусора становятся более распространенными.

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

 nodetool compactionstats  

Для мониторинга всего этого вы можете использовать инструмент Datastax Opscenter. Этот инструмент позволит вам легко контролировать весь кластер из одного места, а версия для сообщества совершенно бесплатна.

Я действительно думаю, что что-то еще не так, потому что я регулярно сравниваю инстансы Amazon m1.large и обнаруживаю, что около 10 из них могут поддерживать трафик со скоростью 40–50 тыс. операций записи/с без какой-либо нестабильности системы.

4) Как отметил Эрик, для распределенной системы, ориентированной на производительность, такой как Cassandra, очень сложно оставаться доступной и производительной, а также поддерживать ограничения на поведение клиента. Компромиссом стало увеличение скорости для минимальной проверки состояния системы при записи. Это позволяет выполнять очень быструю запись, но возлагает на сопровождающего ответственность за надлежащую подготовку и мониторинг своей системы.

person RussS    schedule 12.02.2014
comment
Синтаксис вашего драйвера также немного странный. Если вы используете Python-драйвер, более распространено подготовить оператор, а затем использовать ps.Bind(params).execute(). - person RussS; 12.02.2014