sunchaojin的java博客

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            11 隨筆 :: 0 文章 :: 46 評論 :: 0 Trackbacks

          一.問題的背景

               前段時間,我們作了一個項目,讓客戶使用了一段時間后發(fā)現(xiàn)一些問題,我們打開客戶的數(shù)據(jù)庫看了看,發(fā)現(xiàn)存在很多重復(fù)數(shù)據(jù),重復(fù)數(shù)據(jù)從何而來,我看了看我們的程序,好像我們的代碼已經(jīng)保證了數(shù)據(jù)的唯一性。當(dāng)時通過我深入的研究發(fā)現(xiàn)在里面存在一個大大問題。

          二.問題的引入 

          我們程序的思路代碼思路大致如下(這里為了闡述問題,只是舉個簡單例子)
          1.
          假如存在一個表student,表結(jié)構(gòu)如下:

             studentid int (z主鍵,遞增字段,MsSql 2000通過identity標識,oracle通過sequence和觸發(fā)器來實現(xiàn))
             name    varchar(20)
             age      int
          假如此表的數(shù)據(jù)如下:

          studentid

          name

          age

          1

          張三

          16

          2

          李四

          20

          3

          王五

          23

          ...

          ...

          ...

           
          2. 程序處理步驟:
             (a)外部傳來一個stuidstunamestuage

             (b)先根據(jù)外部傳來的stuid在數(shù)據(jù)庫中查詢此stuid對應(yīng)的記錄行,如果數(shù)據(jù)庫里沒有此條件的記錄,則把   (stuid,stuname,stuage)插入數(shù)據(jù)庫;如果數(shù)據(jù)庫里有此紀錄行,則把此記錄行的studentnameage列的數(shù)據(jù)改為stuname,stuage;程序代碼如下:

          public class Student{
            
          public static void insert(String stuname,String stuage)
              {
                 
          try{
                     DataBase db
          =new DataBase();//對數(shù)據(jù)庫連接,查詢進行了封裝
                     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是自動產(chǎn)生的,oracle里用sequence實現(xiàn)
                      }else{//如果找著了stuid對應(yīng)的行,則更新
                            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處理

              當(dāng)然,程序是通過servlet來調(diào)用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不仔細看,還真覺得沒什么問題,程序好像也保證了向數(shù)據(jù)庫插入或更新數(shù)據(jù)的唯一性。哈哈,這只是表面現(xiàn)象,因為事實是數(shù)據(jù)庫里存在重復(fù)數(shù)據(jù)是千真萬確的。如果你不信,現(xiàn)在來測試一下。我們用多線程來測試,每個線程就相當(dāng)于一個客戶端。線程如下:

          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();
                  }
              }
          }
             從上面的代碼可以看出,我要向數(shù)據(jù)庫里插入studentname='小龍' and  studentage=20的數(shù)據(jù),但先要檢查是否存在studentid=3的記錄,如果沒有才插入。通過此線程我們會發(fā)現(xiàn)數(shù)據(jù)庫里有時候會出現(xiàn)兩條關(guān)于name為='小龍'并且age=20的記錄。 有時候運行此測試代碼一段時間后數(shù)據(jù)里依然沒有重復(fù)數(shù)據(jù),你可以把此代碼同時放到多個機器,如果是雙核cpu的話出現(xiàn)重復(fù)的機率可能會大一些。
             大概你已經(jīng)知道問題所在,問題就在于并發(fā)。因為有很多用戶可能同時在向數(shù)據(jù)庫發(fā)送請求。

           

          public class Student{
            
          public static void insert(String stuname,String stuage)
              {
                 
          try{
                     DataBase db
          =new DataBase();//對數(shù)據(jù)庫連接,查詢進行了封裝
                     db.conectDb();
                     String sql
          ="select * from student where name='"+stuname+"' ";
                     ResultSet rs
          =db.query(sql);
                     
          boolean b=false;
                      String stuid="";
           /*假設(shè)有兩個線程同時運行到此處,假設(shè)這兩個線程都查找student表里name為"小龍"的記錄,此時他們都會發(fā)現(xiàn)student表里沒有記錄,所以他們都會向student表里插入“小龍”的記錄,這就造成了重復(fù)記錄。*/

                     
          while(rs.next){
                          stuid=rs.getString("studentid");
                           b
          =true;
                          break;
                      }
                      if(b)//如果表里沒有stuname,則插入一條新紀錄
                      {
                            sql
          ="insert into student (name,age)values("'"+stuname+"'","+age+")";//student表里的studentid是自動產(chǎn)生的,oracle里用sequence實現(xiàn)
                      }else{//如果找著了stuid對應(yīng)的行,則更新
                            sql=”update student set name='"+stuname+"'",age="+age+"  where studentid="+stuid;
                      }
                      db.update(sql);

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


          上述紅體分析是產(chǎn)生數(shù)據(jù)庫出現(xiàn)重復(fù)的原因,也是程序員容易犯的一個錯誤,最簡單的解決辦法是加上唯一性約束條件, 假設(shè)表student的name是唯一的,那我們就給name加唯一性約束unique。所以數(shù)據(jù)庫會保證只有一個唯一確定的name,當(dāng)兩個請求同時向數(shù)據(jù)庫插入相同的name時,會采用搶占式插入,誰先插入其他方就不能再插入數(shù)據(jù)。

          上述方法解決了數(shù)據(jù)庫里出現(xiàn)重復(fù)性數(shù)據(jù)問題。但還可以用其他的方法解決,這就涉及到數(shù)據(jù)庫的事務(wù)的并發(fā)控制。下次再討論。
          posted on 2007-05-14 15:15 sunchaojin 閱讀(1761) 評論(2)  編輯  收藏

          評論

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

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


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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 大石桥市| 洛川县| 西藏| 遂溪县| 洪洞县| 新余市| 天水市| 乳源| 通化县| 土默特左旗| 杭锦后旗| 太湖县| 建宁县| 鹤岗市| 涞水县| 错那县| 灵台县| 平潭县| 昌江| 万载县| 临漳县| 剑阁县| 新余市| 西贡区| 上高县| 天全县| 山东| 荃湾区| 崇文区| 宁都县| 景东| 唐山市| 固始县| 高唐县| 财经| 城固县| 历史| 宜良县| 平塘县| 高唐县| 时尚|