sunchaojin的java博客

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  11 随笔 :: 0 文章 :: 46 评论 :: 0 Trackbacks

一.问题的背景

     前段时间,我们作了一个项目,让客户使用了一段时间后发现一些问题,我们打开客户的数据库看了看,发现存在很多重复数据,重复数据从何而来,我看了看我们的程序,好像我们的代码已经保证了数据的唯一性。当时通过我深入的研究发现在里面存在一个大大问题。

二.问题的引入 

我们程序的思路代码思路大致如下(这里为了阐述问题,只是举个简单例子)
1.
假如存在一个表student,表结构如下:

   studentid int (z主键,递增字段,MsSql 2000通过identity标识,oracle通过sequence和触发器来实现)
   name    varchar(20)
   age      int
假如此表的数据如下:

studentid

name

age

1

张三

16

2

李四

20

3

王五

23

...

...

...

 
2. 程序处理步骤:
   (a)外部传来一个stuidstunamestuage

   (b)先根据外部传来的stuid在数据库中查询此stuid对应的记录行,如果数据库里没有此条件的记录,则把   (stuid,stuname,stuage)插入数据库;如果数据库里有此纪录行,则把此记录行的studentnameage列的数据改为stuname,stuage;程序代码如下:

public class Student{
  
public static void insert(String stuname,String stuage)
    {
       
try{
           DataBase db
=new DataBase();//对数据库连接,查询进行了封装
           db.conectDb();
           String sql
="select * from student where name='"+stuname+"' ";
           ResultSet rs
=db.query(sql);
           
boolean b=false;
            String stuid="";
           
while(rs.next){
                stuid=rs.getString("studentid");
                 b
=true;
                break;
            }
            if(b)//如果表里没有stuname,则插入一条新纪录
            {
                  sql
="insert into student (name,age)values("'"+stuname+"'","+age+")";//student表里的studentid是自动产生的,oracle里用sequence实现
            }else{//如果找着了stuid对应的行,则更新
                  sql=”update student set name='"+stuname+"'",age="+age+"  where studentid="+stuid;
            }
            db.update(sql);

            db.close();
       }
catch(Exception e)
      {
               db.close();
               e.printStackTrace();
        }
 }

代码片段1-1
 

   (3)servlet处理

    当然,程序是通过servlet来调用Student.insert(stuid,stuname,stuage)来处理用户的请求的,servlet代码如下:

public class InsertServlet extends HttpServlet {
    
   
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       String stuname
=request.getParameter(“stuname”);
       String stuage
=request.getParameter(“stuage”);
          Student.insert(stuname,stuage);
        
}
 


三.问题分析

   从上面代码片段1-1不仔细看,还真觉得没什么问题,程序好像也保证了向数据库插入或更新数据的唯一性。哈哈,这只是表面现象,因为事实是数据库里存在重复数据是千真万确的。如果你不信,现在来测试一下。我们用多线程来测试,每个线程就相当于一个客户端。线程如下:

public class Test extends Thread{
    
    
public Test(){
        start();
    }
    
public void run(){
               Student.insert(
"小龙", 20+"");
        }
    
public static void main(String[] args){
        
        
for(int i=0; i<1000; i++){
            
new Test();
        }
    }
}
   从上面的代码可以看出,我要向数据库里插入studentname='小龙' and  studentage=20的数据,但先要检查是否存在studentid=3的记录,如果没有才插入。通过此线程我们会发现数据库里有时候会出现两条关于name为='小龙'并且age=20的记录。 有时候运行此测试代码一段时间后数据里依然没有重复数据,你可以把此代码同时放到多个机器,如果是双核cpu的话出现重复的机率可能会大一些。
   大概你已经知道问题所在,问题就在于并发。因为有很多用户可能同时在向数据库发送请求。

 

public class Student{
  
public static void insert(String stuname,String stuage)
    {
       
try{
           DataBase db
=new DataBase();//对数据库连接,查询进行了封装
           db.conectDb();
           String sql
="select * from student where name='"+stuname+"' ";
           ResultSet rs
=db.query(sql);
           
boolean b=false;
            String stuid="";
 /*假设有两个线程同时运行到此处,假设这两个线程都查找student表里name为"小龙"的记录,此时他们都会发现student表里没有记录,所以他们都会向student表里插入“小龙”的记录,这就造成了重复记录。*/

           
while(rs.next){
                stuid=rs.getString("studentid");
                 b
=true;
                break;
            }
            if(b)//如果表里没有stuname,则插入一条新纪录
            {
                  sql
="insert into student (name,age)values("'"+stuname+"'","+age+")";//student表里的studentid是自动产生的,oracle里用sequence实现
            }else{//如果找着了stuid对应的行,则更新
                  sql=”update student set name='"+stuname+"'",age="+age+"  where studentid="+stuid;
            }
            db.update(sql);

            db.close();
       }
catch(Exception e)
      {
               db.close();
               e.printStackTrace();
        }
 }


上述红体分析是产生数据库出现重复的原因,也是程序员容易犯的一个错误,最简单的解决办法是加上唯一性约束条件, 假设表student的name是唯一的,那我们就给name加唯一性约束unique。所以数据库会保证只有一个唯一确定的name,当两个请求同时向数据库插入相同的name时,会采用抢占式插入,谁先插入其他方就不能再插入数据。

上述方法解决了数据库里出现重复性数据问题。但还可以用其他的方法解决,这就涉及到数据库的事务的并发控制。下次再讨论。
posted on 2007-05-14 15:15 sunchaojin 阅读(1713) 评论(2)  编辑  收藏

评论

# re: 出现数据库重复数据的分析与解决 2007-09-21 16:38 WangUta
想看看涉及到数据库的事务的并发控制的讨论。
现在正在研究数据重复产生的原因及避免办法,希望能交流一下。  回复  更多评论
  

# re: 出现数据库重复数据的分析与解决[未登录] 2007-09-21 19:03 sunchaojin
您好,数据库的事务的并发控制的讨论,过段时间再讨论。现在正忙于翻译HTTP1.1协议  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: