我是一名软件工程师,拥有大约 10 年为小型企业构建 Laravel Web 应用程序的经验。我对数据库设计非常满意,但这不是我最大的优势。
我构建的应用程序之一是用于监控电子邮件通讯打开和点击的第三方工具。该应用程序存储打开和单击事件,该工具的用户查询这些事件以查看他们的电子邮件通讯的运行情况。分析事件表当前的行数约为 15 亿,相当于一年半的数据。
我计划迁移到新的数据库设计,并想看看我对数据库设计的理解是否步入正轨。
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
}
}
我计划在具有 64GB RAM 的专用 linode 服务器上使用 MariaDB,按日历月对数据库进行分区,并按如下方式调整 InnoDB:
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
针对该数据库的查询将根据打开次数和点击次数对联系人进行分段。例如:
- 过去 30 天内打开了 5 个或更多的联系人
- 过去 5 天内至少点击 1 次的联系人
- 过去 30 天内每天点击 1 次的联系人
- 等,标准电子邮件通讯分段查询(如 Mailchimp 上所示)
查询一次将返回 10k-300k 个联系人。
我做了相当多的研究,看来索引、分区和/或分片似乎是帮助加快查询速度的最佳选择。索引和分区的组合似乎最适合这个特定的用例。
这种使用听起来是否像一个健全的数据库设计,其中 opens 表每年增长约 10 亿行,clicks 表每年增长约 3 亿行?
更新 1: 架构不会改变。它与几年前的设计方式相比略有变化,但只是为了适应本问题中概述的更改,以使查询更快。
更新 2: 300k~ 联系人查询用于群发邮件(电子邮件通讯)。更新了上面 MySQL 表中的数据类型。
汇总表将是您的救星!就此而言,您真的需要原始数据(事实表)吗?
1B 行/年 = 30/秒(加上峰值)。当前的计算机可以轻松处理此问题。糟糕的是,如果您进行表扫描来生成这些报告。
您会在 12 个月后清除数据吗?如果是这样,则对数据进行分区。
有关我对数据仓库优化的讨论,请参阅数据仓库。它引发了对高速摄取的讨论(您可能不需要)和汇总表(您绝对应该研究)。
当查询返回“一次 300k 联系人”时,这是群发邮件吗?(我想不出这么大的结果集还有什么其他用途。)
Buffer_pool = 48G——很好。我假设磁盘空间足以容纳数据。您每年可能需要 100GB 的磁盘空间来存储这些表。(或者如果您不长期保留事实表,但要永远保留汇总表,则要少得多。)