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 閱讀(1761) 評論(2)  編輯  收藏

          評論

          # re: 出現數據庫重復數據的分析與解決 2007-09-21 16:38 WangUta
          想看看涉及到數據庫的事務的并發控制的討論。
          現在正在研究數據重復產生的原因及避免辦法,希望能交流一下。  回復  更多評論
            

          # re: 出現數據庫重復數據的分析與解決[未登錄] 2007-09-21 19:03 sunchaojin
          您好,數據庫的事務的并發控制的討論,過段時間再討論。現在正忙于翻譯HTTP1.1協議  回復  更多評論
            


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 平安县| 卢湾区| 盈江县| 育儿| 宜州市| 墨江| 万荣县| 邯郸县| 阜平县| 读书| 延长县| 白玉县| 庄河市| 板桥市| 盐边县| 土默特左旗| 滨海县| 页游| 兴安县| 广饶县| 通化县| 许昌市| 运城市| 凌云县| 富锦市| 青神县| 抚宁县| 五寨县| 汶上县| 忻城县| 延边| 家居| 彰武县| 门头沟区| 曲沃县| 交城县| 平利县| 绥德县| 江北区| 漳浦县| 务川|