インフィニットループ 技術ブログ

2024年04月03日 (水)

著者 : y-kanno

Redisの隠れた落とし穴:単純なハッシュ操作で大きなトラブルに!

Redis にはハッシュというデータタイプがあり、 1 つの key に対して複数の field / value を保存することができます。

PHP / Laravel で Redis のハッシュに複数のデータを書き込んだ時に、意図しない挙動となってしまったことがあったので、紹介します。

以下のコードの前提

  • $this->redisPhpRedisConnection のインスタンスです
  • 1 つの key に対して 3 つの field / value を保存する set 関数を定義します

BAD

public function set(string $key, string $data1, string $data2, string $data3): void
{
    $this->redis->hSet($key, self::DATA1_KEY, $data1);
    $this->redis->hSet($key, self::DATA2_KEY, $data2);
    $this->redis->hSet($key, self::DATA3_KEY, $data3);
}

GOOD

public function set(string $key, string $data1, string $data2, string $data3): void
{
    $this->redis->multi();
    $this->redis->hMSet($key, [
        self::DATA1_KEY => $data1,
        self::DATA2_KEY => $data2,
        self::DATA3_KEY => $data3,
    ]);
    $this->redis->exec();
}

何が起きたか

BAD の例だと、例えば 1 つ目の hSet が終わった直後のタイミングでこのハッシュが参照された場合、 DATA1_KEY しか含まれていない状態になります。
問題が起きた箇所は、ハッシュがあるなら 3つの key が全て存在する前提でコードを書いていたため、 参照時に DATA2_KEY DATA3_KEY が存在しないことで、意図しないエラーになってしまいました。

対処

1: HSET ではなく HMSET を使う

HMSET を使う事で、ハッシュに対してアトミックに複数の field / value を同時に設定できます。
ただし、 redis-cli では Redis 4.0.0 から HSET で複数の field / value を指定できるようになったため、 HMSET は現在非推奨と見なされるとのこと。

As of Redis version 4.0.0, this command is regarded as deprecated.

It can be replaced by HSET with multiple field-value pairs when migrating or writing new code.

https://redis.io/commands/hmset/

PhpRedisConnection には hMSet 関数はありますが、 内部実装的にも HMSET 使ってるようです。 HMSET を避けようと思うと結局コマンド数が増えてしまったりするので、ひとまずは hMSet 関数を利用するようにしました。

2: トランザクションを利用する

MULTI でトランザクションを開始します。
その後、各コマンドを発行すると、その時点では実行されずにキューに溜まっていきます。
そして EXEC を実行すると、キューに溜まったコマンドが実際に実行されていきます。
トランザクション中は排他処理になるため、今回のような問題は起きなくなります。
PhpRedisConnection にも multi() exec() があるので、GOOD の例のように利用できます。

なお、この対処だけでも当初の問題は解決しますが、 1. もやっておくとコマンド数が減るので良いと思います。

ちなみに transaction 関数を用いる方法もあるようです。
今回のケースでは callbackuse を書くのが冗長に感じたので利用しませんでしたが、どこからどこまでがトランザクションの範囲かが明確になるので、状況によって使い分けると良さそうです。

おわりに

ILでは Redis のようなインメモリ DB を用いてアプリケーションを高速化する事に興味がある方の採用応募もお待ちしております。

今回の記事で IL に少しでも興味を持たれた方は是非、弊社採用情報ページからご応募よろしくお願いします!

参考資料

ブログ記事検索

このブログについて

このブログは、札幌市・仙台市の「株式会社インフィニットループ」が運営する技術ブログです。 お仕事で使えるITネタを社員たちが発信します!