自适应缓冲的作用是在无需服务器游标开销的情况下检索任何类型的大值数据。应用程序可以在受驱动程序支持的所有 SQL Server 版本中使用自适应缓冲功能。

通常,当 Microsoft SQL Server JDBC Driver 执行查询时,驱动程序会将服务器中的所有结果检索到应用程序内存中。尽管这种方法可以最大程度地减少 SQL Server 上的资源占用,但它可能会在 JDBC 应用程序中针对生成非常大的结果的查询引发 OutOfMemoryError

为了使应用程序可以处理非常大的结果,Microsoft SQL Server JDBC Driver 提供了自适应缓冲。借助于自适应缓冲,驱动程序可以在应用程序需要时从 SQL Server 中检索语句执行结果,而不是一次性检索全部结果。一旦应用程序不再访问结果,驱动程序还会立即丢弃它们。以下是可以使用自适应缓冲的一些示例:

注意: 使用自适应缓冲时,JDBC Driver 只会缓冲它必须缓冲的那些数据。该驱动程序未提供任何公共方法来控制或限制缓冲区的大小。

设置自适应缓冲

从 JDBC Driver 2.0 开始,驱动程序的默认行为是“adaptive”。换言之,要获取自适应缓冲行为,应用程序不必显式请求自适应行为。不过,在版本 1.2 中,默认情况下,缓冲模式为“full”,即应用程序必须显式地请求自适应缓冲模式。

应用程序可以通过三种方法请求语句执行应使用自适应缓冲:

使用 JDBC Driver 1.2 时,应用程序需要将语句对象强制转换为 SQLServerStatement 类才能使用 setResponseBuffering 方法。读取大型数据的示例使用存储过程读取大型数据的示例 中的代码示例说明了这种旧的用法。

但是,使用 JDBC Driver 2.0 时,应用程序无需关于实现类层次结构的任何假设,即可使用 isWrapperFor 方法和 unwrap 方法来访问供应商特定的功能。有关示例代码,请参阅更新大型数据的示例主题。

使用自适应缓冲检索大型数据

当使用 get<类型>Stream 方法一次性读取大值,并且 SQL Server 按返回的顺序访问 ResultSet 列和 CallableStatement OUT 参数时,自适应缓冲在处理结果时可以最大程度地减少使用的应用程序内存。使用自适应缓冲时:

  • SQLServerResultSetSQLServerCallableStatement 类中定义的 get<Type>Stream 方法默认情况下返回只读取一次的流,尽管可以重置流(如果应用程序已进行标记)。如果应用程序要对流执行 reset,它必须先对该流调用 mark 方法。
  • SQLServerClobSQLServerBlob 类中定义的 get<Type>Stream 方法返回的流始终可以重定位至流的开始位置,而不必调用 mark 方法。

当应用程序使用自定义缓冲时,由 get<Type>Stream 方法检索的值仅供检索一次。如果您在调用同一列或同一参数的 get<Type>Stream 方法后,试图对同一对象调用任何 get<Type> 方法,则将引发异常并显示消息“数据已访问,不可用于此列或此参数”。

自适应缓冲使用准则

开发人员应遵循以下重要准则,以尽可能减少应用程序占用的内存:

  • 应避免使用连接字符串属性 selectMethod=cursor 来允许应用程序处理非常大的结果集。自适应缓冲功能允许应用程序在不使用服务器游标的情况下处理非常大的只进、只读结果集。请注意,设置 selectMethod=cursor 时,该连接生成的所有只进只读结果集都会受到影响。换言之,如果应用程序例行处理只有几行的短结果集,则与没有将 selectMethod 设置为 cursor 的情况相比,针对每个结果集创建、读取和关闭服务器游标在客户端和服务器端都会使用更多的资源。
  • 通过使用 getAsciiStreamgetBinaryStream,getCharacterStream 方法(而不是 getBlobgetClob 方法),将大文本或二进制值作为流进行读取。从版本 1.2 开始,SQLServerCallableStatement 类提供了新的 get<Type>Stream 方法来实现此目的。
  • 确保在 SELECT 语句中将可能具有大值的列放在列列表的最后,并且使用 SQLServerResultSetget<Type>Stream 方法按选择列时的顺序来访问这些列。
  • 确保在用来创建 SQLServerCallableStatement 的 SQL 语句的参数列表中,最后声明可能具有大值的 OUT 参数。此外,确保使用 SQLServerCallableStatementget<Type>Stream 方法,按照声明 OUT 参数时的顺序访问这些参数。
  • 避免同时对同一连接执行一条以上的语句。如果在处理上一条语句的结果之前执行另一条语句,可能导致将未处理的结果缓冲到应用程序内存中。
  • 但在某些情况下,使用 selectMethod=cursor 而不是 responseBuffering=adaptive 可能更有利,例如:
    • 如果应用程序对只进只读结果集的处理速度很慢(例如,在某些用户输入后再读取每一行),则使用 selectMethod=cursor 代替 responseBuffering=adaptive 可以有助于减少 SQL Server 使用的资源。
    • 如果应用程序在同一连接上同时处理两个或更多的只进只读结果集,则处理这些结果集时,使用 selectMethod=cursor 代替 responseBuffering=adaptive 可能有助于减少驱动程序需要的内存。
    在这两种情况下,需要考虑创建、读取和关闭服务器游标的开销。

此外,下面的列表针对可滚动的结果集和只进的可更新结果集提供了一些建议:

  • 对于可滚动结果集,在提取行块时,驱动程序始终会将 SQLServerResultSet 对象的 getFetchSize 方法所指示的行数读入内存,即使在启用了自适应缓冲的情况下也是如此。如果滚动导致 OutOfMemoryError,您可以调用 SQLServerResultSet 对象的 setFetchSize 方法将提取大小设置为较少的行数(如果必要,甚至可减小到 1 行),从而减少提取的行数。如果这样还是无法防止 OutOfMemoryError, ,则应避免在可滚动的结果集中包含非常大的列。
  • 对于只进的可更新结果集,在提取行块时,驱动程序通常会将 SQLServerResultSet 对象的 getFetchSize 方法所指示的行数读入内存,即使在该连接上启用了自适应缓冲的情况下也是如此。如果调用 SQLServerResultSet 对象的 next 方法导致 OutOfMemoryError,您可以调用 SQLServerResultSet 对象的 setFetchSize 方法将提取大小设置为较少的行数(如果必要,甚至可减小到 1 行),从而减少提取的行数。执行该语句前,也可以调用 SQLServerStatement 对象的 setResponseBuffering 方法并提供参数“adaptive”来强制驱动程序不缓冲任何行。因为该结果集是不可滚动的,所以如果应用程序使用 get<Type>Stream 方法之一访问大型列值,驱动程序将会在应用程序读取该值后立即将其丢弃,正如对只进只读结果集所做的那样。

请参阅

借助 JDBC 驱动程序提高性能和可靠性