2015-11-01 2 views
0

Я хочу проверить пропускную способность моей системы, которая имеет подключение к базе данных postgresql. Моя система состоит из двух основных компонентов: ThreadPoolExecutor как newFixedThreadPool с максимум 10 потоками и PGPoolingDataSource с именем connectionPool, который имеет максимум 10 подключений к базе данных. Я вызываю хранимые процедуры в базе данных postgres, хранимая процедура выполняет простую вставку и возвращает сообщение об ошибке, если вставка не удалась. Выполнение одного вызова этой хранимой процедуры занимает около 20-30 мс.Тестирование Пропускная способность базы данных postgres с использованием пула потоков и пула соединений. Но почему у меня только 300 вставок в секунду, когда это должно быть 6000?

Система работает следующим образом: Основной поток создает задачи сообщений и передает их в пул потоков. Задача сообщения выполняет следующие действия: он получает соединение из пула соединений и вызывает хранимую процедуру на сервере postgres. Он ждет ответа, а затем задача завершена. Теперь поток в пуле потоков может работать над новой задачей сообщения.

Теперь, я думаю, это должно работать нормально, и это в некоторой степени. Это очень медленно, и я не знаю, почему. Используя следующий код, я записываю около 300-500 вставок в секунду, когда это должно быть 6000 вставок в секунду. Понятия не имею почему. При использовании монитора системы я вижу, что весь процессор имеет нагрузку около 20%. Когда я раскомментирую раздел, обозначенный (1), 1 процессор находится на 100% нагрузке, а остальные около 0%, что для меня загадка.

Если кто-то может рассказать немного о том, что я делаю неправильно, это было бы здорово. Является ли это, что мой сервер postgres настроен неправильно? Когда я использую верхнюю команду, это показывает, что java использует около 20% процессора, и есть 8 процессов postgres, каждый из которых использует около 3%. (Я на Ubuntu 14.04, используя Eclipse).

Вот мой код MainTester, содержащий основную функцию. Он создает пул потоков и пул соединений с базой данных.

public class MainTester { 
public static ThreadPoolExecutor threadPoolExecutor; 
    public static PGPoolingDataSource connectionPool; 

public static void main(String[] args) { 

    establishConnectionPool(10); 
    threadPoolExecutor = (ThreadPoolExecutor) 
    Executors.newFixedThreadPool(10); 

    Operator operator = new Operator(1, 2, 30); 
     operator.run(); 
// i created an other thread here before. 
//Now I just use the main thread to run the operator 
} 


private static void establishConnectionPool(int nrOfConnections) 
    { 
     connectionPool = new PGPoolingDataSource(); 
     connectionPool.setDataSourceName("ConnectionPool"); 
     connectionPool.setServerName(dbServerName); 
     connectionPool.setDatabaseName(dbName); 
     connectionPool.setUser(dbUser); 
     connectionPool.setPassword(dbPassword); 
     connectionPool.setMaxConnections(nrOfConnections); 
    } 

Это мой код оператора. Он порождает задачи сообщений и передает их пулу потоков. Я хочу, чтобы он работал в течение 2 минут, а затем проверял количество вставленных сообщений. Я хочу постоянно держать очередь пула потоков, поэтому я проверяю, имеет ли очередь пула потоков меньше 1000 задач. Если у него меньше, я создаю новые задачи для пула потоков, чтобы пережевывать.

public class Operator implements Runnable{ 

private int minutesToRun = 2; 

private void run() { 

    long startTime = System.currentTimeMillis(); 

    while (System.currentTimeMillis() - startTime < minutesToRun * 60 * 1000 + 10) { 

      while(MainTester.threadPoolExecutor.getQueue().size() < 1000) { 
       MessageTask messageTask = new MessageTask(QueueOperation.SEND, 1, 1, 1, "abc"); 
       MainTester.threadPoolExecutor.execute(messageTask); 
      } 

      try { // (1) 
       Thread.sleep(100); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
    } 
} 

}

(1), когда я не сплю здесь, система мониторинга показывает, что 1 процессор на 100%, а остальные на 0%. Для меня это не имеет смысла. Конечно, этот метод будет полностью занимать один процессор, но потоки из пула потоков должны работать на другом процессоре нет?

Вот мой код для выполнения этой задачи сообщений:

public class MessageTask implements Runnable { 

private QueueOperation operation; 
private int senderId; 
private int receiverId; 
private int queueId; 
private String message; 


public MessageTask (QueueOperation op, int senderId, int receiverId, int queueId, String message) 
{ 
    operation = op; 
    this.senderId = senderId; 
    this.receiverId = receiverId; 
    this.queueId = queueId; 
    this.message = message; 
} 

@Override 
public void run() { 

    Connection connection = null; 
    try { 
     connection = MainTester.connectionPool.getConnection(); 
    } catch (SQLException e) { 
     e.printStackTrace(); 
    } 

    try{ 

     Statement statement = connection.createStatement(); 

     String dbStoredProcedure = "SELECT send(" + senderId + "," + receiverId + "," + queueId + "," + "'"+message+"'"+ ");";; 

     ResultSet resultSet = statement.executeQuery(dbStoredProcedure); 
     resultSet.next(); 
     String dbResponse = resultSet.getString(1); 
    } 

    catch (SQLException e) { 
    } 

    finally { 
     try { 
      connection.close(); 
     } catch (SQLException e) { 
      e.printStackTrace(); 
     } 
    } 

} 

Так что мои вопросы: почему это так медленно? Почему все 8 из моего процессора только в 20% емкости? Возможно, я неправильно настроил сервер postgresql? Я ничего не изменил в конфигурации по умолчанию. Я неправильно понял, как работает пул потоков? Или пул соединений не работает так, как я предполагал?

+0

Почему вы используете хранимую процедуру для простого 'INSERT'? Когда вы вызываете SP из «SELECT» из-за переключения контекста, всегда присуща медлительность. Кроме того, использование переменных привязки еще больше замедляет анализ, который должен выполнять Postgres перед выполнением инструкции. –

+2

Основные вопросы для параллельной скорости вставки: 1. Когда происходит COMMIT? У каждой вставки? 2. есть ли первичный ключ в таблице? # 1 хорошо избегать блокировки, но плохо для производительности. # 2 - противоположное. –

+0

А что такое дисковая нагрузка? –

ответ

0

Когда вы измеряете сохраненное время выполнения proc, вы, вероятно, не учитываете, сколько времени занимает фиксация. Вы также фокусируетесь на процессоре и полностью игнорируете дисковый ввод-вывод и стоимость флеш-дисков.

300 транзакций в секунду - довольно разумное количество для типичной системы с базовым SSD. Поэтому я бы сказал, что вы совершаете после каждой вставки.

Чтобы получить быстрые результаты, которые вы должны будете:

  • пакетной работы в операции, которые делают несколько вставок;
  • включить commit_delay и установить synchronous_commit = off (имеет некоторый риск потери данных); или
  • получить более быстрый диск

Для получения дополнительной информации см How to speed up insertion performance in PostgreSQL

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