MySQL 日志表改造为分区表

 更新时间:2025年02月20日 10:58:02   作者:Bing@DBA  
本文主要介绍了MySQL 日志表改造为分区表,以解决业务日志表占用大量存储空间且删除操作慢的问题,下面就来具体介绍一下,感兴趣的可以了解一下

前言

业务有一张日志表,只需要保留 3 个月的数据,仅 3 月的数据就占用 80G 的存储空间,如果不定期清理那么磁盘容纳不下,但是每次清理的时候,使用 DELETE 删除非常慢,还会产生大量的 Binlog 日志,而且删除后会产生大量的空间碎片,回收需要重建表,期间还会造成临时空间增长(Online DDL 排序需要使用临时空间)需要先扩磁盘,等待空间收缩后再缩容,非常麻烦。

了解到这张表几乎不会查询,只会在某种特殊情况下才会查询,所以非常适合使用分区表。所以就提出将普通表改造成分区表的方案,本文将介绍整个过程,如果业务也有相似的场景,可以作为参考。

1. 分区表改造方法

分区表改造,需要全程锁表,业务表示无法给出窗口时间,所以需要借助 OnlineDDL 工具,通过无锁变更的方式来改造。这里使用的工具是 gh-ost 它的原理大致如下:

官方图解 (https://github.com/github/gh-ost)

在这里插入图片描述

主要执行过程:

  • 检查是否有外键触发器及主键信息;
  • 检查是否主库或从库,是否开启 log_slave_updates 以及 binlog 信息;
  • 检查 gho 和 ghc 结尾的临时表是否存在;
  • 创建 ghc 结尾的表,存数据迁移的信息,以及 binlog 信息等;
  • 初始化 stream 的连接,添加 binlog 的监听;
  • 根据 alter 语句创建 gho 结尾的幽灵表;
  • 开启迁移数据,按照主键把源表数据写入到 gho 结尾的表上,以及 binlog apply;
  • 进入 cut-over 阶段,锁住主库的源表,等待 binlog 应用完毕,然后替换 gh-ost 表为源表;
  • 清理 ghc 表,删除 socket 文件。

cut-over 即表 rename 阶段,gh-ost 利用了 MySQL 的一个特性,原子性的 rename 请求,在所有被 blocked 的请求中,rename 优先级永远是最高的。gh-ost 基于此设计了该方案:一个连接对原表加锁,另启一个连接尝试 rename 操作,此时会被阻塞住,当释放 lock 的时候,rename 会首先被执行,其他被阻塞的请求会继续应用到新表。

2. 操作步骤

下方为脱敏后的表结构,目前已有 80G 的数据,业务依赖 created_at 作为保留日期参考字段,目前有 4~8 月的数据。

CREATE TABLE `xxxx_log` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
  `user_id` bigint(20) NOT NULL COMMENT '用户id',
  `user_name` varchar(60) DEFAULT NULL COMMENT '用户名',
  `user_ip` varchar(60) NOT NULL COMMENT '用户ip',
  `service_ip` varchar(60) NOT NULL COMMENT '服务端ip',
  `url` varchar(500) NOT NULL COMMENT '访问url',
  `req_method` varchar(60) DEFAULT NULL COMMENT '请求类型',
  `access_time` bigint(20) DEFAULT NULL COMMENT '请求时间',
  `service_id` varchar(60) DEFAULT NULL COMMENT '服务id',
  `parameter` varchar(500) DEFAULT NULL COMMENT '请求参数',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `api_id` bigint(20) DEFAULT NULL COMMENT '资源ID',
  `request_result` varchar(200) DEFAULT NULL COMMENT '请求结果',
  `response_param` text COMMENT '响应出参'
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`) USING BTREE,
  KEY `idx_created_at` (`created_at`) USING BTREE,
  KEY `idx_service_id` (`service_id`) USING BTREE,
  KEY `idx_server_id` (`server_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='请求日志表';

2.1 调整主键

调整主键,该操作不会锁表,不会影响用户写入,但是会造成一定负载,建议业务低峰执行:

ALTER TABLE xxxx_log DROP PRIMARY KEY, ADD PRIMARY KEY (id, created_at), ALGORITHM=INPLACE, LOCK=NONE;

2.2 无锁变更

gh-ost 的使用方法参加之前的文档:

无锁变更工具使用说明:MySQL gh-ost DDL 变更工具

分区表执行的 DDL 语句如下:

ALTER TABLE xxxx_log
PARTITION BY RANGE(to_days(created_at)) (
    PARTITION p2024_01 VALUES LESS THAN (to_days('2024-02-01')),
    PARTITION p2024_02 VALUES LESS THAN (to_days('2024-03-01')),
    PARTITION p2024_03 VALUES LESS THAN (to_days('2024-04-01')),
    PARTITION p2024_04 VALUES LESS THAN (to_days('2024-05-01')),
    PARTITION p2024_05 VALUES LESS THAN (to_days('2024-06-01')),
    PARTITION p2024_06 VALUES LESS THAN (to_days('2024-07-01')),
    PARTITION p2024_07 VALUES LESS THAN (to_days('2024-08-01')),
    PARTITION p2024_08 VALUES LESS THAN (to_days('2024-09-01')),
    PARTITION p2024_09 VALUES LESS THAN (to_days('2024-10-01')),
    PARTITION p2024_10 VALUES LESS THAN (to_days('2024-11-01')),   
    PARTITION p2024_11 VALUES LESS THAN (to_days('2024-12-01')),     
    PARTITION p2024_12 VALUES LESS THAN (to_days('2025-01-01')) 
);

执行完后,该表就被改造为分区表。

2.3 回滚策略

调整主键,由于 id 本身就是唯一的,所以对业务来说没有影响,不需要回滚。

调整分区表,从刚才的原理介绍可以了解到,整个过程只会增加负载,在 copy 数据到影子表的过程中,切换后还可以选择保留原表,测试无误后删除,随时可以再 rname 回去。

3. 分区表维护

3.1 创建分区

需要提前创建好分区,否则插入数据会失败,调整分区表的语句,已经创建了 2024 年整年的分区,所以到 2025 年之前,需要提前创建好 2025 年的分区,这个业务负责人和 DBA 都需要注意,否则会造成故障,分区要提前创建。

-- 创建 2025 年的分区 SQL 语句。
ALTER TABLE xxxx_log ADD PARTITION (
  PARTITION p2025_01 VALUES LESS THAN (to_days('2025-02-01')),
  PARTITION p2025_02 VALUES LESS THAN (to_days('2025-03-01')),
  PARTITION p2025_03 VALUES LESS THAN (to_days('2025-04-01')),
  PARTITION p2025_04 VALUES LESS THAN (to_days('2025-05-01')),
  PARTITION p2025_05 VALUES LESS THAN (to_days('2025-06-01')),
  PARTITION p2025_06 VALUES LESS THAN (to_days('2025-07-01')),
  PARTITION p2025_07 VALUES LESS THAN (to_days('2025-08-01')),
  PARTITION p2025_08 VALUES LESS THAN (to_days('2025-09-01')),
  PARTITION p2025_09 VALUES LESS THAN (to_days('2025-10-01')),
  PARTITION p2025_10 VALUES LESS THAN (to_days('2025-11-01')),
  PARTITION p2025_11 VALUES LESS THAN (to_days('2025-12-01')),
  PARTITION p2025_12 VALUES LESS THAN (to_days('2026-01-01'))
);

3.2 删除分区

清理数据,了解业务只需要保留 3 个月的数据,那么可以直接 drop 分区清理数据,比如清理 2024 年第一季度的数据。

ALTER TABLE xxxx_log DROP PARTITION p2024_01, p2024_02, p2024_03;

3.3 分区表查询

分区表查询的方式和普通表没有差别,不过建议查询时带上分区字段,否则查询要扫描所有的分区,会比较慢。当然也可以直接选择在某个分区里面查询。

-- 在 p2024_04 查询最大和最小的 created_at
SELECT max(created_at), min(created_at) FROM xxxx_log PARTITION (p2024_04);

后记

这类日志表类型的表,需要定期清理和归档,且业务平时也不会查询,历史数据都是静态的,分区表的特性就比较友好。改造为分区表后可大幅提升可维护性。

到此这篇关于MySQL 日志表改造为分区表的文章就介绍到这了,更多相关MySQL 日志表改分区表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • mysqli多查询特性 实现多条sql语句查询

    mysqli多查询特性 实现多条sql语句查询

    mysqli相对于mysql有很多优势,mysqli连接数据库和mysqli预处理prepare使用,不仅如此,mysqli更是支持多查询特性
    2012-12-12
  • MySQL中的行级锁、表级锁、页级锁

    MySQL中的行级锁、表级锁、页级锁

    这篇文章主要介绍了MySQL中的行级锁、表级锁、页级锁,以及分享了多种避免死锁的方法,感兴趣的小伙伴们可以参考一下
    2016-01-01
  • MySQL中四种常见的备份表方式详解

    MySQL中四种常见的备份表方式详解

    MySQL备份是数据库管理的核心环节之一,通过备份能够有效地防止数据丢失,确保数据的安全和恢复能力,备份的方式多种多样,本文给大家介绍了四种常见的MySQL备份表的方式,需要的朋友可以参考下
    2026-03-03
  • Mysql 常用的时间日期及转换函数小结

    Mysql 常用的时间日期及转换函数小结

    本文是脚本之家小编给大家总结的一些常用的mysql时间日期以及转换函数,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-05-05
  • mysql中获取一天、一周、一月时间数据的各种sql语句写法

    mysql中获取一天、一周、一月时间数据的各种sql语句写法

    今天抽时间整理了一篇mysql中与天、周、月有关的时间数据的sql语句的各种写法,部分是收集资料,全部手工整理,自己学习的同时,分享给大家,并首先默认创建一个表、插入2条数据,便于部分数据的测试,其中部分名词或函数进行了解释说明。直入主题
    2014-05-05
  • mysql中coalesce()的使用技巧小结

    mysql中coalesce()的使用技巧小结

    在mysql中,其实有不少方法和函数是很有用的,这次介绍一个叫coalesce的,拼写十分麻烦,但其实作用是将返回传入的参数中第一个非null的值,下面这篇文章主要给大家介绍了在mysql中coalesce()使用技巧的相关资料,需要的朋友可以参考下。
    2017-06-06
  • 微信开发中mysql字符编码问题

    微信开发中mysql字符编码问题

    本文给大家介绍微信开发过程中mysql字符编码问题,本文介绍的非常详细,感兴趣的朋友一起来学习吧
    2015-08-08
  • 远程连接mysql数据库注意事项记录(远程连接慢skip-name-resolve)

    远程连接mysql数据库注意事项记录(远程连接慢skip-name-resolve)

    有时候我们需要远程连接mysql数据库,就需要注意下面的问题,方便大家解决,脚本之家小编特为大家准备了一些资料
    2012-07-07
  • mysql 设置查询缓存

    mysql 设置查询缓存

    查询缓存绝不返回过期数据。当数据被修改后,在查询缓存中的任何相关词条均被转储清除。
    2009-08-08
  • 通过mysql show processlist 命令检查mysql锁的方法

    通过mysql show processlist 命令检查mysql锁的方法

    show processlist 命令非常实用,有时候mysql经常跑到50%以上或更多,就需要用这个命令看哪个sql语句占用资源比较多,就知道哪个网站的程序问题了。
    2010-03-03

最新评论