2015-11-01 173 views
0

我想测试我的系统与postgresql数据库的连接的吞吐量。我的系统由2个主要组件组成:ThreadPoolExecutor作为newFixedThreadPool最多10个线程和一个名为connectionPool的PGPoolingDataSource,最多有10个连接到数据库。我在postgres数据库中调用存储过程,存储过程做了一个简单的插入操作,并在插入失败时返回错误消息。执行此存储过程的单个调用大约需要20-30毫秒。使用线程池和连接池测试postgres数据库的吞吐量。但是,为什么我应该是6000时每秒只能有300个插入?

系统是这样工作的:主线程创建消息任务并将它们传递给线程池。消息任务执行以下操作:它从连接池获取连接并调用postgres服务器上的存储过程。它等待响应,然后任务完成。线程池中的线程现在可以处理新的消息任务。

现在,我认为这应该工作得很好,它在一定程度上。这只是非常缓慢,我完全不知道为什么。使用下面的代码,我记录大约300-500秒插入,当它应该是每秒6000插入。我不知道为什么。当使用系统监视器时,我看到所有的cpus都处于大约20%的负载。当我取消注释(1)所指的部分时,1个CPU处于100%负载,而其他CPU处于0%左右,这对我来说是个谜。

如果任何人都可以分享我做错的事情,那会很棒。难道我的postgres服务器配置不正确?当我使用top命令时,它显示java使用大约20%cpu,并且有8个postgres进程,每个使用大约3%。 (我使用Eclipse在Ubuntu 14.04上)。

这是我的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级的CPU为100%,其余为0%。这对我来说没有意义。当然,这种方法将完全占用一个cpu,但线程池中的线程应该在另一个cpu上运行。

这里是我的消息任务代码:

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'?由于上下文切换,从“SELECT”调用SP时总会有内在的缓慢。此外,不使用绑定变量进一步减慢了Postgres在执行语句之前必须执行的解析。 –

+2

平行插入速度的基本问题:1.什么时候发生COMMIT?在每个插入? 2.表中有主键吗? #1有利于避免锁定,但对性能不利。 #2是相反的。 –

+0

什么是磁盘负载? –

回答

0

当您测量存储的proc执行时间时,您可能不考虑提交需要多长时间。您似乎也专注于CPU并完全忽略磁盘I/O和磁盘刷新的成本。

对于具有基本SSD的典型系统,每秒300笔交易是一个相当合理的数字。所以我会说你在每次插入后都会提交。

为了得到你需要更快的结果:

  • 批处理工作成做多的刀片交易;
  • 启用commit_delay并设置synchronous_commit = off(有一定的数据丢失风险);或
  • 获得更快的磁盘

欲了解更多信息,请参阅How to speed up insertion performance in PostgreSQL

如果你不介意崩溃,你也可以使用unlogged表后丢失整个数据集。但是,确实,在不洁净的关机后,您的数据将被清除。不见了。不可恢复的。所以一定要确定你的意思。