Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

クエリキャッシュ

最終更新 Markdown で表示Agent セットアップ

クエリキャッシュがオンのとき、Hyperdrive は Worker がデータベースへ送るキャッシュ可能な読み取りクエリを自動でキャッシュします。これによりデータベース負荷が下がり、よく使うクエリでデータベースまでのネットワーク往復を避けられます。クエリキャッシュはデフォルトで有効です。

Hyperdrive がキャッシュするもの

Hyperdrive はデータベースプロトコルを使い、変更クエリ(データベースへ書き込むクエリ)と非変更クエリ(読み取り専用クエリ)を区別します。対象となる読み取り専用クエリのレスポンスはキャッシュし、書き込みはキャッシュしません。

SELECTINSERT の違いを見るだけでなく、Hyperdrive はデータベースのワイヤープロトコルを解析し、変更クエリか非変更クエリかを判断します。

たとえば、ニュースサイトのトップページを組み立てる読み取りクエリはキャッシュされます。

-- Cacheable: uses a parameterized date value instead of CURRENT_DATE
SELECT * FROM articles WHERE DATE(published_time) = $1
ORDER BY published_time DESC LIMIT 50
-- Cacheable: uses a parameterized date value instead of CURDATE()
SELECT * FROM articles WHERE DATE(published_time) = ?
ORDER BY published_time DESC LIMIT 50

変更クエリ(INSERTUPSERTCREATE TABLE を含む)と、PostgreSQL が volatile または stable と定める関数を使うクエリはキャッシュされません。

-- Not cached: mutating queries
INSERT INTO users(id, name, email) VALUES(555, 'Matt', 'hello@example.com');

-- Not cached: LASTVAL() is a volatile function
SELECT LASTVAL(), * FROM articles LIMIT 50;

-- Not cached: NOW() is a stable function
SELECT * FROM events WHERE created_at > NOW() - INTERVAL '1 hour';
-- Not cached: mutating queries
INSERT INTO users(id, name, email) VALUES(555, 'Thomas', 'hello@example.com');

-- Not cached: LAST_INSERT_ID() is a volatile function
SELECT LAST_INSERT_ID(), * FROM articles LIMIT 50;

-- Not cached: NOW() returns a non-deterministic value
SELECT * FROM events WHERE created_at > NOW() - INTERVAL 1 HOUR;

キャッシュ対象外 のよくある PostgreSQL 関数は次のとおりです。

関数 PostgreSQL の volatility 区分 キャッシュ
NOW() STABLE いいえ
CURRENT_TIMESTAMP STABLE いいえ
CURRENT_DATE STABLE いいえ
CURRENT_TIME STABLE いいえ
LOCALTIME STABLE いいえ
LOCALTIMESTAMP STABLE いいえ
TIMEOFDAY() VOLATILE いいえ
RANDOM() VOLATILE いいえ
LASTVAL() VOLATILE いいえ
TXID_CURRENT() STABLE いいえ

PostgreSQL が IMMUTABLE と定める関数(同じ入力に対して戻り値が変わらない関数)だけが、Hyperdrive のキャッシュと互換です。クエリが STABLE または VOLATILE 関数を使う場合は、関数呼び出しをアプリケーションコード側へ移し、結果の値をクエリパラメーターとして渡してください。

デフォルトのキャッシュ設定

Hyperdrive のデフォルトのキャッシュ動作は次のとおりです。

  • max_age = 60 秒(1 分)
  • stale_while_revalidate = 15 秒

max_age は、クエリレスポンスをキャッシュから返す最大寿命を決めます。ほとんど使われないキャッシュレスポンスは、この時間より前に追い出されることがあります。

stale_while_revalidate を使うと、Hyperdrive はキャッシュを再検証しているあいだ、追加の期間だけ古いキャッシュ結果を返し続けます。多くの場合、再検証はすぐに完了します。

max_age の上限は 1 時間です。

書き込み直後の読み取り

Hyperdrive は、アプリケーションがデータベースへ書き込んでも、キャッシュ済みの読み取りクエリ結果をパージしたり無効化したりしません。あとから一致する SELECT は、設定した max_age が切れるまでキャッシュ結果を返すことがあります。Hyperdrive は、バックグラウンドでキャッシュを更新しているあいだ、stale_while_revalidate の期間中も結果を返せます。

書き込みはデータベースへ届きます。Hyperdrive がキャッシュするのは、対象となる読み取りクエリのレスポンスだけです。

そのため、各読み取りにどれだけ新しいデータが必要かに応じて、キャッシュ戦略を選んでください。

  • 短い古さを許容できる読み取りには、クエリキャッシュを使います。 公開コンテンツ、ダッシュボード、検索結果、商品カタログなど、書き込み後の短い遅延が許容できる大量読み取りが向いています。
  • 短い古い期間で足りる場合は、max_agestale_while_revalidate を下げます。 クエリキャッシュは有効のまま、Hyperdrive が古い結果を返せる時間を短くできます。
  • 必ず新しい読み取りが必要な場合は、キャッシュ無効の Hyperdrive 構成を使います。 --caching-disabled で 2 つ目の Hyperdrive 構成を作り、キャッシュありの構成と並べてバインドし、その読み取りはキャッシュ無効のバインディング経由にします。認証、セッション、権限、課金状態、管理者設定、書き込み直後の読み取りなどが該当します。例は キャッシュを無効にする を参照してください。
  • ほとんどの読み取りを新しく保つ必要があるときだけ、クエリキャッシュを全体で無効にします。 キャッシュを無効にしても、Hyperdrive のコネクションプーリングと高速な接続セットアップはそのまま使えます。

オブジェクトリレーショナルマッピング(ORM)ライブラリや認証ライブラリが SQL を握っている場合は、キャッシュありとキャッシュ無効の Hyperdrive バインディング用に、別々のデータベースクライアントを作成します。新しい読み取りが必要なライブラリやモジュールにはキャッシュ無効のクライアントを渡し、設定した古い期間を許容できる読み取りにはキャッシュありのクライアントを使います。

キャッシュを無効にする

キャッシュは Hyperdrive 構成ごとに、Wrangler CLI の --caching-disabled オプションで無効にします。

同じデータベースに対して、キャッシュ無効の Hyperdrive 構成を別に作るには:

npx wrangler hyperdrive create my-database-fresh --connection-string="<DATABASE_CONNECTION_STRING>" --caching-disabled

既存の Hyperdrive 構成でキャッシュをオフにするには:

npx wrangler hyperdrive update <HYPERDRIVE_CONFIG_ID> --caching-disabled

1 つのアプリケーションから複数の Hyperdrive 接続を構成できます。よく使うクエリ向けにキャッシュを有効にした接続と、クエリキャッシュを使わない新しい読み取り向けの 2 つ目の接続です。

同じデータベースに複数の Hyperdrive 構成を使う場合は、構成全体のオリジン接続数を考慮してください。案内は 接続プールを調整する を参照してください。

データベースドライバーを使う例:

index.tsts
export default {
	async fetch(request, env, ctx): Promise<Response> {
		// Create clients inside your handler — not in global scope
		const client = postgres(env.HYPERDRIVE.connectionString);
		// Use the cache-disabled binding for auth, permissions, and reads after writes.
		const clientNoCache = postgres(env.HYPERDRIVE_CACHE_DISABLED.connectionString);
		// ...
	},
} satisfies ExportedHandler<Env>;
index.tsts
export default {
	async fetch(request, env, ctx): Promise<Response> {
		// Create connections inside your handler — not in global scope
		const connection = await createConnection({
			host: env.HYPERDRIVE.host,
			user: env.HYPERDRIVE.user,
			password: env.HYPERDRIVE.password,
			database: env.HYPERDRIVE.database,
			port: env.HYPERDRIVE.port,
		});
		// Use the cache-disabled binding for auth, permissions, and reads after writes.
		const connectionNoCache = await createConnection({
			host: env.HYPERDRIVE_CACHE_DISABLED.host,
			user: env.HYPERDRIVE_CACHE_DISABLED.user,
			password: env.HYPERDRIVE_CACHE_DISABLED.password,
			database: env.HYPERDRIVE_CACHE_DISABLED.database,
			port: env.HYPERDRIVE_CACHE_DISABLED.port,
		});
		// ...
	},
} satisfies ExportedHandler<Env>;

Wrangler の設定は PostgreSQL でも MySQL でも同じです。

{
	"hyperdrive": [
		{
			"binding": "HYPERDRIVE",
			"id": "<YOUR_HYPERDRIVE_CACHE_ENABLED_CONFIGURATION_ID>",
		},
		{
			"binding": "HYPERDRIVE_CACHE_DISABLED",
			"id": "<YOUR_HYPERDRIVE_CACHE_DISABLED_CONFIGURATION_ID>",
		},
	],
}
[[hyperdrive]]
binding = "HYPERDRIVE"
id = "<YOUR_HYPERDRIVE_CACHE_ENABLED_CONFIGURATION_ID>"

[[hyperdrive]]
binding = "HYPERDRIVE_CACHE_DISABLED"
id = "<YOUR_HYPERDRIVE_CACHE_DISABLED_CONFIGURATION_ID>"

次のステップ

役に立ちましたか?