摘要

某些场景会有这样的需求:无记录则插入,有记录则更新。例如:新增用户,以身份证号码作为唯一身份标识,插入时若先查询是否存在记录再决定插入还是更新,在高并发情况下必然存在问题。本文提供三种解决方案。

方案一 加锁

无论通过synchronized锁、ReentranLock锁还是分布式锁,都可以解决该问题。缺点是,加锁会影响性能。方法二和三都是数据库层面解决方案,个人感觉比方法一好一些。

方案二 Unique和Replace Into … SELECT …

首先对唯一性的字段添加唯一索引ALTER TABLE tb_name ADD UNIQUE (col1、col2...),,通过唯一索引即可保证数据的唯一性。

加入唯一索引后,通过INSERT INTO插入相同数据就会报错,此时需要使用REPLACE INTO插入数据,用法是一样的。通过REPLACE INTO插入数据时,若存在相同数据,会将之前的记录删除,再重新插入数据。缺点是,存在先删除再插入的过程,sql需要考虑全部数据列,不然会丢失部分列的数据。缺点是,建立唯一索引会影响插入效率。下面是具体的例子。

# 建立索引ALTER TABLE user ADD UNIQUE (id_card);# 假设user表只有id,name,id_card三个字段,且id字段自增。# 现在需要插入name=ly,id_card=142733的记录。# 但是,若之前存在id_card=142733的记录,修改name=ly即可。REPLACE INTO user (id,name,id_card) SELECT id,'ly',142733 FROM user RIGHT JOIN (SELECT 1) AS tab ON user.id_card = 142733;

方案三 通过预插入语句判断是否存在记录

通过预插入语句,尝试插入,判断修改的记录是否大于0,若大于0表示插入成功,若为0则表示记录已存在,需要执行更新操作。

# 预插入INSERT INTO user (name,id_card)SELECT 'ly',142733 FROM DUAL WHERE NOT EXISTE (SELECT id_card FROM user WHERE id_card = 142733) ;# 若预插入语句插入成功(修改记录数=1),则无需后续操作。否则执行更新操作。UPDATE user SET name = 'ly' WHERE id_card = 142733;

若不存在id_card=142733的记录,则伪表DUAL记录为一行且内容是'ly',142733,预插入语句修改记录为1,此时不必执行更新语句。

Mysql批量插入更新性能优化

对于数据量较大的插入和更新,因io/cpu等性能瓶颈,会产生大量的时间消耗,目前主流的优化主要包括预编译、单条sql插入多条数据、事务插入等,下面详细介绍一下:

单条插入(Mybatis)

INSERT INTO SYS_CITY (CITY_CODE, CITY_NAME, PROVINCE_NAME, ALIAS, ABBRE_PY) VALUES(${cityCode}, ${cityName}, ${provinceName}, ${alias}, ${abbrePy})
INSERT INTO SYS_CITY (CITY_CODE, CITY_NAME, PROVINCE_NAME, ALIAS, ABBRE_PY) VALUES(#{cityCode}, #{cityName}, #{provinceName}, #{alias}, #{abbrePy})
INSERT INTO SYS_CITY (CITY_CODE, CITY_NAME, PROVINCE_NAME, ALIAS, ABBRE_PY) VALUES("cityCode1", "cityName1", "provinceName1" "alias1", "abbrePy1"),("cityCode2", "cityName2", "provinceName2" "alias2", "abbrePy2")

1、合并后日志量(MySQL的binlog和innodb的事务让日志)减少了,降低日志刷盘的数据量和频率,从而提高效率;

2、通过合并SQL语句,减少网络传输的IO;

3、通过合并SQL语句,减少SQL语句解析的次数;

注意事项

1、数据库sql长度是有限制,sql长度别溢出,会报错;

2、乱序插入时候速度超过innodb_buffer的容量,每次定位索引涉及较多的磁盘读写操作,性能下降较快;

事务插入

事务插入即在插入前开启事务,插入结束关闭事务进行提交即可。

快的原因

1、进行一个INSERT操作时,MySQL内部会建立一个事务,在事务内才进行真正插入处理操作。通过使用事务可以减少创建事务的消耗;

注意事项

1、事务不能过大,MySQL有innodb_log_buffer_size配置项,事务超出这个时,会刷磁盘,导致性能下降;

2、乱序插入时候速度超过innodb_buffer的容量,每次定位索引涉及较多的磁盘读写操作,性能下降较快;

测试结果

环境:i5-4200U 1.6GHZ,12G内存,固态硬盘

\ :单条插入: :单条预编译: :单条插入多条: :事务插入:
1000 4600毫秒 3334毫秒 8毫秒 704毫秒
10000 27204毫秒 26249毫秒 2959毫秒 2959毫秒
100000 240954毫秒 254716毫秒 17286毫秒 20539毫秒

总结

采用合并sql+事务插入组合,效率最高,乱序插入时候速度超过innodb_buffer的容量,每次定位索引涉及较多的磁盘读写操作,性能下降较快;尽量采用非乱序方式即可。以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

更多相关文章

  1. android如何实现开机自动启动Service或app
  2. Android如何实现全屏
  3. Android(安卓)如何实现屏幕转换方向
  4. Android:如何实现例如iOS的listview 的弹性效果
  5. Android(安卓)Activity生命周期是如何实现的
  6. android APP如何实现launcher
  7. Android如何实现ListView的Item松开渐变效果?
  8. 如何实现对Android设备进行文本的模拟输入
  9. 在android平台上如何实现H264解码

随机推荐

  1. Android 开发环境安装配置手册
  2. Android(安卓)MonkeyRunner测试NotePad例
  3. MainFest.xml android
  4. 20个Android游戏源码,…
  5. js 判读是iPhone还是Android
  6. android tween动画效果
  7. android在google map上画出导航路线图
  8. Android APN
  9. Android免费课程分享
  10. Android:TextView文字跑马灯的效果实现