<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

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

    評論

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

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


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


    網站導航:
     
    主站蜘蛛池模板: 亚洲AV无码国产剧情| 亚洲一级高清在线中文字幕| 亚洲AV无码XXX麻豆艾秋| 一二三四免费观看在线视频中文版| 亚洲第一福利视频| 久久综合国产乱子伦精品免费| 亚洲精选在线观看| 国产成人精品免费视| 亚洲男人的天堂久久精品| 美女视频黄免费亚洲| 亚洲中文字幕一区精品自拍| 免费观看的毛片手机视频| 国产精品久久久久久亚洲小说| 国产成人无码a区在线观看视频免费 | 亚洲成人午夜电影| 日本阿v免费费视频完整版| 亚洲一级片在线播放| 免费观看a级毛片| 日韩一级片免费观看| 亚洲国产精品一区二区久久hs| 国产高清不卡免费视频| 亚洲伦理一二三四| 国产女高清在线看免费观看| japanese色国产在线看免费| 亚洲国产国产综合一区首页| 97在线线免费观看视频在线观看| 亚洲熟妇无码AV| 中文字幕亚洲无线码a| 亚洲一区免费视频| 美国毛片亚洲社区在线观看| 亚洲伊人久久大香线蕉综合图片| 久久永久免费人妻精品下载| 亚洲精品无码专区在线| 在线观看亚洲成人| 美女网站免费福利视频| ssswww日本免费网站片| 亚洲精品亚洲人成在线麻豆| 免费在线观看日韩| 8x成人永久免费视频| 亚洲aⅴ无码专区在线观看| 国产aⅴ无码专区亚洲av|