Efficient way to store 1:n records in Redis

Viewed 48

I have no previous experience with Redis and I am writing a simple application to get to know it better.

In my app, there is a player (with his player_id) who can play one or more game (each with its game_id), and I would like to save the player's start_date and last_played informations.

I created a registration key in Redis as a Hash, where I planned to add several <player_id_game_id> records with a JSON value of

{
  "start_date": ...,
  "last_played": ...
}

but I quickly ran into an issue, I can't get all the games for a single user with a Redis query (something like HGET registration 10_* to get all player 10's games)

I then switched to a player-list of games approach

{
  "games":[
    {
      "start_date": ...,
      "last_played": ...
    },
    {
      "start_date": ...,
      "last_played": ...
    }...
  ]
}

so that a HGET registration 10 would get me the list of all player 10's games

However this comes with the awareness that each update operation requires me to get the list from Redis, modify it and then update the record in Redis, and I am worried about the performances with a larger volume of data and/or operations.

Is this a good approach, or are there better ways? I don't care about the specific implementation of this sample application (eg. it's not important to have the data stored as JSON), but rather the "correct" (or best) design for a similar case.
Thanks in advance.

1 Answers

There will be multiple redis get/update for the use case anyway.

Having multiple keys to hold data is usually the best approach because you can optimise data fetching/update by using different data types (keys, hashes, lists etc...) to get as often as possible some 0(1) commands.

Here having a list named player_id holding game_id and hashes named game_id holding player_id:start and player_id:end seams like a good idea because:

  1. You can get all games of a player with the command LRANGE player_id 0 -1. It's O(N) (same for HGETALL) but you can use commands like LPUSH or RPOP (or RPOPLPUSH ) to have a kind of rolling queu if you only want to store the last 10 games

  2. Once you remove an element from the player_id list with RPOP (or RPOPLPUSH ) you can directly delete the game_id's player_id:start and player_id:end keys with HDEL as it's not needed anymore. Doing only 2 get/update.

  3. You can easily know if a player took part in a good in a 0(1) manner thanks to hashes properties.

The problem with this solution is that you cannot easily find all players of a game, the only way being HGETALL but it's O(N) and you dont need half of the data it will give you. I think you can split this hash in two : game_id:start and game_id:end each holding the corresponding player_id data. There is no real tradeoff going for this data structure because the number of read/write is the same, you just optimise the retrieving of all player of a game.

PS: It's almost never a good idea to store stringified JSON because you cannot leverage the almighty power of redis. If you really want you can use RedisJSON to have best of both worlds.

Related