🔑

Redis キー最適化ガイド

2024/10/16に公開

はじめに

Redis 公式ドキュメントによると、キーのサイズは最大 512MB まで対応しています。しかし、1024 バイト以上の長いキーは Redis のメモリ使用効率やキー検索のパフォーマンスに悪影響を及ぼす可能性があります。一方で、短すぎるキーは可読性を損なう恐れがあります。例えば、user:1000:followers のような形式は適切ですが、u1000flw のような短縮形は推奨されません。

しかし、キーをユニークにするために複数のパラメータを組み合わせると、結果として長いキーになってしまうケースがあります。ここでは、ユニーク性を保ちながらキーを短縮する方法として、ハッシュ化の手法をご紹介します。

ハッシュ化とは

ハッシュ化とは、元のデータを固定長の不規則な文字列(ハッシュ値)に変換する処理のことです。暗号化とは異なり、ハッシュ化では元のデータを復元することはできません。ハッシュ関数には様々な種類があり、選定する際は以下の点を考慮することが重要です:

  1. ビット長:出力されるハッシュ値の長さ
  2. 衝突耐性:異なる入力から同じハッシュ値が生成される可能性の低さ
  3. パフォーマンス:ハッシュ値の生成に要する処理速度
  4. セキュリティ:暗号学的安全性の有無(cryptographic vs non-cryptographic)

一般的に、ビット長が長くなるほど衝突耐性は向上しますが、同時に処理速度が低下する傾向があります。 また、非暗号学的ハッシュ関数は、暗号学的ハッシュ関数と比較して、一般的に処理速度が速く、リソース消費が少ない傾向があります。

Redis キーの場合、異なるパラメータから同じハッシュ値が生成されると、意図しないデータが保存または取得される可能性があります。このため、Redis キーのハッシュ化には十分な衝突耐性を持つハッシュ関数の選択が重要です。一方で、Redis キーのハッシュ化においては高いパフォーマンスが求められる反面、暗号学的な安全性は通常不要です。そのため、非暗号学的ハッシュ関数が Redis キーの用途に適しています。

代表的な非暗号学的ハッシュ関数には、MurmurHash3xxHashCityHashFNV(Fowler-Noll-Vo)などがあります。

ハッシュ化の実装方法

Go 言語でのハッシュ化関数の実装とその使い方は以下の通りです。

package main

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"hash"
	"sort"

	"github.com/cespare/xxhash/v2"
)

func main() {
	h := xxhash.New()
	fmt.Println(HashKeys(h, "Hello", "世界")) // 出力: 98d938f71d70aaea
	fmt.Println(HashKeys(h, "世界", "Hello")) // 出力: 98d938f71d70aaea
}

func HashKeys(h hash.Hash, keys ...string) string {
	// パラメータをソートすることで、順序に関係なく
	// 同じ値の組み合わせであれば同じハッシュ値を生成できます
	sort.Slice(keys, func(i, j int) bool {
		return keys[i] < keys[j]
	})
	// 新しいハッシュ計算を開始する前にハッシュオブジェクトをリセット
	h.Reset()
	for _, k := range keys {
		fmt.Fprint(h, k)
	}
	return fmt.Sprintf("%x", h.Sum(nil))
}

HashKeys関数の第一引数はhash.Hashインターフェースを使用しているため、様々なハッシュライブラリに対応できます。上記の例では xxHash ライブラリを使用しています。

ソート処理は任意ですが、パラメータの順序に関係なく同じ値の組み合わせで同じハッシュ値を生成したい場合、ハッシュ化前にソート処理を行うことをお勧めします。

注意事項

  • Redis キーを生成するためのパラメータがハッシュ化されるため、Redis キーの可読性が低下します。対策としては、一意の識別子とハッシュ化の出力を組み合わせることです。例: articles_ids:98d938f71d70aaea
  • ハッシュ化のメリットを得るためには、元のパラメータの長さの合計が、少なくともハッシュ値の長さを上回る必要があります。例えば、本記事で紹介した xxHash ライブラリは、xxHash アルゴリズムの 64 ビットバリアントを使用しています。このため、ハッシュ値の長さは 16 バイト(64 ビットを 16 進数表記にすると 16 文字になります)となります。

おわりに

Redis キーが長くなるほど、メモリ使用効率やキー検索のパフォーマンスに影響を与えます。長いキーをハッシュ化することで、これらの問題を軽減できます。一方で、ハッシュ化によって CPU 負荷が増加する可能性があることにも注意が必要です。

ハッシュ関数のアルゴリズムを選定する際は、衝突耐性とパフォーマンスのバランスを考慮する必要があります。 実際の環境でベンチマークを行い、キーのハッシュ化が Redis のパフォーマンス向上につながるかどうかを確認することをお勧めします。 システムの特性や要件によって最適な方法が異なる場合があるため、慎重に評価することが重要です。

Discussion