1

いくつかのテーブルからすべてのデータを取得し、何かを再計算して保存するクライアント/サーバー アプリケーションがあります。

例:

各アイテムには「部品表」があります。これは、それを構成する他のアイテムのリストと数量です。したがって、アイテムのコストは、BOM 内のアイテムのコスト * 数量の合計です。最終的に、一部の「ベース」アイテムには BOM がなく、コストが個別に設定されているだけです。(すなわち: 原材料)

例: A の BOM には、2xB と 3xC で構成されていると記載されています。

私が今していることは、なぜこのようにするのか覚えていませんが、DB からすべてのアイテムとすべての BOM を取得し、一度に各アイテムのコストを再帰的に計算することです。1 つのアイテムを計算したら、フラグを立てて、コストを再度やり直さないようにします。(無限再帰も防ぎます)

問題は、これはちょっとばかげているということです。まず、その速度は遅く、変更されていないものを再計算します。さらに悪いことに、十分な大きさの DB を与えると、メモリが不足します。

代わりに、必要に応じてアイテムを再計算できます。アイテムの BOM が変更された場合、その BOM を再計算し、この更新されたアイテムを含むすべての BOM を選択し、それらも再計算します。変更されたアイテムに依存する DB 内の BOM がない最上部に到達するまで、すすぎ、再帰的に繰り返します。

これが実際に意味すること: 一部のアイテムは原材料であり、そのコストは頻繁に更新される可能性があり、一部のアイテムは BOM がほとんど変更されない「エンドユーザー」のものであるとします。ユーザーがこれらの材料の 1 つのコストを変更すると、何千ものアイテムを調べて再計算することになる場合があります。1 つのアイテム/BOM の SELECT に 15 ミリ秒かかるとします (私は Postgresql を使用しています)。1000 のアイテム/BOM を選択するだけで 15 秒かかるため、再計算されたコストを DB のアイテムに更新する必要があります...ああ親愛なる、遅延は数分に変わる可能性があります。

私が働いている会社が使用しているERPソフトウェアは、最初のアプローチを採用しています。つまり、DB全体を一度にバッチ再計算します。これには文字通り何時間もかかり、10年以上の使用でこのアプローチでは問題が蓄積されているようです. バッチ再計算は毎週行われます。

実際に「これを大声で書いた」ので、数分かかることはあまり問題ではないと思います。問題は、私がデータベースをよく理解していないことと、同時実行性について心配していることです。アイテム A の更新には時間がかかるため、アイテム A が更新されている間に誰かが 2 番目のアイテム B を更新する可能性があります。更新しました。

アイテム D は上記の A と B から作られているとします。ユーザー 1 が A を更新すると、サーバー ソフトウェアは DB で数分間マスターベーションを開始し、最終的に D を更新します。しかし、その間にユーザー 2 が B を更新するため、サーバーは最終的に D を再度更新します。

Postgresql のトランザクションを使用すると問題は解決しますか? トランザクションはその時点での DB の状態で開始されるため、トランザクション 1 は D が A1 と B1 から作成され、A1 から A2 に更新されていることを確認しますが、トランザクションが終了してコミットする前に、トランザクション 2 が開始され、A1 も確認されます。そしてB1。T1 は、D = A2 + B1 を再計算してコミットします。しかし、T2 はすでに始まっており、新しい A、A2 は表示されません。そのため、最終的に D = A1 + B2 という DB にコミットしますが、これは正しくありません。D = A2 + B2 である必要があります。

また、一部の処理が重複し、サーバー時間が無駄になります。

T1 と T2 を並列ではなく順番に実行すると、答えは正しいのですが、ユーザー 2 はさらに長く待たなければなりません。また、トランザクションのグループが互いに関係がない場合 (完全に独立した... 依存関係ツリー。つまり、A=X+Y および B=N+M)、並列計算により正しい答えが得られ、さらに高速になります。ユーザー。

重要な注意: 順番に処理する場合でもトランザクションを使用するので、コストを再計算する関数を除いて、ソフトウェアの残りの部分はそのデータを並行して処理できます。

さて、この「プロセス・イン・シーケンス」全体は、もし....DBレイテンシがそれほど「ひどい」ものではないなら、それほど悪くはありません。たとえば、データ全体が RAM に保持される場合、1000 個のオブジェクトを通過するのは簡単です。ああ、でも、データのチャンクをディスク/RAM との間ですばやく移動し、キャッシング (DB を置き換える) を行うシステムを構築したとしても、それはうまくいきません。並行して作業できます。(上記の「重要な注意」)したがって、別のDBを構築することになります。少し速いかもしれませんが、それはばかげている/時間の無駄です。

各アイテムのコストを「キャッシュ」する理由は、それを使用するたびに再計算しないようにするためです。これは、限られたリソースを浪費するだけでなく、DB レイテンシが大きすぎ、同時実行の問題がさらに悪化するためです。

なぜ「彼ら」が大量にそれを行ったのか不思議ではありません...これは私の頭を悩ませています.

Q1: 「最適な」方法でこれをどのように解決しますか?

私の現在の理解から (つまり、以前は黙って無視していた同時実行性の問題に直面した後)、その関数にトランザクションを順番に使用させ、アプリの残りの部分はデータを並行して使用できると考えています。ユーザーに最適です。それが目標です。ユーザーにとっては最善ですが、システムの正確性は保証されています。

後でハードウェアを投入して、ソフトウェアのブラック マジックを使ってレイテンシを短縮できるかもしれませんが、今は自分に嘘をつき始めています。

また、過去 2 か月間、私はいくつかの明らかなこと (プログラミングに関係のないものもありました) に完全に目をつぶっていました。 | |

4

2 に答える 2

4

なぜこんなことをしたのか覚えていません...

最初に取り組む必要があることとして、これは私に飛びつきます!

各 BOM の総コストを計算するためだけに、データをアプリケーションにフェッチする必要があるわけではありません。SQL で「パーツ展開」または階層データ セットを操作するための手法は多数あります。

私のプレゼンテーション「SQL Antipatterns Strike Back 」でいくつかのソリューションを取り上げています。または、「 Joe Celko の Trees and Hierarchies in SQL 」などの本を読むこともできます。

ベンダー固有のソリューションもあれば、プレーンな SQL DBMS で実行できるソリューションもあります。データベースのブランドはわかりませんでしたが、Jonathan のおかげで、あなたが PostgreSQL を使用していることを正しく認識できました。

その場合、WITHPostgreSQL 8.4 で新しく追加された " " クエリについて読んで、高度な再帰クエリ効果を実行できるようにする必要があります。

http://www.postgresql.org/docs/current/static/queries-with.html

BOM が個々のリソースの階層で構成されるシステムを実装しましたが、説明しているバッチ処理を行う必要はありませんでした (確かに、私が作業している間、データベースには数千のリソースしかありませんでした)それ)。

SUM()および(SQL に関する本にはこれが含まれているはずです) のような SQL での集計関数の使用方法GROUP BYと、エンティティの階層関係を格納する手法も学ぶ必要があります。

あなたはデータベースをよく理解していないと言うので、実際のシステムに変更を加える前に、「おもちゃ」のシステムを実装してみることをお勧めします。私は個人的な経験から話しているだけですが、実際のプロジェクトでそのスキルを使用しようとしていると同時に、新しい技術スキルを習得することはできません.

于 2009-09-24T07:42:10.813 に答える
2

これは、多かれ少なかれ、使用する実装方法に関係なく、データベース内のストアド プロシージャであることが役立つ計算のように思えます。これにより、クライアントとサーバー間のトラフィックが削減され、このような複雑な一連の計算のパフォーマンスがほぼ常に向上します。

あなたは言う:

私が今していることは、なぜこのようにするのか覚えていませんが、DB からすべてのアイテムとすべての BOM を取得し、一度に各アイテムのコストを再帰的に計算することです。1 つのアイテムを計算したら、フラグを立てて、コストを再度やり直さないようにします。(無限再帰も防ぎます)。

私はこの説明の「フラグを立てる」という部分に困惑しています - そして、あなたがそのやり方で何かをする理由がわからないのは悪いニュースです. 自分が何をしているのかを本当に理解する必要があります。

BOM 処理を行う方法はたくさんあります。Bill Karwin が興味深い情報を教えてくれました (SQL アンチパターンへのリンクは約 250 枚のスライドです!)。SQL アンチパターン セクションでは、「ナイーブ ツリー」(以下に概説するものなど) について説明します。ただし、ソリューションは、同じサブツリーが複数の親で使用される可能性がある以下に概説するケースには対応していません (1 つのサブアセンブリが複数の製品の構成要素になる可能性があるため)。

  • パスの列挙が機能しません。含まれている製品情報をパスに組み込むため、同じサブアセンブリ情報を使用することはできません。
  • サブアセンブリが 1 つの製品で使用されている場合、ネストされたセットは正常に機能します。サブアセンブリが多くの製品で使用されている場合ではありません。
  • 「クロージャ テーブル」ソリューションは、これをカバーするように適応させることができます。これは、多かれ少なかれ以下の 2 番目の選択肢です。

影響を受ける部分のボトムアップ スキャンを実行することが理にかなっているのか、それとも幅優先スキャンまたは深さ優先スキャンを実行した方がよいのかを検討する必要があります。この意思決定の要因の 1 つは、BOM データの性質です。一部のサブアッセンブリーが複数の製品の構成部品として使用されている構造の場合、サブアッセンブリーでの部品使用を製品ごとに個別に記録していますか、それとも製品がサブアッセンブリーを使用していると記録していますか?

明確にするために:

  • サブアセンブリ A (P001) には、24 x 8mm ナット (P002)、24 x 8mm x 50 mm ボルト (P003)、1 x ベースプレート (P004)、1 x カバープレート (P005) が含まれます。
  • 製品 B (P006) には、1 x サブアセンブリ A とその他の多数の部品が含まれています。
  • 製品 B (P007) には、1 x サブアセンブリ B とその他の多数の部品が含まれています。

BOM レコードは次のようになります (単純なツリー)。

Part      Component     Quantity
P001      P002          24
P001      P003          24
P001      P004          1
P001      P005          1
P006      P001          1
P007      P001          1

または、次のようになります (クロージャ テーブル)。

Part      Component     Quantity
P001      P002          24
P001      P003          24
P001      P004          1
P001      P005          1
P006      P002          24
P006      P003          24
P006      P004          1
P006      P005          1
P007      P002          24
P007      P003          24
P007      P004          1
P007      P005          1

この 2 番目のケースはあまり望ましくありません - 値を正しく取得するのははるかに難しく、二重に、ナットやボルトなどの部品の場合のように、複数のサブアセンブリが同じ部品を使用できる場合は、正しい数を取得する必要があります。主要な成果物 (P006、P007) は非常に困難です。ただし、2 番目のケースでは、部品のコストを再計算する方がはるかに簡単です。部品を構成する各コンポーネントの「コスト×数量」の合計を計算するだけです。ナイーブ ツリーを保持して部品構造の内訳を記録し、一部の製品またはサブアセンブリの構造 (価格ではない) が変更されたときにクロージャー テーブルを (再) 計算する場合、おそらく最も楽観的な状態に近いと言えます。取得するため。

どこか (ただし、このコンピューターとは別のコンピューター上) には、架空のアセンブリを使用して、このようなものをいじるための古いコードがあります。コーディングは完了しました...つぶやき、つぶやき...ずっと前に、特定のDBMS用に一時テーブルを使用します(ネストされたセットやパス列挙については言及していません。クロージャテーブルを計算します)-それはそうでなければなりません他の DBMS に適合します。聞いてください、私はそれを掘り出します。

于 2009-09-24T08:27:26.867 に答える