1

Oracle db に対して Web アプリケーションの休止状態の基準に時間がかかりすぎます。log4j.logger.org.hibernate.SQL=debug SQL を有効にし、sql で同じバインド変数を使用して sql クエリを実行し、結果はインスタンスです。休止状態のログを有効にしてログを調べます。hibernate がクエリをこするのに時間がかかりすぎる原因は何ですか? 助言がありますか?

更新 1:

休止状態が生成する同じクエリを SQLPlus で実行すると、オラクルは異なる実行計画を使用するようです。Hibernate からの SQL クエリ:

select count(*) as y0_ from SUMMARY_VIEW this_ where this_.ser_id like :1 and this_.TYPE=:2 and this_.TIME_LOCAL>=:3 and this_.TIME_LOCAL<=:4 

SQLPlus で実行される SQL クエリ:

select count(*) as y0_ from SUMMARY_VIEW this_ where this_.ser_id like :ser_id and this_.TYPE=:type and this_.TIME_LOCAL>=:startdate and this_.TIME_LOCAL<=:enddate

更新 2: さらなる調査により、その startdate および endate バインド変数が sqlplus から varchar2 として渡されたが、これらは app からタイムスタンプとして渡されたことが明らかになりました ( :) )。このため、実行計画は異なります。

select sql_text, v.sql_id, name, value_string, datatype_string from v$sql_bind_capture vbc  join v$sql v using (hash_value) where v.sql_id in (?)

バインド変数の型は実行計画に影響しますか? もしそうなら、クエリへのバインドパラメータとして日付変数を渡す他のツールはありますか?

更新 3: 互換性のないデータ型によるパフォーマンスの問題。列のデータ型 (DATE) と休止状態のデータ型 (TIMESTAMP) の不一致により、暗黙的なデータ型変換が発生しているようです。Oracle は INTERNAL_FUNCTION を使用して日付列を転送し、渡されたバインド変数の hibernate データ型 TimeStamp と一致させます。

同様の問題:

jdbc Timestamp または Date を使用する場合の Oracle との無視できない実行計画の違い

DATE 列に java.sql.Timestamp を渡すと Oracle が非常に遅くなるのはなぜですか?

4

1 に答える 1

1

Hibernate を使用している場合は、ここに示されているソリューションでこの問題を解決できます。

http://blog.jooq.org/2014/12/29/leaky-abstractions-or-how-to-bind-oracle-date-correctly-with-hibernate/

基本的に、次を使用する必要がありますUserType

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Objects;

import oracle.sql.DATE;

import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;

public class OracleDate implements UserType {

    @Override
    public int[] sqlTypes() {
        return new int[] { Types.TIMESTAMP };
    }

    @Override
    public Class<?> returnedClass() {
        return Timestamp.class;
    }

    @Override
    public Object nullSafeGet(
        ResultSet rs, 
        String[] names, 
        SessionImplementor session, 
        Object owner
    )
    throws SQLException {
        return rs.getTimestamp(names[0]);
    }

    @Override
    public void nullSafeSet(
        PreparedStatement st, 
        Object value, 
        int index, 
        SessionImplementor session
    )
    throws SQLException {
        // The magic is here: oracle.sql.DATE!
        st.setObject(index, new DATE(value));
    }

    // The other method implementations are omitted
}

そして、そのタイプですべてのエンティティに注釈を付けます:

@Entity
@TypeDefs(
    value = @TypeDef(
        name = "oracle_date", 
        typeClass = OracleDate.class
    )
)
public class Rental {

    @Id
    @Column(name = "rental_id")
    public Long rentalId;

    @Column(name = "rental_date")
    @Type(type = "oracle_date")
    public Timestamp rentalDate;
}

繰り返し定型句がたくさんあると思いますが、少なくとも実行計画を再びスピードアップすることができます。

JDBC およびその他の API

記録として、同様の記事で、JDBC レイヤー、特に jOOQ でこの問題を解決する方法が紹介されています。

http://blog.jooq.org/2014/12/22/are-you-binding-your-oracle-dates-correctly-i-bet-you-arent/

于 2014-12-29T15:12:35.557 に答える