Skip to content

Menu
Menu

如何删除大表

Posted on 2022年9月27日2022年11月1日 by zhezimi

最近163邮箱总是收到RDS的报警邮件。

我赶紧登录了一下RDS,看下具体什么情况。从下面我们可以看到目前的存储空间是600G,已经用了527G。

按照目前项目的业务量的话,不应该有这么多数据的,我们用一条sql来看一下有哪些大表。

SELECT file_name, concat(TOTAL_EXTENTS,’M’) as ‘FIle_size’ FROM INFORMATION_SCHEMA.FILES order by TOTAL_EXTENTS DESC

我们可以看到有三个log表就将近占据了450个G。经过技术部和业务部门的讨论,按照我们目前的业务,只要保留最近3天的就可以了。

那么有的朋友会问为什么会产生这么多数据呢?有以下两个原因:

  1. 有部分log表是对接的第三方服务,一开始不是很稳定,出问题的时候需要我们这边提供详细的信息给第三方,所以前期一直没有删除数据
  2. 前期业务没有起来,数据一直不多,就没重视,后期业务起来了,log表就越来越大了。

既然现在出现了这个问题,那么我们就得解决。我们首先看下其中一个表的数据有多少

我们可以看到表的数据有7600万条,那么我们如何来删除多余的数据呢?

解决思路:

(1)首先,表的话有7600万条数据,不建议直接delete或者truncate全表,如果这样操作的话,rds配置不高的情况下可能就直接挂掉了。

(2)将大的问题分解成小的问题,然后通过解决一个一个的小问题,最后大问题也就解决了。因此,我们的建议是用脚本定期来删除。每一次脚本执行10条sql,每条sql删除10万数据,脚本时间可以是半个小时执行一次,或者一个小时执行一次。具体脚本的删除记录数跟执行频率数,大家根据RDS的配置来定。

(3)删除数据的话,不能影响到公司的正常业务。大多数情况下都建议半夜来执行,因为那时候客户是最少的。

(4)经过前面3个步骤,基本上就只保留了3天的数据了,那么接下来我们就要执行以下的567步了。

(5)选择不删除的行导入到原表结构相同的空表中

INSERT INTO t_copy SELECT * FROM t WHERE … ;

(6)使用RENAME TABLE将原来的表命名为备份表,并将副本重命名为原来的名称

RENAME TABLE t TO t_old, t_copy TO t;

(7)删除原来的表

DROP TABLE t_old;

下面我们来开始实际操作

以下是脚本代码:

public function actionDel()
{


    $path = __DIR__;
    chdir($path);
    /**
     * 查出ID最小的一条数据
     */
    $minRecord = Log::getMinOne();
    /**
     * 用当前时间减去最小记录的创建时间,如果小于等于3,那么就需要删除了
     */
    $diffDay = (int)((time() - $minRecord['create_time']) / 86400);
    if ($diffDay <= 3) {
        echo 'end';
        exit;
    }
    /**
     * 每次删除一百万条数据,分10条sql执行,每条sql删除十万条数据,删除的时候利用ID区间去删除,这样的话更好得利用索引
     */
    $minId = $minRecord['id'];
    $number = 1000000;
    $pageSize = 100000;
    $count = $number / $pageSize;
    for ($i = 1; $i <= $count; $i++) {
        $startId = $minId + ($i - 1) * $pageSize;
        $sql = 'delete from ' . Log::tableName() . ' where id>' . $startId . ' and id<=' . ($startId + $pageSize);
        echo $sql . PHP_EOL;
        \Yii::$app->db->createCommand($sql)->execute();
    }
    echo 'success';
}
/**
 * @return array
 * @throws \yii\db\Exception
 */
public static function getMinOne(): array
{
    $sql = 'SELECT * FROM `log` ORDER BY `id` ASC LIMIT 1';
    return Yii::$app->db->createCommand($sql)->queryOne();
}

我们在脚本执行前要先手动执行一次,来验证sql的效率,如果执行太慢或者RDS的负载上去了,那么对应的脚本就要修改。

我们从这可以看到,RDS的负载基本没有太大的问题。那么接下来我们就执行5,6,7步就可以了。最终优化后的效果

大家可能会有疑问,为什么删除了将近100G的数据,最后空间只减少12个G呢,那么因为在跑脚本的这个期间,其他的log表的数据又变大了,RDS又扩容了100个G。

后续的log表也可以按照前面的步骤来执行。

参考链接:

https://dev.mysql.com/doc/refman/8.0/en/delete.html

https://dev.mysql.com/doc/refman/8.0/en/rename-table.html

相关文章

  • 衡量SQL查询的三个指标

  • 备份与恢复

  • 字符集设置

  • 数据类型:JSON

  • 事务ID分配时间

发表评论 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注

近期文章

  • 排查网络故障常用命令
  • PHP-FPM异常问题
  • RabbitMQ 1:介绍
  • 观察者模式
  • 装饰者模式

近期评论

没有评论可显示。

分类

  • cdn
  • css
  • docker
  • git
  • http
  • javascript
  • linux
  • mysql
  • nginx
  • php
  • RabbitMQ
  • 代码规范
  • 性能
  • 正则表达式
  • 网络协议
  • 设计模式
© 2025 | Powered by Minimalist Blog WordPress Theme