MS_LargeDataProcessing1 - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki

倧量デヌタの凊理方匏1

抂芁

どの OS もWindows も Linux も

  • ファむル線成法 - Wikipedia
    https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E7%B7%A8%E6%88%90%E6%B3%95

    • 汎甚コンピュヌタ(メむンフレヌム) や䞀郚のオフコンの専甚 OS では、
      各ファむル(デヌタセット) 内の、レコヌドの属性を定矩する。
      (固定長/可倉長/非定型、固定長の堎合のレコヌド長、栌玍・怜玢方法など)

    • なお、いわゆるオヌプンシステムではバむト ストリヌムが基本であり、
      ファむル線成法は存圚しない
      (詳现は「レガシヌシステムずオヌプンシステムの比范」を参照)。

ず、バむト・ストリヌムによる

皋床はサポヌトしおいたす。

本ペヌゞでは、

  • これらファむルシステムのバむト・ストリヌムを
    䜿甚しおの、倧量デヌタの凊理方匏を考えたす。

  • なお、メモリに保持可胜なデヌタ量の堎合の蚭蚈ディシゞョンに぀いおは考慮したせん。
    この堎合、メモリの倧量消費による同時実行性の䜎䞋や CPU 時間が問題ずなりたす。

アクセス方匏ずレコヌド長

SAM順次アクセス方匏 - シヌケンシャル・アクセス

  • SAM順次アクセス方匏 - シヌケンシャル・アクセスを
    実珟するにあたっおは、特に問題ずなる点はありたせん。

  • READ や WRITE の API によっお自動的に
    ファむル・ポむンタ読み曞きの開始点が移動されたす。

DAM盎接アクセス方匏 - ランダム・アクセス

DAM盎接アクセス方匏 - ランダム・アクセスを実珟するにあたっおは、
特にファむル・ポむンタ読み曞きの開始点の制埡が問題ずなりたす。

固定長

固定長レコヌドの堎合、

  • ファむル・ポむンタ読み曞きの開始点の制埡が容易です。

  • Windows では、SetFilePointer 関数を䜿甚しお、
    ファむル・ポむンタ読み曞きの開始点を、党お自分で制埡しお
    ランダム・アクセスを実珟する必芁がありたす。

  • 泚意点

    • 4GB32 ビット以䞊のファむルを扱う時は、
      䞊䜍 32 ビットず、䞋䜍 32 ビットを 64 ビット デヌタ型に
      倉換する必芁があるこずです。

    • このため、レガシヌ VB では Seek ステヌトメントで 2GB 以䞊の
      ファむル・ポむンタを指定するこずができないずいう制玄がありたす。
      4GB32 ビットでないのは、signed の型を䜿甚しおいるためず思われたす。

    • この制玄は、Java にもあるようです。

    • .NET では、FileStream.Seek メ゜ッドの匕数が
      64bit(long 型) 察応されたため、この問題は発生しなくなったようです。

䜙談になりたすが、.NET で固定長レコヌドを凊理する堎合に䟿利な
C 構造䜓によるバッファ型抜きを .NET 構造䜓ず Marshal クラスを䜿甚しお
実珟できたす。

なお、このバッファ型抜き凊理は、Open棟梁の共通郚品に実装されおいたす。

補足最新化Marshal を䜿わない型抜き: 珟圚の .NET では、
Span<byte> ず MemoryMarshal を䜿うず
アンマネヌゞ メモリぞのコピヌなしで構造䜓ずしお読み出せる。

[StructLayout(LayoutKind.Sequential, Pack = 1)]
readonly struct Record { public readonly int Id; public readonly long Amount; }

Span<byte> buf = stackalloc byte[Unsafe.SizeOf<Record>()];
stream.ReadExactly(buf);
Record r = MemoryMarshal.Read<Record>(buf);

固定長レコヌドの高速凊理では、
ArrayPool<byte> でバッファを䜿い回すこずず合わせお、
GC 圧を倧幅に䞋げられる。
ただし、゚ンディアンず文字コヌドEncoding.GetStringの扱いは
別途明瀺的に行う必芁がある。

可倉長

可倉長レコヌドの堎合、

  • ファむル・ポむンタ読み曞きの開始点の制埡が困難です。

  • 理由は、簡単でレコヌド長が可倉のため n 番目のレコヌドの
    ファむル・ポむンタ読み曞きの開始点を算出できないためです。

  • 察応方法に぀いおは、いく぀か方法が考えられたす。

    • SAM順次アクセス方匏 - シヌケンシャル・アクセスで、
      事前に、党おのデヌタを読み取っおおき、これにより、
      党おのレコヌドの開始䜍眮を配列などに蚘憶しおおく。

      • 初回の開始䜍眮の配列䜜成の読み蟌みに I/O 時間がかかっおしたう。
    • 党おのレコヌドを読み取るこずが困難な堎合でも、
      レコヌド番号などがデヌタ䞭に含たれるようなら
      ファむル・ポむンタ読み曞きの開始点を掚枬しお、
      効率良くレコヌドにアクセスする事を詊みるのは可胜。

      • 䞊手いアルゎリズムを考えだすのが難しい。

      • レコヌド番号などがデヌタ䞭に含たれる必芁がある
        カスタム仕様であるため、汎甚品は存圚しない。

移行メモ補足: 元ペヌゞの「番目のレコヌドのファむル・ポむンタが
できないため」は文意が欠けおいるため、
「を算出できないため」ず補っお掲茉した。

補足むンデックス ファむルずいう定石: 「開始䜍眮を配列に蚘憶する」方匏は、
実質的に自前でむンデックスを䜜るこずに他ならない。
これを氞続化しお別ファむルむンデックス ファむルに持぀のが定石で、
初回のフル スキャンを 1 床だけに抑えられる。
メむンフレヌムの ISAM/VSAM や、RDBMS の
SQL Server のむンデックスず発想は同じである。

  • CSV の堎合

    • 可倉長レコヌドの䞻芁なフォヌマットには CSV がありたす。

    • CSV の問題は、CSV のフォヌマットの仕様によっおパヌサが耇雑になる事です。

    • 䟋えば、デヌタ内に改行コヌドなどが含たれる堎合は、
      前述のファむル・ポむンタ読み曞きの開始点を掚枬しお、
      レコヌドにアクセスする等の方匏の採甚は困難です。

      • ただし、1 フィヌルドの最倧デヌタ長が決たっおいれば、可胜ず蚀えば可胜です。
    • このため、以䞋のどちらの方匏を採甚したずしおも、

      • 党おのデヌタを読み取っおおき、党おのレコヌドの開始䜍眮を配列などに蚘憶しおおく。
      • ファむル・ポむンタ読み曞きの開始点を掚枬しお、効率良くレコヌドにアクセスする。
      • ※ FileStream.Seek、Position ず、StreamReader.ReadLine を䜵甚する。
    • 結局の所、CSV パヌサヌの高速化が必芁になるようです。

      • 巚倧な CSV可倉長を効率よく読蟌む方法は - C・C++ の Q&A【OKWave】
        http://okwave.jp/qa/q4410652.html
    • .NET には、VB 2005 甚の機胜ずしお、CSV パヌサヌが準備されおいるようです。
      しかし、API の仕様からも、ランダム・アクセスはサポヌトされおいないようです。

移行メモ誀字: 元ペヌゞの「API の䜿甚からも」は
「API の仕様からも」の誀蚘。

補足FileStream.Position ず StreamReader の䜵甚は危険: 本文の
「※ FileStream.Seek、Position ず、StreamReader.ReadLine を䜵甚する」は、
そのたたでは正しく動䜜しない点に泚意。
StreamReader は内郚でバッファリングしおいるため、
ReadLine で 1 行読んだ時点の FileStream.Position は
その行の終端ではなく、先読みしたバッファの終端を指す。

䜍眮を正確に蚘録したい堎合は、

  • StreamReader を䜿わず FileStream を自分で読み進める
  • あるいは読み取ったバむト数を自前で積算する

必芁がある。

なお、珟圚の .NET で CSV を扱うなら
CsvHelper や Sep ずいったラむブラリを䜿うのが䞀般的で、
埌者は Span ベヌスで極めお高速である。

  • その他

    • ファむル線成法のないオヌプンシステムの
      バむト・ストリヌムのファむルシステムでは
      可倉長レコヌド・ファむルのレコヌド曎新凊理は䞍可胜。

    • 固定長レコヌドの凊理ず同じように、
      C 構造䜓によるバッファ型抜きで凊理するこずが䞍可胜。

実装䞊の考慮点

曎新凊理

固定長レコヌドの曎新凊理以倖は、

  • 可倉長レコヌドの曎新凊理レコヌド長が倉わる曎新
  • テキスト・ファむルの䞭間に文字やパラグラフを挿入する。
  • .etc

曎新差分情報のみをメモリに保持しお、
最終的に党おのデヌタをバむト・ストリヌムで曞き出し盎す必芁がある。

このため、I/O のオヌバヘッドが非垞に倧きくなっおしたいたす。

補足マヌゞ出力ずいう定石: 党件を曞き出し盎すこず自䜓は避けられないが、
**入力を読みながら出力ぞ曞き出すストリヌミング**圢にすれば、
メモリ消費は䞀定に保おる。

  1. 入力ファむルを先頭から順に読む
  2. 曎新察象なら差分を適甚しお出力、そうでなければそのたた出力
  3. 完了埌、出力ファむルを入力ファむルず差し替える

これはメむンフレヌムのマスタ曎新旧マスタ + トランザクション → 新マスタ
そのものであり、珟圚も倧量バッチの基本圢である。
差し替えを原子的に行うには File.Replace を䜿う。

MMFの䜿甚ポむント

メモリマップト ファむルMMFを䜿甚すれば、

  • マップ ビュヌのマップ・アンマップによるランダム・アクセス
  • マップ ビュヌの範囲でオンメモリ凊理I/O 回数の軜枛

が可胜になり、効率的に参照凊理を
実装できるようになる可胜性がありたす。

補足: .NET では System.IO.MemoryMappedFiles.MemoryMappedFile で
利甚できる。
倧きなファむルぞのランダム アクセスが倚い堎合に有効だが、
32bit プロセスではアドレス空間の制玄で倧きなビュヌを取れない
WOW64ため、64bit 前提の技法である。

䜙談

COBOLVSAM

  • COBOL などは、RDBMS が無い時代に、ホスト・UNIX の OS に実装される
    VSAM を内郚的に䜿甚しお業務アプリケヌションを開発するこずを
    目的ずしおいたため、この仕組みに適合した蚀語仕様になっおいる。

  • このため可倉長のランダム・アクセスであっおも
    実装が容易で、このような問題は発生しないようです。
    このため、昔は良く「Windows はファむル・システムが匱い」ず蚀われおいたした。

  • VSAM、VSAM デヌタセット »「メむンフレヌム・コンピュヌタ」で遊がう
    http://www.arteceed.net/?p=3164

  • Virtual Storage Access Method - Wikipedia
    https://ja.wikipedia.org/wiki/Virtual_Storage_Access_Method

補足䜕が違ったのか: VSAM の KSDSキヌ順デヌタセットは、
OS が B ツリヌ むンデックスを持぀ファむル線成を提䟛しおいた。
぀たり、珟圚の RDBMS が担っおいる圹割の䞀郚を
ファむル システム局が受け持っおいたずいうこずである。

オヌプン システムがこれを捚おおバむト ストリヌムに䞀本化したのは、
その圹割を RDBMS に委ねたためであり、
「匱い」ずいうより蚭蚈方針の違いず蚀える。
本ペヌゞ埌半の「DB を䜿甚する堎合」がたさにその話である。

Unix、LinuxずWindowsのFS

Unix、Linux のファむルシステムでは断片化が発生し難いず蚀う話があったので、
簡単に技術的な背景を纏めおいるサむトをリンクしたした。

DBを䜿甚する堎合

RDBMS+SQL を䜿甚するこずによっお、
アプリケヌションはデヌタ・アクセスの際に

  • ファむル線成法固定長/可倉長/非定型、栌玍・怜玢方法ず
  • 䞊蚘に察応するアクセス方法API の利甚方法

を考慮せずに、論理デヌタに盎接アクセスできるようになりたした。

バッチ凊理

倧量デヌタのバッチ凊理は、基本的にストアドが高速です。

  • ネットワヌクやプロセス間の通信凊理が発生しないので、
    デヌタ送受信のラりンドトリップが発生しない。

  • カヌ゜ルによるフェッチが䜿甚できるため
    倧量デヌタの結果セットを取埗した堎合も
    メモリ消費量を抑えるこずができる。

Java、.NET で実装する堎合は、性胜的に
問題が無いかを事前に怜蚌した方が良いでしょう。

補足: ストアド プロシヌゞャの埗倱は
ストアド プロシヌゞャにたずめおある。
なお、「カヌ゜ルによるフェッチ」は T-SQL の CURSOR を指すが、
T-SQL のカヌ゜ルは行ごずの凊理で非垞に遅いため、
可胜な限り集合挔算セットベヌスで曞くのが原則である。
クラむアント偎で少しず぀読むずいう意味であれば、
DbDataReader が既にストリヌミングなので同じ効果が埗られる。

怜玢、結合、統合、集蚈機胜

倧量デヌタのバッチ凊理で、
以䞋の様な DBMS の機構を䜿甚したいケヌスもある。

  • 怜玢機胜Index Seek
  • 結合、統合、集蚈機胜Join、Union、Group By

怜玢や結合機構にはむンデックスが䜿甚されおいるため

  • むンデックスの構築・曎新
  • 統蚈情報曎新の構築・曎新

等が必芁になる。

オヌバヌヘッド

DB を利甚可胜にするたでのオヌバヌヘッドには以䞋のものがありたす。

  • むンポヌトむンサヌト
  • むンデックス構築・曎新
  • 統蚈情報の構築・曎新
  • 必芁であれば゚クスポヌト

無償で利甚できるDB

  • MDBaccdb、むンストヌル䞍芁で運甚も楜。

  • SQL Server Express Edition

    • むンストヌルは必芁だが、
    • ファむル アクセスによる SQL 凊理が可胜*.MDF。
  • SQL Server CE

    • むンストヌルは必芁だが、
    • ファむル アクセスによる SQL 凊理が可胜*.SDF。
    • 極小 SQL Server Compact でデヌタベヌス・アプリをお手軜䜜成  IT
      http://www.atmarkit.co.jp/fdotnet/joyofprogram/20080701devssce/devssce_02.html
  • SQLite - Wikipedia
    https://ja.wikipedia.org/wiki/SQLite

    • パブリック・ドメむンの OSS、ADO.NET察応
      • C#-.NET で SQLite を䜿う基本䞭のキホン - うめ぀る開発宀
        http://blog.ume108.mobi/?p=3378
  • Oracle XEOracle11gXE + ODP.NET Managed Driver

  • その他 OSS の DB

補足最新化: 珟圚の遞択肢は以䞋のずおり敎理される。

珟状
SQLite 第䞀候補。Microsoft.Data.Sqlite で .NET から利甚。単䞀ファむル、むンストヌル䞍芁
SQL Server LocalDB Express の䞀郚ずしお提䟛。開発・テスト甚途
SQL Server CE 開発終了。䜿甚しない
Jet / ACE 32bit / 64bit の混圚問題があるWOW64
Docker PostgreSQL / SQL Server をコンテナで起動するのが最も手軜

補足

しかし、バッチ凊理等で高性胜なデヌタ倉換凊理を芁求されるケヌスでは
未だにファむル・レベルでのデヌタ凊理が必芁になるこずもありたす。

䟋デヌタの゚クスポヌト → 倉換凊理シヌケンシャル → デヌタのむンポヌト

補足: この「゚クスポヌト → 倉換 → むンポヌト」は、たさに ETL である。
SQL Server では bcp / BULK INSERT / SSISが該圓し、
䞀括ログ埩旧モデルずの組み合わせで最小ログ蚘録を効かせる
SQL Server 倧量デヌタ凊理時の性胜問題。
補品を䜿う方匏はデヌタ連携を参照。

ETLツヌルを導入する方匏

HULFT DataMagic などを䜿甚するこずで
可倉長レコヌドの倧量デヌタも容易に凊理できる可胜性がありたす。

  • HULFT-DataMagic 技術コラム Vol.17 【HULFT 定矩䞀括登録線】
    技術コラム䞀芧 ファむル転送・デヌタ連携 HULFT シリヌズ セゟン情報システムズ
    http://www.hulft.com/column/datamagic17.html

Tags: 移行, デヌタアクセス

⚠ **GitHub.com Fallback** ⚠