Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >[NewLife.XCode]批量添删改操作(提升吞吐率)

[NewLife.XCode]批量添删改操作(提升吞吐率)

作者头像
大石头
发布于 2022-05-10 01:50:51
发布于 2022-05-10 01:50:51
63500
代码可运行
举报
文章被收录于专栏:智能大石头智能大石头
运行总次数:0
代码可运行

NewLife.XCode是一个有15年历史的开源数据中间件,支持netcore/net45/net40,由新生命团队(2002~2020)开发完成并维护至今,以下简称XCode。

整个系列教程会大量结合示例代码和运行日志来进行深入分析,蕴含多年开发经验于其中,代表作有百亿级大数据实时计算项目。

开源地址:https://github.com/NewLifeX/X (求star, 1067+)

大数据分析处理中,需要对海量数据进行添删改操作,常规单行操作难以满足要求,批量操作势在必行!

飞仙(http://feixian.newlifex.com/)有收藏各种数据库批量插入数据的性能排行榜,其中MySql冠军是60万tps,SQLite冠军是56.6万tps

!!阅读本文之前,建议阅读:https://www.yuque.com/smartstone/xcode/curd

批量添加

常规MySql数据库的单行添加性能只有3000tps左右,而使用批量添加以后可轻松增加到20000tps。

先来看批量插入用户:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var list = new List<UserX>();
for (var i = 0; i < 5; i++)
{
    list.Add(new UserX { Name = "name" + i });
}
list.Insert(true);

这是一个对IEnumerable<TEntity>的扩展方法,在支持批量插入的数据库上走批量插入流程,其它走for循环插入。参数true表示启用事务保护,早期不支持批量插入的SQLite版本,事务插入特别重要,100倍以上性能差异。

支持批量插入的数据库技术:

  • MySql、SQLite,生成带有多组values的insert语句,例如

Insert Into table(column1, column2),(v11, v12) values(v21, v22) ... ,(vn1, vn2)

  • Oracle,还是普通的Insert语句,参数化,但每个数值变量传入数组而不是单个数值,同时设置OracleCommand.ArrayBindCount为行数,在设置OracleCommand.BindByName为true;
  • SqlServer,借助特有的SqlBatcher来实现

尽管各家技术截然不同,但XCode做了很好的封装,可以无视底层差别。

PostgreSQL其实也支持MySql那样的批量插入,但是XCode用户极少用PostgreSQL,因此没有封装。

上面批量插入用户代码,在SQLite上得到的SQL语句

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Insert Into User(Name,Password,DisplayName,Sex,Mail,Mobile,Code,Avatar,RoleID,RoleIDs,DepartmentID,Online,Enable,Logins,LastLogin,LastLoginIP,RegisterTime,RegisterIP,Ex1,Ex2,Ex3,Ex4,Ex5,Ex6,UpdateUser,UpdateUserID,UpdateIP,UpdateTime,Remark) 
Values('name0',null,null,0,null,null,null,null,0,null,0,0,0,0,null,null,null,null,0,0,0,null,null,null,null,0,null,'0001-01-01 00:00:00',null),
('name1',null,null,0,null,null,null,null,0,null,0,0,0,0,null,null,null,null,0,0,0,null,null,null,null,0,null,'0001-01-01 00:00:00',null),
('name2',null,null,0,null,null,null,null,0,null,0,0,0,0,null,null,null,null,0,0,0,null,null,null,null,0,null,'0001-01-01 00:00:00',null),
('name3',null,null,0,null,null,null,null,0,null,0,0,0,0,null,null,null,null,0,0,0,null,null,null,null,0,null,'0001-01-01 00:00:00',null),
('name4',null,null,0,null,null,null,null,0,null,0,0,0,0,null,null,null,null,0,0,0,null,null,null,null,0,null,'0001-01-01 00:00:00',null)

此外,还有一个BatchInsert扩展,允许指定要批量插入的列

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var list = new List<UserX>();
for (var i = 0; i < 5; i++)
{
    list.Add(new UserX { Name = "name" + i });
}
var columns = UserX.Meta.Table.DataTable.Columns.Where(e => e.Name == "Name").ToArray();
list.BatchInsert(columns);

得到的SQL语句

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Insert Into User(Name) Values('name0'),('name1'),('name2'),('name3'),('name4')

虽然批量插入性能很高,但并不是越多越好,根据经验,尽量把每一批待插入数据控制在一万行以内,再多的话,生成的Insert语句过长,也是够吃力的。

显而易见,MySql/SQLite的技术通用性强,但是开发者拼接比较吃力;Oracle的批操作技术更灵活,SqlServer需要引入专用依赖,限制有些大。如果各家ADO.Net都能像Oracle这样统一支持批量操作就好了。

在XCode中,强烈建议仅在百万级以上数据表中使用批量插入技术,不建议几十几百行的表也使用,因为它有一些缺点,譬如插入后无法得到自增ID,跟普通循环逐行插入的行为不同。

批量更新

只有Oracle支持批量更新,具体技术跟批量插入一样,因为它是由ADO.Net驱动提供支持。

SqlServer理论上也支持,但没有经过测试。

MySql有Replace之类的操作,但它毕竟不是批量Update。

来看看批量更新的两个扩展

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static Int32 Update<T>(this IEnumerable<T> list, Boolean? useTransition = null) where T : IEntity;
public static Int32 BatchUpdate<T>(this IEnumerable<T> list, IDataColumn[] columns = null, ICollection<String> updateColumns = null, ICollection<String> addColumns = null) where T : IEntity;

对于非Oracle数据库,Update扩展将会走for循环逐行更新。

BatchUpdate支持指定要覆盖更新或者累加更新的字段。

小数据量建议循环更新而不是批量更新!

批量添加或更新

批量Upsert,这是一个丝毫不逊色于批量Insert的大杀器。

在多节点多线程的大数据分析中,很可能多线程都需要修改同一张表,譬如写入统计数据。传统的查找并决定插入或更新很容易带来多线程冲突问题,并且在大表中性能很差。如果能够让数据库决定有则更新无则插入就好了,那就是Upsert,并且是批量Upsert。

MySql的Upsert技术

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
insert into stat (siteid,statdate,`count`,cost,createtime,updatetime) values
(1,'2018-08-11 09:34:00',1,123,now(),now()),
(2,'2018-08-11 09:34:00',1,456,now(),now()),
(3,'2018-08-11 09:34:00',1,789,now(),now()),
(2,'2018-08-11 09:34:00',1,456,now(),now())
on duplicate key update 
`count`=`count`+values(`count`),cost=cost+values(cost),
updatetime=values(updatetime);

SQLite的Upsert技术

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
insert into stat (siteid,statdate,`count`,cost,createtime,updatetime) values
(1,'2018-08-11 09:34:00',1,123,now(),now()),
(2,'2018-08-11 09:34:00',1,456,now(),now()),
(3,'2018-08-11 09:34:00',1,789,now(),now()),
(2,'2018-08-11 09:34:00',1,456,now(),now())
On Conflict(siteid,statdate) Do Update Set 
count=count+excluded.count,cost=cost+excluded.cost,
updatetime=excluded.updatetime;

跟MySql很像,但是要指定一个唯一索引的字段,很不方便。

Oracle的技术

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var sb = Pool.StringBuilder.Get();
sb.AppendLine("BEGIN");
sb.AppendLine(insert + ";");
sb.AppendLine("EXCEPTION");
// 没有更新时,直接返回,可用于批量插入且其中部分有冲突需要忽略的场景
if (!update.IsNullOrEmpty())
{
    sb.AppendLine("WHEN DUP_VAL_ON_INDEX THEN");
    sb.AppendLine(update + ";");
}
else
{
    //sb.AppendLine("WHEN OTHERS THEN");
    sb.AppendLine("WHEN DUP_VAL_ON_INDEX THEN");
    sb.AppendLine("RETURN;");
}
sb.AppendLine("END;");

SqlServer的技术

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 先更新,根据更新结果影响的条目数判断是否需要插入
var sb = Pool.StringBuilder.Get();
sb.Append(update);
sb.AppendLine(";");
sb.AppendLine("IF(@@ROWCOUNT = 0)");
sb.AppendLine("BEGIN");
sb.Append(insert);
sb.AppendLine(";");
sb.AppendLine("END;");

来个批量更新用户的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var list = new List<UserX>();
for (var i = 0; i < 5; i++)
{
    list.Add(new UserX { ID = i + 1, Name = "name" + i });
}
list.Upsert();

在SQLite上得到语句

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Insert Into User(Name,Password,DisplayName,Sex,Mail,Mobile,Code,Avatar,RoleID,RoleIDs,DepartmentID,Online,Enable,Logins,LastLogin,LastLoginIP,RegisterTime,RegisterIP,Ex1,Ex2,Ex3,Ex4,Ex5,Ex6,UpdateUser,UpdateUserID,UpdateIP,UpdateTime,Remark) 
Values('name0',null,null,0,null,null,null,null,0,null,0,0,0,0,null,null,null,null,0,0,0,null,null,null,null,0,null,'0001-01-01 00:00:00',null),
('name1',null,null,0,null,null,null,null,0,null,0,0,0,0,null,null,null,null,0,0,0,null,null,null,null,0,null,'0001-01-01 00:00:00',null),
('name2',null,null,0,null,null,null,null,0,null,0,0,0,0,null,null,null,null,0,0,0,null,null,null,null,0,null,'0001-01-01 00:00:00',null),
('name3',null,null,0,null,null,null,null,0,null,0,0,0,0,null,null,null,null,0,0,0,null,null,null,null,0,null,'0001-01-01 00:00:00',null),
('name4',null,null,0,null,null,null,null,0,null,0,0,0,0,null,null,null,null,0,0,0,null,null,null,null,0,null,'0001-01-01 00:00:00',null) 
On Conflict(Name) Do Update Set Name=excluded.Name,Logins=Logins+excluded.Logins

这样表有个唯一索引Name字段,同时Logins打开了累加,因此生成的语句也有所不同。

批量删除

实体列表的批量删除扩展并非数据库功能,而是由XCode检测主键,构造in操作的delete语句。

批量删除用户的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var list = new List<UserX>();
for (var i = 0; i < 5; i++)
{
    list.Add(new UserX { ID = i + 1, Name = "name" + i });
}
list.Delete();

得到语句

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Delete From User Where ID In(1,2,3,4,5)

最后再次提醒,批量操作不是万能灵药,一定要慎用!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-01-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
[NewLife.XCode]实体队列(多线程生产的大数据集中保存)
NewLife.XCode是一个有15年历史的开源数据中间件,支持netcore/net45/net40,由新生命团队(2002~2020)开发完成并维护至今,以下简称XCode。
大石头
2022/05/10
4890
[NewLife.XCode]实体队列(多线程生产的大数据集中保存)
[NewLife.XCode]高级增删改
NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netstandard,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
大石头
2019/05/24
1.6K0
[NewLife.XCode]增删改查入门
NewLife.XCode是一个有10多年历史的开源数据中间件,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
大石头
2019/05/24
8680
[NewLife.XCode]实体类详解
NewLife.XCode是一个有10多年历史的开源数据中间件,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
大石头
2019/05/24
1.3K0
[NewLife.XCode]分表分库(百亿级大数据存储)
NewLife.XCode是一个有15年历史的开源数据中间件,支持netcore/net45/net40,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
大石头
2019/09/12
1.1K0
[NewLife.XCode]分表分库(百亿级大数据存储)
[NewLife.XCode]增量累加
NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netstandard,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
大石头
2019/05/24
1.7K0
C# 几种数据库的大数据批量插入
转载:http://www.cnblogs.com/luluping/archive/2012/08/09/2629515.html
跟着阿笨一起玩NET
2018/09/20
3K0
SqlAlchemy 2.0 中文文档(十七)
本节利用了首次在 SQLAlchemy 统一教程中展示的 ORM 映射,如声明映射类一节所示,以及映射类继承层次结构一节中展示的继承映射。
ApacheCN_飞龙
2024/06/26
4970
GZY.EFCore.BulkExtensions 支持达梦数据库的EF Core批量操作库详解
EFCore.BulkExtensions是一个常用的EF core 批量处理数据的库.
GuZhenYin
2024/12/06
1670
[NewLife.XCode]角色权限
NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
大石头
2019/07/02
2.2K0
Mybatis批量操作解析
我们在项目中会有一些批量操作的场景,比如导入文件批量处理数据的情况(批量新增商户、批量修改商户信息),当数据量非常大,比如超过几万条的时候,在Java代码中循环发送SQL到数据库执行肯定是不现实的,因为这个意味着要跟数据库创建几万次会话。即使在同一个连接中,也有重复编译和执行SQL的开销。 例如循环插入10000条(大约耗时3秒钟)∶
向着百万年薪努力的小赵
2022/12/02
1K0
Mybatis批量操作解析
C#开发BIMFACE系列14 服务端API之批量获取转换状态详情
上一篇《C#开发BIMFACE系列13 服务端API之获取转换状态》中介绍了根据文件ID查询单个文件的转换状态。
张传宁IT讲堂
2019/09/18
6070
[NewLife.XCode]导入导出(实体对象百变魔君)
NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
大石头
2019/07/01
1.3K0
[NewLife.XCode]导入导出(实体对象百变魔君)
EFCore批量操作,你真的清楚吗
EntityFramework Core有许多新的特性,其中一个重要特性便是批量操作。批量操作意味着不需要为每次Insert/Update/Delete操作发送单独的命令,而是在一次SQL请求中发送批量组合指令。
心莱科技雪雁
2019/12/05
3.5K0
mybatis-plus批量插入你用对了吗
记得有一次我们小组code review,组长看了下我们批量插入是使用mybatis原生的xml foreach实现的,于是二话不说,拍桌子,说这有性能问题。叫我们直接使用mybatis-plus,可是为啥呢?怎么用,需要注意哪些地方,也没给我们说个明白。好吧,我们对这一块也没具体调研过,就直接按他的想法去实现了。性能有没有提升了好几倍呢,其实也没实践过,反正review过了。直到有一天。。。
阿建dong一点
2022/11/26
3.8K1
架构师技能9-深入mybatis:Creating a new SqlSession到查询语句耗时特别长
撸码人平时大多数时间都在撸码或者撸码的路上,很少关注框架的一些底层原理,当出现问题时没能力第一时间解决问题,出现问题后不去层层剖析问题产生的原因,后续也就可能无法避免或者绕开同类的问题。因此不要单纯做Ctrl+c和Ctrl+V,而是一边仰望星空(目标规划),一边脚踏实地去分析每个问题。
黄规速
2024/05/24
3890
架构师技能9-深入mybatis:Creating a new SqlSession到查询语句耗时特别长
spring-data-mongodb之批量更新操作
用过mongodb的同学想必都知道,以java驱动的语法举例,插入式有insert方法的,支持插入集合,也就是批量插入。 但是update方法却只能执行一个更新条件,参数不支持传集合进去,也就意味着是不知道批量更新的。 当然原生的语法是支持的,只是驱动没有封装而已,官方文档也是推荐用db.runCommand()来实现的。 下面的语法中我们可以看到updates是个数组,可以执行多条更新语句,但是我们一般是在项目中使用,如果封装这个方法就行批量插入一样,今天我们就用spring-data-mongodb来做
猿天地
2018/04/03
5K0
Mybatis Plus 批量插入性能优化,非常实用!
物联网平台背景,传感器采集频率干到了1000Hz,分了100多张表出来,还是把mysql干炸了。当前单表数据量在1000来w,从kafka上拉数据异步批量插入,每次插入数据量1500条,测试的时候还没问题,结果上线没多久,kafka服务器直接挂了,赶忙看日志,kafka服务器堆积了几十G的数据,再去看生产环境日志,发现到最后单次批量插入用时固定在10多秒,甚至20多秒,kafka直接把消费端踢出了消费组…从而kafka消息一直没有消费,总重导致kafka数据堆积挂掉了…
码猿技术专栏
2023/05/01
7.6K2
Mybatis Plus 批量插入性能优化,非常实用!
【MyBatis-4】MyBatis批量insert、update、delete数据
在日常开发中,批量操作数据库数据是常见场景,比如批量插入、批量修改、批量删除数据。MyBatis为批量操作数据提供了非常便利的方案。
云深i不知处
2020/09/16
3K0
Mybatis批量插入或更新的正确姿势
另外一篇博客是对本文的补充,也可以参考一下:https://blog.csdn.net/w605283073/article/details/88652042
全栈程序员站长
2022/11/18
1.6K0
Mybatis批量插入或更新的正确姿势
相关推荐
[NewLife.XCode]实体队列(多线程生产的大数据集中保存)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验