关于on duplicate key update导致主键不连续自增的问题
最近项目上需要实现这么一个功能:从服务器数据库获取一个json字符串,通过解析获得一个10万量的数据,存入本本地数据库,最开始采用先更新后新增的逻辑进行操作,通过测试逐条更新执行完,整个代码是在10分钟左右,效率低下,所以采用了如下代码<insert id="insertPackWmsOperator" parameterType="java.util.List" useGeneratedK
最近项目上需要完成一个功能:从服务器数据库获取一个json字符串,通过解析获得一个10万量的数据,存入本本地数据库,最开始采用先更新后新增的逻辑进行操作,通过测试逐条更新执行完,整个代码是在10分钟左右,效率低下,所以采用了如下代码
<insert id="insertPackWmsOperator" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
insert into pack_wms_operator(user_id,user_name,create_time,del_falg) VALUES
<foreach collection="list" separator="," item="item">
(#{item.userId},#{item.userName},#{item.createTime},#{item.delFalg})
</foreach>
ON duplicate KEY UPDATE
`user_name`=VALUES(user_name)
</insert>
为了效率用到了foreach 进行批量新增以及更新,又采用了ON duplicate KEY UPDATE 进行自动判断是更新还是新增,以唯一索引 user_id作为条件 进行操作,一段时间后发现该表的主键id(已设置主键自增),不是连续的自增,总是跳跃的增加,这样就造成id自增过快,会慢慢超过最大值了
根据官方文档和各方资料产生的问题有以下几点:
1、mysql数据库innodb_autoinc_lock_mode锁的问题,默认为1
(1)为0时是每次分配自增id的时候都会锁表.
(2)为1时只有在bulk insert的时候才会锁表,简单insert的时候只会使用一个light-weight mutex,比0的并发性能高
(3)为2时取消auto_inc锁,这个模式下的性能是最好的;但是它也有一个问题,就是
对于同一个语句来说它所得到的auto_incremant值可能不是连续的。2、使用
ON DUPLICATE KEY UPDATE
时,如果将行作为新行插入,则每行的受影响行值为 1,如果更新现有行,则为 2,如果将现有行设置为其当前值,则为 0。(换句话说就是默认级别为1,在进行insert操作时此时会锁表所以只能影响一行,但是ON DUPLICATE KEY UPDATE本质是先删除后插入,遇到重复是会进行回滚所以它占了两个索引,一切是以主键索引自增为基础的
)
总结下来要解决id跳跃自增有三个方法:
1.先更新后新增
把sql分为update和insert两个操作,先更新后添加(这种方法也是试过的,只适合小批量的数据更新,大批量看着这效率头大,这种方法肯定还能细化提升效率,但本人学疏才浅只能放弃)
2.改变数据库锁级别
改变innodb_autoinc_lock_mode锁的级别,改为0,但是由于该代码数据量大,同时需要更新和添加的数据量多,效率也是低下的,其次项目是要上线的供买家使用的环境要统一默认的(不建议私自更改)
3.先删除表内容重置索引后新增
在原有的insert逻辑上加一个删除逻辑,在添加之前执行TRUNCATE TABLE “表名”清空表内数据,并重置索引(目前正在使用,但是还点问题,数据是完全清空的,如果这个表必须有个时间标志,是无法分别出旧数据和新数据,如果哪位大佬有更好的方法,万分感谢)
下面是使用on duplicate key update的心得可以看以下:
1、on duplicate key update 语句根据主键id或唯一键来判断当前插入是否已存在。
2、记录已存在时,只会更新on duplicate key update之后指定的字段。
3、如果同时传递了主键和唯一键,以主键为判断存在依据,唯一键字段内容可以被修改。
4、on dupdate key update之后没有用values的情况
分为两种情况:
1,如果为如上面的name = "abc",则会一直更新为"abc".
2,如果为如上面的name = name ,则name会保持数据库中的值,不会更新。
6,对values使用判断
如下sql
insert into tbl_test(id,name,age,address) values(1,'huawei101',18,'北京市')
on duplicate key update
name = ifnull(values(name),name),
age = values(age)
达到的效果是,如果传入的name值为null,则不更新。不为null则更新。这里与mybatis配合使用比较好。
更多推荐
所有评论(0)