MySQL の query_cache を無効にすべき理由(8.0で廃止)

MySQL のクエリキャッシュ(query_cache)は、一見便利な機能に思えますが、実は多くの問題を抱えており、MySQL 8.0 では完全に廃止されました。この機能の問題点と、代替手段について解説します。

クエリキャッシュとは

クエリキャッシュは、SELECT クエリの結果をメモリにキャッシュし、同一クエリが来たらキャッシュから返す仕組みでした。MySQL 5.7 まで存在していた機能です。

-- MySQL 5.7以前で確認
SHOW VARIABLES LIKE 'query_cache_type';
SHOW VARIABLES LIKE 'query_cache_size';

なぜ廃止されたのか

クエリキャッシュは、以下の理由でパフォーマンス上の問題を引き起こすことが多かったのです。

グローバルロック

キャッシュへの読み書きに排他ロックが必要でした。高並列環境では、このロック競合がボトルネックになります。

キャッシュ無効化の問題

テーブルに対する INSERT / UPDATE / DELETE が発生すると、そのテーブルに関連するすべてのキャッシュが無効化されます。更新が頻繁なテーブルでは、キャッシュがほとんど活用されません。

完全一致が必要

クエリ文字列が完全に一致しないとキャッシュヒットしません。スペースの違いや、パラメータの違いでも別クエリとして扱われます。

具体的な問題のシナリオ

高頻度で更新されるテーブルがある場合を考えます。

SELECT でキャッシュに登録

INSERT で全キャッシュが無効化

次の SELECT で再度キャッシュに登録

また UPDATE で無効化

このサイクルでは、キャッシュに登録するオーバーヘッドだけがかかり、ヒットする機会がほとんどありません。

MySQL 8.0 での対応

MySQL 8.0 ではクエリキャッシュ関連のパラメータ自体が削除されました。

-- MySQL 8.0 ではエラーになる
SHOW VARIABLES LIKE 'query_cache%';
-- Empty set

my.cnf にクエリキャッシュ関連の設定が残っていると、MySQL 8.0 へのアップグレード時にエラーになることがあるので、事前に削除しておく必要があります。

MySQL 5.7 で無効化する方法

まだ MySQL 5.7 を使っている場合、クエリキャッシュは無効化することをおすすめします。

[mysqld]
query_cache_type = 0
query_cache_size = 0

動的に無効化する場合は以下のようにします。

SET GLOBAL query_cache_type = 0;
SET GLOBAL query_cache_size = 0;

代替手段

クエリキャッシュに頼らず、以下の方法でパフォーマンスを向上させましょう。

アプリケーション層のキャッシュ

Redis や Memcached を使って、アプリケーション層でキャッシュを管理します。キャッシュの有効期限や無効化をきめ細かく制御できます。

InnoDB バッファプール

innodb_buffer_pool_size を適切に設定することで、データ自体をメモリにキャッシュします。クエリキャッシュより効率的です。

適切なインデックス設計

そもそもクエリ自体を高速化することで、キャッシュの必要性を減らします。

Redis を使ったキャッシュの例

アプリケーションコードでキャッシュを制御する例です(擬似コード)。

def get_user(user_id):
    cache_key = f"user:{user_id}"
    
    # キャッシュを確認
    cached = redis.get(cache_key)
    if cached:
        return json.loads(cached)
    
    # DBから取得
    user = db.query("SELECT * FROM users WHERE id = %s", user_id)
    
    # キャッシュに保存(有効期限5分)
    redis.setex(cache_key, 300, json.dumps(user))
    
    return user

この方法なら、ユーザー情報が更新されたときに該当キーだけを無効化できます。テーブル全体のキャッシュが消えることはありません。

ProxySQL のクエリキャッシュ

どうしてもデータベース層でキャッシュしたい場合、ProxySQL のクエリキャッシュ機能が選択肢になります。MySQL 本体のクエリキャッシュより柔軟な制御が可能です。

MySQL クエリキャッシュ

テーブル更新で全無効化、グローバルロック

ProxySQL クエリキャッシュ

TTL ベースの制御、ロック競合なし

まとめ

MySQL 8.0 ではクエリキャッシュは廃止済み
MySQL 5.7 以前でも無効化を推奨
高並列・高更新環境では特に問題になりやすい
アプリ層のキャッシュ(Redis / Memcached)を活用
InnoDB バッファプールの設定を見直す

クエリキャッシュは過去の機能です。現代的なアーキテクチャでは、アプリケーション層でのキャッシュ管理がベストプラクティスとなっています。