球形の馬
最も簡単な請求について説明していると仮定します(この方法は請求だけでなく、それでも非常に明確に見えることは明らかです)。 サブスクライバーの呼び出しに関するデータが蓄積されるテーブルは、この場合、次の形式になります。
CREATE TABLE billing.calls
(
call_id BIGINT,
call_time TIMESTAMP ,
subscriber_id INTEGER ,
duration INTERVAL
);
* This source code was highlighted with Source Code Highlighter .
それで、それはすべて簡単です。 通話データはこのレートで非常識なレートで送信され、妥当な期間課金される必要があります。
請求については、次のシグネチャを持つデータベース関数があります。
FUNCTION calculate( IN subscriber_id INTEGER , IN duration INTERVAL , OUT status_code text) RETURNS void
* This source code was highlighted with Source Code Highlighter .
このリクエストを実行して、5分ごとに請求を実行します。
SELECT calculate(subscriber_id, duration) FROM billing.calls;
* This source code was highlighted with Source Code Highlighter .
ある時点で、このリクエストには5分で完了する時間がないことを理解しています。 この間に、より多くのデータが実行され、それからさらに多くのデータが実行されます。ストリームが少し弱まり、キューが最終的にレイクする夜に座って待機します。 これが見通しです。 私たちは一人で座って待っていなかったと言わなければなりません。 私たちと一緒に、サーバーの残りの3つのカーネル(たとえば)を費やしましたが、1つはリクエストを積み上げていました。 残念ながら、PostgreSQLはクエリ自体を並列化する方法を知りませんが、この場合、これは必要ありません。 非常にシンプルで明らかなトリックから、はるかに優れた結果が得られます。 関数「subscriber_idを4で割った余り」のインデックスを作成します。
CREATE INDEX billing.calls_subscriber_id_mod_idx ON billing.calls USING btree ((subscriber_id % 4));
* This source code was highlighted with Source Code Highlighter .
そして今、4つのスレッド(たとえば、4つの異なるジョブ)で起動します。
SELECT calculate(subscriber_id, duration) FROM billing.calls WHERE subscriber_id % 4 = @mod;
* This source code was highlighted with Source Code Highlighter .
modは0、1、2または3です(ストリームごとに)。
結果として
この手法は、2つの異なるフローが1人のサブスクライバからコールを取得した場合に発生する可能性のあるロックの問題を解決します。 また、並行して、これらのジョブは、データベース自体を並列化する場合よりも高速に機能します(たとえば、postgreではなくOracleを使用している場合)。
このメソッドは、関数ごとのインデックスをサポートするすべてのデータベースに適用できます(Oracle、Postgresql)。 MSSQLの場合、計算列とその上にインデックスを作成できます。 MySQLは機能インデックスをサポートしていませんが、回避策として、インデックスを含む新しい列を作成し、トリガーで更新できます。