【问题标题】:Is connection pooling with JDBC still improving performance nowadays?如今,使用 JDBC 的连接池是否仍在提高性能?
【发布时间】:2011-09-26 16:14:14
【问题描述】:

我的应用程序没有使用任何形式的连接池,我直接使用连接。该应用程序主要执行简短的查询。从日志中我可以看到它经常打开和关闭连接,通常只执行一个或几行之间的单选。这些通常需要大约 100 毫秒(包括打开和关闭连接)。

关于连接池如何提高应用程序性能的文章和博客文章数不胜数,但它们似乎都相当陈旧(5 年或更长时间)。

连接池是否仍能提供合理的性能优势,还是已经过时。我正在使用带有 Microsoft JDBC 驱动程序 3.0 版的 SQLServer 2008,如果这很重要的话。


结果/更新:自从我提出这个问题后发生了很多事情(我们切换了 JDBC 驱动程序和许多其他东西)。有时我做了很多重构和其他工作,并且在那个时候还为该应用程序添加了连接池。 通过连接池,一些查询的执行速度现在比日志时间戳粒度可以测量的更快(我相信不到 16 毫秒)。

因此,总而言之,如果您需要频繁连接/断开连接,连接池仍然值得努力。

【问题讨论】:

    标签: java performance jdbc connection-pooling


    【解决方案1】:

    如果每个查询 100 毫秒适合您,那么您不需要连接池。如果您需要少于 20 毫秒的查询,则重用连接是必不可少的。

    如果您的驱动程序支持自己的连接池,我建议您使用它(以防它尚未为您执行此操作)。只有当您想更好地控制连接的池化方式时,您才能使用额外的库(我自己从来没有找到一个很好的用途)

    注意:您无需使用池来重用连接。

    重用连接的一种简单方法是使用一个持久连接(具有适当的线程安全防护措施)如果您的查询不频繁,这可能就是您所需要的。

    如果您希望能够同时执行查询并且只有几个线程可以执行查询,您可以将连接存储在 ThreadLocal 字段中。

    如果您需要多个连接,并且执行查询的线程数多于您希望的连接数,请使用池。

    对于 ThreadLocal 模型你可以做

    public static final ThreadLocal<Connection> CONNECTION = new ThreadLocal<Connection>() {
         public Connection initialValue() {
             LOG.info(Thread.currentThread()+": created a connection.");
             return createConnection();
         }
    };
    

    如果你想控制如何清理连接。

    private static final Map<Thread, Connection> connections = new ConcurrentHashMap();
    public static final ThreadLocal<Connection> CONNECTION = new ThreadLocal<Connection>() {
         public Connection initialValue() {
             LOG.info(Thread.currentThread()+": created a connection.");
             Connection conn = createConnection();
             connections.put(Thread.currentThread(), conn);
             return conn;
         }
    };
    public static void cleanUp() {
         for(Map.Entry<Thread, Connection> entry: connections.entrySet()) {
             Thread t = entry.getKey();
             if (!t.isAlive()) {
                 LOG.info(t+": closed a connection.");
                 connections.remove(t);
                 entry.getValue().close();
             }
         }
    }
    

    如果担心连接死机,可以重写 ThreadLocal 的 get(),在返回前测试连接。

    【讨论】:

    • 一个持久连接不起作用,但执行查询的线程数是有限的(我认为它目前设置为 2xCPUS)。在我的情况下,ThreadLocal 的想法可能相当容易实现,尽管在关闭时可能很难正确关闭它们。目前我在连接时间方面没有紧迫的性能问题,但是当客户端使用更广泛时,这可能会改变。
    • 你多久关闭一次线程?你能保持做这个静态的线程数吗?如果没有,您可以拥有Map&lt;Thread, Connection&gt; 的集合,用于查找死线程的连接并关闭它们的连接。
    • 线程仅在运行服务器部分的服务重新启动或终止时关闭,即 JVM 退出时。在实践中,如果我从不清理这些连接,它不会杀了我,我猜。线程数在启动时确定,服务器选择一个随机空闲线程并让它执行所需的任何服务。我唯一的问题是这个线程池不是由我的应用程序维护的,而是一些框架(我必须小心触摸,因为它也被其他应用程序使用)。
    • 当您关闭服务器时,您现在是否明确关闭连接?例如如果您正在使用连接?当你关闭一个应用程序时,它的所有 TCP 连接都会被操作系统关闭(可能不那么干净)
    • 此外 - 无论延迟如何 - 一些 RDBMS 服务器会花费大量精力来建立您的连接。因此,即使延迟对每个客户端来说并不重要,您也会给数据库服务器带来一些可以轻松避免的痛苦。
    【解决方案2】:

    这在一定程度上取决于数据库软件。一些(例如 MySQL)具有相对轻量级的连接,可以快速打开。其他的,比如 Oracle,连接是大型的喇叭结构,开销很大。

    不过,一般来说,使用连接池是个好主意,尤其是用于快速、简短、大容量的查询。您的应用程序包含连接的速度越快,它就变得越重要。这在过去几年中没有改变 - 如果有的话,它比以往任何时候都更加重要,因为应用程序会扩大规模并变得更加复杂。

    如果您需要令人信服,我建议您进行一些基准测试,包括合并和不合并。如果您在特定情况下没有获得性能优势,那么也许不值得费心。

    【讨论】:

      猜你喜欢
      • 2010-10-24
      • 2014-12-05
      • 1970-01-01
      • 2015-11-18
      • 2012-06-16
      • 2013-10-12
      • 2010-11-30
      相关资源
      最近更新 更多