Skip to content

大ファイル処理

大きな JSON ファイル(ログ、設定、データエクスポートなど)をメモリに直接ロードすると、メモリ不足になる可能性があります。json ライブラリは複数の効率的な処理方法を提供しています。

WARNING

ForeachFileForeachFileChunked はイテレーション前にファイル全体をメモリにロードします。「チャンク」動作はメモリ内データのイテレーション方法にのみ影響し、ファイルの読み込み方法には影響しません。メモリを厳密に制御する必要がある超大ファイルの処理には、JSONL 形式と組み合わせて NDJSONProcessor を使用するか、StreamIterator を使用してください。

選択肢

方法適用シナリオメモリ使用量
Processor.ForeachFile構造化イテレーションによるファイル処理ファイル全体をロードし、1 件ずつイテレーション
Processor.ForeachFileChunkedバッチチャンクイテレーション処理ファイル全体をロードし、チャンクでイテレーション
NDJSONProcessorJSONL ファイルの行単位処理メモリ制御可能、真のストリーム処理

統一 API:Processor

設定オプション

大ファイル処理の設定は Config に統合されています:

go
type Config struct {
    // ... その他の設定 ...

    // 大ファイル処理設定
    ChunkSize       int64 // チャンクサイズ(デフォルト 1MB)
    MaxMemory       int64 // 最大メモリ使用量(デフォルト 100MB)
    BufferSize      int   // 読み取りバッファサイズ(デフォルト 64KB)
    SamplingEnabled bool  // サンプリングを有効にするか(デフォルト true)
    SampleSize      int   // サンプリング数(デフォルト 1000)
}

基本的な使用方法

go
package main

import (
    "log"
    "github.com/cybergodev/json"
)

func main() {
    // Processor の作成(デフォルト設定を使用)
    processor, err := json.New()
    if err != nil {
        log.Fatal(err)
    }
    defer processor.Close()

    // 方法 1:1 件ずつ処理(推奨)
    count := 0
    err = processor.ForeachFile("large-data.json", func(key any, item *json.IterableValue) error {
        count++

        // IterableValue の便利なメソッドでフィールドにアクセス
        id := item.GetInt("id")
        name := item.GetString("name")
        email := item.GetString("email")

        // パスを使用してネストされたプロパティにアクセス
        city := item.GetString("profile.city")
        interests := item.GetArray("profile.interests")

        if count%10000 == 0 {
            log.Printf("処理済み %d件、サンプル: id=%d name=%s email=%s city=%s 興味数=%d",
                count, id, name, email, city, len(interests))
        }
        return nil
    })

    if err != nil {
        log.Fatal(err)
    }
    log.Printf("処理完了、合計 %d 件", count)
}

バッチ処理

go
// 方法 2:バッチ処理(データベースへの一括書き込みに適しています)
err := processor.ForeachFileChunked("large-data.json", 1000, func(chunk []*json.IterableValue) error {
    log.Printf("バッチ処理:%d 件", len(chunk))

    // データベースへの一括書き込み
    for _, item := range chunk {
        id := item.GetInt("id")
        name := item.GetString("name")
        // ... データ処理
    }
    return nil
})

中断制御付き

go
// 方法 3:中断制御付き(特定のデータを見つけたら停止)
// item.Break() を返すとイテレーションを停止、nil を返すとイテレーションを継続
err := processor.ForeachFile("large-data.json", func(key any, item *json.IterableValue) error {
    id := item.GetInt("id")

    if id == targetID {
        // ターゲットを発見、イテレーションを停止
        fmt.Printf("ターゲット発見: ID=%d, Name=%s\n", id, item.GetString("name"))
        return item.Break() // イテレーションを停止(中断シグナルを返す)
    }

    return nil // イテレーションを継続
})

オブジェクトファイルの処理

go
// 方法 4:JSON オブジェクトファイルの処理(キー・バリュー構造)
// ファイル形式:{"user1": {...}, "user2": {...}, ...}
err := processor.ForeachFile("config-map.json", func(key any, item *json.IterableValue) error {
    fmt.Printf("Key: %s, Name: %s\n", key, item.GetString("name"))
    return nil
})

カスタム設定

go
// カスタム大ファイル処理設定
cfg := json.DefaultConfig()
cfg.ChunkSize = 10 * 1024 * 1024   // 10MB チャンク
cfg.MaxMemory = 500 * 1024 * 1024  // 500MB メモリ制限
cfg.BufferSize = 128 * 1024        // 128KB バッファ

processor, err := json.New(cfg)
if err != nil {
    panic(err)
}
defer processor.Close()

IterableValue の便利なメソッド

ForeachFile* シリーズのメソッドは IterableValue インターフェースを提供し、便利なデータアクセスをサポートします:

メソッド説明
Get(path)値の取得item.Get("field")
GetString(path)文字列の取得item.GetString("name")
GetInt(path)整数の取得item.GetInt("id")
GetFloat64(path)浮動小数点数の取得item.GetFloat64("score")
GetBool(path)真偽値の取得item.GetBool("active")
GetArray(path)配列の取得item.GetArray("tags")
GetObject(path)オブジェクトの取得item.GetObject("profile")
Exists(path)フィールドの存在確認item.Exists("email")
IsNull(path)null かどうかの確認item.IsNull("deleted_at")
IsEmpty(path)空かどうかの確認item.IsEmpty("notes")
Break()中断シグナルの返却return item.Break()

パスナビゲーションをサポート

go
city := item.GetString("profile.address.city")      // ネストされたオブジェクト
firstTag := item.GetString("tags[0]")               // 配列インデックス
lastTag := item.GetString("tags[-1]")               // 負のインデックス(最後の要素)
nested := item.GetString("data.items[0].name")      // 複雑なパス

ストリーム処理設定

Config を使用してストリーム処理パラメータを設定します:

go
cfg := json.DefaultConfig()

// 大ファイル処理設定
cfg.ChunkSize = 10 * 1024 * 1024   // 10MB チャンク
cfg.MaxMemory = 500 * 1024 * 1024  // 500MB メモリ制限
cfg.BufferSize = 128 * 1024        // 128KB バッファ

processor, err := json.New(cfg)
if err != nil {
    panic(err)
}
defer processor.Close()

StreamLinesInto ジェネリック関数の使用

go
type User struct {
    Name string `json:"name"`
}

file, _ := os.Open("users.jsonl")
defer file.Close()

_, err := json.StreamLinesInto[User](file, func(lineNum int, user User) error {
    fmt.Printf("処理: %s\n", user.Name)
    return nil
})

並列処理

並列処理が可能なタスクには、複数の goroutine を使用できます:

go
package main

import (
    "sync"
    "github.com/cybergodev/json"
)

func main() {
    processor, err := json.New()
    if err != nil {
        panic(err)
    }
    defer processor.Close()

    // worker プールを使用
    workers := 4
    items := make(chan any, 100)
    var wg sync.WaitGroup

    // worker を起動
    for i := 0; i < workers; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            for item := range items {
                // item を処理
                _ = item
            }
        }(i)
    }

    // ストリーム読み込みして配信
    processor.ForeachFile("large-data.json", func(key any, item *json.IterableValue) error {
        items <- item.Get("")
        return nil
    })

    close(items)
    wg.Wait()
}

パフォーマンス最適化のヒント

メモリ制御

go
// 利用可能なメモリに基づいて設定
cfg := json.DefaultConfig()
cfg.MaxMemory = 500 * 1024 * 1024 // 500MB
cfg.ChunkSize = 10 * 1024 * 1024  // 10MB

processor, err := json.New(cfg)
if err != nil {
    panic(err)
}
defer processor.Close()

ベストプラクティス

  1. ファイルサイズの事前確認:処理前にファイルサイズを確認し、適切な戦略を選択
  2. メモリ制限の設定:OOM を防ぐために MaxMemory を使用
  3. バッチコミット:一定数蓄積してからデータベースに一括書き込み
  4. エラー処理JSONLContinueOnErr を実装するか、失敗したエントリを記録
  5. 進捗の監視:定期的に処理の進捗を出力

選択ガイド

ファイルサイズ推奨方法
< 10MB直接ロードjson.ParseAny + Get
10-100MBProcessor.ForeachFile1 件ずつ処理
100MB-1GBProcessor.ForeachFileChunkedチャンクイテレーション処理
> 1GBNDJSONProcessor / JSONL 形式真のストリーム処理、メモリ制御可能

API リファレンス

このセクションでは、大ファイル処理 API の関数シグネチャとパラメータ表を要約し、すぐに参照できるようにします。

Processor メソッド

ForeachFile

シグネチャ:func (p *Processor) ForeachFile(filePath string, fn func(key any, item *IterableValue) error, cfg ...Config) error

大規模ファイル内の JSON 配列要素を 1 件ずつ処理します。基本的な使用方法中断制御付きを参照してください。

パラメータ

名前説明
filePathstringJSON ファイルパス
fnfunc(key any, item *IterableValue) error処理コールバック

コールバック戻り値

戻り値説明
nil次の要素の処理を継続
item.Break()イテレーションを停止、エラーは返さない
その他の errorイテレーションを停止し、エラーを返す

ForeachFileChunked

シグネチャ:func (p *Processor) ForeachFileChunked(filePath string, chunkSize int, fn func(chunk []*IterableValue) error, cfg ...Config) (err error)

大規模ファイルをチャンク単位でバッチ処理します。バッチ処理を参照してください。

パラメータ

名前説明
filePathstringJSON ファイルパス
chunkSizeint1 バッチあたりの要素数
fnfunc(chunk []*IterableValue) errorバッチ処理コールバック

ForeachFileWithPath

シグネチャ:func (p *Processor) ForeachFileWithPath(filePath, path string, fn func(key any, item *IterableValue) error, cfg ...Config) error

ファイル内の指定したパスの JSON 配列またはオブジェクトを処理します。

パラメータ

名前説明
filePathstringJSON ファイルパス
pathstringJSON パス式
fnfunc(key any, item *IterableValue) error処理コールバック
go
// ファイル内の users 配列の各要素を処理
err := p.ForeachFileWithPath("data.json", "users", func(key any, item *json.IterableValue) error {
    fmt.Printf("Name: %s\n", item.GetString("name"))
    return nil
})

ForeachFileNested

シグネチャ:func (p *Processor) ForeachFileNested(filePath string, fn func(key any, item *IterableValue) error, cfg ...Config) error

ファイル内のすべてのネストされた JSON 構造を再帰的に走査します。

go
// すべてのネストされた要素を再帰的に走査
err := p.ForeachFileNested("data.json", func(key any, item *json.IterableValue) error {
    fmt.Printf("Key: %v, Type: %T\n", key, item.GetData())
    return nil
})

パッケージレベル関数

Processor メソッドとは別に、以下の関数は Processor インスタンスを作成せずに直接呼び出せます。これらは内部でグローバルプロセッサを使用します。

ForeachFile(パッケージレベル関数)

シグネチャ:func ForeachFile(filePath string, fn func(key any, item *IterableValue) error, cfg ...Config) error

ファイルから JSON を読み込み、イテレーションします。

go
err := json.ForeachFile("data.json", func(key any, item *json.IterableValue) error {
    fmt.Printf("[%v] %v\n", key, item.GetData())
    return nil
})

ForeachFileWithPath(パッケージレベル関数)

シグネチャ:func ForeachFileWithPath(filePath, path string, fn func(key any, item *IterableValue) error, cfg ...Config) error

ファイルから JSON を読み込み、指定パスでイテレーションします。

go
err := json.ForeachFileWithPath("data.json", "users", func(key any, item *json.IterableValue) error {
    name := item.GetString("name")
    fmt.Printf("ユーザー: %s\n", name)
    return nil
})

ForeachFileChunked(パッケージレベル関数)

シグネチャ:func ForeachFileChunked(filePath string, chunkSize int, fn func(chunk []*IterableValue) error, cfg ...Config) error

ファイル内の JSON 配列をチャンク単位でイテレーションします。

go
err := json.ForeachFileChunked("large_data.json", 100, func(chunk []*json.IterableValue) error {
    for _, item := range chunk {
        processItem(item)
    }
    return nil
})

ForeachFileNested(パッケージレベル関数)

シグネチャ:func ForeachFileNested(filePath string, fn func(key any, item *IterableValue) error, cfg ...Config) error

ファイルから JSON を読み込み、すべてのネストされた構造を再帰的にイテレーションします。

go
err := json.ForeachFileNested("config.json", func(key any, item *json.IterableValue) error {
    fmt.Printf("パス: %v, 型: %T\n", key, item.GetData())
    return nil
})

関連

次のステップ