【问题标题】:query hangs oracle 10g查询挂起oracle 10g
【发布时间】:2011-07-14 04:11:09
【问题描述】:

我的软件有这个奇怪的问题。 是在生产 5 年,我们没有这样的问题...

问题:

我们有一个 spring 作业(调度程序),它通过 hibernate 进行查询,检索对象并修改它们。

嗯,这工作了好几年,但一个月前,查询每天挂起 5-10 次(每 10 分钟调用一次查询)。当它挂起时,我们必须重新启动服务。

以下代码执行查询:

@SuppressWarnings("unchecked")
public List<Delivery> findScheduledForDelivery(final String inType, final int max, final String benefitType ) {


    //getHibernateTemplate().clear();

    return getHibernateTemplate().executeFind(new HibernateCallback() {
        public Object doInHibernate(Session session) throws SQLException {
            Criteria criteria = session.createCriteria(Delivery.class);

            criteria.createAlias("reward","r");
            criteria.createAlias("r.customer","c");
            criteria.createAlias("c.inNe","i");
            criteria.createAlias("r.promotion","p");
            criteria.createAlias("benefit","b");

            String sqlCustAlias = StringHelper.generateAlias("c", 2);

            criteria.add(Expression.disjunction()
                .add(Expression.eq("inStatus", INStatus.InterfaceFailure))
                .add(Expression.eq("inStatus",INStatus.Initial)));

            criteria.add(Expression.le("deliverAt", new Date()));

            String dateString = "2000/01/01";
            DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
            Date startDate = new Date();
            try {
                startDate = dateFormat.parse(dateString);
                criteria.add(Expression.ge("deliverAt", startDate));
            }
            catch(ParseException e) {
                e.printStackTrace();
            }

            String sqlEqual = "decode(delivered,null,0,1) = 0";
            criteria.add(Expression.sql(sqlEqual));

            sqlEqual = "decode(" + sqlCustAlias + ".deleteDate,null,1,0) = 1";
            criteria.add(Expression.sql(sqlEqual));

            if(inType  != null ) {
                for(INType i : INType.values())
                    if(i.toString().equals(inType)) {
                        criteria.add(Expression.eq("i.inType", i));
                        break;
                    }
            }

            criteria.add(Expression.eq("p.active", true));

            if(benefitType != null) {
                if(benefitType.equals("FREECREDIT")) 
                    criteria.add(Expression.disjunction()
                            .add(Expression.eq("b.type", BenefitType.FREE_CREDIT))
                            .add(Expression.eq("b.type", BenefitType.FREE_CREDIT_FTAM)));
                else if(benefitType.equals("NONFREECREDIT")) {
                    criteria.add(Expression.conjunction()
                            .add(Expression.ne("b.type", BenefitType.FREE_CREDIT))
                            .add(Expression.ne("b.type", BenefitType.OTHER))
                            .add(Expression.ne("b.type", BenefitType.VOUCHER)));
                    criteria.add(Expression.isNull("b.md3Profile")); 
                }
                if(max != 0)
                    criteria.setMaxResults(max);
            }

            criteria.addOrder( Order.desc("p.priority") );
            criteria.addOrder( Order.asc("deliverAt") );



            return criteria.list(); <===== hangs here
        }
    });
}

数据源是这样定义的(我知道这不应该在生产中,但这是它工作的唯一方式 - 我尝试使用 oracle 连接池但查询挂起更频繁..):

<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
      <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="connectionProperties">
         <props>
          <prop key="tcp.nodelay">yes</prop>
          <prop key="delayRowPrefetch">20</prop>
          <prop key="defaultBatchSize">5</prop>
         </props>
        </property>
      </bean>
    </beans>

使用的软件:

  • 春天 1.2.7
  • 休眠 3.0.5
  • oracle 10.2.0.1 (RAC)
  • oracle jdbc 10.1.0.2
  • 红帽 3 EL
  • Java 1.5_06

到目前为止我已经尝试过:

  • 使用oracle连接池作为数据 来源->挂起失败 连接
  • 使用过 oracle jdbc 10.2.0.5 -> 我以为我已经解决了......几个小时后它又挂了:(

据我所知,oracle 上没有数据库锁...

可能是什么问题?

更新:

在 oracle EM 中:

ADDM 发现: 发现消耗大量数据库时间的 SQL 语句。 查询会消耗大量数据库时间。影响 81%。 用户 I/O 等待 97%。

  • 负责的各个 SQL 语句 对于重要的用户 I/O 等待是 找到了。
  • 单个数据库段 负责重要的用户 I/O 等待被发现。
  • I/O 子系统的吞吐量 明显低于 预计。

更新:(15.03.2011)

目前,该服务可以运行近 48 小时而不会挂起。

我怀疑这是否能解决问题,但我对代码做了一些更改:

删除了查询中的 decode(delivered,null,0,1) = 0decode(" + sqlCustAlias + ".deleteDate,null,1,0) = 1 函数,并将它们替换为 is null 语句。
传递的字段已编入索引,但不能在 decode 函数中使用索引。

你认为这只是巧合吗?

更新:(16.03.2011)

alert.log 现在显示许多这样的条目:

ORA-01555 caused by SQL statement below (SQL ID: affkpm4j7azc4, Query Duration=232624 sec, SCN: 0x0003.dca70559):
Tue Mar 15 17:43:06 2011
select * from ( select this_.id as id5_, this_.deliverAt as deliverAt68_5_, this_.delivered as delivered68_5_, this_.inDelivery as inDelivery68_5_, this_.lastDeliveryTry as lastDeli5_68_5_, this_.tries as tries68_5_, this_.sentAt as sentAt68_5_, this_.sent as sent68_5_, this_.retry as retry68_5_, this_.inStatus as inStatus68_5_, this_.errorMessage as errorMe11_68_5_, this_.inCvsDelivery as inCvsDe12_68_5_, this_.cvsDelivered as cvsDeli13_68_5_, this_.cvsLastDeliveryTry as cvsLast14_68_5_, this_.cvsTries as cvsTries68_5_, this_.collectedPoints as collect16_68_5_, this_.smsMessage as smsMessage68_5_, this_.inOldStatus as inOldSt18_68_5_, this_.replacedDate as replace19_68_5_, this_.oldMsisdn as oldMsisdn68_5_, this_.deletedDate as deleted21_68_5_, this_.addManualDate as addManu22_68_5_, this_.stornoPromiseDate as stornoP23_68_5_, this_.stornoINDate as stornoI24_68_5_, this_.activationCode as activat25_68_5_, this_.activationExpirationDate as activat26_68_5_, this_.rewardId as rewardId68_5_, this_.benefitId as b

它似乎来自 3 天前的会话...... 232624 秒!

【问题讨论】:

  • +1 因为您显然在周末工作以解决生产问题。我们的职业没有加班,但至少有几个代表:-)
  • 非常感谢。周末留给 EMCY :-)
  • 我的经验是,如果某些东西有效,但现在无效,那只是因为某些东西发生了变化。此代码未更改,这意味着您的数据可能已更改。现在,我对 hibernate 的了解和那边的那块石头一样多,所以我无法分析代码来很好地找到问题,但我会看看数据可能会发生什么变化。当然,我确定你已经完成了这些……但 耸耸肩 值得一提。
  • @alesko - 你解决了吗? Java 应用程序变慢然后停止通常表明它的内存不足。您是否有权访问堆使用情况或垃圾收集统计信息?
  • 您说“查询”每 10 分钟执行一次,但所示方法能够创建和执行许多不同的查询。是否每 10 分钟使用相同的参数调用一次?如果是这样,它们是什么?如果用不同的参数调用,传递的参数和查询是否挂起有什么关系吗?

标签: java oracle hibernate spring jdbc


【解决方案1】:

首先,当此查询挂起时,检查 V$SESSION_WAIT 以查看会话正在等待什么。

第二个观察:您上面显示的代码似乎忽略了max 参数,除非benefitType 参数不为空。这是故意的吗?是不是只有当benefitType参数为null时查询才“挂起”?

抱歉,我假设您有某种方法可以在 Oracle 中识别正确的会话。尝试这样的查询:

select v2.sid,
       v2.module,
       substr(v1.sql_text,1,180) sql_text,
       v1.rows_processed,
       v2.event,
       v2.seq#
from v$sqlarea v1, v$session v2
where v1.users_executing > 0
  and v2.sql_address (+) = v1.address;

这将显示当前正在执行的所有 SQL,如果可能的话,还会显示相关的会话 ID 和它正在等待的事件。您应该能够使用 SQL 文本来识别您感兴趣的会话。

【讨论】:

  • 我会在它再次发生时检查它。这应该不会花很长时间。
  • 并且要清楚,检查几次,看看等待事件是否改变或序列号改变。即使它正在等待同一个事件但序列号发生变化,这也意味着它正在工作,只是非常缓慢。
  • 问题是如何找出哪个会话是挂起的?有 16 个以上的会话带有事件“来自/到客户端的 SQL*Net 消息”。
  • 很抱歉在 session_wait 中找不到任何可疑内容。也许客户端无法获得到 oracle 的连接,所以我们看不到它。
  • "SQL*Net message from/to client" 通常意味着数据库会话是空闲的。
猜你喜欢
  • 2011-03-11
  • 2013-10-31
  • 1970-01-01
  • 2013-03-01
  • 1970-01-01
  • 2018-05-22
  • 2011-07-21
  • 1970-01-01
相关资源
最近更新 更多