NHibernate を ORM として使用するアプリケーションがあり、データへのアクセス方法が原因でパフォーマンスの問題が発生することがあります。NHibernate のパフォーマンスを改善するには、どのようなことを行うことができますか? (回答ごとに 1 つの推奨事項に制限してください)
12 に答える
NHibernate で遭遇する最初の最も劇的なパフォーマンスの問題は、作成するすべてのセッションに対して新しいセッション ファクトリを作成する場合です。アプリケーションの実行ごとにセッション ファクトリ インスタンスを 1 つだけ作成し、すべてのセッションをそのファクトリで作成する必要があります。
これらの方針に沿って、理にかなっている限り、同じセッションを使い続ける必要があります。これはアプリケーションによって異なりますが、ほとんどの Web アプリケーションでは、要求ごとに 1 つのセッションが推奨されます。セッションを頻繁に破棄すると、そのキャッシュのメリットが得られません。セッション キャッシュを賢く使用すると、線形 (またはさらに悪い) 数のクエリを使用するルーチンを、多くの作業なしで一定数に変更できます。
同様に重要なのは、オブジェクト参照を遅延読み込みしていることを確認することです。そうでない場合は、最も単純なクエリでもオブジェクト グラフ全体が読み込まれる可能性があります。これを行わない理由はいくつかありますが、遅延読み込みから始めて、必要に応じて元に戻すことをお勧めします。
これにより、遅延読み込みの反対である熱心なフェッチが可能になります。オブジェクト階層をトラバースしたり、コレクションをループしたりしているときに、作成しているクエリの数を簡単に見失い、指数関数的な数のクエリになってしまうことがあります。Eager fetching は、FETCH JOIN を使用してクエリごとに実行できます。常に結合をフェッチする特定のテーブルのペアがある場合など、まれな状況では、その関係の遅延読み込みをオフにすることを検討してください。
いつものように、SQL プロファイラーは、実行速度が遅いクエリや繰り返し実行されているクエリを見つける優れた方法です。私の最後の仕事では、ページ リクエストごとのクエリもカウントする開発機能がありました。ルーチンに対する多数のクエリは、ルーチンが NHibernate でうまく機能していないことを示す最も明白な指標です。ルーチンまたはリクエストごとのクエリ数が適切に見える場合は、おそらくデータベースのチューニングにかかっています。実行計画とデータをキャッシュに保存するのに十分なメモリがあることを確認し、データを正しくインデックス付けするなど。
私たちが遭遇した小さなトリッキーな問題の 1 つは、SetParameterList() に関するものでした。この関数を使用すると、パラメーターのリストをクエリに簡単に渡すことができます。NHibernate は、渡された項目ごとに 1 つのパラメーターを作成することでこれを実装しました。これにより、パラメーターの数ごとに異なるクエリ プランが作成されます。私たちの実行計画は、ほぼ常にキャッシュから解放されていました。また、パラメーターが多数あると、クエリの速度が大幅に低下する可能性があります。単一のパラメーターで区切られたリストとして項目を送信するために、NHibernate のカスタム ハックを行いました。このリストは、SQL Server でテーブル値関数によって分離されていました。この関数は、ハックによってクエリの IN 句に自動的に挿入されました。用途によっては、このような地雷が他にもある可能性があります。SQL プロファイラーは、それらを見つけるための最良の方法です。
NHibernate の SessionFactory はコストのかかる操作であるため、メモリ内に SessionFactory のインスタンスが 1 つしかないことを保証するシングルトンを作成することをお勧めします。
public class NHibernateSessionManager
{
private readonly ISessionFactory _sessionFactory;
public static readonly NHibernateSessionManager Instance = new NHibernateSessionManager();
private NHibernateSessionManager()
{
if (_sessionFactory == null)
{
System.Diagnostics.Debug.WriteLine("Factory was null - creating one");
_sessionFactory = (new Configuration().Configure().BuildSessionFactory());
}
}
public ISession GetSession()
{
return _sessionFactory.OpenSession();
}
public void Initialize()
{
ISession disposeMe = Instance.GetSession();
}
}
次に、Global.Asax Application_Startup で初期化できます。
protected void Application_Start()
{
NHibernateSessionManager.Instance.Initialize();
}
実行速度の遅いクエリに対して遅延読み込みから熱心なフェッチにいつ切り替えるかを認識することで、 Select N + 1 の問題を回避および/または最小化します。
発生しているパフォーマンスの問題の種類に関する具体的な情報はありませんが、一般化することしかできません。私の経験では、ほとんどのデータベース クエリのパフォーマンスの問題は、適切なインデックスが不足しているために発生します。したがって、最初のアクションとして、インデックスが作成されていないクエリのクエリ プランを確認することをお勧めします。
NHibernate は、箱から出してすぐに非常に高速な SQL を生成します。私はそれを 1 年間使用してきましたが、それを使って生の SQL を書く必要はまだありません。私のパフォーマンスの問題はすべて、正規化とインデックスの欠如によるものです。
最も簡単な解決策は、クエリの実行計画を調べて、特に外部キー列に適切なインデックスを作成することです。Microsoft SQL Server を使用している場合は、「データベース エンジン チューニング アドバイザ」が大いに役立ちます。
「回答ごとに1つの推奨事項」のみですか?次に、私はこれに行きます:
2 つ以上の並列対多関連に沿った結合による結合の重複 (AKA デカルト積) を回避します。代わりに、Exists-subqueries、MultiQueries、または FetchMode "subselect" を使用してください。
プロファイリングは、最大の利益をどこで得ることができるかを見つけるための最初のステップです-単純な時限単体テストでさえ-
コレクションの場合は、バッチサイズを設定して、発行されるselectステートメントの数を減らすことを検討してください。詳細については、パフォーマンスの向上のセクションを参照してください。
キャッシング、キャッシング、キャッシング -- 第 1 レベルのキャッシングを正しく使用していますか [途中でセッションを閉じたり、StatelessSession を使用して第 1 レベルのキャッシングをバイパスしたりしています]? 頻繁に変更されない値に対して単純な 2 次キャッシュを設定する必要がありますか? 頻繁に変更されないクエリを高速化するために、クエリの結果セットをキャッシュできますか?
[設定も -- アイテムを不変に設定できますか? クエリを再構築して、必要な情報だけを取り出して元のエンティティに変換できますか? バットマンはダムにたどり着く前にリドラーを止めることができるでしょうか? ……あ、調子に乗ってすいません。]
回答を 1 つの選択肢に限定することはできますか? その場合、NHibernate の第 2 レベルのキャッシュ メカニズムを実装することを選択します。
このようにして、マッピング ファイル内の各オブジェクトに対して、キャッシュ戦略を定義できます。2 番目のレベルのキャッシュは、既に取得されたオブジェクトをメモリに保持するため、データベースへの別のラウンドトリップは行われません。これは、パフォーマンスを大幅に向上させます。
目標は、アプリケーションが常にアクセスするオブジェクトを定義することです。その中には、一般的な設定などが含まれます。
nhibernate のセカンド レベル キャッシュとその実装方法に関する情報はたくさんあります。
幸運を :)
まだ遅延読み込みを (適切に) 使用していない場合は、開始してください。不要なコレクションを取得するのは、すべての無駄です。
章パフォーマンスの改善では、パフォーマンスを改善するためのこの方法とその他の方法について説明します。
多くの自由時間が言ったこと。
ドキュメントの第 19 章「パフォーマンスの向上」を参照してください。
NHibernate: http://nhibernate.info/doc/nhibernate-reference/performance.html
休止状態: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html
SQL プロファイラー (または使用しているデータベースの同等のもの) を使用して、実行時間の長いクエリを見つけます。これらのクエリを適切なインデックスで最適化します。
アプリケーションのほぼすべてのページで使用されるデータベース呼び出しでは、CreateMultiQuery を使用して、1 つのデータベース クエリから複数の結果セットを返します。
もちろん、キャッシュも。ページ/コントロールの OutputCache ディレクティブ。データの Hibernate キャッシング。