如何在Spring Boot中使用@Scheduled写定时任务判断数据量是否过大,过大则进行分表操作,多张表使用临时视图查询

当数据量过大,在定时任务中执行分表操作

1、复制表结构及数据

在xml中编写复制表结构及数据(newTableName为新表名、originalTableName为原始表名)

只复制表结构:

sql 复制代码
CREATE TABLE ${newTableName} AS SELECT * FROM ${originalTableName} WHERE 1=0;

复制表结构以及数据:

sql 复制代码
CREATE TABLE ${newTableName} AS SELECT * FROM ${originalTableName};

在使用 CREATE TABLE ... AS SELECT * FROM ... 语句时,添加 WHERE 1=0 和不添加的区别在于是否复制原表的数据。

  • 不加 WHERE 1=0:这会将原表中的数据一同复制到新表中。新表将包含原表中所有的行数据。
  • 加上 WHERE 1=0:这样做不会复制任何原表中的数据,只会复制原表的结构(列定义)到新表中,但新表不会包含任何行数据。

因此,如果只复制表的结构而不需要复制数据,可以在 CREATE TABLE ... AS SELECT * FROM ... 语句后面加上 WHERE 1=0。如果需要同时复制表的结构和数据,就不需要添加这个条件。

2、清空原始表中的数据

清空原表:使用 DELETETRUNCATE 语句清空原表中的数据。例如:

  • 使用 DELETE 语句逐行删除原表中的数据:
sql 复制代码
DELETE FROM original_table;
  • 使用 TRUNCATE 语句一次性清空原表中的所有数据:
sql 复制代码
TRUNCATE TABLE original_table;

注意:TRUNCATE 语句会更快地清空表中的数据,但无法回滚操作。

在执行清空原始表中的数据操作之前,请务必备份好原表中的数据,以防止数据丢失或意外删除。

3、示例代码

定时任务类:

java 复制代码
package com.yutu.garden.task;

import com.yutu.garden.mapper.gardens.SanitationJobStatisticsMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 定时分区、分表
 */
@Component
@EnableScheduling
//@ConditionalOnProperty(name = "scheduled.tasks.enabled", havingValue = "true") //application.yml控制所有task任务启动或不启动
public class SplitTrajectoryTableTask {

	@Resource
	private SanitationJobStatisticsMapper sanitationJobStatisticsMapper;

//	@Scheduled(cron = "0 0 * * * ?") //每小时的整点执行一次任务
	@Scheduled(cron = "0 0/1 * * * ?") //一分钟执行一次
	public void checkDataSizeAndSplitTable() {
	
		int trajectorySize = sanitationJobStatisticsMapper.getTrajectorySize(); // 获取数据量
		
		if (trajectorySize >= 200000) { // 判断数据量是否过大
			splitTable(); // 执行分表操作
		}
		
	}
	private void splitTable() {
		// 获取需要分表的原始表名和新表名前缀(根据实际情况设置)
		String originalTableName = "card_device_trajectory_info"; //原始表名
		
		String newTableNamePrefix = "card_device_trajectory_info_"; //新表 (拼接日期)
		
		// 获取当前日期作为分表后缀(或者使用其他规则)
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
		
		String tableSuffix = sdf.format(new Date());
		
		// 生成新表名
		String newTableName = newTableNamePrefix + tableSuffix;
		
		// 创建新的分表 并 将原始表中符合条件的数据迁移到新表(根据实际情况设置条件)
		sanitationJobStatisticsMapper.copyTable(originalTableName,newTableName);
		
		// 更新原始表 (将原始表中的数据清空)注意:测试前先备份好原始数据,以防丢失
		sanitationJobStatisticsMapper.truncateTable(originalTableName);
	}
}

Mapper方法:

java 复制代码
	int getTrajectorySize();

	boolean copyTable(@Param("originalTableName") String originalTableName,@Param("newTableName") String newTableName);

	boolean truncateTable(@Param("originalTableName") String originalTableName);

Sql语句:

xml 复制代码
    <select id="getTrajectorySize" resultType="int">
        select count(*) from card_device_trajectory_info
    </select>

    
    <update id="copyTable">
        <![CDATA[
        CREATE TABLE ${newTableName} AS SELECT * FROM ${originalTableName};
        ]]>
    </update>

    <delete id="truncateTable">
        TRUNCATE TABLE ${originalTableName};
    </delete>

在 Mybatis 的 XML 中,使用 <![CDATA[ ]]> 包裹 SQL 语句是为了防止 XML 解析器将其中的特殊字符(如 <, >)解析成 XML 标签,从而导致语法错误。因此,加上<![CDATA[ ]]> 是一种良好的实践,可以确保 SQL 语句被正确解析。

但是,在某些情况下,如果 SQL 语句中不包含需要转义的特殊字符,也可以省略 <![CDATA[ ]]>。例如,如果 SQL 语句只包含简单的 SELECT 语句,没有特殊字符,那么可以直接写在 <update> 标签内,而无需使用 <![CDATA[ ]]> 包裹。

以下是不使用 <![CDATA[ ]]> 包裹 SQL 语句的示例:

xml 复制代码
<update id="copyTable">
  CREATE TABLE ${newTableName} AS SELECT * FROM ${originalTableName};
</update>

如果 SQL 语句中包含特殊字符或需要转义的内容,建议仍然使用 <![CDATA[ ]]> 对 SQL 进行包裹

4、多表创建临时视图查询

创建临时视图 card_device_trajectory_info_view

sql 复制代码
CREATE TEMPORARY VIEW card_device_trajectory_info_view AS
SELECT *
FROM card_device_trajectory_info  --表1
UNION ALL
SELECT * FROM card_device_trajectory_info_bf; --表2

执行业务查询

sql 复制代码
 select * from card_device_trajectory_info_view where imei = '15127423721' and DATE(gps_time) = CURRENT_DATE order by gps_time asc

手动删除视图资源 (如果不手动删除也会自动删除,所以这一步可以省略)

sql 复制代码
 DROP VIEW card_device_trajectory_info_view;

临时视图普通视图(永久视图)之间有以下区别:

  • 生命周期:临时视图只在当前会话有效,会话结束后会自动删除。而普通视图是永久性的,会一直存在数据库中,除非显式删除。
  • 可见性:临时视图只对创建它的会话可见,其他会话无法访问。而普通视图对所有会话都可见,可以被多个会话共享和使用。
  • 存储方式:临时视图的数据可以存储在内存或者临时表中,查询速度较快。而普通视图的数据存储在磁盘上,查询速度可能相对较慢。
  • 持久性:临时视图是临时创建的,不会被数据库系统持久化存储。而普通视图是一个已经定义好的查询,可以被保存并在需要时重新使用。
  • 数据更新:临时视图一般用于查询数据,不能进行数据更新操作。而普通视图可以根据定义的规则进行数据更新,例如使用触发器或者规定的权限。
  • 使用场景:临时视图通常用于会话级别的临时计算或者中间结果的存储。普通视图用于复杂查询、数据重用和提供简化的数据模型。
sql 复制代码
CREATE TEMPORARY VIEW table_view --创建临时视图(会话结束会自动删除)
CREATE VIEW table_view --创建普通视图(会话结束不会自动删除,需要手动 DROP VIEW table_view 进行删除)

总的来说,临时视图适用于需要在会话中临时存储和处理数据的场景,而普通视图适用于长期的数据查询和数据模型定义。选择使用哪种类型取决于业务需求和数据处理的特点。

相关推荐
李元豪1 小时前
grpo nl2sql qwen3 模型强化学习训练有效果的成立条件有哪些
数据库·oracle
白仑色3 小时前
Spring Cloud Gateway 实战指南
spring boot·微服务·路由转发·限流熔断
Hello.Reader4 小时前
RedisJSON 路径语法深度解析与实战
数据库·redis·缓存
TDengine (老段)5 小时前
TDengine 使用最佳实践(2)
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
设计师小聂!7 小时前
Linux系统中部署Redis详解
linux·运维·数据库·redis
kfepiza7 小时前
Debian-10编译安装Mysql-5.7.44 笔记250706
linux·数据库·笔记·mysql·debian·bash
Touper.7 小时前
Redis 基础详细介绍(Redis简单介绍,命令行客户端,Redis 命令,Java客户端)
java·数据库·redis
不剪发的Tony老师7 小时前
phpMyAdmin:一款经典的MySQL在线管理工具又回来了
数据库·mysql·phpmyadmin
极限实验室7 小时前
TDBC 2025 可信数据库发展大会,极限科技邀您来赴约!
数据库
FreeBuf_8 小时前
黄金旋律IAB组织利用暴露的ASP.NET机器密钥实施未授权访问
网络·后端·asp.net