3

mssql db で mvc3 nhibernate orm レイヤーを使用して最初のアプリケーションを開発しています。

これは、nhibernate を使用して作成した最初のアプリケーションであり、最初の応答時間以外はすべて問題ありません。いくつかの調査の後、Web リクエストごとにセッションを実装しました。これは間違いなくアップグレードです。エンティティは最初の呼び出し後にはるかに高速に読み込まれますが、問題は同じままです。

domainname.com と入力して Enter キーを押すと、最初の応答時間が非常に遅くなります。10~15秒 これはコンテンツの実際の読み込み時間ではなく、その後 10 ~ 15 秒かかります。私のサイトの読み込みが始まります。あと数秒です。

セッションファクトリが必要なすべての「もの」を初期化する必要があるのはその時ですが、私はそれが何か他のものでなければならないと思います。これは受け入れがたい。

私のアプリはサイト メモリ割り当て 200 MB の winhost で実行されているので、これは問題ではないと思います。

どんなヒントでも大歓迎です。詳細が必要な場合は、お問い合わせください。

ありがとう

更新: nhibernate プロファイラーでアプリケーション セッションの使用状況を調べたところ、興味深いことがわかりました。私はプロファイラーを使用するのが本当に初心者なので、高価なセッションを見つけたと思います。一般的な統計では、67 個のエンティティが 36.571 秒で読み込まれます。この秒の値は本当に奇妙です。ロードするのに 10 ~最大 15 秒かかるからです。

2 回目の更新: global.asax

public class MvcApplication : System.Web.HttpApplication{     

public static ISessionFactory SessionFactory =
        MyDomain.Infrastructure.SessionProvider.CreateSessionFactory();

//My session factory is open in Application_Start() like this 
SessionFactory.OpenSession();

}

オブジェクトのマッピングに流暢なアプローチを使用しています。したがって、ドメインプロジェクトのセッションプロバイダーは次のようになります

//This should be used from web app, global.asax.cs calls
        public static ISessionFactory CreateSessionFactory()
        {
            string conStringName = "ConnectionString";
            var cfg = Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008
                .ConnectionString(c => c.FromConnectionStringWithKey(conStringName)))
                .Mappings(m => m.FluentMappings.Add<Entity1>())
                .Mappings(m => m.FluentMappings.Add<Entity2>())
                .Mappings(m => m.FluentMappings.Add<Entity3>())
                .ExposeConfiguration(p => p.SetProperty("current_session_context_class", "web"))
                .BuildConfiguration();

            return cfg.BuildSessionFactory();            
        }

Update 3 この問題の解決策はまだありません

更新 4 と最終 私の問題は、間違いなく sessionFactory にあります。構成オブジェクトをシリアル化する必要があると思います。ここに示した私のコードを流暢にconfで使用して、誰かがそれを行う方法を示すのに十分親切である場合. 私は喜んで彼/彼女の答えを受け入れます。ありがとう。

4

3 に答える 3

12

多くの詳細が欠けていますが、これがあなたの最初の NHibernate アプリだと言うので、チェックする可能性が高いものをお勧めします。

  • アプリケーションの開始時に NH SessionFactory を 1 回作成します (@Rippo が取得しているため)。SessionFactoryの作成にはコストがかかります。Application_Start() で実行します
  • Web リクエストごとに新しい NH セッションを開きます。依頼が終わったら捨てる。NH ISession は安価で迅速に作成できます。一般に、ISession を長期間再利用またはキャッシュすることはお勧めできません。単純な実装の場合、必要に応じてコントローラーで実行できます。これは、要求ごとにしか存在しないためです。
  • クエリを実行する場合 (NH LINQ? QueryOver? 何を使用しますか)、返されるレコードを必ず制限してください。テーブル全体を .ToList() しないで、20 だけを表示します。Skip/Take を使用します。
  • SELECT N+1 問題に注意してください。これにより、OR/M でのパフォーマンスが低下する可能性があります。

これらは私の推奨事項であり、目に見えないコードです。

更新:したがって、主な問題は 10 ~ 15 秒の起動にあるようです。これは、Application_Start 中の SessionFactory の初期化時間である可能性があります。

まだ試していませんが、起動時間を短縮するための一般的な推奨事項は、NH 構成オブジェクトをディスク (マッピングを含む) にシリアル化し、起動のたびにそれをロードすることです (プリミティブ キャッシュ)。マッピングが変更された場合は、それを検出するか、手動でロードする (シリアル化された構成ファイルを削除する) 必要があります。

コードでは、Fluent NHibernate を使用して FluentNHibernate.Cfg.FluentConfiguration インスタンスを構築し、その上で cfg.BuildSessionFactory() を呼び出して新しい ISessionFactory を返します。シリアル化する必要がある構成は、NHibernate.Cfg.Configuration です。したがって、おそらくコードを次のように変更します。

    public static ISessionFactory CreateSessionFactory()
    {
        string conStringName = "ConnectionString";

        // http://weblogs.asp.net/ricardoperes/archive/2010/03/31/speeding-up-nhibernate-startup-time.aspx
        System.Runtime.Serialization.IFormatter serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

        NHibernate.Cfg.Configuration cfg = null;

        if (File.Exists("Configuration.serialized"))
        {
            using (Stream stream = File.OpenRead("Configuration.serialized"))
            {
                cfg = serializer.Deserialize(stream) as Configuration;
            }
        }
        else
        {
            // file not exists, configure normally, and serialize NH configuration to disk
            cfg = Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008
                .ConnectionString(c => c.FromConnectionStringWithKey(conStringName)))
                .Mappings(m => m.FluentMappings.Add<Entity1>())
                .Mappings(m => m.FluentMappings.Add<Entity2>())
                .Mappings(m => m.FluentMappings.Add<Entity3>())
                .ExposeConfiguration(p => p.SetProperty("current_session_context_class", "web"))
                .BuildConfiguration();

            using (Stream stream = File.OpenWrite("Configuration.serialized"))
            {
                serializer.Serialize(stream, cfg);
            }
        }

        return cfg.BuildSessionFactory();
    }

これにより、構成がディスクにキャッシュされるため、アプリの起動が高速になります。もちろん、NH マッピングを変更する場合は、それを検出して Fluent 構成をリロードするか、手動でキャッシュ ファイルを削除する必要があります。

その他のチューニングに関するコメント:

  • .Mappings(m => m.FluentMappings.Add()).Mappings(m => m.FluentMappings.Add()) などがあります。ここで推測するだけですが、1つずつ追加すると複数のHBMファイルが作成される場合がありますフードの下。外部アセンブリからマッピングを追加してみて、 .Mappings(M => M.FluentMappings.AddFromAssemblyOf()) を使用できます
  • 実際には、Application_Start() で SessionFactory.OpenSession() を実行しないでください。そこに SessionFactory を作成し、コードで SessionFactory.GetCurrentSession() にアクセスするだけです。global.asax には次のものが必要です。

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        // we open one NH session for every web request, 
        var nhsession = SessionFactory.OpenSession();
        // and bind it to the SessionFactory current session
        CurrentSessionContext.Bind(nhsession);
    }
    
    protected void Application_EndRequest(object sender, EventArgs e)
    {
        // close/unbind at EndRequest
        if (SessionFactory != null)
        {
            var nhsession = CurrentSessionContext.Unbind(SessionFactory);
            nhsession.Dispose();
        }
    }
    

それがセッションごとのリクエストを行う方法です。

于 2012-05-25T22:13:30.140 に答える
3

余分なポイントのカップル:-

  1. bin フォルダーに何かを書き込んでいますか? これにより、アプリのリサイクルが発生し、セッション ファクトリが再構築されます。
  2. NHProf が表示している SQL を Management Studio で実行すると、時間がかかります。完全に索引付けされていますか?
  3. ログオンを導入して、セッション ファクトリを複数回構築していないことをトリプル チェックします。Application_start

これらの問題は SQL サーバーまたはコードに関連しており、NHibernate が原因ではありません!

また、コードにトレースを追加して、実際のボトルネックがどこにあるかを確認するか、 ANTS プロファイラーを使用する必要があると思います(14 日間の無料トライアルを利用できます)。

于 2012-05-26T05:52:27.037 に答える
2

@raulg はいくつかの素晴らしい点を挙げました。次の点も考慮してください。

  1. NH 構成をディスクにシリアル化し、アプリの開始時にロードします。グーグルでいくつかの例を検索してください。
  2. NHibernate のログを確認してください。これは人々を捕まえます。log4net などを使用している場合は、NHibernate 情報を「すべて」ログに記録していないことを確認してください。これはパフォーマンスを低下させ、問題のデバッグにのみ使用する必要があります。
  3. マッピングで not.lazyload を使用しないように注意してください - 気付かないうちにクエリで DB 全体をロードしてしまう可能性があります! 必要に応じて、クエリでイーガー ロードします。

それに固執する-それは素晴らしいツールです。

NH のシリアル化:

  if (_loadConfiguration && _configurationFile.Exists && _IsConfigurationFileValid())
            {
                configuration = _LoadConfigurationFromFile();
            }
            else
            {
                configuration = Fluently.Configure()
                    .Database(MsSqlConfiguration.MsSql2008
                        .ConnectionString(x => x.FromConnectionStringWithKey("Development"))
                        #if DEBUG
                        .ShowSql()
                        #endif
                    )
                    .ProxyFactoryFactory("NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate")
                    .Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<UserMap>()
                                              .Conventions.Setup(MappingConventions.GetConventions()))
                    .Mappings(mappings => mappings.HbmMappings.AddFromAssemblyOf<UserMap>())
                    .BuildConfiguration();

                _SaveConfigurationToFile(configuration);
            }

  private bool _IsConfigurationFileValid()
        {
            var assInfo = new FileInfo(Assembly.GetCallingAssembly().Location);
            return _configurationFile.LastWriteTime >= assInfo.LastWriteTime;
        }

        private void _SaveConfigurationToFile(Configuration configuration)
        {
            //#if DEBUG
            //return;
            //#endif
            _logger.Debug("Starting to save NHibernate configuration to " + _configurationFile.FullName);
            using(var file = _configurationFile.OpenWrite())
            {
                new BinaryFormatter().Serialize(file, configuration);
                file.Close();
            }
            _logger.Debug("Finished saving NHibernate configuration to " + _configurationFile.FullName);
        }

        private Configuration _LoadConfigurationFromFile()
        {
            //#if DEBUG
            //    return null;
            //#endif
            _logger.Debug("Starting to load NHibernate configuration from " + _configurationFile.FullName);
            using(var file = _configurationFile.OpenRead())
            {
                var binaryFormatter = new BinaryFormatter();
                var config = binaryFormatter.Deserialize(file) as Configuration;
                file.Close();
                _logger.Debug("Finished loading NHibernate configuration from " + _configurationFile.FullName);
                return config;
            }
        }
于 2012-05-26T05:16:04.870 に答える