BigCache

Goライブラリキャッシュ高スループット低GCプレッシャーメモリ効率

GitHub概要

allegro/bigcache

Efficient cache for gigabytes of data written in Go.

スター7,982
ウォッチ109
フォーク604
作成日:2016年3月23日
言語:Go
ライセンス:Apache License 2.0

トピックス

cachecachinggolang-libraryhacktoberfestperformance

スター履歴

allegro/bigcache Star History
データ取得日時: 2025/10/22 08:06

ライブラリ

BigCache

概要

BigCacheは、数百万エントリに対応するGoの高スループットキャッシュライブラリです。ガベージコレクション(GC)プレッシャーを最小化し、大規模データセットでの効率的なメモリ使用を実現するよう設計されています。

詳細

BigCache(ビッグキャッシュ)は、Goのガベージコレクション最適化を活用して、ギガバイト単位のデータを高性能で処理できるインメモリキャッシュです。Goの1.5バージョンで導入された最適化(issue-9477)を利用し、キーと値にポインタを持たないmap[uint64]uint32を使用することで、GCがマップの内容をスキャンしなくなります。エントリは大きなバイトスライスに保存され、GCは各シャードにつき1つのポインタのみを認識するため、数百万のエントリでもGCパフォーマンスに影響を与えません。シャーディングによる高い並行性を提供し、各シャードが独立したミューテックスを持つことで、異なるシャードでの操作を並列実行できます。動的サイジング、設定可能な退避ポリシー、コールバック、統計情報をサポートし、大容量データセットの効率的な管理を実現します。

メリット・デメリット

メリット

  • 低GCプレッシャー: 2000万エントリでもGCポーズが1.5ms以下
  • 高スループット: 300万アイテム/秒のアクセス性能
  • メモリ効率: 5-7%の低メモリオーバーヘッド
  • 高並行性: シャーディングによる並列アクセス最適化
  • 動的サイジング: 事前のキャッシュサイズ決定不要
  • 設定可能: 退避ポリシーとコールバック機能
  • 統計情報: ヒット/ミス率と衝突監視機能

デメリット

  • 衝突未対応: ハッシュ衝突時に新しいアイテムが既存値を上書き
  • Go専用: Go言語以外での使用不可
  • 複雑性: 単純な用途には機能が過剰
  • メモリ制約: 全データをメモリに保持する必要
  • 永続化なし: アプリケーション再起動時にデータ消失

主要リンク

書き方の例

基本的な使用方法

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/allegro/bigcache"
)

func main() {
    // デフォルト設定で10分間キャッシュ
    cache, _ := bigcache.New(context.Background(), bigcache.DefaultConfig(10*time.Minute))
    defer cache.Close()

    // データを設定
    cache.Set("my-unique-key", []byte("Hello, BigCache!"))

    // データを取得
    entry, _ := cache.Get("my-unique-key")
    fmt.Println(string(entry)) // "Hello, BigCache!"
}

カスタム設定

package main

import (
    "context"
    "log"
    "time"

    "github.com/allegro/bigcache"
)

func main() {
    config := bigcache.Config{
        Shards:             1024,        // シャード数(2の倍数)
        LifeWindow:         10 * time.Minute, // アイテムの生存期間
        CleanWindow:        5 * time.Minute,  // クリーンアップ間隔
        MaxEntriesInWindow: 1000 * 10 * 60,   // ウィンドウ内の最大エントリ数
        MaxEntrySize:       500,         // エントリの最大サイズ(バイト)
        HardMaxCacheSize:   8192,        // 最大キャッシュサイズ(MB)
        StatsEnabled:       true,        // 統計情報を有効化
        Verbose:            true,        // 詳細ログを有効化
    }

    cache, err := bigcache.New(context.Background(), config)
    if err != nil {
        log.Fatal(err)
    }
    defer cache.Close()

    // 使用例
    cache.Set("user:1", []byte(`{"name":"Alice","age":30}`))
    cache.Set("user:2", []byte(`{"name":"Bob","age":25}`))
}

コールバック付きキャッシュ

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/allegro/bigcache"
)

func main() {
    config := bigcache.Config{
        Shards:     1024,
        LifeWindow: 1 * time.Minute,
        OnRemove: func(key string, entry []byte) {
            fmt.Printf("Key %s removed: %s\n", key, string(entry))
        },
        OnRemoveWithReason: func(key string, entry []byte, reason bigcache.RemoveReason) {
            fmt.Printf("Key %s removed (reason: %v): %s\n", key, reason, string(entry))
        },
    }

    cache, _ := bigcache.New(context.Background(), config)
    defer cache.Close()

    cache.Set("temp-key", []byte("temporary data"))
    
    // 1分後にコールバックが呼ばれる
    time.Sleep(70 * time.Second)
}

統計情報の取得

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/allegro/bigcache"
)

func main() {
    config := bigcache.DefaultConfig(10 * time.Minute)
    config.StatsEnabled = true

    cache, _ := bigcache.New(context.Background(), config)
    defer cache.Close()

    // いくつかの操作を実行
    cache.Set("key1", []byte("value1"))
    cache.Set("key2", []byte("value2"))
    cache.Get("key1") // ヒット
    cache.Get("key3") // ミス

    // 統計情報を取得
    stats := cache.Stats()
    fmt.Printf("Hits: %d\n", stats.Hits)
    fmt.Printf("Misses: %d\n", stats.Misses)
    fmt.Printf("Collisions: %d\n", stats.Collisions)
    fmt.Printf("DelHits: %d\n", stats.DelHits)
    fmt.Printf("DelMisses: %d\n", stats.DelMisses)
}

イテレータを使用したエントリ処理

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/allegro/bigcache"
)

func main() {
    cache, _ := bigcache.New(context.Background(), bigcache.DefaultConfig(10*time.Minute))
    defer cache.Close()

    // サンプルデータを追加
    for i := 0; i < 10; i++ {
        key := fmt.Sprintf("key:%d", i)
        value := fmt.Sprintf("value:%d", i)
        cache.Set(key, []byte(value))
    }

    // イテレータでエントリを処理
    iterator := cache.Iterator()
    for iterator.SetNext() {
        current, err := iterator.Value()
        if err != nil {
            continue
        }
        
        fmt.Printf("Key: %s, Value: %s\n", 
            current.Key(), string(current.Value()))
    }
}

エラーハンドリングとリセット

package main

import (
    "context"
    "fmt"

    "github.com/allegro/bigcache"
)

func main() {
    cache, _ := bigcache.New(context.Background(), bigcache.DefaultConfig(10*time.Minute))
    defer cache.Close()

    // データを設定
    cache.Set("test-key", []byte("test-value"))

    // データの取得(エラーハンドリング付き)
    value, err := cache.Get("test-key")
    if err != nil {
        if err == bigcache.ErrEntryNotFound {
            fmt.Println("エントリが見つかりません")
        } else {
            fmt.Printf("エラー: %v\n", err)
        }
    } else {
        fmt.Printf("値: %s\n", string(value))
    }

    // 特定のキーを削除
    err = cache.Delete("test-key")
    if err != nil {
        fmt.Printf("削除エラー: %v\n", err)
    }

    // キャッシュ全体をリセット
    err = cache.Reset()
    if err != nil {
        fmt.Printf("リセットエラー: %v\n", err)
    }
}