Can MySQL scale for hot record use case? - mysql

I am building a gaming system where users can exchange tokens in real time. Some of these users are super users who can receive millions of tokens from other users. I need persistent data store that keeps following user-token snapshot:
U1 300
U2 480
U3 900
U4 190000
If U1 sends 100 tokens to U4, following txns needs to happen as atomic
1. U1 debit 100
2. U4 credit 100
Now U4 being a superuser could have thousands of txns per second with various users. That is u4 record will be a hot record.
Can I use MySQL for persistence data store of U - token value? Will it scale? Remember I need ACID compliance since token is essentially money here so no room for error? Any suggestions?

Related

Reading data from table using LIMIT while the order changes

Okay, let me explain.
You know how Facebook Messenger and Discord both have the last messaged friends list? It is a list of all of your friends order by whoever texted you last. Now, if you had over 100 people in that list, it would be better to send the list to the client in chunks of 10. Once the client reaches the bottom, it asks for the next 10. This can be done with the LIMIT offset, amount.
But now, the problem. The user might open the messenger, talk to someone for 10 minutes, and then scroll further down in the last messaged friends list. In this case, the table has changed before the user has retrieved the full list. The list in database is now in a different order because someone messaged them in the meanwhile and is now on top of the list, but the client already has the first chunk of the list, but this doesn't contain the people that texted them.
In case my explanation was not enough, here's a visual demonstration:
Visual demo
'Last messaged friends' list in database (ordered by latest timestamp):
Person 23
Person 77
Person 93
Person 99
Person 67
Person 85
User connects, asks for the first 3 entries.
Client now has (ordered by latest timestamp):
Person 23
Person 77
Person 93
'Person 99' messages that user. 'Person 99' is now on top of the list.
'Last messaged friends' list in database (ordered by latest timestamp):
Person 99
Person 23
Person 77
Person 93
Person 67
Person 85
User scrolls down. Client asks for the next 3 entries.
Client now has (ordered by latest timestamp):
Person 23
Person 77
Person 93
Person 93 (duplicate)
Person 67
Person 85
('Person 99' missing)
Is this something I could fix/implement with a more advanced SQL query?
If I can't, how could I implement this in other ways?
For information, I have a Socket.io (a.k.a more advanced WebSocket) connection between the server and the client, I can send whatever necessary information thru that.
It sounds like you want aggregation to avoid duplicates:
select person_id
from messages
group by person_id
order by max(timestamp) desc;
Thank you, Hector Vido, for the suggestion. (he made a comment right below my question, go upvote)
"Selecting messages by timestamp don't solve this? You keep the last timestamp and then ask by anoter 10 registries >= that timestamp"
Solution
I'll keep the oldest and newest timestamp in the client side.
If the client scrolls down, I will request 10 entries before the oldestTimestamp and then the new oldestTimestamp will be the oldest timestamp of the received entries.
Also after every 10 seconds, I could request for entries after the newestTimestamp and then the new newestTimestamp will be the newest timestamp of the received entries.

Saving chat message in MySQL DB, scheme design

I'm building a pretty simple chat app that allows both 1 on 1 message and chat rooms for groups of people. I'm planning to have one Message table to store all chat messages, each message will also keep the sender ID and receiver ID, in the case of messages sent in a chat room, we also keep the ID of that chat room. Below is the table:
Message Table
ID Message Sender Receiver Chatroom Timestamp
1 Hello, David 123 321 1495330074
2 Hi, Linda 321 123 1495930032
3 Hi everyone! 456 999 1495930132
4 What up? 321 123 1495930192
...
Then if I'm user 321, and I want to retrieve my conversation with user 123, I just need to SELECT * FROM Message WHERE Sender=123 or Receiver=123 or Sender=321 or Receiver=321 and Chatroom IS NULL
There is one issue with this design - a user can't delete a message that he doesn't want to see any more.
To solve that, I think I can have a separate table to store what messages a user received or sent, like below:
User Message Table
ID UserID MessageID
1 123 1
2 321 1
3 321 4
...
It seems a little redundant, but this way David can delete a message in his conversation with Linda, while Linda can still see full conversation history.
Is there better design of the tables? And is this good practice to throw all chat messages in one giant table? Should I add some index to make query faster?
You can use this query.
delete from Message
where sender = 123 and user = 321
But this will delete all the chat messages between this user and sender. To delete specific message you can use ID
delete from Message
where ID = 1

How to model a database for days and time scheduling?

I have to model over a relational database the following scenario.
Imagine you have a number (say 10.000) of persons.
Imagine each of those person may, or may not, offer a given service inside a timespan of a given day. Let's call these services "Answer phone", "Answer email", and "Answer SMS".
I have 48 timespans a day (00:00 - 00:30, 00:30 - 01:00, 01:00 - 01:30, etc.)
I have to schedule 7 week days (1 to 7)
Each service can be overlapped to another.
I'm currently thinking about a structure like this:
id | user_id | day | t00 | t05 | t10 | [... more timespans ...] | service_type
x 001 1 1 1 0 ... 'answer_phone'
y 001 1 1 1 1 ... 'answer_email'
z 002 1 0 0 1 ... 'answer_phone'
And so on. About the t* columns:
every t* column is a boolean value
t00 means "service is ON from 00:00 to 00:29"
t05 means "service is ON from 00:30 to 00:59"
t10 means "service is ON from 01:00 to 01:29"
and so on. So, at row "x" i've modeled that
user 001 will answer phone between 00:00 and 00:59, while answering
emails from 00:00 to 01:29 on Monday.
After thinkin about for a while, this approach seems to be enough straightforward, but i fear it will suffer performance and disk space issues when dealing with thousands of users.
Infact, for 10k users, i would have (10k * how_many_services * 7days) rows, which means 210.000 records. Not that much, but users may grow, or new services may be added.
Can you suggest a better approach?
This is a terrible design. It's not normalized at all.
I would imagine there's a 1:many relationship between a user and their activity schedules. I'd model it that way.
If you don't know what normal forms are and why they're important, you shouldn't be doing relational modeling. Get someone who understands it to help you.
I would have separate tables for TIMES, SERVICES, USERS and ACTIONS.
TIMES would contain just the time splits (including a textual description of the time period)
SERVICES would have the service types such as answer_phone, answer_email etc. This allows for easy future expansion.
USERS would have any info on the users of the system. Such as userID, name, department, whatever.
The ACTIONS table would be for linking all the above tables together using foreign keys.
An entry in the actions table would have its own primary key, user_FK, time_FK, service_FK.

How to build a compound WHERE clause that is limited to the current record

I have an (example) application that sends baseball scores to users. The use is able to select which inning the score should be sent for some specific teams (e.g. 'Send me Yankee scores after 7 innings). There is also a setting for 'All other' teams (e.g. 'Send me scores for any other teams after 8 innings).
These settings are saved to a table which stores the user, a team ID, and the number of innings. Team ID '99' is used for 'All other teams'. So our user's records would look like:
**User - Team - Innings**
Bob - 13 (Yankees) - 7
Bob - 99 (all other teams) - 8
Now it comes time to check the scores and send some notifications. I find that the Yankees game has reached the end of the 7th inning and fire off a message to Bob.
20 minutes later, that same Yankees game has reached the 8th inning. Bob should NOT receive a message this time, since he got one after 7 innings.
Now consider Julie:
**User - Team - Innings**
Julie - 13 (Yankees) - 8
Julie - 99 (all other teams) - 7
Julie has used the settings to say 'send me all scores after 7 innings, except for Yankees scores which should wait until 8 innings'. This time, after 7 innings in the Yankees game, Julie should NOT receive a notification.
Finally, Dirk. Dirk is a little confused:
**User - Team - Innings**
Dirk - 13 (Yankees) - 7
Dirk - 99 (all other teams) - 7
His Yankees setting is pretty redundant, but whatever - as long as he doesn't receive the same notification twice, he's fine.
The Yankees game has just completed the 7th inning. What is the best way to query my table and decide who needs to get alerts?
I am fairly new to SQL, but I think I can make a plain-language representation:
Send alerts to:
Any people that care about the team A team after X innings, unless
they also care about ALL teams after < X innings (they would already
have received an alert)
PLUS
Any people that care about ALL teams
after X innings, unless they also care about team A after < X innings
(they would already have received an alert)
(I don't think this actually covers Dirk's scenario though?)
My best guess is that I need to make a WHERE clause that matches the team AND the innings, but then also tests if there is a record in the same table for the same user that meets the criteria above.
This is way beyond me - I don't even know what techniques to google. I couldn't even come up with a decent question title :/
Start by making a select that shows specific team settings plus general team settings. You can do that by joining the table on itself.
select s.*, g.innings as g_innings
from settings s join settings g
on (g.user = s.user and g.team = 99) -- 'g.team is null' would be better, yes...
The rest is easy: use this select twice with appropriate where clauses, and union the result.
EDIT: use a single query and OR the two where clauses.
select s.*, g.innings as g_innings
from settings s join settings g
on (g.user = s.user and g.team = 99) -- 'g.team is null' would be better, yes...
where (s.innings = X AND g.innings >= X)
OR (g.innings = X AND s.innings >= x)

Database structure for affiliate system

I am creating an affiliate tracking system and am looking for the best database structure to use on mySQL for low load on the server.
There will be 1000 affiliates. Each affiliate will have stats per day.
I am thinking of this scenario. One main table for the affiliates:
Affiliates
id affiliate_id username password and so on (other affiliates details)
1 0000001 johndeer password
... and creating a new table for each affiliate which will have store his statistics like this:
Table name : affiliate-userid
Date Clicks sales sale_price total_earned bonus
12/12/12 45 2 20 40 0
12/13/12 12 3 20 60 0
So in this case each affiliate will have his own statistics table.
Is this correct or do I need to do something else?
Create two table
Affiliates(af_id(pk),*) and
Stats(stat_id(pk),af_id(fk),*)
Then, Refer Affiliates table in stats table. you can add any number of stats for any number of affiliates. No need to go for a table for each affiliate. It's a bad Idea.I think you understood.