多数のレコードを持つテーブルがあり、次の条件で1つ以上のインデックスを追加する必要があるとします。
- それらの生成は可能な限り高速でなければなりません
- 生成をバッチで実行できるようにします。
たとえば、300Mレコード用のテーブルがあり、時間後にのみ作業できる場合、プロセス全体を1億レコードの3泊に分割できます。 - 新しいインデックスの出現とそれらの生成のプロセスは、クラス/テーブルの現在の作業を妨げてはなりません。
これを行うには、よく知られているメソッド
%BuildIndices()を使用できますが、この場合は条件を満たしません。
出口は何ですか?
理論
新しい
%Library.IndexBuilderクラスが
Caché2013.1に追加されましたが、1つの強力な
%ConstructIndicesParallel()メソッドがあります。
名前から、生成はすべてのプロセッサコアの関与と並行して発生することがすでに明らかになっています。
したがって、このメソッドのパラメーターをより詳細に検討してください。
ClassMethod %ConstructIndicesParallel( pTaskId = "" 、 pStartId As%Integer = 0 、 pEndId As%Integer = -1 、 pSortBegin As%Integer = 1 、 pDroneCount As%Integer = 0 、 pLockFlag As%Integer = 1 、 pJournalFlag As% Booan 1 ) as%ステータス- pTaskId-バックグラウンドプロセスのID。 インタラクティブコールの場合は空白のままにしてください。
- pStartId-生成を開始するID。 デフォルト1
- pEndId-生成を完了するID。 デフォルトは-1で、テーブルの最後のIDを意味します
- pSortbegin-生成時に$ SortBeginを使用するかどうかを決定する1/0フラグ。
- pDroneCount-インデックスを生成するためのバックグラウンドプロセスの数。
デフォルト値は0です。この場合、コードは、使用可能なコア/プロセッサーの数と処理されたレコードの数に基づいて、最適なプロセス数を個別に決定します。 - pLockFlagは、生成中のロックの動作を定義するフラグです。
- 0 =ロックなし
- 1 =エクステントロック-生成中にエクステント全体で排他ロックを取得します
- 2 =行レベルのロック-処理された各行と要素のインデックスノードで共有ロックを取得します。 特定の行のインデックス生成が完了すると、行はすぐにロックされます。
- pJournalFlag-ロギングの使用を定義する0/1フラグ:
1-インデックス生成はログに記録され、0-ログは記録されません。
練習する
次に、新しいクラスを適用する例を考えてみましょう。
最初に、
USER領域にクラスを作成し、可変長文字列[1-100]で1Mレコードを入力し、従来の
%BuildIndices()を使用してインデックスを作成します。
クラスdemo.test Extends%Persistent
{
インデックス idxn On n As SQLUPPER ( 6 );
プロパティ n As%文字列 ( MAXLEN = 100 );
ClassMethod Fill( n As%整数 = 10000000 )
{
set data = $ Replace ( $ Justify ( "" 、100)、 "" 、 "a" )
設定 時間 = $ ZHorolog
無効にする ^%NOJRN
do .. %KillExtent ()
セット ^ demo.testD = n
set ^ demo.testD(1)= $ ListBuild ( "" 、 $ Extract ( data 、1、 $ Random (100)+1))
for i = 2:1: n set ^( i )= $ ListBuild ( "" 、 $ Extract ( data 、1、 $ Random (100)+1))
^%NOJRNを有効にする
「insert = "」 、 $ ZHorolog - time 、 「sec 」 と 書き込み ます。 !
}
ClassMethod BIndex()
{
設定 時間 = $ ZHorolog
do .. %BuildIndices (、1,1)
" reindexing =" 、 $ ZHorolog - time 、 "sec。"を 書き ます。 !
}
}
私の結果:
USER>do ##class(demo.test).Fill()
= 9.706935 .
USER>do ##class(demo.test).BIndex()
= 71.966953 .
ここで、新しい
%IndexBuilderクラスを使用します。 これを行うには、次の手順を実行します。
- 最初に、 %PurgeIndices()メソッドを使用して前のテストのインデックスデータをクリアします(オプションのステップ)
- クラスを%IndexBuilderから継承します
- INDEXBUILDERFILTERパラメーターにコンマで区切られたインデックスのリストを書き込みます。
このパラメーターを空白のままにすると、すべてのインデックスが再生成されます - オプティマイザがまだ操作の準備ができていないインデックスを使用しないように、SQLに対してインデックスを非表示にします。
これを行うには、 $ SYSTEM.SQL.SetMapSelectability()メソッドを使用します。
ClassMethod SetMapSelectability( pTablename As%Library.String = "" 、 pMapname As%Library.String = "" 、 pValue As%Boolean = "" ) as%Library.String
引数の説明:
- pTablename-テーブル名
- pMapname-インデックス名
- pValue -SQLオプティマイザーのインデックスの可視性(1)または非可視性(0)を決定する0/1フラグ
注:インデックスは、クラスに追加されるずっと前に非表示にすることができます。
- %ConstructIndicesParallel()メソッドを呼び出す
- インデックスをSQLから見えるようにします
- 利益!
その結果、クラスは次の形式を取ります。
クラスdemo.test Extends ( %Persistent 、 %IndexBuilder )
{
パラメーター INDEXBUILDERFILTER = "idxn" ;
パラメーター BITMAPCHUNKINMEMORY = 0 ;
インデックス idxn On n As SQLUPPER ( 6 );
プロパティ n As%文字列 ( MAXLEN = 100 );
...
ClassMethod FastBIndex()
{
do .. %PurgeIndices ( $ ListBuild ( "idxn" ))
$ SYSTEM .SQLを実行し ます。 SetMapSelectability ( $ classname ()、 "idxn" 、 $$$ NO )
do .. %ConstructIndicesParallel (,,, 1 ,, 2,0)
$ SYSTEM .SQLを実行し ます。 SetMapSelectability ( $ classname ()、 "idxn" 、 $$$ YES )
}
}
私の結果:
USER>do ##class(demo.test).FastBIndex()
Building 157 chunks and will use parallel build algorithm with 4 drone processes.
SortBegin is requested.
Started drone process: 3812
Started drone process: 4284
Started drone process: 7004
Started drone process: 7224
Expected time to complete is 43 secs to build 157 chunks of 64,000 objects using 4 processes.
Waiting for processes to complete....done.
Elapsed time using 4 processes was 34.906643.
ご覧のとおり、速度は2倍になりました。
ハードウェアとデータで、結果はさらに良くなる可能性があります。
もっと速い?
しかし、インデックスの再生成をさらに加速する機会はありますか?
RAMが大量にある場合は、はい。
インデックスを生成するプロセスでは、内部ニーズのコンストラクターがいわゆるビットマップブロックを一時的に形成します。 デフォルトでは、プライベートグローバルに書き込まれますが、
BITMAPCHUNKINMEMORYブールパラメータを使用して、RAMで形成するように指定できます。 これを行うには、パラメーターに1を割り当てます。
RAMが少し割り当てられ、インデックスが大きい場合、
<STORE>エラーが発生する可能性があることに注意してください。
デフォルトでは、
BITMAPCHUNKINMEMORYは0です。