祝大家新年快乐。我希望在以下情况下获得一些一般性指导......
我有一个已经运行了大约 10 年的应用程序。数据存储在 mysql 中(现在在 AWS Aurora 上)。
一些处于一对多关系的表开始有更多的行:
Records (~1.4million rows)
|
V
(1 to many)
|
V
SubRecords (~10million rows)
|
V
(1 to many)
|
V
SubSubRecords (~22million rows)
这些行中存储的实际数据并不多(即 subSubRecords 总共大约 5gb),而且我运行的查询非常简单,使用没有连接的索引键。例如...
SELECT ... FROM Records WHERE id = ?;
SELECT ... FROM SubRecords WHERE recordId = ?;
SELECT ... FROM SubSubRecords WHERE subRecordId = ?;
到目前为止,一切都继续保持高性能。
但是,我开始担心这种设计会随着时间的推移而保持不变。虽然在 SubSubRecords 中达到 2200 万行需要 10 年,但现在数据库的增长速度要快得多。看到该表在接下来的 5 年内攀升至 1 亿行,我不会感到惊讶,这感觉很多。而且我不确定它会在什么时候成为问题。
我意识到这是一个相当广泛的问题,并且取决于情况。但在这些情况下,一般推荐哪些类型的解决方案?
设置分区?(这些表使用外键来强制完整性,我的理解是这与分区不兼容。)
将 subRecords 和 subSubRecords 中的数据转换为 json 有效负载并将其直接存储在主记录表的 json 列中?(如果重要的话,数据量相同,但行数更少。)
移动到一个完全不同的数据库?(Mongo?我对此一无所知,但听说在某些情况下更擅长缩放。)
忽略它直到它成为一个问题?:D
欢迎那些遇到过类似问题的人提出任何建议/智慧的珍珠。在此先感谢您的帮助!
附录:
根据要求,这是上述表格的 CREATE TABLE 语法...
CREATE TABLE records (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
typeId TINYINT(1) UNSIGNED NOT NULL,
userId INT UNSIGNED NOT NULL,
updated TIMESTAMP DEFAULT NOW() NOT NULL,
savename VARCHAR(100) NOT NULL,
title VARCHAR(100) NOT NULL,
instructions TEXT NOT NULL,
FULLTEXT ftRecords(savename, title),
PRIMARY KEY(id),
FOREIGN KEY(typeId) REFERENCES recordTypes(id),
FOREIGN KEY(userId) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB CHARACTER SET=utf8;
CREATE TABLE subRecords (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
recordId INT UNSIGNED NOT NULL,
thumbnailId INT UNSIGNED NULL,
sortOrder SMALLINT NOT NULL,
enabled TINYINT(1) DEFAULT 0 NOT NULL,
title VARCHAR(100) NOT NULL,
instructions TEXT NOT NULL,
parameters VARCHAR(500) NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(recordId) REFERENCES records(id) ON DELETE CASCADE,
FOREIGN KEY(thumbnailId) REFERENCES thumbnails(id) ON DELETE SET NULL
) ENGINE=InnoDB CHARACTER SET=utf8;
CREATE TABLE subSubRecords (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
subRecordId INT UNSIGNED NOT NULL,
thumbnailId INT UNSIGNED NULL,
sortOrder SMALLINT NOT NULL,
caption VARCHAR(200) NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(subRecordId) REFERENCES subRecords(id) ON DELETE CASCADE,
FOREIGN KEY(thumbnailId) REFERENCES thumbnails(id) ON DELETE SET NULL
) ENGINE=InnoDB CHARACTER SET=utf8;
100M 行并不可怕。
分区——不。它不太可能增加任何性能优势。但是,如果您需要清除“旧”数据,则可能需要进行分区。
如果
recordId
是INDEX
——SubRecords
好。如果
recordId
是--的第一列就PRIMARY KEY
更好SubRecords
了。向我们展示
SHOW CREATE TABLE
进一步的建议。如果您将 3 个表放在一个 3 中,您的3
SELECTs
会运行得更快。JOINed
SELECT
聚类
一个轻微的改进是改进您同时获取的行的“集群”。对于
SubRecords
,这样,当您获得一条记录的多个子记录时,它们将彼此相邻。这是因为
PRIMARY KEY
(在 InnoDB 中)与数据“聚集”在一起。是INDEX(id)
为了保持AUTO_INCREMENT
快乐。在数据集大于 RAM 之前,此更改可能不会显示任何明显的改进。
类似的事情可以用
SubSubRecords
.做好未来规划固然好,但您也可能担心可能永远不会存在的问题。听起来您的表非常轻量级,因为您的 2200 万条记录表只有大约 5 GB 大。
通过将子表的数据非规范化为 JSON 并将其填充到主表的列中,实际上可能会使系统变慢,因为现在主表的每条记录的数据量都变得更大。当主表从磁盘加载到内存中时,该操作可能会变慢,因为与当前的规范化设置相比,每行需要从磁盘加载更多数据(您可能并不总是需要)。
当涉及到写入表和调用该数据的标准操作时,大多数数据库系统在性能方面彼此相提并论。与 MySQL 相比,MongoDB 没有任何独特之处可以使您的示例用例更快地用于更大的数据集。此外,NoSQL 数据库系统与 RDBMS 之间的选择主要取决于数据的结构,而不是通常的性能问题。
B-Tree 可以非常有效地处理大量节点。因此,您的表的索引可以轻松处理存储其行的数据并非常快速地进行查找。B-Tree 搜索时间的 Big-O 表示法是 O(log n),这意味着如果您有 10,000,000 行,log(10,000,000) = 7,如果您的表增长到 100,000,000 行的 10 倍,则 log(100,000,000)最多只能达到 8 (对于您的数据每增长 10 倍,搜索时间的变化非常小)。
如果没有有关您的服务器配置的信息,我只能给您一个我曾经使用过的数据库的示例以供参考,其中一个表有数十亿条记录,并且在这些索引上的查找时间永远不会超过几秒钟(通常在一秒)。这是在 AWS 中的 8 核 CPU、16 GB 内存、常规 SSD 服务器上。该表本身也有大约 1 TB 的数据(行更宽并且存储了很多详细数据)。
尽管大数据这个术语有些主观,但总的来说,这些天数据的总大小应该在 10 到 100 TB 大,被认为是大数据,并且可能需要标准 RDBMS 实施之外的替代解决方案。尽管这就是分区和分片之类的东西,以帮助常规 RDBMS 处理如此大的数据。我认为在你的情况下,听起来你的系统永远不会达到这样的限制。