数据插入违反约束

今天做OA的及时消息系统时,

当有大量的消息发网服务器的时候,问题就来了,如果立即持久化到数据库中,那么数据库连接必然不够用,也会占用大量的CPU资源,我就做了一个缓存,大概是这样工作的:

当大量的信息发送到数据库的时候,我首先把它存放在内存中,当数量达到一定的阀值的时候,启动一个线程,执行批插入数据操作,这样做效果相当好~~可是,比这更大的信息发送到数据库,当一条持久化线程还没有运行结束的时候,又有一条持久化线程启动了,这就造成2条线程在执行批插入处理,更大的更多的消息到来的时候,就有可能N条线程一起执行插入数据库操作,这时,问题来了:

因为我插入的数据在数据库中对应了2张表,这2张表是有关系的,存在依赖的,刚才提到的,产生的持久化数据存在一个先后关系,也就是说,后生成的数据有可能会依赖前面产生的数据,因为是N条线程一起执行插入操作,那么就存在一种可能::就是当父数据项没有插入到数据库中的时候,插入子数据必然产生无外键依赖异常, 当我发现这个错误后,我又做了修改:

插入数据库线程,必须排队,只能按照排队的先后顺序,线程一条一条的启动,是用一个队列来实现的,很简单.队列代码如下:

 

package  src.netmes.message.control;
/*
 * 用于消息缓存MessageManagerCache的消息持久化线程协调,
 * 因为只能运行一条持久化线程,这样才能保证在插入数据时候,不会产生没有外键依赖的数据库错误
 
*/

public   class  PThreadList  {
    
private MessageStoreThread head=null;
    
private MessageStoreThread last=null;
    
    
private static PThreadList plist=new PThreadList();
    
private PThreadList(){}
    
public static PThreadList getPThreadList(){
        
return plist;
    }

    
public MessageStoreThread getHead() {
        
return head;
    }

    
public void setHead(MessageStoreThread head) {
        
this.head = head;
    }

    
/*
     * 
     
*/

    
public  synchronized void startIT(MessageStoreThread mt){
          
if(mt==null){//表示启动等待的线程
                  if(head==null)return;
                     
                  head
=head.getNextT();
                   
if(head!=null)
                     start(head);
                      
return;
              }
  
        
/
        if(head==null){//只有第一个线程有启动的机会,其余全部等待
                head=mt;
                last
=mt;
                 start(head);
            }

          
else{
              last.setNextT(mt);
              last
=mt;
            }

          
        
        
    }

    
private void start(Runnable thread){
        
new Thread(thread).start();
        
    }

     
}

 线程代码:

 

package  src.netmes.message.control;

import  java.util.Hashtable;
import  java.util.Vector;
import  src.common.ServiceLocator;
import  src.common.tools;
import  src.netmes.message.MessageServiceIF;

/*
 * 消息批插入BEAN,用于MessageManagerCache的消息持久化
 
*/

public   class  MessageStoreThread  implements  Runnable {
     
     
private static int threadnums=0;//当前持久化线程数量
     private  Hashtable messagequeue=null;
     
private Vector receiverqueue=null;
     
private MessageStoreThread nextT=null;//用于组成线程连表
     private static MessageServiceIF services=(MessageServiceIF) ServiceLocator.findMyBean("MessageService");//用于持久化
     private long  starttime=0;//该线程启动的时间
     
    
    
    
/*
     * 持久化消息队列和接收队列,一定要先持久化消息,在持久化接收消息,防止主键约束
     
*/

     
public MessageStoreThread(Hashtable messagequeue,Vector receiverqueue){
         
this.messagequeue=messagequeue;
         
this.receiverqueue=receiverqueue;
         
          changeThreadNUMS(
1);
     }

     
/*
      * 持久化线程计数,opreation=1表示加 opreation=-1表示减
      *  
*/

     
public synchronized void changeThreadNUMS(int opreation){
         threadnums
=threadnums+opreation;
         
     }

     
public void run(){
         starttime
=System.currentTimeMillis();
        
//tools.printCue("持久化线程:---启动了--"+tools.millsTODateString(System.currentTimeMillis()));
    System.out.println("持久化线程:---启动了--"+tools.millsTODateString(System.currentTimeMillis())+" 剩余线程数量:"+threadnums);
        
        
//changeThreadNUMS(1); 
        try{
             services.addBatchMEG2REV(
this.messagequeue,this.receiverqueue);
        
         }
catch(Exception s){
             tools.printERROR(
" 批插入消息和接收人失败--"+s.getMessage()+" ");
             s.printStackTrace();
         }
finally{
             
             PThreadList.getPThreadList().startIT(
null);//启动等待线程    
         }

         
long time= System.currentTimeMillis()-starttime;
         changeThreadNUMS(
-1); 
         System.out.println(
"持久化线程:---保存数据完成--耗时:"+time+" 毫秒"+" 剩余线程数量:"+threadnums); 
         
         
     }

     
     
     
public MessageStoreThread getNextT() {
            
return nextT;
        }

        
public void setNextT(MessageStoreThread nextT) {
            
this.nextT = nextT;
        }

        
        
protected void finalize()
        
throws Throwable{
            System.out.println(
"huishou le --"+threadnums);
            
        }

        
        
        
//获取和设置
        public long getStarttime() {
            
return starttime;
        }

        
public static int getThreadnums() {
            
return threadnums;
        }

        
public Hashtable getMessagequeue() {
            
return messagequeue;
        }

    
        
public Vector getReceiverqueue() {
            
return receiverqueue;
        }

        
}

 

问题又来了,虽然数据在内存中产生是有先后顺序的,因为并发量很大,他不一定是按照先后顺序加入到数据库中的,当排队执行插入时,有可能在当前运行线程中的数据的父数据是排在后面的线程中的,再次造成了无父数据依赖异常,于是,我再次改进:

如果插入数据发现异常,回滚,并将该线程序放到队列的最末尾,以后排队轮到该线程的时候,再次执行数据库操作,问题顺利解决~~~当时的并发规模是:1000人同时向服务器不间断发送21条数据,对应了三个消息接收人,也就是说,总共会产生21*1000*4条数据,为了让更多的线程排队,我把阀值设置的很小,为3,表示每3条数据就会产生一个持久化线程.测试结果表明:持久化线程序最长排了将近6000个线程等带启动,程序运行良好!数据没有丢失,数据准确性也很高!

现在将并发人数提高到1300人,也就是说会产生1300*21*4条数据!问题又来了,出现大量数据丢失,我也不知道为什么~~~~现在还在改.

不过,实际应用中,应该不会出现上面的问题,原因是:

     1.OA同时在线人数一般不会有1000人.

     2.就算有1000人,他们也不大可能在短时间内产生大量数据,如上提到的

     3.实际应用我的阀值会设置的比较大,也就是说持久化线程生成的数量不可能达到很多!

     4.实际应用应该是服务器,我现在用的是PC机器1.25G的内存.2.0的CPU,机器性能不能等同

 

哈哈哈~~~不要为自己找理由了,继续努力DEBUG吧~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值