数据库性能优化之分表

embedded/2024/9/25 3:11:19/

```markdown
# 1 背景

在生产站点,我们发现 MySQL 任务表的数据超过了 1700 万行,占用了高达 23G 的空间,导致数据库性能急剧下降,并出现了大量的 504 错误。分析数据后发现,有两个客户疯狂地创建任务,其中一个客户每月超过 150 万条。

我们面临一个问题:只要有 10 个客户每天生成 150 万条任务,在线表的容量就会达到极限。未来,如何处理 1500 万条数据?

# 2 表结构分析

```sql
CREATE TABLE
    IF NOT EXISTS `task`
(
    `customer_id`             CHAR(36) COLLATE `ascii_bin`                NOT NULL,
    `created_time`            INT UNSIGNED                                NOT NULL,
    `root_task_id`            CHAR(17) COLLATE `ascii_bin`                NOT NULL,
    `sub_task_sequence`       SMALLINT UNSIGNED                           NOT NULL,
    `sub_task_id`             CHAR(36) COLLATE `ascii_bin`                NOT NULL,
    `action`                  TINYINT                          DEFAULT -1 NOT NULL,
    
    `task_status`             TINYINT                          DEFAULT -1 NOT NULL,
    `ui_root_task_status`     TINYINT                          DEFAULT -1 NOT NULL,
    xxxx
    PRIMARY KEY (
                 `customer_id`,`created_time` DESC, `root_task_id` DESC,`sub_task_sequence`
        )
) COMMENT 'Response Task Table';
```

分析上述表结构,表中的字段数已达到 实际40 多个,未来可能会进一步增加。

常见的处理方法是分库分表。分表有两种类型:水平分表和垂直分表。

# 3 第一步

## 3.1 分离读写服务

创建两个连接池,一个用于写操作(主库),另一个用于读操作(从库)。根据操作类型选择相应的连接池。

## 3.2 清理 180 天的数据

用户数据保留时间能否缩短?90 天?60 天?(审计日志可追溯)

## 3.3 添加 UDSO 限制 1 万(保存 2 万历史任务)(1 天)

发现大多数客户调用添加 UDSO。

公共接口每天仅触发 1 万次

添加 UDSO 仅保存 2 万行历史数据,因为每天仅有 1 万次

限制每日公共 API 调用次数

## 3.4 监控任务数量

如果超过 100 万,将显示错误日志(同步任务或清理任务)(0.5 天)

# 4 第二步

## 4.1 按业务垂直分表

### 4.1.1 按 rootTask 和 subTask 分表

根据 rootTask 和 subTask 分表的主要原因是将获取 subTask 转变为异步请求。这样,用户请求任务接口将大大减少 504 错误,并进一步提高响应速度。

### 4.1.2 将 subTable 分为主表和次表

当前的 subTask 表太大,未来字段可能会继续增加。将其拆分为常用字段和不常用字段,或需要更改的字段等,以减少每次读取数据的压力。

## 4.2 水平分表

### 4.2.1 按时间范围分表

响应业务的特点是具有时间概念。可以使用 MySQL 分区表。每个月的数据存储在一个独立的分区中,后续删除数据时只需删除整个分区。大多数业务查询只需查询当前和最近的分区数据。

分区表实际上有一个独立的物理表,但在逻辑上看起来像一个表。这一特性意味着在业务层面不需要进行重大更改。

### 4.2.2 按 customer_id 分表

目前,仅有部分客户的数据量很大。如果未来监控到某些客户的数据量较大,可以将这些客户的数据提取出来放在一个独立的表中。然而,如果未来超过 10 个客户的数据量很大,一个月的数据量将达到 1500 万。

## 4.3 水平分表 + 垂直分表

假设我们当前 MySQL 表能够承载的数据量为 1000 万,约 12GB 数据

如果表水平分表,rootTask 分为三个表,单表容量为 5GB。如果这样,单表可以承载 2000 万数据

如果按时间分表,每个月创建一个表,用 6 个表(180 天)来分担压力。这样,20 个客户每个月可以生成 100 万数据。

如果每天有超过 20 个客户这样做,可能需要考虑按客户分表。

具体分析:

1 按照时间分表

# 1 创建分区表
CREATE TABLE task_partitioned (`customer_id`             CHAR(36) COLLATE `ascii_bin`                NOT NULL,`created_time`            INT UNSIGNED                                NOT NULL,..............PRIMARY KEY (`customer_id`, `created_time` DESC, `root_task_id` DESC, `sub_task_sequence`)
) PARTITION BY LIST RANGE (created_time) (PARTITION sp0 VALUES LESS THAN (UNIX_TIMESTAMP('2024-07-01')),PARTITION sp1 VALUES LESS THAN (UNIX_TIMESTAMP('2024-08-01')),PARTITION sp2 VALUES LESS THAN (UNIX_TIMESTAMP('2024-09-01'))
);

创建触发器,Event,每个月自动建立表

2 按照customer_id分表

  1. 创建新分区表 or 针对某些customer新建一张表

# 1 创建分区表
CREATE TABLE task_partitioned (`customer_id`             CHAR(36) COLLATE `ascii_bin`                NOT NULL,`created_time`            INT UNSIGNED                                NOT NULL,..............PRIMARY KEY (`customer_id`, `created_time` DESC, `root_task_id` DESC, `sub_task_sequence`)
) PARTITION BY LIST COLUMNS (customer_id) (PARTITION p0 VALUES IN ('68960c94-9be6-4343-a4ca-6408de7aa331', '64d5ff66-4157-4a93-bd27-a991277d809b'),
);

2.数据迁移

  • 先将这部分customer 某时间T1之前的数据全部迁移过去

INSERT INTO `task_partitioned` SELECT * FROM `task` WHERE customer_id in ('68960c94-9be6-4343-a4ca-6408de7aa331', '64d5ff66-4157-4a93-bd27-a991277d809b') and created_time <1719195232;

代码层修改,写task表时候,需要同时写task和task_partitioned表

  • 数据(T1-T2数据迁移)

  • 数据检查

  • 数据切读,某部分customer读新表

  • 切换只写新表

  1. 后期维护

目前只有EU环境中两个customer数据比较多,未来如果有超过几十甚至上百个customer,可能需要,将customer都迁移出来,那本次的设计考虑到未来的迁移,需要有一定扩展性。

  • 新增分区(又有customer数据非常多)

ALTER TABLE task_partitioned
ADD PARTITION (PARTITION p_new VALUES IN ('new_customer_id'));
  • 再次进行数据平滑迁移

优化方案-----无数据迁移

1 plan overview

  • 考虑task本身的特性(存在有效期,90天)

  • build上线后用户数据全部保存至新表中,旧表的历史数据不迁移仅更新

  • 共存阶段同时查询新表和旧表数据做数据聚合,运行3个月后(旧库数据已全部失效)

  • 用户请求的读写全部切到新库中,旧库部分可以删除。

2 details

  1. 创建新表

新表可以采用分区表(按照customer和时间都可以),表结构与原来的保持一致

# 1 创建分区表 CREATE TABLE task_partitioned ( `customer_id` CHAR(36) COLLATE `ascii_bin` NOT NULL, `created_time` INT UNSIGNED NOT NULL, .......... ......... PRIMARY KEY (`customer_id`, `created_time` DESC, `root_task_id` DESC, `sub_task_sequence`) ) PARTITION BY LIST COLUMNS (customer_id) ( PARTITION p0 VALUES IN ('68960c94-9be6-4343-a4ca-6408de7aa331', '64d5ff66-4157-4a93-bd27-a991277d809b'), PARTITION p_default VALUES IN ('default_customer') );

2.业务层修改

(1)读的时候,读的是新表和旧表,然后进行数据耦合

(2)写:写的时候只写新表

但是后续有其他的用户数据增多了,可能还需要增加配置写入新表中


http://www.ppmy.cn/embedded/116412.html

相关文章

Python闭包与装饰日高级概念

在Python中&#xff0c;闭包&#xff08;Closure&#xff09;和装饰器&#xff08;Decorator&#xff09;是两个高级且强大的编程概念&#xff0c;它们在函数式编程和面向对象编程中扮演着重要角色。下面将详细讲解这两个概念。 一、闭包&#xff08;Closure&#xff09; 1. …

如何使用ssm实现基于VUE的儿童教育网站的设计与实现+vue

TOC ssm676基于VUE的儿童教育网站的设计与实现vue 第一章 课题背景及研究内容 1.1 课题背景 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全…

vue3开发中易遗漏的常见知识点

文章目录 组件样式的特性Scoped CSS之局部样式的泄露Scoped CSS之深度选择器CSS Modules在CSS中使用v-bind 非props属性继承组件通信父子组件的相互通信props/$emit父组件传递数据给子组件子组件传递数据给父组件 非父子组件的相互通信Provide/inject全局事件总线 组件插槽作用…

WPF 控件数据源绑定

WPF 控件数据源绑定 前提&#xff1a;我的数据源都放在 DataProcessView 类中&#xff0c;然后在 MainWindow 中声明该类的对象 DataProcess&#xff0c;如果是指定了 DataContext &#xff0c;就将该对象赋值给 DataContext &#xff08;如下&#xff09;&#xff0c;否则不赋…

SpringBoot基础知识

谈一谈你对SpringBoot的理解&#xff0c;它有哪些特性&#xff08;优点&#xff09;&#xff1f; SpringBoot用来快速开发Spring应用的一个脚手架&#xff0c;其目的是用来简化新Spring应用的初始搭建以及开发过程。 优点&#xff1a; 简化配置&#xff1a;提供了很多内置的…

工作笔记:Vue 3 中使用 vue-router 进行导航与监听路由变化

一、使用 useRouter 进行导航 在 Vue 3 组件中&#xff0c;你可以使用 useRouter 来方便地进行页面导航。以下是一个简单的示例&#xff1a; <template><button click"navigateToHome">跳转到首页</button> </template><script setup&g…

使用HID硬件实现自动化脚本防检测、防风控

我们在做自动化脚本的过程中&#xff0c;经常会遇到风控问题&#xff0c;比如游戏脚本&#xff0c;视频脚本等。有些app会检测手机是否root、是否开启调试模式、是否开启无障碍模式。如果我们使用的平台有开启这些就有可能被检测到&#xff0c;我们可以使用HID硬件来模拟外接键…

IDE配置Java注释模板

步骤很详细&#xff0c;直接上教程 一. 配置类注释 /** * ClassName : ${NAME} * Description : ${description} * Author : [Your Name] * Date: ${YEAR}-${MONTH}-${DAY} ${HOUR}:${MINUTE} */效果演示 二. 配置方法注释 **$params$* return * Author: Amoorzheyu *…