如何处理高并发情况下的DB插入
1、 我们需要接收一个外部的订单,而这个订单号是不允许重复的
2、 数据库对外部订单号没有做唯一性约束
3、 外部经常插入相同的订单,对于已经存在的订单则拒绝处理
对于这个需求,很简单我们会用下面的代码进行处理(思路:先查找数据库,如果数据库存在则直接退出,否则插入)
package com.yhj.test;
import com.yhj.dao.OrderDao;
import com.yhj.pojo.Order;
/**
* @Description:并发测试用例
* @Author YHJ create at 2011-7-7 上午08:41:44
* @FileName com.yhj.test.TestCase.java
*/
public class TestCase {
/**
* data access object class for deal order
*/
private OrderDao orderDao;
/**
* @Description:插入测试
* @param object 要插入的object实例
* @author YHJ create at 2011-7-7 上午08:43:15
* @throws Exception
*/
public void doTestForInsert(Order order) throws Exception {
Order orderInDB = orderDao.findByName(order.getOrderNo());
if(null != orderInDB)
throw new Exception("the order has been exist!");
orderDao.save(order);
}
}
对于这种情况,好像如果不用数据库做唯一性约束又不借助外部其他的一些工具,是没有办法实现的。那怎么做呢?
引入缓存,我们看下面的代码
package com.yhj.test;
import com.yhj.dao.OrderDao;
import com.yhj.pojo.Order;
import com.yhj.util.MemcacheUtil;
import com.yhj.util.MemcacheUtil.UNIT;
/**
* @Description:并发测试用例
* @Author YHJ create at 2011-7-7 上午08:41:44
* @FileName com.yhj.test.TestCase.java
*/
public class TestCase {
/**
* data access object class for deal order
*/
private OrderDao orderDao;
/**
* @Description:插入测试
* @param object 要插入的object实例
* @author YHJ create at 2011-7-7 上午08:43:15
* @throws Exception
*/
public void doTestForInsert(Order order){
String key=null;
try{
Order orderInDB = orderDao.findByName(order.getOrderNo());
//查DB,如果数据库已经有则抛出异常
if(null != orderInDB)
throw new Exception("the order has been exist!");
key=order.getOrderNo();
//插缓存,原子性操作,插入失败 表明已经存在
if(!MemcacheUtil.add(key, order, MemcacheUtil.getExpiry(UNIT.MINUTE, 1)))
throw new Exception("the order has been exist!");
//插DB
orderDao.save(order);
}catch (Exception e) {
e.printStackTrace();
}finally{
MemcacheUtil.del(key);
}
}
}
1、 查找数据库,如果数据库已经存在则抛出异常
2、 插入缓存,如果插入失败则表明缓存中已经存在,抛出异常
3、 如果上述2步都没有抛出异常,则执行插入数据库的操作
4、 删除缓存
机器异常情况下,不能执行finally语句,但是放在memcache中的数据会在1分钟后超时。
貌似没有问题。使用LodeRunner测试100个并发的操作,发现仍然有重复的订单插入,这个是为什么呢?我们再来看这段代码!
public void doTestForInsert(Order order){
String key=null;
try{
Order orderInDB = orderDao.findByName(order.getOrderNo());
//查DB,如果数据库已经有则抛出异常
if(null != orderInDB)
throw new Exception("the order has been exist!");
key=order.getOrderNo();
//插缓存,原子性操作,插入失败 表明已经存在
if(!MemcacheUtil.add(key, order, MemcacheUtil.getExpiry(UNIT.MINUTE, 1)))
throw new Exception("the order has been exist!");
//插DB
orderDao.save(order);
}catch (Exception e) {
e.printStackTrace();
}finally{
MemcacheUtil.del(key);
}
}
时刻1:
时刻
线程2到达,查数据库发现没有
时刻
线程2开始写缓存
时刻
线程2写缓存失败,抛出异常,执行finally
时刻
线程2执行finally,删除缓存,开始构建返回结果
时刻
线程2成功返回
时刻
因此上述代码仍然有插入多条重复记录的可能,我们在并发20的测试中发现成功插入了5笔订单,其中4笔是不应该插入的!
那我们应该怎么解决呢?其实只要解决一个问题,只有插入DB时候的异常是可以删除的,其他地方不应该删除,那能不能将代码改成下面的呢?
public void doTestForInsert(Order order){
String key=null;
try{
Order orderInDB = orderDao.findByName(order.getOrderNo());
//查DB,如果数据库已经有则抛出异常
if(null != orderInDB)
throw new Exception("the order has been exist!");
key=order.getOrderNo();
//插缓存,原子性操作,插入失败 表明已经存在
if(!MemcacheUtil.add(key, order, MemcacheUtil.getExpiry(UNIT.MINUTE, 1)))
throw new Exception("the order has been exist!");
//插DB
orderDao.save(order);
MemcacheUtil.del(key);
}catch (Exception e) {
e.printStackTrace();
}//finally{
// MemcacheUtil.del(key);
// }
}
这样显然不行,为什么呢?
这样是保证了只有插入DB成功了才会删除缓存,但是当插入DB的时候发生了一个异常,删除缓存就不会再执行,虽然我们有一分钟超时,但意味着我们一分钟内该笔订单是不能再被处理的,而实际上这边订单并没有处理成功,所以这样是不满足需求的!
继续改进
代码如下:加一个标志位
public void doTestForInsert(Order order){
String key=null;
boolean needDel=false;
try{
Order orderInDB = orderDao.findByName(order.getOrderNo());
//查DB,如果数据库已经有则抛出异常
if(null != orderInDB)
throw new Exception("the order has been exist!");
key=order.getOrderNo();
//插缓存,原子性操作,插入失败 表明已经存在
if(!MemcacheUtil.getExpiry(UNIT.MINUTE, 1)))
throw new Exception("the order has been exist!");
needDel=true;
//插DB
orderDao.save(order);
}catch (Exception e) {
e.printStackTrace();
}finally{
if(needDel)
}
}
这样是不是完美解决了呢?
在其他异常执行的时候是不会删除缓存的,我们套在之前的代码上,线程2判断缓存中存在抛出异常执行finally的时候是不会删除缓存的,因此线程3没有机会执行写缓存的操作,从而保证了线程1是唯一能够插入DB的。
还有没有其他漏洞呢?期待大家发现……
如何处理高并发情况下的DB插入的更多相关文章
- 关于WCF服务在高并发情况下报目标积极拒绝的异常处理
最近弄了个wcf的监控服务,偶尔监控到目标服务会报一个目标积极拒绝的错误.一开始以为服务停止了,上服务器检查目标服务好好的活着.于是开始查原因. 一般来说目标积极拒绝(TCP 10061)的异常主要是 ...
- WCF服务在高并发情况下报目标积极拒绝的异常处理 z
http://www.cnblogs.com/kklldog/p/5037006.html wcf的监控服务,偶尔监控到目标服务会报一个目标积极拒绝的错误.一开始以为服务停止了,上服务器检查目标服务好 ...
- Jackson高并发情况下,产生阻塞
情况:在高并发情况下,查看线程栈信息,有大量的线程BLOCKED. 从线程栈得知,线程栈中出现了阻塞,锁在了com.fasterxml.jackson.databind.ser.SerializerC ...
- Linux的虚拟内存管理-如何分配和释放内存,以提高服务器在高并发情况下的性能,从而降低了系统的负载
Linux的虚拟内存管理有几个关键概念: Linux 虚拟地址空间如何分布?malloc和free是如何分配和释放内存?如何查看堆内内存的碎片情况?既然堆内内存brk和sbrk不能直接释放,为什么不全 ...
- 高并发情况下分布式全局ID
1.高并发情况下,生成分布式全局id策略2.利用全球唯一UUID生成订单号优缺点3.基于数据库自增或者序列生成订单号4.数据库集群如何考虑数据库自增唯一性5.基于Redis生成生成全局id策略6.Tw ...
- c# redis 利用锁(StackExchange.Redis LockTake)来保证数据在高并发情况下的正确性
之前有写过一篇介绍c#操作redis的文章 http://www.cnblogs.com/axel10/p/8459434.html ,这篇文章中的案例使用了StringIncrement来实现了高并 ...
- 小D课堂 - 新版本微服务springcloud+Docker教程_6-05 高级篇幅之高并发情况下
笔记 5.高级篇幅之高并发情况下接口限流特技 简介:谷歌guava框架介绍,网关限流使用 1.nginx层限流 2.网关层限流 开始 mysql最大的连接数就是3千多.如果想把应用搞好 ...
- Java高并发情况下的锁机制优化
本文主要讲并行优化的几种方式, 其结构如下: 锁优化 减少锁的持有时间 例如避免给整个方法加锁 1 public synchronized void syncMethod(){ 2 othercode ...
- Mysql高并发情况下的解决方案(转)
查询了下Mysql 关于高并发的处理的资料,在这记录一下. 高并发大多的瓶颈在后台数据逻辑处理,在存储,mysql的正常的优化方案如下: 1.代码中sql语句优化 2.数据库字段优化,索引优化 3.加 ...
随机推荐
- Node.js处理I/O数据之Buffer模块缓冲数据
一.前传 在之前做web时也经常用到js对象转json和json转js对象.既然是Node.js处理I/O数据,也把这个记下来. Json转Js对象:JSON.parse(jsonstr); //可以 ...
- ajax读取图片后排列问题(先加载完图片再排列)
网上找了个瀑布流的图片排列插件.从数据库读取图片路径后显示时出现了位置重叠问题. $.ajax({ type: "POST", url: "index.aspx" ...
- spring 下载地址及相关概念解析
http://www.springsource.org/download/community Release Candidate(RC)候选版本,用于指软件或操作系统的发布,一般与Debug版本相对应 ...
- spark集群搭建(java)未完待续
环境 操作系统:windows10 虚拟机工具:VMware14.1 NUX版本:Centos7.2(64) JDK:1.8(64) 一.安装linux,master(桥接模式上网),slave(na ...
- c#基础学习(0806)之抽象类实现多态
首先,要判断是否使用抽象类,可以从下面两个方面进行判断: 1.是不是需要被实例化 2.父类中有没有默认的实现 如果不需要被实例化,父类中没有默认的实现,则用抽象类(否则用虚方法来实现) 下面举个简单的 ...
- BZOJ4466 [Jsoi2013]超立方体
Description 定义"超立方图"为:有\(2^k\)个点,以\(k\)位二进制数编号,两个点之间有边当且仅当它们的编号恰有一位不同.给出一个图,问它是否与"超立方 ...
- VMware安装vnwaretools
1. 在VMware Fusion 6.0.4下安装Ubuntu镜像:ubuntu-14.04.1-desktop-amd64.iso 2. 点击虚拟机菜单栏-安装VMware Tools 3. 进入 ...
- 【小程序】微信小程序绑定企业微信后怎样获取到用户信息
一.获取access_token 1.https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRECT Cor ...
- ulimit命令&pthread_create() error: Resource temporarily unavailable
http://www.ibm.com/developerworks/cn/linux/l-cn-ulimit/ https://my.vertica.com/docs/5.0/HTML/Master/ ...
- Android可伸缩布局-FlexboxLayout(支持RecyclerView集成)
Android可伸缩布局-FlexboxLayout(支持RecyclerView集成) 1 . 前言 前几天看到Google官方的博客介绍了Google开源的一个强大的布局-FlexboxLayou ...