1
\$\begingroup\$

I'm designing an app that will process a set of social media messages which are posted by users. Each message is then scored and there is a reporting layer that will spit out a set of reports. The app is implemented with spring-boot and has to use a Redis DB.

I'm more familiar with RDBM databases and perhaps I'm being too much of an ORM mapping mindset to this issue.

The main rules i need to enforce

  • A user can post multiple messages.
  • A message can have multiple scores.
  • It should be possible to chart a user's scores over time.

I've started with the logical mapping of data that i believe i need to record in the app. There are three main objects and I'm using spring-data-redis to define the @RedisHash, @Id, @Index and @Reference fields.

Message

The raw message details with thread, date, user and message content.

@RedisHash("messages")
public class Message {

    @Id String messageId;
    @Indexed String threadId;
    @Reference String userId;
    String message;
    Date date;
    @Reference List<User> userScores;
}

Score

A record of a score that each message has been recorded with

@RedisHash("scores")
public class Score {
    @Id String id;
    @Reference String messageId;
    Long score;
    @Reference User user;
}

User

The user details with title

@RedisHash("users")
public class User {

    @Id String userId;
    String grade;
    @Reference List<Score> scores;
}

I believe the Message and User objects are correctly defined, but my concern is I've over complicated the Score object mapping. Any comments or advise would be welcome.

\$\endgroup\$
  • \$\begingroup\$ @ emeraldjava - Are you able to model the data? Could you please guide ? \$\endgroup\$ – JavaSystem Nov 9 '18 at 14:03
2
\$\begingroup\$

There's nothing really wrong with your modelling approach. If you feel confident with the modelling you are good to go.

Data modelling in Redis depends on the functional and non-function requirements you need to meet. In Redis and your case this boils down to whether read performance is more important than write performance, and whether performance is an issue at all.

In my opinion (and after having developed quite a few commercial-grade products based on Redis), Redis has not been made with object relationships in mind. At its core it is just a very fast, efficient and lightweight key-value based database, i.e. you lookup a value by its (complex) key. That's it. Simple, plain and wonderful.

With this said, I would strongly recommend you to keep data models as simple as possible, when working with Redis. In classic database scheme design your are encouraged to perform database normalizations. However, Redis is not a RDMBS and thus is used most efficiently, when object relations are kept at a minimum (or even better avoided altogether, if possible).

The @Reference annotation actually is just a convenience to developers, taking care of automatically loading referenced objects from Redis database. On a side note, please keep in mind that referenced objects need to be updated/saved separately from the referencing object (related). Resolution of references in Spring Data Redis just mean additional queries under the hood and thus mean less read performance.

For maximum read performance, I would suggest the following changes to your model. Please note that this not necessarily the best model for your purpose, as many constraints of your project remain in the dark. Just consider it as an alternative to your "classic" model you may want to think about.

@RedisHash("messages")
public class Message {
    @Id String messageId;
    @Indexed String threadId;
    @Reference User user;
    String message;
    Date date;
    List<Score> userScores;
}

@RedisHash("users")
public class User {
    @Id String userId;
    String grade;
    List<Score> scores;
}

public class Score {
    String scoreId
    String messageId;
    Long score;
    User userId;
}

This approach basically eliminates the object relationships between Message and Score and User and Score by embedding Score information into the Message and User hash objects as standalone data. So you gain substantial read performance at the cost of data redundancy.

When querying a message (e.g. for message rendering) this means Redis Data just needs one lookup to fetch the full data from database (because message scores are already part of the stored value). The same is valid for querying a user for charting his/her scores. However, the drawback with this approach is, that you need to write/update the Score twice whenever a new score is added to a message by a user: once for the update to the user object and once for the update to the message object. However, using @Reference you still had to write the score object once, then write the updated references to User and Message, respectively.

Another (and probably more important) drawback is the increased memory usage due to the data duplication in User and Message objects. As Redis holds all its data in RAM at the time of this writing (albeit in a very efficient manner), this can become an issue if you deal with a huge amount of users and messages.

The proposed model should work particularly well in a strongly asymmetric producer-consumer model, where you have a lot more consumers (people just reading messages) than producers (users contributing messages and scores to messages). YMMV depending on the actual project style and setup.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.