最代码官方的gravatar头像
最代码官方 2017-02-28 22:03:34
java高并发写入用户信息到数据库的几种方法

java高并发写入用户信息到数据库的几种方法

原文:http://blog.csdn.net/dlf123321/article/details/57955491

作者:程序员小董

假定存在这样一种情况
多个用户对数据库进行写,我们的业务逻辑规定,每个用户只能写一次,大部分用户也只发一次请求。
 

public void write(Uers u){
  // do something
}

但是有一种情况(1%的情况下吧)的就是有的用户会发两次甚至更多次写请求(因为数据库限制,我们不方便在主键上做文章)。


如果这个特殊的用户发送的两次请求时间间隔比较大,那就简单了,再每次写入的时候,写去数据库里看看,这个人有没有写过,如果已经写过了,就直接抛弃这个请求。

public void write(Uers u){  
  if(!checkIfExistUser(u)){  
      // do something  
   }  
} 

不过最大的问题就是,如果用户几乎在瞬时,发送了两个写操作。
而且假定我们的do something比较耗时,那么上面的策略就有可能失败。
为啥失败?我不用解释了吧。

那咋办?

方法一

万年不变的synchronized。

public synchronized void write(Uers u){  
  if(!checkIfExistUser(u)){  
      // do something  
   }  
}  

当然,我们得承认,有了上面的方法,就不会出现,数据库里有两条张三的记录了
但上面的锁的粒度太大了,张三写的时候,李四也不能写了。
其实我们想要的只是:张三自己本人,不能同时多次写入。

方法二

类 String 维护一个字符串池。 当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。可见,当String相同时,String.intern()总是返回同一个对象,因此就实现了对同一用户加锁。由于锁的粒度局限于具体用户,使系统获得了最大程度的并发。

public synchronized void write(Uers u){  
    synchronized(u.getUserId.intern()) {  
      // do something  
   }  
} 

上面的思路就保证了张三写的时候,李四可以写,但是不能两个张三一块写。

方法三

其实我个人觉得,方法二已经很好了,如果非要说方法二还有什么问题的话,只能说:
String.inter()的缺陷是类 String 维护一个字符串池是放在JVM perm区的,如果用户数特别多,导致放入字符串池的String不可控,有可能导致OOM错误或者过多的Full GC。
那咋办?

public synchronized void write(Uers u){  
    String userSuffix=getSuffix(u);  
    synchronized(userSuffix.intern()) {  
      // do something  
   }  
} 

至于那个获得后缀的策略,大家自己想。
有了这个策略,我就能保证1亿个用户,可能只有10000个不同的后缀。
有可能张三李四的后缀一样,但是张三李四同时发请求的概率,应该也不会太大。就算真的同时发了,那你等一下不行么?
 

方法四

Map locks = new Map();     
List lockKeys = new List();     
for(int number : 1 - 10000) {     
   Object lockKey = new Object();     
   lockKeys.add(lockKey);     
    locks.put(lockKey, new Object());     
}     
    
public void doSomeThing(String uid) {     
   Object lockKey = lockKeys.get(uid.hash() % lockKeys.size());     
   Object lock = locks.get(lockKey);     
        
   synchronized(lock) {     
      // do something     
   }     
}  

个人感觉和方法三的核心差不多。

方法五

如果是集群情况下,两个张三几乎瞬时进入两台服务器,那java语言级别的锁都得报废。
可以使用redis的分布式锁
参考: Redis下分布式锁的实现

方法六

使用zookeeper
只是听说有这么一个思路,但是本人没用过zookeeper,这个方法就不多说了。


打赏
最近浏览
huanghaik  LV1 2020年12月31日
wkc  LV21 2020年6月28日
zhangchuanda  LV1 2020年6月8日
zhijian992714  LV6 2019年12月31日
346675489  LV2 2019年11月29日
chenlei1996  LV1 2019年8月17日
duan0507 2019年8月5日
暂无贡献等级
dongzhan  LV12 2019年3月6日
从小贼帅  LV9 2018年12月29日
68180556  LV2 2018年11月8日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友