Как я могу увеличить количество запросов на чтение / секунду в моей базе данных? - PullRequest
8 голосов
/ 22 февраля 2012

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

Сначала я попробовал, mysql и использовал механизм хранения памяти (он мог умещаться в памяти), создал составной первичный ключ для репликации словаря, который был в моей программе, проиндексировал его, отключил блокировку, но я мог только получить 11 000 запросов в секунду от него.

Затем я попробовал redis (слышал, что это было похоже на memcache) и создал тот же ключ / значение, что и раньше (вот фактический режим Могу ли я сделать два столбца уникальными друг для друга? Или использовать составной первичный ключ в redis ? ) и удалил все, что есть в fsync, так что мы надеемся, что он никогда не попадет во ввод-вывод с жестким диском, но я все еще получаю только около 30 000 запросов в секунду. Я посмотрел на улучшения системы (я использую linux), запустив программу в ramdrive и т. Д., Но все еще схожий результат.

У меня есть сценарий установки, и я попытался сделать это на ec2 с использованием экземпляра с высоким процессором, но результат схожий (запросы не сильно увеличиваются для обоих решений). Я вроде как в своем уме, но не хочу сдаваться, потому что я читаю о людях на stackoverflow, рассказывающих о том, как они получили более 100 000 000 запросов в автономном режиме. Я чувствую, что моя модель данных очень проста (два столбца INT или я могу сделать ее одной строкой с двумя объединенными INT, но это, похоже, не замедляется), и как только данные созданы (и запрошены другим процессом), у меня есть нет необходимости в настойчивости (именно поэтому я стараюсь не писать на жесткий диск). Какие настройки мне не хватает, что позволяет разработчикам получать такую ​​производительность? Требуется ли специальная конфигурация вне создания таблицы? или это единственный способ получить такую ​​производительность через распределенные базы данных? Я знаю, что проблема с базой данных, потому что, когда я закрываю базу данных в середине процесса, мое приложение python достигает 100% на каждом ядре, на котором оно работает (хотя оно ничего не пишет), это заставляет меня думать, что процесс ожидания (для чтения, Я подозреваю) это то, что замедляет его (у меня много свободного процессора / памяти, поэтому мне интересно, почему его не хватает, у меня 50% процессорного времени и 80% моей памяти свободно во время этих заданий, поэтому я понятия не имею, что происходит).

У меня есть mysql, redis и hbase. надеюсь, что я смогу сделать так, чтобы одно из этих решений работало так быстро, как мне хотелось бы, но если нет, я не справлюсь ни с одним решением (на самом деле это просто временная хеш-таблица, которую могут запрашивать распределенные процессы).

Что я могу сделать?

Спасибо!

Обновление: как требуется в комментариях, вот некоторый код (после логики конкретного приложения, которая, кажется, работает нормально):

    cursor.execute(""" SELECT value1 FROM data_table WHERE key1='%s' AND value1='%s' """ % (s - c * x, i))
    if cursor.rowcount == 1:
       cursor.execute(""" SELECT value1 FROM data_table WHERE key1='%s' AND value1='%s' """ % (s, i+1))
       if cursor.rowcount == 0:
           cursor.execute (""" INSERT INTO data_table (key1, value1) VALUES ('%s', '%s')""" % (s, i+1))
           conn.commit() #this maybe not needed
           #print 'commited ', c

выше - код с 3 поисками на MySQL. Я также попытался сделать один большой поиск (но на самом деле это было медленнее):

       cursor.execute ("""
 INSERT INTO data_table (key1, value1) 
  SELECT  '%s', '%s'
  FROM dual
  WHERE ( SELECT COUNT(*) FROM data_table WHERE key1='%s' AND value1='%s' )
        = 1
    AND NOT EXISTS
        ( SELECT * FROM data_table WHERE key1='%s' AND value1='%s' )
          """ % ((s), (i+1), (s - c * x), (i), (s), (i+1)))   

Вот дизайн таблицы на MySQL:

cursor.execute ("DROP TABLE IF EXISTS data_table")
cursor.execute ("""
    CREATE TABLE data_table(
        key1    INT SIGNED NOT NULL,
        value1    INT SIGNED NOT NULL,
        PRIMARY KEY (key1,value1)
    )  ENGINE=MEMORY
""")
cursor.execute("CREATE INDEX ValueIndex ON data_table (key1, value1)")

в Redis, его симлаир со структурой 3 запросов (поскольку он был самым быстрым, который я мог получить в MySQL, за исключением того, что мне не нужно искать, если значение существует, я просто перезаписываю его, чтобы сохранить запрос):

if r_server.sismember(s - c * x, i):
    r_server.sadd(s, i + 1)

Моя структура данных для redis находится в связанном вопросе (в основном это список, 3 => 1 2 3 вместо mysql с 3 строками для представления 3 = 1, 3 = 2, 3 = 3.

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

1 Ответ

5 голосов
/ 23 февраля 2012

Глядя на предоставленные фрагменты кода, я бы сказал, что основным узким местом здесь являются циклические прерывания по сети или TCP. И MySQL, и Redis являются синхронными хранилищами клиент / сервер. Каждый раз, когда вы отправляете запрос и ждете ответа, вы платите за планирование ядра, задержку в сети, коэффициент неудачного попадания в кэш процессора и т. Д. *

Люди, которые выполняют сотни тысяч запросов в секунду на TCP-серверах, не используют один сокет для целевого сервера, но несколько соединений для параллелизма на стороне клиента и / или направляют свои запросы в порядке ограничить влияние этой задержки.

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

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

Вы можете сначала прочитать страницу теста Redis . Описаны все типичные узкие места производительности, с которыми вы можете столкнуться при тестировании Redis.

Вот несколько советов для достижения максимальной пропускной способности для вашего теста:

  • использовать эффективный язык (Python, Ruby, Javascript намного медленнее, чем C)
  • Направляйте ваши запросы насколько возможно
  • если клиент и сервер находятся в одном и том же окне, используйте доменные сокеты Unix, а не TCP loopback.
  • оптимизация на уровне системы и сети только после оптимизации программного обеспечения (NUMA, настройка NIC и т. Д.) *

Я провел простой тест с использованием hiredis (клиент C Redis), чтобы смоделировать ваш вариант использования на Xeon X5670@2.93GHz. Код можно найти здесь .

if r_server.sismember(s - c * x, i):
    r_server.sadd(s, i + 1)

Программа реализует похожий код, конвейеризуя запросы. Он группирует элементы и отправляет несколько команд sismember, чтобы узнать, существуют элементы, или нет, а затем несколько команд sadd для элементов, которые он должен добавить.

Результаты:

  • Без конвейерной передачи, с обратной связью по TCP => 66268 к / с
  • Без конвейеризации, с сокетами Unix-доменов => 89485 к / с
  • С конвейерной передачей и TCP loopback => 273757 к / с
  • С конвейерными и unix-сокетами домена => 278254 к / с

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

Результаты могут быть дополнительно улучшены путем настройки конфигурации системы / сети, но следующим шагом для увеличения пропускной способности обычно является запуск нескольких экземпляров Redis и разбиение данных с использованием механизма хеширования (попытка распараллеливания на стороне сервера).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...