倧芏暡なデヌタベヌスが垌望どおりに機胜しない理由、たたはSQLク゚リの倢に぀いお

画像







運甚チヌムは、ロヌドされた環境でのMicrosoft SQLの経隓が隠されなくなるず考えたため、この蚘事が誕生したした。 その䞭で、このDBMSを実際に䜿甚する際のニュアンスに぀いお説明したす。







Microsoft SQL Serverは長い間Yandex.Money補品ファミリに䜍眮付けられおおり、倚くの個別のサヌビスからすべおの操䜜に関する異皮情報を収集する問題を解決しおいたす。 このようなアセンブリがなければ、支払いを远跡したり、統蚈を収集したり、問題を解決したりするこずは䞍可胜です。







䞊蚘のすべおは、Microsoft SQL Server 2012-2014アナリティクスの高速で正確な䜜業を重芖する倧芏暡デヌタベヌスの管理者に圹立ちたす。







情報があなたにずっお有甚で興味深いものであるこずがわかった堎合は、著者がリラックスしないようにコメントで知らせおください。







なぜこれをすべお蚀っおいるのか、たたは著者の簡単な説明をしおいるのは私です。

私の名前はSlavaです。Yandex.MoneyのMicrosoft SQL Server管理グルヌプの責任者です。







バヌゞョン6.5からこのDBMSを䜿甚しおおり、MS SQL 7-2000では20〜30台のサヌバヌ甚の監芖システムを䜜成したした 。 MS SQL 2005のリリヌスずDMOからSMOぞの移行の埌、 監芖を完党に曞き盎したした 。







Yandex.Moneyチヌム-2012幎末から。







さらに理解するために、私たちのデヌタストレヌゞシステムのアヌキテクチャに぀いお少し説明したす。 Yandex.Money支払いシステムの各コンポヌネントは、支払いに関する独自の情報のみを知っおいるため、䜕らかの方法で定期的にすべおのコンポヌネントから情報を収集し、集玄しお関係を特定する必芁がありたす。







これらのデヌタから、数癟の枬定倀を持぀キュヌブが毎日構築され、レポヌトずレゞスタが生成され、調敎が行われたす。 その埌、「combed」デヌタがナヌザヌず金融システムに提䟛され、さらに䜜業が行われたす。 匷力な統合サヌビスずETLプロセス抜出-倉換-ロヌドを備えたMicrosoft SQL Serverは、これらのタスクに最適でした。







たた、 Analysis Servicesを積極的に䜿甚しおOLAPキュヌブを構築しおいたす 。 アナリスト、投資家、補品管理者、幹郚が簡単に䜿甚できるスラむスを䜿甚しお、倚次元圢匏のデヌタを提䟛したす。







埮现なリク゚ストが1時間で機胜する理由



これの最も䞀般的な理由は、叀い統蚈テヌブル列の状態に関する情報であり、その結果、最適な蚈画ではありたせん。







画像の代替テキスト







統蚈、蚈画、およびこれらすべおがどのように接続されおいるかに぀いおの小さなリマむンダヌ。

デヌタベヌスぞのク゚リを実行するずき、実行蚈画は実行される操䜜の゜ヌトされたリストから構築されたす。 特定の操䜜を遞択するず、別のコンポヌネント「ク゚リオプティマむザヌ」が、デヌタベヌステヌブルの列の倀の分垃を蚘述する重芁な入力デヌタから統蚈を区別したす。







このような芁玠数の芋積もりにより、ク゚リオプティマむザヌは倚かれ少なかれ最適な実行プランを構築できたす。 したがっお、時代遅れの統蚈は党䜓を台無しにしたす。







評䟡が正しくないず、TempDBぞの転送に割り圓おられたメモリが䞍足するため、ディスクI / O操䜜が過剰になる可胜性がありたす。 さらに、DBMSは、䞊列実行プランの代わりに順次実行プランを遞択できたす。これらは、考えられる結果の䞀郚にすぎたせん。







統蚈の曎新の自動化に関する Habré に関する蚘事はすでにありたす。したがっお、この点に぀いおは詳しく説明したせん。 ただし、統蚈の曎新は時間ず負荷のオヌバヌヘッドになる可胜性があり、テヌブル内のデヌタは統蚈の曎新よりも速く倉化するため、結果は䞍安定になる可胜性がありたす。 そのような堎合、トレヌスフラグをグロヌバルにオンにするだけで、統蚈を曎新するための20のしきい倀が、テヌブルの倉曎の動的に倉化する割合に倉曎されたす。







DBCC TRACEON (2371,-1)
      
      





フラグを有効にするず、行数が増加するに぀れお統蚈がより頻繁に曎新されたす。







これは倧いに圹立ちたすが、10億行以䞊のテヌブルでは、通垞の蚈画にもかかわらず、プロセスが非垞に遅くなる堎合がありたす。







この堎合、統蚈の非同期曎新を有効にするず次のこずができたす。







 ALTER DATABASE dbName SET AUTO_UPDATE_STATISTICS_ASYNC ON
      
      





統蚈の曎新䞭、SQLはプロセスが完了するのを埅たずに機胜し続けたす。 テストによるず、このオプションを有効にしないデヌタベヌスク゚リは非垞に䞍安定です。 この実隓を自分で詊しおください。







ただし、適切な蚈画ず最新の統蚈を䜿甚しおも、ク゚リの実行に原因䞍明の遅延が発生する堎合がありたす。 私の経隓では、間違った蚈画の埌のこれの最も䞀般的な理由は、芁求されたリ゜ヌスの正匏な䞍足のために実行のためにキュヌにあったサヌバヌ芁求芁求でした。







メモリヌに貪欲な5぀以䞊のク゚リを同時に起動し、デフォルトで「片手」の制限を25にするず、最初の4぀にはすべおのメモリヌが䞎えられたす。 残りのリク゚ストは、 RESOURCE_SEMAPHOREむンゞケヌタヌでのみキュヌに入れられ、メモリが解攟されるのを埅ちたす。







䜿甚可胜なサヌバヌRAMの玄半分が芁求の実行に䜿甚されたす。 RAMが128 GBを超えるシステムでは、25が非垞に重芁です。







私の芳察によるず、ク゚リは割り圓おられたメモリの20〜80を䜿甚しないだけです。これは、リク゚ストの操䜜䞭にコマンドを実行するず完党に衚瀺されたす。







 select * from sys.dm_exec_query_memory_grants
      
      





コマンドを実行した埌、プロセスで発生した堎合、デヌタベヌスで珟圚凊理されおいるク゚リずリ゜ヌスの期埅倀が衚瀺されたす。







画像の代替テキスト







テストデヌタベヌスの1぀でリク゚ストを凊理した結果。







芁求は9 GBを芁求しお受信したしたが、10しか䜿甚したせんでした。 ideal_memory_kbフィヌルドに泚意しおください -リク゚ストの未実珟の倢は修正されおいたす。







Resource Governorを䜿甚しおク゚リを実行するずきに、無駄なメモリ消費を効率的に凊理できたす。 CPU䜿甚量ずメモリ䜿甚量に制限を蚭定できたす。

sp_WhoIsActiveストアドプロシヌゞャの䜿甚もお勧めしたす 。 動的衚珟DMVを介しおサヌバヌステヌタス情報を収集したす。 非垞にシンプルで匷力なツヌル。

このパラメヌタヌセットでsp_WhoIsActiveプロシヌゞャを䜿甚したす。







 sp_whoisactive @not_filter = 'ReportServer', @not_filter_type = 'database', @get_plans=1, @find_block_leaders = 1, @get_task_info = 2, @get_additional_info=1--, @get_locks = 1
      
      





このようなリク゚ストは、過剰な負荷がかかったずきにアクティブなリク゚ストを芳察するだけで識別できたす。 たた、5〜10分ごずにsys.dm_exec_query_memory_grantsク゚リからselect *を実行し、結果をテヌブルに保存するタスクを䜜成するこずもできたす。 grant_memory_kbずmax_used_memory_kbの違いにより、合蚈倀を分析し、問題のあるク゚リを芋぀けるこずができたす。







次に、必芁以䞊にメモリを䜿甚する接続を特定し、ResourceGovernorを䜿甚しお、メモリが80ではなく10-20のマヌゞンで割り圓おられおいるグルヌプに接続をリダむレクトする必芁がありたす。







REQUEST_MAX_MEMORY_GRANT_PERCENTパラメヌタヌの倀䜿甚可胜なメモリヌの割合ずしお蚭定は、次のスクリプトを䜿甚しお蚈算できたす。







 SELECT res.name, sem.target_memory_kb /1024 as target_memory_Mb, sem.available_memory_kb / 1024 as available_memory_MB, sem.granted_memory_kb/1024 as granted_memory_Mb, sem.used_memory_kb / 1024 as used_memory_Mb, sem.grantee_count, sem.waiter_count FROM sys.dm_exec_query_resource_semaphores sem join sys.resource_governor_workload_groups res on sem.pool_id = res.pool_id where sem.resource_semaphore_id =0
      
      





その結果、サヌバヌがク゚リおよび远加たたは構成する各グルヌプ甚に䜿甚できるメモリ量を決定できたす。







䞊蚘のすべおの操䜜の埌、芁求がメモリ割り圓おを単に埅機し、䜕もしないずいう最小限の状況で、芁求間のメモリの分散がより効率的になりたす。







䜿甚䞭のデヌタベヌスでのAlwaysON



デヌタベヌスの高可甚性環境であるAlwaysONの長所ず短所は、怠け者によっおだけ曞かれたものではありたせん。 ただし、テラバむトのデヌタベヌスに察するこの技術の実際の䜿甚に関する情報はそれほど倚くありたせん。 2013幎にYandex.Moneyに高可甚性グルヌプを導入したずき、軍事環境での実際の運甚に関する情報すらありたせんでした。 圓時のメむンデヌタベヌスは玄4 TBしか䜿甚しおいなかったため、倚くのこずを自分で行う必芁がありたした。







兞型的なクラスタヌノヌドの構成。

高可甚性クラスタヌは、192 GBのメモリ、サヌバヌごずにSASディスクを備えた2぀の「シェルフ」、SATAドラむブを䜿甚したバックアップ甚の個別のシェルフ、TEAMの4ギガビットむンタヌフェむスのネットワヌクの2぀のノヌドで構成されたした。







4幎間で、このデヌタベヌスのボリュヌムは20 TBに増加したため、サヌバヌは新しい負荷にほずんど察凊できず、むンデックスの最適化により、以䞋で説明する远加の問題が発生したした。 ネットワヌク経由でキュヌブを凊理する際のデヌタベヌスパフォヌマンスは、クラスタヌが別の匷力なスむッチに転送されるたで、トラフィックがネットワヌク内の他のコンポヌネントに干枉するほどでした。







ピヌク時には、ノヌドがお互いを倱い、2番目のノヌドのデヌタベヌスが切断されたクラスタヌクォヌラムにこんにちはポむントになりたした。 しかし、これは新しいネットワヌクカヌドの远加によっお解決されたした。 そしお、新しいマシンでは、すぐに10 GBのカヌドを入れたした。







リストされたアヌキテクチャ䞊の問題を陀いお、AlwaysONテクノロゞの䞀般的な印象は肯定的です。









クラスタリングサヌビスの䞍合理な決定の数を最小限に抑えお、2぀のノヌドを持぀構成でクラスタヌを消滅させるために、セカンダリノヌドの音声を匷制的に奪う゜リュヌションを開発したした。







 Import-Module FailoverClusters $node = "Srv1" (Get-ClusterNode $node).NodeWeight = 0
      
      





このようなパラメヌタを䜿甚するず、セカンダリノヌドでの䜜業䞭に穏やかになりたす。







AlwaysONグルヌプが同期しおいない



AlwaysONグルヌプで負荷分散を行う倧芏暡なデヌタベヌスの操䜜䞭に、メむンノヌドずセカンダリノヌドの同期が発生する堎合がありたす。 メむンノヌドで同時に曎新されおいるテヌブルに察しおROノヌドで長いSELECT操䜜を実行するず、倉曎ログチェヌンLSNの回埩がブロックされたす。 非同期はすべおのテヌブルに圱響し、ク゚リが実行されおいる限り保持されたす。この時点で、レプリカ䞊のデヌタベヌスのデヌタは無関係になりたす。







通垞、非同期の明らかな兆候はありたせんが、 saずしお起動された読み取り専甚ノヌドに新しいプロセスが衚瀺されたす。 カスケヌドロックも頻繁に発生したす。これは、䞊蚘のsp_whoisactiveプロシヌゞャを䜿甚しおすばやく怜出できたす。







その理由は、Selectが分離レベルREAD COMMITTED぀たり、デフォルト倀で実行されおいるこずにありたす。







解決策ずしお、ク゚リ自䜓でWITHNOLOCKパラメヌタを䜿甚するか、デヌタベヌスたたはセッションの分離レベルをSNAPSHOTに倉曎しお、テヌブルぞの長いク゚リク゚リ䞭にそれを分離できたす。







ナヌザヌの前に同期が倖れおいるこずを知るために、監芖甚のスクリプトを䜿甚したす。 5分に1回、同期のステヌタスを確認し、問題が発生した堎合に電子メヌルを送信したす。







むンタヌネットから䞍明な著者を監芖するためのサンプルスクリプト
 declare @Delay int set @Delay = 2 select * into #tmpag_availability_groups from master.sys.availability_groups select group_id, replica_id,replica_server_name,availability_mode into #tmpdbr_availability_replicas from master.sys.availability_replicas select replica_id,group_database_id,database_name,is_database_joined,is_failover_ready into #tmpdbr_database_replica_cluster_states from master.sys.dm_hadr_database_replica_cluster_states select * into #tmpdbr_database_replica_states from master.sys.dm_hadr_database_replica_states select replica_id,role,is_local into #tmpdbr_availability_replica_states from master.sys.dm_hadr_availability_replica_states select ars.role, drs.database_id, drs.replica_id, drs.last_commit_time into #tmpdbr_database_replica_states_primary_LCT from #tmpdbr_database_replica_states as drs left join #tmpdbr_availability_replica_states ars on drs.replica_id = ars.replica_id where ars.role = 1 SELECT --'<tr><td align="center">' + AR.replica_server_name + '- ' + AG.name + ' ' + dbcs.database_name + '</td>' AS SRV_AG_DB, AR.replica_server_name + '- ' + AG.name + ' ' + dbcs.database_name AS SRV_AG_DB, --CASE dbcs.is_failover_ready WHEN 1 THEN 0 ELSE ISNULL(DATEDIFF(ss, dbr.last_commit_time, dbrp.last_commit_time), 0) END AS [EstimatedDataLoss], --'<td align="center">' + Cast(ISNULL(dbr.last_redone_time, 0) AS varchar(50)) + '</td>' As LastRedoneTime , Cast(ISNULL(dbr.last_redone_time, 0) AS varchar(50)) As LastRedoneTime , --'<td align="center">' + Cast((CASE dbcs.is_failover_ready WHEN 1 THEN 0 ELSE ISNULL(DATEDIFF(ss, dbr.last_commit_time, dbrp.last_commit_time), 0) END)as nvarchar(10)) + '</td>' AS [EstimatedDataLoss2], Cast((CASE dbcs.is_failover_ready WHEN 1 THEN 0 ELSE ISNULL(DATEDIFF(ss, dbr.last_commit_time, dbrp.last_commit_time), 0) END)as nvarchar(10)) AS [EstimatedDataLoss2], --'<td align="center">' + Cast(ROUND(ISNULL(CASE dbr.redo_rate WHEN 0 THEN -1 ELSE CAST(dbr.redo_queue_size AS float) / dbr.redo_rate END, -1),-1) as nvarchar(10)) + '</td> </tr>' AS [EstimatedRecoveryTime] Cast(ROUND(ISNULL(CASE dbr.redo_rate WHEN 0 THEN -1 ELSE CAST(dbr.redo_queue_size AS float) / dbr.redo_rate END, -1),-1) as nvarchar(10)) AS [EstimatedRecoveryTime] INTO #tt FROM #tmpag_availability_groups AS AG INNER JOIN #tmpdbr_availability_replicas AS AR ON AR.group_id=AG.group_id INNER JOIN #tmpdbr_database_replica_cluster_states AS dbcs ON dbcs.replica_id = AR.replica_id LEFT OUTER JOIN #tmpdbr_database_replica_states AS dbr ON dbcs.replica_id = dbr.replica_id AND dbcs.group_database_id = dbr.group_database_id LEFT OUTER JOIN #tmpdbr_database_replica_states_primary_LCT AS dbrp ON dbr.database_id = dbrp.database_id INNER JOIN #tmpdbr_availability_replica_states AS arstates ON arstates.replica_id = AR.replica_id WHERE --(AG.name='Nastro') and ISNULL(CASE dbr.redo_rate WHEN 0 THEN -1 ELSE CAST(dbr.redo_queue_size AS float) / dbr.redo_rate END, -1) > @Delay --(AG.name=@AGN) and ISNULL(CASE dbr.redo_rate WHEN 0 THEN -1 ELSE CAST(dbr.redo_queue_size AS float) / dbr.redo_rate END, -1) > @Delay IF EXISTS (SELECT * from #tt) BEGIN declare @tableHTML nvarchar(max) set @tableHTML =N'<H3><FONT SIZE="3" FACE="Tahoma">AlwaysOn Status </FONT></H3>' set @tableHTML = @tableHTML +N'<table border="1" bgcolor=D7D1F8>' + -- change the background color if you want N'<FONT SIZE="2" FACE="Calibri">' + N'<tr><th align="center">Server Group DB</th>' + N'<th align="center">LastRedoneTime</th>' + N'<th align="center">EstimatedDataLoss</th>' + N'<th align="center">EstimatedRecoveryTime</th>' + N'</tr>' + ISNULL(CAST ( ( SELECT * from #tt FOR XML PATH('tr'), TYPE ) AS NVARCHAR(MAX) ),'') + N'</FONT>' + N'</table>' ; --send email EXEC msdb.dbo.sp_send_dbmail @profile_name = 'mail', -- change here !! @recipients='Admin1@yandex.ru;Admin2@yandex.ru', -- change here !! @subject = 'AlwaysON Report', @body = @tableHTML, @body_format = 'HTML' ; END DROP TABLE #tmpdbr_availability_replicas DROP TABLE #tmpdbr_database_replica_cluster_states DROP TABLE #tmpdbr_database_replica_states DROP TABLE #tmpdbr_database_replica_states_primary_LCT DROP TABLE #tmpdbr_availability_replica_states drop table #tmpag_availability_groups SELECT * from #tt drop table #tt
      
      





同期解陀のもう1぀の䞀般的な原因は、むンデックスのメンテナンスです。 たずえば、500 GBの容量を持぀テヌブルでクラスタヌむンデックスを再構築するず、10時間の同期が取れなくなりたした。







この時間を最小化する最も簡単な方法は、むンデックスを䜜成たたは再構築するずきにMAXDOPオプションを䜿甚するこずです。 通垞、10〜20 GBのむンデックスには「2」、それより小さな「4」には倀「2」を蚭定したす。







読み取り専甚芁求ルヌティング



読み取り専甚デヌタベヌスレプリカからの読み取り芁求のニュアンスは、接続文字列でApplicationIntent = ReadOnlyを指定するだけでは十分ではないこずです。 ルヌティングを構成する必芁もありたす 。 もちろん、この蚭定は、䞀床構成するずタッチしなくなるものに適甚されたすが、His䞋のチャンスは砎られ、そのようなスキヌムはありたせん。 たずえば、䜜業の過皋で、レプリカROからメむンリク゚ストにリク゚ストをリダむレクトする必芁がある堎合がありたす。







これを行うには、レプリカ自䜓の蚭定を䜿甚し、ROぞのリク゚ストを単に犁止しおいたした。 しかし、レプリカ蚭定を切り替える次の機胜のために、いく぀かの䞍快な状況の埌、アプロヌチを倉曎する必芁がありたした。









埌で、ルヌティングの倉曎を通じおRO芁求をリダむレクトする方が正しいこずが刀明したした。







次に、2ノヌドの分散負荷構成の䟋を瀺したす。







 ALTER AVAILABILITY GROUP [AGGroupName] MODIFY REPLICA ON N'PrimaryDB01′ WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST=('SecondaryDB01','PrimaryDB01'))); -- ,  RO       SecondaryDB01         ALTER AVAILABILITY GROUP [AGGroupName] MODIFY REPLICA ON N'PrimaryDB01′ WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST=('PrimaryDB01'))); --          PrimaryDB01;
      
      





ルヌティングを倉曎するプロセスでは、リク゚ストは通垞​​完了し、次のリク゚ストはメむンのレプリカに盎接送られたす。







システムの動䜜の4幎間で、ROレプリカの障害が1回発生したした。 デヌタファむルにCRC゚ラヌがあるセクションの1぀が萜ちたした。 メむンノヌドは匕き続き機胜したしたが、ROレプリカにはベヌスがありたせん。







ROノヌドの基本レベルでの障害凊理ずメむンノヌドぞの自動トラフィックスむッチングは、同じルヌティングを䜿甚しお実装できたす。







  1. デヌタベヌス内の任意のテヌブルぞのク゚リを䜿甚しおタスクを䜜成したす。







  2. 芁求が゚ラヌで完了した堎合、同じ方法でメむンノヌドのデヌタベヌスを確認したす。







  3. メむンノヌドですべお問題なければ、トラフィックを切り替えたす。 週末たたは倜に、それはあなたが良い倜の睡眠を埗お、新鮮な心で問題に察凊するこずを可胜にしたす。


このような障害が発生した埌のデヌタベヌスの埩元に぀いおは、可甚性グルヌプから倧きなデヌタベヌスを削陀しメむンノヌドではリスナヌを介しおも動䜜し続けたす、非埩旧モヌドで読み取り専甚ノヌドにバックアップを埩元する方が簡単です。 次に、埩元したデヌタベヌスを参加専甚モヌドの可甚性グルヌプに远加したす。







远加

アプリケヌションが遅く、SSMSが速い...-リンクAlanDentonをありがずう








All Articles