当前位置: 首页 > 创领中心 > 网络优化

这才是批量Update的正确姿态!

  • 网络优化
  • 2024-11-15

最近我有位小同伴问我,在实践上班中,批量降级的代码要怎样写。

这个疑问挺有代表性的,当天拿进去给大家一同分享一下,宿愿对你会有所协助。

有一天上午,在我的常识星球群里,有位小同伴问了我一个疑问:批量降级你们普通是经常使用when case吗?还是有其余的批量降级方法?

我的回答是:我们星球的商城名目中,有批量降级的代码可以参考一下,这个名目中很多代码,大家往常可以多看看。

而后我将关键代码发到群里了,这是批量重置用户明码的业务场景:

<update parameterType="cn.net.susan.entity.sys.UserEntity"><foreach collection="list" item="entity" separator=";">UPDATE sys_userSET password = #{entity.password},update_user_id=#{entity.updateUserId},update_user_name=#{entity.updateUserName}<where>id = #{entity.id}</where></foreach></update>

有小同伴说,第一次性见到这种写法,涨常识了。

还有小同伴问,上方这种写法,跟间接for循环中update有什么区别?

for(UserEntity userEntity: list) {userMapper.update(userEntity);}

间接for循环须要屡次恳求数据库,网络有必定的开支,很显然没有批量一次性恳求数据库的好。

有小伙说,他之前不时都是用的case when的写法。

相似上方这样的:

<update parameterType="cn.net.susan.entity.sys.UserEntity">update sys_user<trim prefix="set" suffixOverrides=","><trim prefix="password = case id" suffix="end,"><foreach collection="list" item="item">when #{item.id} then #{item.password}</foreach></trim><trim prefix="update_user_id = case id" suffix="end,"><foreach collection="list" item="item">when #{item.id} then #{item.updateUserId}</foreach></trim><trim prefix="update_user_name = case id" suffix="end"><foreach collection="list" item="item">when #{item.id} then #{item.updateUserName}</foreach></trim></trim><where>id in (<foreach collection="list" separator="," item="item">#{item.id}</foreach>)</where></update>

但这种写法显然须要拼接很多条件,有点复杂,而且性能也不太好。

还有些文章中引见,可以经常使用在insert的时刻,可以在语句最后加上ON DUPLICATE KEY UPDATE关键字。

<update parameterType="cn.net.susan.entity.sys.UserEntity">insert into sys_user(id,username,password) values<foreach collection="list" index="index" item="item" separator=",">(#{item.id},#{item.username},#{item.password})</foreach>ON DUPLICATE KEY UPDATEpassword=values(password)</update>

在拔出数据时,数据库会先判别数据能否存在,假设不存在,则口头拔出操作。假设存在,则口头降级操作。

这种形式我之前也用过,普通须要创立惟一索引。

由于很多时刻主键id,是智能增长的或许依据雪花算法生成的,每次都不一样,没法辨别屡次相反业务参数恳求的惟一性。

因此,倡导创立一个惟一索引,来保障业务数据的惟一性。

比如:给username创立惟一索引,在insert的时刻,发现username已存在,则口头update操作,降级password。

这种形式批量降级数据,性能比拟好,但普通的大公司很少会用,由于十分容易发生死锁的疑问。

因此,目前批量降级数据最好的选用,还是我在文章扫尾引见的第一种方法。

群里另外一位小同伴,依照我的倡导,在自己的名目中尝试了一下foreach的这种批量降级操作,但代码报了一个意外:

sql injection violation, multi-statement not allow

这个意外是阿里巴巴druid包的WallFilter中报进去了。

它外面有个checkInternal方法,会对sql语句做一些校验,假设不满足条件,就会抛意外:

而druid自动不允许一条sql语句中蕴含多个statement语句,例如:我们的批量update数据的场景。

此外,MySQL自动也是封锁批量降级数据的,不过我们可以在jdbc的url要上,参与字符串参数:&allowMultiQueries=true,开启批量降级操作。

比如:

datasource:type: com.alibaba.druid.pool.DruidDataSourcedruid:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/console?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=trueusername: rootpassword: root

这个改变十分便捷。

但WallFilter中的校验疑问如何处置呢?

于是,我上网查了一下,可以经过参数调整druid中的filter的判别逻辑,比如:

spring:datasource:url: jdbc:xxx&serverTimeznotallow=Asia/Shanghai&rewriteBatchedStatements=true&allowMultiQueries=trueusername: xxxpassword: xxxdriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:filter:wall:config:multi-statement-allow: truenone-base-statement-allow: true

经过设置filter中的multi-statement-allow和none-base-statement-allow为true,这样就能开启批量降级的性能。

普通经常使用druid的datasource性能,经过上方这样调整是OK的。

但有些小同伴发现,我们的商城名目中,经过上方的两个中央的修正,还是不时报上方的意外:

sql injection violation, multi-statement not allow

这是怎样回事呢?

答:我们商城名目中的订单表,经常使用shardingsphere做了分库分表,并且经常使用baomidou成功多个数据源灵活切换的性能:

<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.1.1</version></dependency>

我们是经常使用了baomidou包下的数据源性能,这特性能在DynamicDataSourceProperties类中:

这个类是数据库的性能类,我们可以看到master和druid的性能是在同一层级的,于是,将application.yml文件中的性能改成上方这样的:

spring:application:name: mall-jobdatasource:dynamic:primary: masterdatasource:master:username: rootpassword: 123456url: jdbc:mysql://localhost:3306/susan_mall?serverTimeznotallow=Asia/Shanghai&characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNulldriver-class-name: com.mysql.cj.jdbc.Driverdruid:wall:multiStatementAllow: truenoneBaseStatementAllow: true

这样改变之后,商城名目中经常使用foreach这种批量降级数据的性能OK了。

本文由一位球友的疑问开局,探讨了批量降级的四种经常出现形式:

虽说有很多种形式,但我团体以为批量update的最佳形式是第2种形式。

但须要须要的中央是,经常使用foreach做批量降级的时刻,一次性性降级的数据不宜太多,尽量控制在1000以内,这样降级的性能还是不错的。

假设须要降级的数据超越了1000,则须要分红多批降级。

此外,假设大家遇到口头批量update操作,不允许批量降级疑问时:

sql injection violation, multi-statement not allow

首先要在数据库衔接的url前面参与&allowMultiQueries=true参数,开启数据的批量降级操作。

假设经常使用了druid数据库驱动的,可以在性能文件中调整filter的参数。

spring:datasource:druid:filter:wall:config:multi-statement-allow: truenone-base-statement-allow: true

关键是multi-statement-allow设置成true。

假设你还经常使用了其余第三方的数据库两边件,比如我经常使用了baomidou成功多个数据源灵活切换的性能。

这时刻,须要检查它的源码,确认它multi-statement-allow的性能参数是怎样性能的,有或许跟druid不一样。

  • 关注微信

本网站的文章部分内容可能来源于网络和网友发布,仅供大家学习与参考,如有侵权,请联系站长进行删除处理,不代表本网站立场,转载联系作者并注明出处:https://www.clwxseo.com/wangluoyouhua/9477.html

猜你喜欢

热门资讯

关注我们

微信公众号