什么会导致 RDS MySQL 数据库可用存储空间快速下降?

use*_*535 6 mysql innodb mysql-5.6 amazon-rds

我在 Amazon RDS 上的 MySQL 数据库最近如何在大约 1.5 小时内从 10.5 GB 免费变为“存储已满”状态?

这是一个运行在 db.t2.micro 实例上的 15GB MySQL 5.6.27 数据库。通常每天只有几百 KB 被添加到它。

大约一天前,免费存储空间在大约 1.5 小时内从 10.5 GB 变为基本上 0 GB。写入 IOPS 图表仅显示在那段时间内我的常规低流量流量,因此显然数据必须是在服务器端生成的。

一个可能相关的说明是我的数据库有大约 7,000 个表,并且将 innodb_file_per_table 设置为 1。

类似的事件显然发生在 8 天前,但没有那么严重,我什至没有注意到它,因为它没有填满存储空间。

屏幕截图显示了 8 天前的事件以及一天前的存储填充事件 在此处输入图片说明

屏幕截图显示了存储填充事件的详细视图 在此处输入图片说明

我不是数据库专家,这是我的一个业余爱好项目,所以我正在努力弄清楚如何开始解决这个问题!

编辑 1

我开始查看@RolandoMySQLDBA 提供的答案,我意识到我遗漏了一些非常有用的细节。

写入数据库的唯一系统是两个 EC2 实例,它们每 30 分钟写入一次,这与图中看到的存储减少相对应。

这两个系统都从网络上收集相同的数据,然后它们都尝试在半小时内将收集到的数据同时写入我的数据库。我使用两个数据收集系统只是为了冗余,并且我对我的写入例程进行了编码,以便每个系统将尝试使用 INSERT IGNORE INTO 写入其所有数据,因此无论哪个系统首先写入该特定数据,第二个系统的插入尝试是干脆忽略了。

在每 30 分钟发生一次的写入期间,除了一个表外,数据库中的数千个表中的每一个都插入了一行。该表中没有插入任何内容,但其(大约)2000 行中的每一行都会更新,一次一个。

编辑 2

在添加了大约 2.5GB 的数据后,我从一个点恢复了数据库的实例(8/16 上的事件,如第一个屏幕截图所示),这样我就可以运行命令而不会遇到“存储已满”错误。

在@RolandoMySQLDBA 的帮助下,我能够看到使用了多少 InnoDB 和 MyISAM 数据(如何监视 MySQL 空间?)。这是输出:

rudy InnoDB 761.72 MB   0.00 B  761.72 MB 
rudy Total  761.72 MB   0.00 B  761.72 MB 
sys InnoDB  16.00 KB    0.00 B  16.00 KB 
sys Total   16.00 KB    0.00 B  16.00 KB 
Database Total  761.73 MB   0.00 B  761.73 MB
Run Code Online (Sandbox Code Playgroud)

我还运行了以下命令来检查数据库中所有表的“Data_Length”:

show table status from rudy;
Run Code Online (Sandbox Code Playgroud)

我将该命令的输出导出到 CSV 文件,将其作为电子表格导入,然后将所有数据长度相加,总数为 798,720,00。

所以在这一点上我很困惑。如果根据您的命令的输出,表中有大约 798MB,整个数据库中有大约 761MB,那么还有什么可能会占用大约 4.5GB(15GB 实例,大约 10.5GB 的免费存储空间)?

有没有其他方法可以查看我的 RDS 实例上还有什么可能占用空间?

编辑 3

我通过仅使用一个系统写入数据库并删除所有更新语句来简化我的测试场景,所以现在我在数据库上执行的所有代码基本上是这样的(使用 python 3 和 pymysql):

query = "INSERT IGNORE INTO {tn} (Timestamp, Price, Flags, Sales, Total) VALUES(%s,%s,%s,%s,%s)".format(tn=table_name)
self.cursor.execute(query, (timestamp, price, flags, sales, total))
Run Code Online (Sandbox Code Playgroud)

这是我要插入的表的 DDL:

query = "CREATE TABLE IF NOT EXISTS {tn} (Timestamp INT PRIMARY KEY, Price BIGINT, Flags INT, Sales INT, Total INT)".format(tn=table_name)
self.cursor.execute(query)
Run Code Online (Sandbox Code Playgroud)

在我的简化代码中,我只插入了大约 2000 个这种类型的表,每个表有 1,000 到 11,000 行。

我可以通过上述测试设置一致地重现该问题。

极光工作正常!

我还尝试将快照迁移到 Aurora 并运行测试场景,没有出现问题!我想坚持使用 MySQL 服务器,因为它更便宜,但如果没有人能帮我解决这个问题,那么我可能会永久转移到 Aurora。

Rol*_*DBA 4

以下是您在 MySQL RDS 服务器中写入的文件夹

mysql> select * from information_schema.global_variables where variable_name in
    -> ('innodb_log_group_home_dir','innodb_data_home_dir','innodb_data_file_path');
+---------------------------+------------------------+
| VARIABLE_NAME             | VARIABLE_VALUE         |
+---------------------------+------------------------+
| INNODB_LOG_GROUP_HOME_DIR | /rdsdbdata/log/innodb  |
| INNODB_DATA_FILE_PATH     | ibdata1:12M:autoextend |
| INNODB_DATA_HOME_DIR      | /rdsdbdata/db/innodb   |
+---------------------------+------------------------+
3 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

您的 ibdata1 文件位于 中/rdsdbdata/db/innodb,重做日志位于/rdsdbdata/log/innodb.

我担心的是你的ibdata1文件。由于innodb_file_per_table已启用并且假设您没有 MyISAM 表,因此唯一可能导致增长的因素是 MVCC。大量的选择和写入可能会导致 InnoDB 创建大量的回滚信息。该信息可以扩展ibdata1文件。多年来我一直在讨论这个问题:

您可以OPTIMIZE TABLE针对所有 InnoDB 表运行以提供一些收缩。请参阅我 5 年前的帖子为什么 InnoDB 将所有数据库存储在一个文件中?有关如何缩小表格的想法。

不幸的是,在你目前的状态下你无法做到这一点。请参阅此 YouTube 视频。至于您无法列出您的数据库,请注意:

mysql>  show global variables like 'tmpdir';
+---------------+----------------+
| Variable_name | Value          |
+---------------+----------------+
| tmpdir        | /rdsdbdata/tmp |
+---------------+----------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

元命令如SHOW创建临时表。整个磁盘已经满了。

坏消息

创建只读副本不会缩小任何内容。RDS 将仅拍摄快照并设置复制。

这样做ALTER TABLE会缩小表,而不是ibdata1

启动新的 RDS 实例并从头开始加载将从新的 ibdata1 开始。

更新 2017-08-25 12:21 美国东部时间

回顾您的图表,我可以看到您每 30 分钟发送的数据过多。尝试一次更新 500 行而不是 2000 行。请记住,就 ibdata1 增长而言,大量更新与大量插入一样糟糕。