zoukankan      html  css  js  c++  java
  • PreparedStatement是如何大幅度提高性能的 (转)

    PreparedStatement是如何大幅度提高性能的 (转)

    PreparedStatement是如何大幅度提高性能XML:namespace prefix = o ns = "urn:schemas-microsoft-com:Office:office" />

    作者:Billy Newport

    本文讲述了如何正确的使用prepared statements。为什么它可以让你的应用程序运行的更快,和同样的让数据库操作变的更快。

    为什么Prepared Statements非常重要?如何正确的使用它?

    数据库有着非常艰苦的工作。它们接受来自众多并发的客户端所发出的sql查询,并尽可能快的执行查询并返回结果。处理statements是一个开销昂贵的操作,不过现在有了Prepared Statements这样的方法,可以将这种开销降到最低。可是这种优化需要开发者来完成。所以本文会为大家展示如何正确的使用Prepared Statements才能使数据库操作达到最优化。

    数据库是如何执行一个statement的?

    显然,我不会在这里写出很多的细节,我们只关注最关键的部分。当一个数据库收到一个statement后,数据库引擎会先解析statement,然后检查其是否有语法错误。一旦statement被正确的解析,数据库会选出执行statement的最优途径。遗憾的是这个计算开销非常昂贵。数据库会首先检查是否有相关的索引可以对此提供帮助,不管是否会将一个表中的全部行都读出来。数据库对数据进行统计,然后选出最优途径。当决创建查询方案后,数据库引擎会将它执行。

    存取方案(Access Plan)的生成会占用相当多的cpu。理想的情况是,当我们多次发送一个statement到数据库,数据库应该对statement的存取方案进行重用。如果方案曾经被生成过的话,这将减少CPU的使用率。

    Statement Caches

    数据库已经具有了类似的功能。它们通常会用如下方法对statement进行缓存。使用statement本身作为key并将存取方案存入与statement对应的缓存中。这样数据库引擎就可以对曾经执行过的statements中的存取方案进行重用。举个例子,如果我们发送一条包含select a, b FROM t WHERE c = 2的statement到数据库,然后首先会将存取方案进行缓存。当我们再次发送相同的statement时,数据库会对先前使用过的存取方案进行重用,这样就降低了CPU的开销。

    注意,这里使用了整个statement为key。也就是说,如果我们发送一个包含SELECT a, b FROM t WHERE c = 3的statement的话,缓存中不会没有与之对应的存取方案。这是因为“c=3”与曾经被缓存过的“c=2”不同。所以,举个例子:

    for (int i = 0; i < 1000; i++)  {

    PreparedStatement ps = conn.prepareStatement("select a,b from t where c = " + i);

    ResultSet rs = Ps.executeQuery();

    rs.close();

    ps.close();

    }

    在这里缓存不会被使用,因为每一次迭代都会发送一条包含不同SQL语句的statement给数据库。并且每一次迭代都会生成一个新的存取方案。现在让我们来看看下一段代码:

    PreparedStatement ps = conn.prepareStatement("select a,b from t where c = ?");

    for (int i = 0; i < 1000; i++)  {

    ps.setInt(1, i);

    ResultSet rs = ps.executeQuery();

    rs.close();

    ps.close();

    }

    这样就具有了更好的效率,这个statement发送给数据库的是一条带有参数“?”的SQL语句。这样每次迭代会发送相同的statement到数据库,只是参数“c=?”不同。这种方法允许数据库重用statement的存取方案,这样就具有了更好的效率。这可以让你的应用程序速度更快,并且使用更少的CPU,这样数据库服务器就可以为更多的人提供服务。

    PreparedStatementJ2EE服务器

    当我们使用J2EE服务器时事情会变的比较复杂。通常,一个perpared statement会同一个单独的数据库连接相关联。当数据库连接被关闭时prepared statement也会被丢弃。通常,一个胖客户端会获取一个数据库连接并将其一直保持到退出。它会用“饿汉”(eagerly)或“懒汉”(lazily)方式创建所有的parepared statements。“饿汉”方式会在应用启动时创建一切。“懒汉”方式意味着只有在使用的时候才去创建。“饿汉”方式会使应用程序在启动的时候梢有延迟,但一旦启动后就会运行的相当理想。“懒汉”方式使应用程序启动速度非常快(但不会做任何准备工作),当需要使用prepared statement的时候再去创建。这样,在创建全部statement的过程中,性能是非常不稳定的,但一旦创建了所有statement后,它会像“饿汉”式应用程序一样具有很好的运行效果。请根据你的需要来选择最好的方式,是快速启动?还是前后一致的性能。

    J2EE应用的问题是它不会像这样工作,连接只会在请求期间被保持。那意味着必须每一次请求的时候都创建prepared statement。这远没有胖客户端那种一直保持prepared statement的执行性能好。J2EE厂商已经注意到了这个问题,并且提供了连接池(ConnectionPool)以避免这种问题。

    当J2EE服务器提供了一个连接给你的应用程序时,其实它并没有给你真正的数据库连接,你只是获得了一个包装器(Wrapper)。你可以去看看你所获得的连接的类名以证实这一点。它并不是一个JdbC连接,而是一个由应用服务器创建的类。所有的JDBC操作都会被应用服务器的连接池管理器所代理。所有的JDBC ResultSets,statements,CallableStatements,preparedStatements等都会被包装并以一个“代理对象”(Proxy object)的形式返回给应用程序。当你关闭了连接,这些对象会被标记为失效,并被垃圾回收器所回收。

    通常,如果你对一个数据库连接执行close,那这个连接会被JDBC驱动程序关闭。但我们需要在J2EE服务器执行close的时候数据库连接会被返回连接池。我们可以创建一个像真正的连接一样的JDBC Connection代理类来解决这个问题。它有一个对真正连接的引用。当我们执行一个连接上的方法时,代理会将操作转给真正的连接。但是,当我们对一个连接执行close时,这个连接并不会关闭,而是会送回连接池,并可以被其他请求所使用。一个已被准备过的prepared statement也会因此而得到重用。

    J2EE PreparedStatement Cache

    J2EE服务器的连接池管理器已经实现了缓存的使用。J2EE服务器保持着连接池中每一个连接准备过的prepared statement列表。当我们在一个连接上调用preparedStatement时,应用服务器会检查这个statement是否曾经准备过。如果是,这个PreparedStatement会被返回给应用程序。如果否,调用会被转给JDBC驱动程序,然后将新生成的statement对象存入连接缓存。

    每个连接都有一个缓存的原因是因为:JDBC驱动程序就是这样工作的。任何prepared statement都是由指定的连接所返回的。

    如果我们想利用这个缓存的优势,那就如前面所说的,使用参数化的查询语句可以在缓存中找到曾经使用过的statement。大部分应用服务器允许你调整prepared statements缓存的大小。

    摘要

    我们绝对应该使用包含参数化的查询语句的prepared statement。这样数据库就会重用准备过的存取方案。缓存适用于整个数据库,所以,如果你安排所有的应用程序使用相同的参数化SQL语句,然后你的其他应用程序就可以重用被准备过的prepared statement。这是应用服务器的一个优势,因为所有的数据库操作都集中在数据库操作层(Database Access Layer,包括O/R映射,实体Bean,JDBC等)。

    第二,正确的使用prepared statement也是利用prepared statement的缓存优势的关键。由于应用程序可以重用准备过的prepared statement,也就减少了调用JDBC驱动程序的次数,从而提高了应用程序的性能。这样就拥有了可以与胖客户端比肩的效率,却又不需要总维持一个连接。

    使用参数化的prepared statement,你的应用程序会具有更好的性能。

    大部分转载 小部分自写
  • 相关阅读:
    Spring Cloud Hystrix Dashboard的使用 5.1.3
    Spring Cloud Hystrix 服务容错保护 5.1
    Spring Cloud Ribbon 客户端负载均衡 4.3
    Spring Cloud 如何实现服务间的调用 4.2.3
    hadoop3.1集成yarn ha
    hadoop3.1 hdfs的api使用
    hadoop3.1 ha高可用部署
    hadoop3.1 分布式集群部署
    hadoop3.1伪分布式部署
    KVM(八)使用 libvirt 迁移 QEMU/KVM 虚机和 Nova 虚机
  • 原文地址:https://www.cnblogs.com/8586/p/1275932.html
Copyright © 2011-2022 走看看