web-dev-qa-db-ja.com

SQL、SQLCLRオブジェクトと効果的なメモリ使用

最近、ERPシステムをIBM UniverseからSQL Serverに変換しました。アプリケーションのパフォーマンスは、一般的には許容範囲ですが、ひどい場合もあります。

32 GbのRAMを搭載したVMWare上のWin Server 2012とSQL Server 2012でデータベースを実行しています。SQLの最大メモリは27Gbに設定されています。 dbサーバーはこのデータベースのみをホストしており、他の機能を実行していません。合計DBサイズは〜110Gbです。アプリケーションには独自の専用サーバーがあります。

ベンダーはCLRを広範囲に使用してコードを移植しています(36,000を超えるスカラー関数)。アプリケーションOLTPコンテキストで個々のCLRが正常に実行されることを理解していますが、セットベースの操作ではなく行ごとに一括ジョブを実行しようとすると、うまくスケールアップしません。 ..クール..次に進みます。

私は Brent Ozarのスクリプト を実行しました。これにより、高い空きメモリが確認対象として特定され、各クエリの実行プランの数も多くなりました。ベンダーは、サーバーにさらにRAM=を追加することを提案しましたが、アプリケーションが現在使用しているものを使用していないようで、これは私を困らせます。

私が興味を持っているのは、SQLのパフォーマンスと全体的な動作です。何かがおかしいと思われるさまざまな症状が出ますが、それを突き止めることはできません。サーバーがrunを拒否するようなものです。 walkと決まっています。

非常に大雑把に言えば、データベースのキャッシュに約10Gbのメモリが使用されており、約11Gbは無料で、プランキャッシュには約3.5Gbであり、残りは説明できません。また、無料か予約済みか、盗まれたかなど、いくつかの定義について少しわかりません。二重にカウントされますか?

アクティビティモニターはこれを示しています:

enter image description here

このクエリを実行すると:

-- what's happening inside my buffer pool?
SELECT counter_name, instance_name, mb = cntr_value/1024.0
  FROM sys.dm_os_performance_counters 
  WHERE (counter_name = N'Cursor memory usage' and instance_name <> N'_Total')
  OR (instance_name = N'' AND counter_name IN 
       (N'Connection Memory (KB)', N'Granted Workspace Memory (KB)', 
        N'Lock Memory (KB)', N'Optimizer Memory (KB)', N'Stolen Server Memory (KB)', 
        N'Log Pool Memory (KB)', N'Free Memory (KB)')
  ) ORDER BY mb DESC;

私は得ます:

+--------------------------------+---------------------+----------+
|          Counter_name          |    instance_name    |    mb    |
+--------------------------------+---------------------+----------+
| Free Memory (KB)               |                     |  11,732  |
| Stolen Server Memory (KB)      |                     |   5,426  |
| Lock Memory (KB)               |                     |      59  |
| Log Pool Memory (KB)           |                     |       4  |
| Optimizer Memory (KB)          |                     |       2  |
| Connection Memory (KB)         |                     |       2  |
| Cursor memory usage            | TSQL Global Cursor  |       1  |
| Cursor memory usage            | TSQL Local Cursor   |       0  |
| Cursor memory usage            | API Cursor          |     -    |
| Granted Workspace Memory (KB)  |                     |     -    |
+--------------------------------+---------------------+----------+

このクエリを実行すると:

-- which db's are using memory and how much. 
SELECT
    (CASE WHEN ([database_id] = 32767)
        THEN N'Resource Database'
        ELSE DB_NAME ([database_id]) END) AS [DatabaseName],
    COUNT (*) * 8 / 1024 AS [MBUsed],
    SUM (CAST ([free_space_in_bytes] AS BIGINT)) / (1024 * 1024) AS [MBEmpty]
FROM sys.dm_os_buffer_descriptors
GROUP BY [database_id];

私は得ます:

+-------------------+----------+---------+
|   DatabaseName    |  MBUsed  | MBEmpty |
+-------------------+----------+---------+
| ERP               |  10,764  |    626  |
| master            |       2  |    -    |
| model             |     -    |    -    |
| msdb              |      11  |      3  |
| Resource Database |      16  |      5  |
| tempdb            |      41  |     13  |
+-------------------+----------+---------+

このクエリを実行すると:

SELECT TOP (12) Type, Name, pages_kb,
       Virtual_Memory_reserved_kb, Virtual_Memory_committed_kb
FROM   sys.dm_os_memory_clerks
ORDER BY pages_kb DESC;

私は得ます:

+---------------------------+-----------------------+----------+----------------------------+-----------------------------+
|           Type            |         Name          | pages_kb | Virtual_Memory_reserved_kb | Virtual_Memory_committed_kb |
+---------------------------+-----------------------+----------+----------------------------+-----------------------------+
| MEMORYCLERK_SQLBUFFERPOOL | Default               | 11224968 |                   12999744 |                      640296 |
| CACHESTORE_SQLCP          | SQL Plans             |  3519552 |                          0 |                           0 |
| CACHESTORE_CLRPROC        | ClrProcCache          |   110232 |                          0 |                           0 |
| CACHESTORE_OBJCP          | Object Plans          |   100776 |                          0 |                           0 |
| USERSTORE_DBMETADATA      | ERP_Live              |    93856 |                          0 |                           0 |
| USERSTORE_SCHEMAMGR       | SchemaMgr Store       |    87544 |                          0 |                           0 |
| CACHESTORE_PHDR           | Bound Trees           |    73464 |                          0 |                           0 |
| MEMORYCLERK_SOSNODE       | SOS_Node              |    62456 |                          0 |                           0 |
| OBJECTSTORE_LOCK_MANAGER  | Lock Manager : Node 0 |    60792 |                     131072 |                      131072 |
| MEMORYCLERK_SQLCLR        | Default               |    40992 |                    6327292 |                      429408 |
| MEMORYCLERK_SQLSTORENG    | Default               |    28472 |                       9472 |                        9472 |
| MEMORYCLERK_SQLQUERYEXEC  | Default               |    20904 |                          0 |                           0 |
+---------------------------+-----------------------+----------+----------------------------+-----------------------------+
  1. 11GBの「空きメモリ」があるようです。これは本当に無料で使用できますか? SQLがそれを利用しないのはなぜですか?

  2. 私には、私のERPシステムが使用しているメモリの約10Gbまたは約3分の1しか使用していないように見えます(が間違っているように感じます)アプリケーションがメモリをより効率的に使用するようにするにはどうすればよいですか

  3. MEMORYCLERK_SQLCLRは6.03Gbのメモリを予約しています。これはCLRの通常の動作ですか?彼らはいつメモリを予約しますか?コンパイル/登録/実行されたとき?彼らはそれをリリースしたことがありますか?これは「空きメモリ」内ですか? (Srutzkyが回答)

  4. Re:多数の実行プランは キャッシュのフラッシュ を助けますか?

  5. 上記の動作に影響を与えるために使用できる機能はありますか?または、これがアプリケーションの動作方法であることを受け入れる必要がありますか?.

  6. サーバーのメモリを実際に保持または使用しているものを説明するにはどうすればよいですか。

他者からの問い合わせ

これら:

SELECT type,
       SUM(pages_kb)/1024 AS [Memory utilized in MB],
       SUM(awe_allocated_kb)/1024 AS [Memory allocated though Windows API]
FROM   sys.dm_os_memory_clerks
GROUP BY type
ORDER BY [Memory utilized in MB] DESC;

SELECT * FROM sys.dm_os_process_memory;

返品:

+----------------------------------+-----------------------+-------------------------------------+
|               type               | Memory utilized in MB | Memory allocated though Windows API |
+----------------------------------+-----------------------+-------------------------------------+
| MEMORYCLERK_SQLBUFFERPOOL        |                  4417 |                                   0 |
| CACHESTORE_SQLCP                 |                  3437 |                                   0 |
| CACHESTORE_CLRPROC               |                   120 |                                   0 |
| USERSTORE_DBMETADATA             |                   100 |                                   0 |
| CACHESTORE_OBJCP                 |                    99 |                                   0 |
| USERSTORE_SCHEMAMGR              |                    76 |                                   0 |
| CACHESTORE_PHDR                  |                    72 |                                   0 |
| MEMORYCLERK_SOSNODE              |                    64 |                                   0 |
| OBJECTSTORE_LOCK_MANAGER         |                    59 |                                   0 |
| MEMORYCLERK_SQLCLR               |                    38 |                                   0 |
| MEMORYCLERK_SQLSTORENG           |                    26 |                                   0 |
| MEMORYCLERK_SQLQUERYEXEC         |                    14 |                                   0 |
| MEMORYCLERK_SQLGENERAL           |                    10 |                                   0 |
| OBJECTSTORE_SNI_PACKET           |                     9 |                                   0 |
| CACHESTORE_SYSTEMROWSET          |                     8 |                                   0 |
| USERSTORE_TOKENPERM              |                     7 |                                   0 |
| MEMORYCLERK_XE                   |                     6 |                                   0 |
| MEMORYCLERK_SQLLOGPOOL           |                     4 |                                   0 |
| CACHESTORE_SEHOBTCOLUMNATTRIBUTE |                     3 |                                   0 |
| MEMORYCLERK_SQLOPTIMIZER         |                     2 |                                   0 |
| MEMORYCLERK_SQLQERESERVATIONS    |                     2 |                                   0 |
| MEMORYCLERK_SQLCONNECTIONPOOL    |                     1 |                                   0 |
| OBJECTSTORE_LBSS                 |                     1 |                                   0 |
| CACHESTORE_STACKFRAMES           |                     0 |                                   0 |
| MEMORYCLERK_SQLHTTP              |                     0 |                                   0 |
+----------------------------------+-----------------------+-------------------------------------+

+---------------------------+---------------------------+----------------------------+--------------------------------+-----------------------------------+------------------------------------+------------------------------------+------------------+-------------------------------+---------------------------+-----------------------------+----------------------------+
| physical_memory_in_use_kb | large_page_allocations_kb | locked_page_allocations_kb | total_virtual_address_space_kb | virtual_address_space_reserved_kb | virtual_address_space_committed_kb | virtual_address_space_available_kb | page_fault_count | memory_utilization_percentage | available_commit_limit_kb | process_physical_memory_low | process_virtual_memory_low |
+---------------------------+---------------------------+----------------------------+--------------------------------+-----------------------------------+------------------------------------+------------------------------------+------------------+-------------------------------+---------------------------+-----------------------------+----------------------------+
|                  28571952 |                         0 |                          0 |                   137438953344 |                          77358808 |                           28786620 |                       137361594536 |       1014012259 |                            99 |                   3734268 |                           0 |                          0 |
+---------------------------+---------------------------+----------------------------+--------------------------------+-----------------------------------+------------------------------------+------------------------------------+------------------+-------------------------------+---------------------------+-----------------------------+----------------------------+

この:

SELECT COUNT(*) AS [NumCachedObjects],
       CONVERT(BIGINT, SUM(CONVERT(BIGINT, size_in_bytes)) / 1024.0) AS [CachedKBytes],
       ISNULL(cacheobjtype, '<-- Totally Total') AS [CacheObjType],
       ISNULL(objtype, '<-- TOTAL') AS [bytes]
FROM   sys.dm_exec_cached_plans
GROUP BY cacheobjtype, objtype WITH ROLLUP;

戻り値:

+------------------+--------------+-------------------+-----------+
| NumCachedObjects | CachedKBytes |   CacheObjType    |   bytes   |
+------------------+--------------+-------------------+-----------+
|             3882 |        62112 | CLR Compiled Func | Proc      |
|             3882 |        62112 | CLR Compiled Func | <-- TOTAL |
|                3 |           24 | CLR Compiled Proc | Proc      |
|                3 |           24 | CLR Compiled Proc | <-- TOTAL |
|               50 |         4168 | Compiled Plan     | Adhoc     |
|            26911 |      3416232 | Compiled Plan     | Prepared  |
|              101 |        99584 | Compiled Plan     | Proc      |
|                5 |         1656 | Compiled Plan     | Trigger   |
|            27067 |      3521640 | Compiled Plan     | <-- TOTAL |
|               17 |          136 | Extended Proc     | Proc      |
|               17 |          136 | Extended Proc     | <-- TOTAL |
|               16 |          536 | Parse Tree        | Check     |
|                3 |           24 | Parse Tree        | Default   |
|              313 |        20632 | Parse Tree        | UsrTab    |
|              535 |        52520 | Parse Tree        | View      |
|              867 |        73712 | Parse Tree        | <-- TOTAL |
|            31836 |      3657624 | <-- Totally Total | <-- TOTAL |
+------------------+--------------+-------------------+-----------+

この:

SELECT * FROM sys.dm_clr_appdomains;

戻り値:

+--------------------+--------------+------------------------------------------+-------------------------+-------+---------+--------------------+-----------------+---------------+-----------+----------+---------------------+-------------------------+---------------------------+--------------------+ 
| appdomain_address  | appdomain_id | appdomain_name                           | creation_time           | db_id | user_id | state              | strong_refcount | weak_refcount | cost      | value    | compatibility_level | total_processor_time_ms | total_allocated_memory_kb | survived_memory_kb | 
+--------------------+--------------+------------------------------------------+-------------------------+-------+---------+--------------------+-----------------+---------------+-----------+----------+---------------------+-------------------------+---------------------------+--------------------+ 
| 0x00000003DECEC200 | 16           | ERP       .CLRExtensionUser[runtime].111 | 2016-07-13 10:51:23.370 | 5     | 5       | E_APPDOMAIN_SHARED | 1               | 3236          | 130810392 | 11534336 | 110                 | 15                      | 112020591                 |                206 | 
+--------------------+--------------+------------------------------------------+-------------------------+-------+---------+--------------------+-----------------+---------------+-----------+----------+---------------------+-------------------------+---------------------------+--------------------+
5

質問へのコメントで投稿したいくつかの質問の返信を待つ間、少なくとも私の質問の1つを繰り返します。「現在の統計については、SQLCLRの使用がパフォーマンスの問題に何らかの形で関係していると疑うようになります。 ?」

私があなたの出力を見ると、SQLCLRはメモリをほとんど使用していません。 ClrProcCache用に110 MBの物理RAMがあります。 OK。これは、_Object Plans_が占める量をわずかに上回っており、_SQL Plans_が占める3.36 GBの一部です。はい、_MEMORYCLERK_SQLCLR_には予約済み約6.03 GB(6.3ではありません-kbの値を取得して_value.0 / 1024 / 1024_)、ただしa)物理ではなく仮想メモリ、b)12.40 GBの仮想メモリの半分未満予約済みバッファプール。 _Virtual_Memory_committed_kb_フィールドまでスクロールすると、_MEMORYCLERK_SQLCLR_が419.34 MBのvirtualメモリをアクティブに使用しているだけであることがわかります。

現在のSQLCLRメモリ使用量を確認するには、次のコマンドを実行できる必要があります。

_SELECT * FROM sys.dm_clr_appdomains;
_

_survived_memory_kb_フィールドを見てください(not_total_allocated_memory_kb_フィールドは、何が解放されたかに関係なく、累積割り当てになるはずです。アップ)。

3つの質問に答えてみましょう。

  1. 11.46 GBの「空き」メモリを「保持」するのは何ですか。

    なぜ何かがそれを「保持」していると疑うのですか? SQL Serverに27 GBの物理RAM=を使用するように指定しました。必要なときに必要なものを使用します。

  2. 私には、ERPシステムはメモリの約10Gbまたは約3分の1しか使用していないようです

    この値は誤算だと思います。サーバーには32 GBの物理RAMが搭載されていますが、SQL Serverには27 GBしか割り当てられていません。 10 GBが実際の合計である場合、それは約37%に相当します。しかし、それは実際の合計ではありません。最終クエリの_pages_kb_フィールド(_sys.dm_os_memory_clerks_に対して)を見ると、これらの行をすべて合計する必要があります。これは、15,424,008 kbになります。次に_SELECT 15424008.0 / 1024 / 1024;_を実行すると、27 GBのうち14.71 GBのRAMが使用されます。27GBのRAM 11.46 GBの「空き」メモリを搭載したサーバー。15.54GBが残り、「使用」する必要があります。14.71GBが使用されていることがわかりますが、これは取得するクエリでTOP (12)を実行することに基づいています使用されたメモリの量。0.83GBの差はフィルターされた行に隠されていると思われるため、TOP (12)を削除すると、15.54 GBに非常に近い数値が得られます。その場合、 「使用済み」メモリは、許可されている物理RAMの約58%です。

  3. MEMORYCLERK_SQLCLRは6.3Gbのメモリを予約しています。

    ではない正確に。物理RAMではなく、6.03 GBの仮想メモリが予約されています。また、上記のように、これは予約済みであり、committed仮想メモリではありません。

    これはCLRの通常の動作ですか?

    「通常」については完全にはわかりませんが、SQLCLRが大規模なコレクションを格納するために仮想メモリを好むことは確かです。

    彼らはいつメモリを予約しますか?コンパイル/登録/実行されたとき?

    あなたが見ているのは、ランタイムメモリであるべきです。これはreservedなので、ある時点で操作に大量のメモリが必要になり、それに対応するために予約サイズが大きくなったと思います。ただし、クエリでは、現在、6.03 GBのうち419.34 MBのみが使用されていることも示しています。

    彼らはそれをリリースしたことがありますか?

    少なくともサービスの再起動時。しかし、おそらくそれより早く。予約済みのスペースに長い間保持されているのを見てきましたが、解放されるかどうか、いつ解放されるかを確認するのにあまり時間をかけません。

    ガベージコレクションが実行されていない、または必要に応じて頻繁に実行されていないことが心配な場合は、GCクラスを呼び出す単一の関数を含む単純なアセンブリを作成して、手動で呼び出すことができます。それを他のアセンブリと同じデータベースにロードし、同じ所有者(つまり、_CREATE Assembly_のAUTHORIZATION句)があることを確認した場合、_SELECT * FROM sys.assemblies;_を使用して確認し、 _principal_id_が一致することを確認してください)、同じAppDomainを使用します。

    これは「空きメモリ」内ですか?

    いいえ。「空き」メモリとは、未使用の量physicalを指しますRAM SQL Serverに許可されている「最大サーバーメモリ」を介して使用してください。予約済みの6.03 GB仮想メモリは、スワップファイル/ページファイルにあります。

  4. キャッシュのフラッシュは役立ちますか?

    ええと、どのようにそれを行うのですか? DBCC FREESYSTEMCACHE('ALL');を実行することを意味する場合は、すべてのAppDomainをアンロードする必要がありますが、仮想メモリが常に解放されるかどうかはわかりません。実際の効果を確認するために、少なくとも一度は試しても害はありません。ただし、システムがAppDomainの再作成、アセンブリ(またはアセンブリ)のロード、およびClrProcCacheに格納するその他のあらゆるものの起動コストを負担するため、私はそれを習慣にしません。

  5. 上記の動作に影響を与えるために使用できる機能はありますか?

    私が知っていることではありません。そして、クエリ処理のために何も残っていないので、SQL Serverが利用可能なすべてのメモリを使い尽くしたくはないと思います。

    または、これがアプリケーションの動作方法であることを受け入れる必要がありますか?.

    私はあなたが遅さはまさにそれがそうであるようであることを受け入れる必要も、受け入れるべきでもないと思います。あなたが言ったように、いくつかのSQLCLR UDFを純粋なT-SQLに置き換え、大幅な改善を得ました。これは、彼らがSQLCLRを誤って不適切に使用していることを示しています。そして、これらのUDFを生成するツールを発見または作成した場合(他にどのようにして36,000を取得しますか?!)、それらが「最適」であるか、個別に取得されているかは疑問です。

  6. サーバーのメモリを実際に保持または使用しているものを説明するにはどうすればよいですか。

    これは、タスクマネージャー([詳細]タブ)とリソースモニター([メモリ]タブ)を使用して確認できます。 physicalRAMの使用量、共有と非両方の量)である「Working Set」という名前の列を探します共有/プライベート。

Activity MonitorにCLR関連の待機タイプが表示されているためにSQLCLRにパフォーマンスの問題が疑われる場合は、それに関連する次のDBA.StackExchange回答を参照してください。 SQL Server Management Studio内のActivity MonitorのSQLCLR待機タイプとは( SSMS)?

質問の次のコメントについて:

アプリケーションOLTP=コンテキストでは個々のCLRが正常に実行されることを理解していますが、セットベースの操作ではなく行ごとに一括ジョブを実行しようとすると、うまくスケールアップしません。

これは非常に正確な理解ではないと思います;-)。 UDFのスケーラビリティの問題は、SQLCLRに固有のものではありません。実際、SQLCLRスカラー関数は、T-SQL UDFができないことを実行できます。並列プランに参加します(IsDeterministictrueに設定されている場合)。 T-SQL UDFはシリアル計画を強制します。それでも、T-SQLでできるほとんどの操作はインラインで実行されます(UDFまたはMultistatement TVFで抽象化されていない-T-SQLインラインTVFは問題ありません)最高のパフォーマンスを発揮します。

キャッシュされたプランの数とどのタイプが何を使用しているかを見ると、キャッシュされたプランの大部分が「準備済み」であることがわかります-その約27,000-は、ORM(例:Entity Framework、 Hibernate/nHiberateなど)。それは高い数値のように見えますが、ORMに関しては「獣の性質」であるため、できることは多くありません(開発者がめったに見ない重大な欠点ですが、 RAM、そうですか?)また、SQLCLR UDFがほぼ4000あることもわかります(36k UDFのいずれかが「デッドコード」であるのではないでしょうか?)。これらは改善できる領域です(残念ながら、ベンダーではなく、ユーザーが改善できます)が、メモリの問題を示すものではありません。

_sys.dm_clr_appdomains_の出力を見ると、出力を取得する数時間前に作成されたと思われるAppDomainは、CPUをほとんど使用していませんが、(何らかの方法で)割り当てられていることがわかります)合計で112,020,591バイト(106.83 MB)。ただし、まだ割り当てられているのは206 Kb)なので、これらのSQLCLRオブジェクトはメモリを保持していません。

60を超える「メモリ不足のため、AppDomain XXXXXはアンロード用にマークされています。」 2016年1月以降のメッセージ。あちこちに奇妙な出来事があり、悪い日に3〜5回続けて。最初、これらはETL抽出の実行中に真夜中に発生していました。しかし最近では、これらは一日中広がっています。

6か月間の約60件の「メモリ不足のためにAppDomainがアンロードされました」というメッセージは完全ではありませんが、それも悪くはありません。これは、3日ごとの平均1です。メモリが少ないと、これが1日に何度も発生します。また、実行時のクエリ処理にそのメモリが必要となる、負荷の高い活動期間中に発生することは理にかなっています。そして、10 GBの「空きメモリ」は、ETLを実行していないとき(またはその他の時間はアクティビティが増加しているとき)だけ「無料」であるという考えに戻ります。

ETL時間中にいくつかのテストを実行して、空きメモリの量、cached_objectsに割り当てられている合計KBの量、およびタイプごとのキャッシュされたプランの数を確認することで、この状況をより明確に把握できます。実際には、パフォーマンスが「ひどいレベルに低下する」ときにこれらのテストを実行すると、メモリ不足が発生している可能性が最も高くなります。

現在、これがメモリの制約の問題であることを示す証拠はありません。これは、アプリケーションアーキテクチャが不十分で、機能(SQLCLRなど)が誤用されているようです。 SQL ServerとSQLCLRをよく理解していないために、ベンダーが正しい考えを持っていない人にはできないことをいくつか実行した可能性があります(たとえば、36kのスカラー関数!)。

ベンダーは、サーバーにさらにRAM=を追加することを提案しましたが、アプリケーションが現在使用しているものを使用していないようで、これは私を困らせます。

その提案も私を困らせますが、別の理由で、彼らはあなたに絶対的な推測でお金を使うように求めています。より多くのメモリが役立つかどうかはわかりません。現在の10 GBの空きメモリの半分でもあり、「CLRで数分かかるクエリの繰り返しの経験が数秒または数秒未満の応答時間に短縮された」場合、どのようにメモリに問題があるのでしょうか。私がここで間違っている場合、多分彼らはあなたにこれがRAM関連であるという理論をサポートするクエリや証拠を提供することができます。しかし、「数秒または1秒未満の応答時間でクエリを実行しなかった」というクエリの書き換えという形で、すでにかなり強力な反証があります。したがって、おそらくベンダーは彼らの提案を待って、RAMをあなたのために購入する必要があります。それが役に立たない場合は、それを彼らに戻すことができます。そして、それが役立つ場合は、それらを完全に償還するか、50/50に分割するかを検討します。多分本当に必要ないので必要ないので、;-)。

7
Solomon Rutzky