springboot 如何删除上亿的数据?
在 Spring Boot 中删除上亿条数据是一个具有挑战性的任务,因为直接一次性删除大量数据可能会导致数据库性能问题、事务超时、锁表等。以下从不同角度介绍一些可行的方法。
1. 分批删除
分批删除是一种常见且有效的策略,将大量数据分成较小的批次进行删除操作,避免一次性处理过多数据对数据库造成过大压力。以下是使用 Spring Data JPA 实现分批删除的示例代码:
实体类
收起
java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class LargeData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 其他属性和 Getter、Setter 方法
// 为简洁起见,这里省略
}
数据访问层(Repository)
收起
java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public interface LargeDataRepository extends JpaRepository<LargeData, Long> {
@Transactional
@Modifying
@Query("DELETE FROM LargeData WHERE id IN :ids")
void deleteByIds(List<Long> ids);
}
服务层
收起
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
public class LargeDataService {
@Autowired
private LargeDataRepository largeDataRepository;
private static final int BATCH_SIZE = 1000;
@Transactional
public void deleteLargeData() {
List<LargeData> allData = largeDataRepository.findAll();
List<Long> idsToDelete = new ArrayList<>();
for (LargeData data : allData) {
idsToDelete.add(data.getId());
if (idsToDelete.size() == BATCH_SIZE) {
largeDataRepository.deleteByIds(idsToDelete);
idsToDelete.clear();
}
}
if (!idsToDelete.isEmpty()) {
largeDataRepository.deleteByIds(idsToDelete);
}
}
}
控制器层
收起
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LargeDataController {
@Autowired
private LargeDataService largeDataService;
@GetMapping("/delete-large-data")
public String deleteLargeData() {
largeDataService.deleteLargeData();
return "Data deleted successfully";
}
}
2. 基于条件范围删除
如果数据具有一定的范围属性(如时间、编号等),可以按照范围分批删除。例如,根据时间范围删除数据:
数据访问层(Repository)添加方法
收起
java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
public interface LargeDataRepository extends JpaRepository<LargeData, Long> {
@Transactional
@Modifying
@Query("DELETE FROM LargeData WHERE createTime BETWEEN :startDate AND :endDate")
void deleteByTimeRange(Date startDate, Date endDate);
}
服务层修改
收起
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Calendar;
import java.util.Date;
@Service
public class LargeDataService {
@Autowired
private LargeDataRepository largeDataRepository;
private static final int DAYS_RANGE = 30;
@Transactional
public void deleteLargeDataByTimeRange() {
Calendar calendar = Calendar.getInstance();
Date endDate = calendar.getTime();
calendar.add(Calendar.DAY_OF_MONTH, -DAYS_RANGE);
Date startDate = calendar.getTime();
while (startDate.before(endDate)) {
largeDataRepository.deleteByTimeRange(startDate, endDate);
calendar.add(Calendar.DAY_OF_MONTH, -DAYS_RANGE);
endDate = startDate;
startDate = calendar.getTime();
}
}
}
3. 利用数据库特性
不同的数据库有不同的优化删除大量数据的方法:
MySQL
- 使用
TRUNCATE
语句:如果要删除整个表的数据,TRUNCATE
比DELETE
更快,因为它不记录每一行的删除操作,而是直接释放表空间。但TRUNCATE
不能带条件,会删除整个表的数据。
收起
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class LargeDataService {
@Autowired
private JdbcTemplate jdbcTemplate;
public void truncateTable() {
jdbcTemplate.execute("TRUNCATE TABLE large_data");
}
}
PostgreSQL
- 使用
DELETE
结合LIMIT
:可以在DELETE
语句中使用LIMIT
分批删除数据。
收起
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class LargeDataService {
@Autowired
private JdbcTemplate jdbcTemplate;
private static final int BATCH_SIZE = 1000;
public void deleteLargeData() {
int deletedRows;
do {
deletedRows = jdbcTemplate.update("DELETE FROM large_data WHERE id IN (SELECT id FROM large_data LIMIT ?)", BATCH_SIZE);
} while (deletedRows > 0);
}
}
4. 考虑性能优化
- 关闭自动提交:在进行大量删除操作时,关闭自动提交事务,手动控制事务的提交和回滚,可以减少事务开销。
收起
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LargeDataService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void deleteLargeData() {
// 执行删除操作
jdbcTemplate.execute("DELETE FROM large_data WHERE some_condition");
}
}
- 索引处理:在删除大量数据前,可以考虑暂时禁用或删除相关索引,删除完成后再重新创建索引,这样可以减少删除过程中索引维护的开销。
5. 异步处理
由于删除大量数据可能会花费较长时间,为了避免阻塞主线程影响系统的响应性能,可以使用异步处理。在 Spring Boot 中可以使用 @Async
注解实现异步方法:
开启异步支持
在主应用类上添加 @EnableAsync
注解:
收起
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
服务层添加异步方法
收起
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LargeDataService {
@Autowired
private LargeDataRepository largeDataRepository;
@Async
@Transactional
public void deleteLargeDataAsync() {
// 执行删除逻辑
}
}
通过上述方法,可以在 Spring Boot 中较为安全、高效地删除上亿条数据。具体使用哪种方法,需要根据数据特点、数据库类型和业务需求来决定。