3

だから私はこれがとても遅い理由についてのアイデアを使い果たしました。多分あなたは助けることができます。そのため、コードによるnHibernateマッピングを使用して、Oracleデータベースから単純なレコードの取得を実行しようとしています。nugetのnHibernateverison3.3.1.4を使用しています。

マッピングコードは次のとおりです。

public class Person
{
    public virtual PersonKey Key { get; set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
}

public class PersonKey
{
    public virtual string PersonId { get; set; }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;
        var t = obj as PersonKey;
        if (t == null)
            return false;
        if (PersonId == t.PersonId)
            return true;
        return false;
    }
    public override int GetHashCode()
    {
        return (PersonId).GetHashCode();
    }
}

public class PersonMap : ClassMapping<Person>
{
    public PersonMap()
    {
        Schema("MyDB");
        Table("Person");
        ComponentAsId(id => id.Key, idMapper => idMapper.Property(p => p.PersonId));
        Property(i => i.FirstName);
        Property(i => i.LastName);
    }
}

これは、sessionfactoryを作成してデータを取得するためのコードです。

var mapper = new ModelMapper();
        var cfg = new Configuration();

        mapper.AddMappings(Assembly.GetExecutingAssembly().GetExportedTypes());

        cfg.DataBaseIntegration(c =>
        {
            c.ConnectionString = @"User Id=user;Password=password;Data Source=MyDB;";
            c.Driver<OracleClientDriver>();
            c.Dialect<Oracle10gDialect>();

            c.LogSqlInConsole = true;
            c.LogFormattedSql = true;
            c.AutoCommentSql = true;
        });

        cfg.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
        var sessionFactory = cfg.BuildSessionFactory();

        stopwatch.Stop();
        Console.WriteLine("Building session factory: {0}", stopwatch.ElapsedMilliseconds);
        stopwatch.Restart();

        Person entity = null;

        using (var session = sessionFactory.OpenSession())
        using(var tx = session.BeginTransaction())
        {
            entity = (Person) session.Get("Person", new PersonKey(){PersonId = "1"});
            tx.Commit();
        }

生成されるクエリは次のとおりです。

SELECT person0_.PersonId as PersonId0_0_, 
person0_.FirstName as FirstName0_0_, person0_.LastName as LastName0_0_,  
FROM MyDB.Person person0_
WHERE person0_.PersonId=:p0;
:p0 = '1' 

取得したオブジェクトにアクセスするには、平均で約80〜100秒かかります。これには、セッションの作成も含まれます。

だから私が探したいくつかの明白なもの:

  • personId列にインデックスが付けられます(これはテーブルの主キーです)。
  • データベースはサーバー上にあるため、時間を浪費しているネットワークではないことを確認するために、AD0.Net(Oracleconnection + Oraclecommand)を使用して上記の生成されたクエリを実行しました。これには約180ミリ秒かかります。これには、接続の作成とレコードのエンティティクラスへのマッピングが含まれます。
  • PL / SQL開発者を介してクエリを実行しました(約32ミリ秒かかりました)。
  • 生成されたクエリのクエリプランを確認します(全表スキャンではなく、インデックスの一意のスキャンの使用を確認しました)。
  • 私はローカルで同様のサイズのsql2012dbに対して上記のコードを実行しましたが、NHibernateコードを介して約180ミリ秒という非常に高速な比較です。
  • nhprof(評価)を実行し、次の結果を取得しました。

NHibernateプロファイラーの結果

クエリが実行され、結果がデータベースから非常に高速に返されるように見えますが(nhprofの結果によると)、時間がかかっているエンティティへのクエリの値のハイドレーションである可能性があります。私は時間をかみ砕くことができるものに途方に暮れています!

私の次の動きは、nHibernateソースをソリューションにアタッチしてステップスルーすることですが、職場でのダウンロードアクセスは制限されています(githubにさえ!)。それまで何かアイデアはありますか?

乾杯。

更新:プロジェクトにnHibernateソースがあり、それをステップスルーしました。プログラムがこのコードに到達するまで、すべてが順調に進んでいます。

for (count = 0; count < maxRows && rs.Read(); count++)              

実行が時間を浪費するのはその上rs.Read()です。ここで、rsはoracleデータリーダーです。このコードはDoQuery、Loader.csファイルの関数に含まれています。

奇妙なことに、渡されたクエリがパラメータ化されていない動的クエリ(eg select ... from person where personid = '1')の場合、実行は高速(〜1ms)になりますが、パラメータ化された(eg select ... from person where personid = :p1)の場合、途方もなく遅くなります。nHibernateのパワーを利用したいので、生成されたパラメーター化されたクエリを使用する必要があります。私はまだオラクルリーダーがなぜかを理解しようとしています

4

2 に答える 2

1

DeepShahがここでブログに書いている問題と解決策を見つけました。

基本的に、Oracle用のMicrosoftドライバーを使用したNHibernateでのパラメーター化されたクエリが、ボトルネックの理由でした。

彼はさらに2つの解決策を共有しています。

  1. 現在のMicrosoftOracleドライバーをOracleが開発したOracleDataClientDriverに置き換えます。新しいドライバを使用して同じクエリを実行すると、パフォーマンスの問題が解消されます。これを機能させるには、クエリを実行するマシンにOracle Data Access Components(ODAC)をインストールする必要があります。

  2. NHibernateを介してクエリにパラメータを設定する場合、VARCHAR列に対してクエリを実行する場合は、「query.SetParameter」ではなく「query.SetAnsiString」を使用してください。

私は両方のソリューションを検証しましたが、どちらも機能します。解決策1は、コードによるマッピングを使用していて、クエリの生成をnHibernateに任せているため、私が使用したものです。

于 2012-07-31T01:01:08.897 に答える
0

この投稿で参照されているように、マッピングでデータ型をansi文字列に設定することもできる と思います。NHibernate3は、適切な構文でsqlデータ型を指定します。

于 2012-08-31T18:04:38.417 に答える