【问题标题】:Why SQL select takes more CPU time in java?为什么 SQL 选择在 Java 中占用更多 CPU 时间?
【发布时间】:2009-08-06 14:06:49
【问题描述】:

我有一个 Java Web 应用程序,它从表中选择一列(有 600 万行),它需要大量的 CPU 时间。在查询浏览器中执行时,此选择 (SELECT id FROM mytable WHERE filename = 'unique_filename') 花费的时间明显减少。

是什么原因造成的?
我应该从哪里开始寻找瓶颈?

数据库是 MSSQL 2005 标准
Java 容器是 Tomcat 5.5(带有 sqljdbc 1.2)

更多详情:
1.Java代码

 ResultSet rs = null;    
 PreparedStatement stmt = null;
 Connection conn = null;
 Integer myId=null;
 String myVeryUniqueFileName = strFromSomeWhere;
 try
 {
    conn = Database.getConnection();
    stmt = conn.prepareStatement("SELECT id FROM mytable WHERE filename = ?");
    stmt.setString(1, myVeryUniqueFileName);

    rs = stmt.executeQuery();
    if (rs.next())
    {
       myId= new Integer(rs.getInt(1));
    }              }
    if (rs.next())
    {
       throw new DBException("Duplicate myId: " + myId);
    }
    return myId;
 } catch (Exception e) {
    // handle this
 }

Database 对象使用 DriverManager 接收连接对象。

2.SQL表大约有30列。

 CREATE TABLE [dbo].[calls](    
    [id] [int] NOT NULL,     
    ...    
    [filename] [varchar](50) NOT NULL,
    ...     
 CONSTRAINT [PK_xxxxxxxxxxxx] PRIMARY KEY CLUSTERED     
 (    
    [id] ASC    
 )WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY],     
 CONSTRAINT [UQ_xxxxxxxxxxxx] UNIQUE NONCLUSTERED       
 (      
    [filename] ASC     
 )WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]     
 ) ON [PRIMARY]    

filename 列是唯一的,因此结果集始终为 1 或 null。

【问题讨论】:

  • 当您说 CPU 时间时,您是指 Java 应用程序使用的 CPU 还是数据库服务器使用的 CPU?它们在两台不同的机器上吗?
  • Tomcat 和 MSSQL 在同一台服务器上?您的 Java 应用程序将这 600 万个 ID 值存储在哪些对象中? Java 和 .NET 需要时间来创建和销毁内存中的每个对象,如果需要创建 600 万个对象,则需要大量时间。
  • 这不是一个真正的问题吗?当然,它不是很详细,但 OP 仍然可以解决这个问题。
  • Mark Canlas:CPU 时间是指在 SQL Profiler 中测量的时间与使用 SET STATISTICS TIME ON 的查询浏览器测量的时间。 Bogdan_Ch:Tomcat 和 MSSQL 服务器不在同一台服务器上。我不需要存储所有 600 万个 ID 值,我只需要存储一个 id 值。

标签: java sql tomcat


【解决方案1】:

在更聪明的开发人员的帮助下,我能够解决这个问题。原来我在滥用 PreparedStatement (aricle)。

基于此我将java代码改为:

ResultSet rs = null;    
Statement stmt = null;
Connection conn = null;
Integer myId=null;
String myVeryUniqueFileName = strFromSomeWhere;
try
{
  conn = Database.getConnection();
  stmt = conn.createStatement()
  //
  rs = stmt.executeQuery("SELECT id FROM mytable WHERE filename = '"
                         + myVeryUniqueFileName + "'");
  if (rs.next())
  {
    myId= new Integer(rs.getInt(1));
  }              
  if (rs.next())
  {
    throw new DBException("Duplicate myId: " + myId);
  }
  return myId;
} catch (Exception e) {
  // handle this
}

在这个 dababase 负载从平均 70% 下降到 13% 之后

【讨论】:

    【解决方案2】:

    您可以在执行此查询并检索结果的位置发布您的 java 代码吗?

    导致 Java 代码出现花费明显更长的可能因素是:

    1. 您的查询返回大量记录,而您正尝试在 Java 中检索它们,而查询浏览器只会显示前 100 条(无论该数字是多少)并按需加载其他记录。
    2. 您正在比较不同的时间,例如,您的查询浏览器显示的“查询花费了 X 毫秒”与 Java 从获取连接到关闭连接所用的时间。
    3. 您的对象(保存结果)的创建成本可能很高,或者它们可能在填充时在幕后进行一些处理。

    【讨论】:

      【解决方案3】:

      我不能专门谈论 MSSQL 2005,但是在使用绑定变量的准备好的语句和嵌入值的等效语句之间的执行计划可能会有所不同。

      要测试这个理论,请删除绑定参数,而是将 Java 中的 SQL 查询与实际文件名(用引号括起来)连接起来。这样你就可以比较苹果和苹果了。

      此外,它还可以用于指示您遇到的 CPU 时间差异。是几个数量级还是不到100%。

      【讨论】:

        【解决方案4】:

        您描述的症状通常是由不正确的缓存查询计划引起的。

        重建您的索引或更新您的统计数据。

        【讨论】:

        • 我不会说查询计划与重建索引直接相关。这似乎是你的意思。
        【解决方案5】:

        您可能使用的是语句而不是准备好的语句。语句不会被预编译和缓存,因此查询优化器必须每次都完成这项工作。如果您使用准备好的语句,它将尝试找到执行查询的最佳方法并将其存储。下次您使用它时,它不会费心去尝试找出一个获得结果的好方法,它只会使用它已经拥有的执行计划。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-23
          • 2013-09-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-03-22
          相关资源
          最近更新 更多