前言
业务有一张日志表,只需要保存 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);
复制代码 后记
这类日志表类型的表,需要定期清理和归档,且业务平常也不会查询,汗青数据都是静态的,分区表的特性就比较友好。改造为分区表后可大幅提拔可维护性。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |