惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 江城2211

大数值的精度与格式化显示问题 【知识总结】数据库的事务、并发与锁管理 基于数据源连接,动态构造JPA上下文EntityManager 【转载】使用了HTTPS为啥还要接口数据加密? RSA加密算法,加解密、签名示例 国密SM2算法,加密、签名示例 【知识总结】JVM线程堆栈中的基础概念解读 【问题记录】JVM进程崩溃(hs_err_pid.log致命错误日志) [问题记录】存在视图依赖的数据表,DDL修改字段比如做扩容等注意事项 【编码技巧】批量校验或处理关联引用数据的优化总结 【问题总结】Garmin路线无法同步和地图坐标偏移的解决办法 【工具推荐】磁盘IO检测工具之Diskspd - 江城2211 【编码技巧】总结一个稳定而高效的方法,将二维关系数据转换为树形结构 【编程技巧】SQL脚本快速生成随机测试数据 【使用技巧】CodeDecom.exe批量反编译JAR包+Beyond Compare对比 【问题记录】Cause: java.sql.SQLRecoverableException: No more data to read from socket - 江城2211 JDBC与各数据库产品连接的驱动及URL示例 【问题记录】使用PowerDesigner连接数据库并反向工程生成所有表及关系 使用tcpdump抓取网络包,在wareshark查看对应请求及响应的原始报文
【编程技巧】结合JPA通用分页与排序技术,支持百万级以上数据的DML批量处理方案
江城2211 · 2024-06-22 · via 博客园 - 江城2211

百万级以上的大数据量的批量更新或删除,如果直接采用传统update、delete等DML,在高并发的生产系统有可能导致数据库宕机乃至触发数据库bug导致数据不一致等问题。

考虑了一下可行的解决方案:

方案一,只保留少量数据的删除场景,可以先将保留的数据查出并插入中间表,truncate原表然后再将中间表插入回原表

方案描述已经很明确了,只能适用于非常特定的场景,保留的数据量也很多,或者是数据的更新场景都没法使用

方案二,最朴素的想法,两层循环:外循环每次查询1w条,内循环每次删除1k条delete from t where id in (0…999) 或 delete from t where id= ?1  or  id = ?2 …..

此方案的弊端是每批次操作的数据量太少、应用与DB的交互次数太多,并且根据实际验证SQL脚本太长、in或or的数据量超过500+等情况,在部分数据库上的性能急剧下降。

方案三,采用各数据库的大批量删除方案

此方案的问题是各数据库的方言差异很大,对支持多种数据库的通用产品来说,需要逐个适配。

Oracle:
     delete from products where update_time < to_timestamp(‘2010-01-01 00:00:00’, ‘yyyy-mm-dd hh24:mi:ss’) and rownum <=100000;

SqlServer:
     DELETE TOP (100000)     FROM Purchasing.PurchaseOrderDetail     WHERE DueDate < '20020701';

MySQL:
   delete from t where DueDate < '20020701' limit = 100000;

PostgreSQL:
   delete from t where id in (select id from t where DueDate < '20020701'  limit 100000);

方案四,即另一种方案实践:排序字段分页查询与DML方案+JPA屏蔽数据库差异+数据库索引与批量优势

1、批量删除的场景

// Service层示例代码如下:
String parentId = "50d29225-7fc1-4a5d-a77a-d89e0a315158";
Sort sort = Sort.by(Sort.Direction.ASC, "Id");
Pageable pageable = PageRequest.of(10000, 10, sort);
List list = null;
do {
     list = repository.findByParentId(parentId, pageable);
     if (list == null || list.size() < 10) {  // 不足10w数据,直接删除
         repository.deleteAllByParentId(parentId);
     }
     else {  // 超过10w数据,仅删除前10w数据
         repository.deleteAllByParentIdAndLessThanId(parentId, list.get(list.size()));
     }
} while (list != null && list.size() >= 10);


// Repository层示例代码如下:
@Transactional
@Modifying
@Query(value=”delete from entityName where parentId = ?1")
void deleteAllByParentId(String parentId);

@Transactional
@Modifying
@Query(value=”delete from entityName where parentId = ?1 and id <= ?2")
void deleteAllByParentIdAndLessThanId(String parentId, String id);

@Query(value=”select * from entityName where parentId = ?1")
List queryByParentId(String parentId, Pageable pageable);

2、批量更新的场景

// Service层示例代码:
String parentId = "50d29225-7fc1-4a5d-a77a-d89e0a315158";
Sort sort = Sort.by(Sort.Direction.ASC, "Id");
Pageable pageable = PageRequest.of(10000, 10, sort);
List list = null;
String preid = “”;
do {
     list = repository.queryByParentIdAndGreaterThanId(parentId, preid, pageable);

     if (list == null || list.size() < 10) {  // 不足10w数据,统一更新
        repository.updateByParentIdAndBetweenId(parentId, preid, null);
     }
    else {  // 超过10w数据,仅更新两次id之间的10w数据
        repository.updateByParentIdAndBetweenId(parentId, preid, list.get(list.size()));

        preid = list.get(list.size());
     }
} while (list != null && list.size() >= 10);



// Repository层示例代码:
@Transactional
@Modifying
@Query(value=”update entityName set qty = qty+1 where parentId = ?1 and id > ?2 and (id <= ?3 or  ?3 is null)")
void updateByParentIdAndBetweenId(String parentId, String preId, String nextId);

// 批量更新场景,查询时需要排除已经处理过的数据
@Query(value=”select * from entityName where parentId = ?1 and id > ?2)
List queryByParentIdAndGreaterThanId(String parentId, String id, Pageable pageable);

参考资料:

https://blog.csdn.net/qq_44112474/article/details/109174901 

https://blog.csdn.net/chengyj0505/article/details/128358817 

https://blog.csdn.net/itmyhome1990/article/details/82114519 

https://www.cnblogs.com/ashbur/p/12020584.html 

https://www.cnblogs.com/kerrycode/p/12448322.html

https://blog.csdn.net/caicaimaomao/article/details/123910749

https://blog.csdn.net/Hehuyi_In/article/details/107775528