当前位置: 首页 > 知识库问答 >
问题:

使用线程池和连接池测试postgres数据库的吞吐量。但是为什么我每秒只有300次插入,而它应该是6000次?

太叔马鲁
2023-03-14

我想测试与postgresql数据库有连接的系统的吞吐量。我的系统由两个主要组件组成:一个ThreadPoolExector作为newFixedThreadPool,最多10个线程,一个PGPoolingDataSource,最多10个与数据库的连接。我在postgres数据库中调用存储过程,存储过程执行简单的插入,如果插入失败则返回错误消息。执行此存储过程的单个调用大约需要20-30毫秒。

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

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

如果有人能告诉我我做错了什么,那就太好了。是不是我的postgres服务器配置不正确?当我使用top命令时,它显示java使用大约20%的cpu,有8个postgres进程,每个进程使用大约3%。(我在使用Eclipse的Ubuntu 14.04上)。

这是我的MainTester代码,包含main函数。它创建线程池和数据库连接池。

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%,其他cpu为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个CPU都只有20%的容量?也许我的html" target="_blank">postgresql server配置错误了?我没有更改默认配置中的任何内容。我是否误解了线程池的工作方式?还是连接池没有按我的预期工作?

共有1个答案

令狐灿
2023-03-14

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

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

要获得更快的结果,您需要:

  • 批量工作到执行多个插入的事务
  • 启用提交延迟,并设置同步提交=关闭(有一定的数据丢失风险);或
  • 获取更快的磁盘

有关更多信息,请参阅如何在PostgreSQL中加快插入性能

如果您不介意在崩溃后丢失整个数据集,也可以使用未标记的表。但是,实际上,在不干净的关机之后,您的数据将被删除。跑了。无法恢复。所以你一定要当真。

 类似资料:
  • 本文向大家介绍解释一下什么是池化设计思想。什么是数据库连接池?为什么需要数据库连接池?相关面试题,主要包含被问及解释一下什么是池化设计思想。什么是数据库连接池?为什么需要数据库连接池?时的应答技巧和注意事项,需要的朋友参考一下 池话设计应该不是一个新名词。我们常见的如java线程池、jdbc连接池、redis连接池等就是这类设计的代表实现。这种设计会初始预设资源,解决的问题就是抵消每次获取资源的消

  • 问题内容: 我只是想知道数据库连接池的概念以及如何实现。 问题答案: 数据库 连接 池是一种用于保持数据库连接打开以便其他人可以重用的方法。 通常,打开数据库连接是一项昂贵的操作,尤其是在数据库是远程的情况下。您必须打开网络会话,进行身份验证,检查授权等。池化使连接保持活动状态,以便在以后请求连接时,优先使用活动的连接之一,而不必创建另一个连接。 请参阅下图,了解以下几段: 以最简单的形式,它只是

  • 我有一个类女巫负责向客户端发送数据,所有其他类在需要发送数据时都使用这个。让我们称之为“数据ender.class”。 现在客户端要求我们将吞吐量控制在每秒最多50次调用。 我需要在这个类上创建一个algoritm(如果可能的话),以保持当前秒的调用次数,如果它达到50的最大值,保持进程要么睡眠或某事,并继续而不丢失数据。也许我必须实现一个队列或比简单的睡眠更好的东西。我需要建议或遵循的方向。 为

  • 参考Java的Fork/Join vs ExecutorService-何时使用哪个?,传统的线程池通常用于处理许多独立请求;用于处理连贯/递归任务,其中一个任务可能会产生另一个子任务并稍后加入。 那么,为什么Java-8的默认使用而不是传统的执行器? 在许多情况下,我们在或之后使用,然后提交一个函数式接口作为参数。从我的角度来看,这些任务是独立的,不是吗?

  • 我正在使用多个线程在不同的表中插入插入记录。此外,我正在使用批处理的记录插入,以提高效率。 注意:要插入的记录数以百万为单位。 我的问题是,在这种多线程环境中,我应该使用连接池吗? 我关心的问题: 每个线程将运行相当长的时间来执行数据库操作。所以,如果我的连接池的大小是2,线程的数量是4,那么在给定的时刻只有2个线程将运行。因此,其他两个线程将会在很长一段时间内保持理想状态以获得连接,因为针对百万

  • 问题内容: 我对Hibernate非常陌生,刚刚开始研究将其与MySQL数据库一起使用的Web应用程序。我注意到社区文档教程指出: 内置的Hibernate连接池绝不用于生产用途。它缺乏在任何体面的连接池中都能找到的若干功能。 有人可以详细说明吗?它到底缺失了什么?人们对“默认”有什么问题?在谷歌搜索时,我在这里找到了一个网站,但它并没有真正解释问题,只是您应该使用的是什么。 问题答案: 什么是连