-
Notifications
You must be signed in to change notification settings - Fork 0
MS_LargeDataProcessing1
- 戻る(大量データの処理方式)
- 大量データの処理方式1
- 大量データの処理方式2 / 大量データの処理方式3
どの 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 では、
各ファイル(データセット) 内の、レコードの属性を定義する。
(固定長/可変長/非定型、固定長の場合のレコード長、格納・検索方法など) -
なお、いわゆるオープンシステムではバイト ストリームが基本であり、
ファイル編成法は存在しない
(詳細は「レガシーシステムとオープンシステムの比較」を参照)。
-
と、バイト・ストリームによる
-
SAM(順次アクセス方式)
-
DAM(直接アクセス方式)
程度はサポートしています。
本ページでは、
-
これら(ファイルシステムのバイト・ストリーム)を
使用しての、大量データの処理方式を考えます。 -
なお、メモリに保持可能なデータ量の場合の設計ディシジョンについては考慮しません。
この場合、メモリの大量消費による同時実行性の低下や CPU 時間が問題となります。- これについては、大量データの処理方式2を
参考にして下さい。
- これについては、大量データの処理方式2を
-
SAM(順次アクセス方式) - シーケンシャル・アクセスを
実現するにあたっては、特に問題となる点はありません。 -
READ や WRITE の API によって自動的に
ファイル・ポインタ(読み書きの開始点)が移動されます。
DAM(直接アクセス方式) - ランダム・アクセスを実現するにあたっては、
特にファイル・ポインタ(読み書きの開始点)の制御が問題となります。
固定長レコードの場合、
-
ファイル・ポインタ(読み書きの開始点)の制御が容易です。
-
Windows では、
SetFilePointer関数を使用して、
ファイル・ポインタ(読み書きの開始点)を、全て自分で制御して
ランダム・アクセスを実現する必要があります。- Win32 API でのファイルアクセス
http://chokuto.ifdef.jp/urawaza/fileaccess.html
- Win32 API でのファイルアクセス
-
注意点
-
4GB(32 ビット)以上のファイルを扱う時は、
上位 32 ビットと、下位 32 ビットを 64 ビット データ型に
変換する必要があることです。 -
このため、レガシー VB では
Seekステートメントで 2GB 以上の
ファイル・ポインタを指定することができないという制約があります。
4GB(32 ビット)でないのは、signed の型を使用しているためと思われます。- VBA の 2 GB のファイル制限を超えて移動する方法
https://learn.microsoft.com/ja-jp/office/troubleshoot/access/
- VBA の 2 GB のファイル制限を超えて移動する方法
-
この制約は、Java にもあるようです。
- RandomAccessFile で 2GB 以上 - Java の Q&A【OKWave】
http://okwave.jp/qa/q7022891.html
- RandomAccessFile で 2GB 以上 - Java の Q&A【OKWave】
-
.NET では、
FileStream.Seekメソッドの引数が
64bit(long 型) 対応されたため、この問題は発生しなくなったようです。- FileStream.Seek メソッド (System.IO)
https://learn.microsoft.com/ja-jp/dotnet/api/system.io.filestream.seek
- FileStream.Seek メソッド (System.IO)
-
余談になりますが、.NET で固定長レコードを処理する場合に便利な
C 構造体によるバッファ型抜きを .NET 構造体と Marshal クラスを使用して
実現できます。
- Marshal クラス (System.Runtime.InteropServices)
https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.marshal
なお、このバッファ型抜き処理は、Open棟梁の共通部品に実装されています。
- OpenTouryo CustomMarshaler.cs
https://github.com/OpenTouryoProject/OpenTouryo/blob/develop/root/programs/CS/Frameworks/Infrastructure/Public/Util/CustomMarshaler.cs
補足(最新化:
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 時間がかかってしまう。
-
全てのレコードを読み取ることが困難な場合でも、
レコード番号などがデータ中に含まれるようなら
ファイル・ポインタ(読み書きの開始点)を推測して、
効率良くレコードにアクセスする事を試みるのは可能。-
上手いアルゴリズムを考えだすのが難しい。
-
レコード番号などがデータ中に含まれる必要がある
カスタム仕様であるため、汎用品は存在しない。
-
-
移行メモ(補足): 元ページの「n番目のレコードのファイル・ポインタが
できないため」は文意が欠けているため、
「を算出できないため」と補って掲載した。
補足(インデックス ファイルという定石): 「開始位置を配列に記憶する」方式は、
実質的に自前でインデックスを作ることに他ならない。
これを永続化して別ファイル(インデックス ファイル)に持つのが定石で、
初回のフル スキャンを 1 度だけに抑えられる。
メインフレームの ISAM/VSAM や、RDBMS の
SQL Server のインデックスと発想は同じである。
-
CSV の場合
-
可変長レコードの主要なフォーマットには CSV があります。
-
CSV の問題は、CSV のフォーマットの仕様によってパーサが複雑になる事です。
-
Comma-Separated Values - Wikipedia
https://ja.wikipedia.org/wiki/Comma-Separated_Values -
CSV ファイル フォーマットの解説:CodeZine
http://codezine.jp/article/detail/2364
-
-
例えば、データ内に改行コードなどが含まれる場合は、
前述のファイル・ポインタ(読み書きの開始点)を推測して、
レコードにアクセスする等の方式の採用は困難です。- ただし、1 フィールドの最大データ長が決まっていれば、可能と言えば可能です。
-
このため、以下のどちらの方式を採用したとしても、
- 全てのデータを読み取っておき、全てのレコードの開始位置を配列などに記憶しておく。
- ファイル・ポインタ(読み書きの開始点)を推測して、効率良くレコードにアクセスする。
- ※
FileStream.Seek、Positionと、StreamReader.ReadLineを併用する。
-
結局の所、CSV パーサーの高速化が必要になるようです。
- 巨大な CSV(可変長)を効率よく読込む方法は? - C・C++ の Q&A【OKWave】
http://okwave.jp/qa/q4410652.html
- 巨大な CSV(可変長)を効率よく読込む方法は? - C・C++ の Q&A【OKWave】
-
.NET には、VB 2005 用の機能として、CSV パーサーが準備されているようです。
しかし、API の仕様からも、ランダム・アクセスはサポートされていないようです。- CSV ファイルを読み込むには?[2.0 のみ、C#、VB] - @IT
http://www.atmarkit.co.jp/fdotnet/dotnettips/487csvparser/csvparser.html - TextFieldParser クラス (Microsoft.VisualBasic.FileIO)
https://learn.microsoft.com/ja-jp/dotnet/api/microsoft.visualbasic.fileio.textfieldparser
- CSV ファイルを読み込むには?[2.0 のみ、C#、VB] - @IT
-
移行メモ(誤字): 元ページの「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 のオーバヘッドが非常に大きくなってしまいます。
補足(マージ出力という定石): 全件を書き出し直すこと自体は避けられないが、
**入力を読みながら出力へ書き出す(ストリーミング)**形にすれば、
メモリ消費は一定に保てる。
- 入力ファイルを先頭から順に読む
- 更新対象なら差分を適用して出力、そうでなければそのまま出力
- 完了後、出力ファイルを入力ファイルと差し替える
これはメインフレームのマスタ更新(旧マスタ + トランザクション → 新マスタ)
そのものであり、現在も大量バッチの基本形である。
差し替えを原子的に行うにはFile.Replaceを使う。
メモリマップト ファイル(MMF)を使用すれば、
- マップ ビューのマップ・アンマップによるランダム・アクセス
- マップ ビューの範囲でオンメモリ処理(I/O 回数の軽減)
が可能になり、効率的に参照処理を
実装できるようになる可能性があります。
補足: .NET では
System.IO.MemoryMappedFiles.MemoryMappedFileで
利用できる。
大きなファイルへのランダム アクセスが多い場合に有効だが、
32bit プロセスではアドレス空間の制約で大きなビューを取れない
(WOW64)ため、64bit 前提の技法である。
-
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 のファイルシステムでは断片化が発生し難いと言う話があったので、
簡単に技術的な背景を纏めているサイトをリンクしました。
- デフラグ(ファイルシステム)
RDBMS+SQL を使用することによって、
アプリケーションはデータ・アクセスの際に
- ファイル編成法(固定長/可変長/非定型、格納・検索方法)と
- 上記に対応するアクセス方法(API の利用方法)
を考慮せずに、論理データに直接アクセスできるようになりました。
大量データのバッチ処理は、基本的にストアドが高速です。
-
ネットワークやプロセス間の通信処理が発生しないので、
データ送受信のラウンドトリップが発生しない。 -
カーソルによるフェッチが使用できるため
大量データの結果セットを取得した場合も
メモリ消費量を抑えることができる。
Java、.NET で実装する場合は、性能的に
問題が無いかを事前に検証した方が良いでしょう。
- .NETでバッチは書けるか?(
MS_DotNetBatch.md)
補足: ストアド プロシージャの得失は
ストアド プロシージャにまとめてある。
なお、「カーソルによるフェッチ」は T-SQL のCURSORを指すが、
T-SQL のカーソルは行ごとの処理で非常に遅いため、
可能な限り集合演算(セットベース)で書くのが原則である。
クライアント側で少しずつ読むという意味であれば、
DbDataReaderが既にストリーミングなので同じ効果が得られる。
大量データのバッチ処理で、
以下の様な DBMS の機構を使用したいケースもある。
- 検索機能(Index Seek)
- 結合、統合、集計機能(Join、Union、Group By)
検索や結合機構にはインデックスが使用されているため
- インデックスの構築・更新
- 統計情報更新の構築・更新
等が必要になる。
DB を利用可能にするまでのオーバーヘッドには以下のものがあります。
- インポート(インサート)
- インデックス構築・更新
- 統計情報の構築・更新
- 必要であればエクスポート
-
MDB(accdb)、インストール不要で運用も楽。
- MS-Access を使わずに Jet Database Engine を使用する方法
http://www.f3.dion.ne.jp/~element/msaccess/AcTipsGnrHowToUseJetWithoutMSAccess.html
- MS-Access を使わずに Jet Database Engine を使用する方法
-
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
- C#-.NET で SQLite を使う基本中のキホン - うめつる開発室
- パブリック・ドメインの OSS、ADO.NET対応
-
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 大量データ処理時の性能問題)。
製品を使う方式はデータ連携を参照。
HULFT DataMagic などを使用することで
可変長レコードの大量データも容易に処理できる可能性があります。
- HULFT-DataMagic 技術コラム Vol.17 【HULFT 定義一括登録編】
技術コラム一覧 ファイル転送・データ連携 HULFT シリーズ セゾン情報システムズ
http://www.hulft.com/column/datamagic17.html
Tags: 移行, データアクセス
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。