I'm a software engineer with about 10 years of experience building Laravel web applications for small businesses. I'm pretty comfortable with database design, but it's not my greatest strength.
One of the applications I've built is a third-party tool for monitoring email newsletter opens and clicks. This app stores open and click events, and users of the tool query these events to see how their email newsletters are doing. The current row count for the analytics events table is about 1.5 billion, which is about a year and a half of data.
I plan to migrate to a new database design and would like to see if my understanding of database design is on track.
These are the tables for the new database:
Table contacts {
id integer
email_address string
created_at datetime
updated_at datetime
indexes {
id [pk]
email_address
created_at
}
}
Table contact_opens {
id unsignedBigInteger
contact_id integer
sent_at datetime
opened_at datetime
indexes {
id [pk]
(contact_id, sent_at)
sent_at
opened_at
}
}
Table contact_clicks {
id unsignedBigInteger
contact_id integer
sent_at datetime
clicked_at datetime
indexes {
id [pk]
(contact_id, sent_at)
sent_at
clicked_at
}
}
I plan to use MariaDB on a dedicated linode server with 64gb of RAM, partition the database on a calendar month basis, and tune InnoDB as follows:
innodb_buffer_pool_size = 48GB
InnoDB File-Per-Table = ON
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = 0_DIRECT
innodb_log_file_size = 8GB
table_open_cache = 20
The queries against this database will be segmenting Contacts based on Opens and Clicks. For example:
- Contacts with 5 or more opens in the last 30 days
- Contacts with at least 1 click in the last 5 days
- Contacts with 1 click on each of the last 30 days
- etc., standard email newsletter segmenting queries (as seen on Mailchimp)
The queries will return anywhere from 10k-300k contacts at a time.
I've done a fair amount of research and it appears that indexing, partitioning, and/or sharding appears to be the best option to help speed up the queries. It seems that a combination of indexing and partitioning would be best suited for this particular use case.
Does this use sound like a sound database design where the opens table grows by about a billion rows per year, and the clicks table grows by about 300 million rows per year?
Update 1: The schema will not change. It is changing slightly from how it was designed years ago, but only to accomodate the changes outlined in this quesiton to make the queries faster.
Update 2: The 300k~ contact queries are for mass mailings (email newsletters). Updated the data types in the MySQL tables above.
DROP PARTITION
and add a new partition for a "time series" is a lot faster than a big delete. Have you found an app (in SQL Server or others) whereSELECTs
clearly benefit from Partitioning? ("Partition pruning" is usually no better than a suitable composite index.)