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

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

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

    sunchaojin的java博客

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

    一.問(wèn)題的背景

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

    二.問(wèn)題的引入 

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

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

    studentid

    name

    age

    1

    張三

    16

    2

    李四

    20

    3

    王五

    23

    ...

    ...

    ...

     
    2. 程序處理步驟:
       (a)外部傳來(lái)一個(gè)stuid,stuname,stuage

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

    public class Student{
      
    public static void insert(String stuname,String stuage)
        {
           
    try{
               DataBase db
    =new DataBase();//對(duì)數(shù)據(jù)庫(kù)連接,查詢進(jìn)行了封裝
               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)//如果表里沒(méi)有stuname,則插入一條新紀(jì)錄
                {
                      sql
    ="insert into student (name,age)values("'"+stuname+"'","+age+")";//student表里的studentid是自動(dòng)產(chǎn)生的,oracle里用sequence實(shí)現(xiàn)
                }else{//如果找著了stuid對(duì)應(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)然,程序是通過(guò)servlet來(lái)調(diào)用Student.insert(stuid,stuname,stuage)來(lái)處理用戶的請(qǐng)求的,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);
            
    }
     


    三.問(wèn)題分析

       從上面代碼片段1-1不仔細(xì)看,還真覺(jué)得沒(méi)什么問(wèn)題,程序好像也保證了向數(shù)據(jù)庫(kù)插入或更新數(shù)據(jù)的唯一性。哈哈,這只是表面現(xiàn)象,因?yàn)槭聦?shí)是數(shù)據(jù)庫(kù)里存在重復(fù)數(shù)據(jù)是千真萬(wàn)確的。如果你不信,現(xiàn)在來(lái)測(cè)試一下。我們用多線程來(lái)測(cè)試,每個(gè)線程就相當(dāng)于一個(gè)客戶端。線程如下:

    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ù)庫(kù)里插入studentname='小龍' and  studentage=20的數(shù)據(jù),但先要檢查是否存在studentid=3的記錄,如果沒(méi)有才插入。通過(guò)此線程我們會(huì)發(fā)現(xiàn)數(shù)據(jù)庫(kù)里有時(shí)候會(huì)出現(xiàn)兩條關(guān)于name為='小龍'并且age=20的記錄。 有時(shí)候運(yùn)行此測(cè)試代碼一段時(shí)間后數(shù)據(jù)里依然沒(méi)有重復(fù)數(shù)據(jù),你可以把此代碼同時(shí)放到多個(gè)機(jī)器,如果是雙核cpu的話出現(xiàn)重復(fù)的機(jī)率可能會(huì)大一些。
       大概你已經(jīng)知道問(wèn)題所在,問(wèn)題就在于并發(fā)。因?yàn)橛泻芏嘤脩艨赡芡瑫r(shí)在向數(shù)據(jù)庫(kù)發(fā)送請(qǐng)求。

     

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

               
    while(rs.next){
                    stuid=rs.getString("studentid");
                     b
    =true;
                    break;
                }
                if(b)//如果表里沒(méi)有stuname,則插入一條新紀(jì)錄
                {
                      sql
    ="insert into student (name,age)values("'"+stuname+"'","+age+")";//student表里的studentid是自動(dòng)產(chǎn)生的,oracle里用sequence實(shí)現(xiàn)
                }else{//如果找著了stuid對(duì)應(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ù)庫(kù)出現(xiàn)重復(fù)的原因,也是程序員容易犯的一個(gè)錯(cuò)誤,最簡(jiǎn)單的解決辦法是加上唯一性約束條件, 假設(shè)表student的name是唯一的,那我們就給name加唯一性約束unique。所以數(shù)據(jù)庫(kù)會(huì)保證只有一個(gè)唯一確定的name,當(dāng)兩個(gè)請(qǐng)求同時(shí)向數(shù)據(jù)庫(kù)插入相同的name時(shí),會(huì)采用搶占式插入,誰(shuí)先插入其他方就不能再插入數(shù)據(jù)。

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

    評(píng)論

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

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


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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲精品不卡视频| heyzo亚洲精品日韩| 亚洲av无码专区国产乱码在线观看| 亚洲av成人无码网站…| 成人爱做日本视频免费| 亚洲日韩AV一区二区三区四区| 国产片AV片永久免费观看 | 日本不卡高清中文字幕免费| 亚洲天堂电影在线观看| 国产大片线上免费观看| 亚洲熟妇无码AV不卡在线播放| 嫩草影院免费观看| 美女扒开尿口给男人爽免费视频| 国产精品麻豆免费版| 免费观看四虎精品成人| 亚洲日韩乱码中文无码蜜桃臀网站| 国产性生大片免费观看性| 亚洲国产综合91精品麻豆| 在线视频精品免费| 色五月五月丁香亚洲综合网| 免费人成在线观看视频播放| a级毛片免费观看在线| 亚洲人成人无码网www电影首页| 无码精品人妻一区二区三区免费看 | 亚洲高清无码专区视频| 久久国产美女免费观看精品| 亚洲国产精品不卡在线电影| 成人免费一级毛片在线播放视频| 亚洲久热无码av中文字幕 | 午夜dj免费在线观看| eeuss影院www天堂免费| 亚洲精品私拍国产福利在线| 无人在线观看免费高清视频 | 免费一区二区视频| a在线免费观看视频| 亚洲va精品中文字幕| 日韩亚洲国产二区| 最近最好最新2019中文字幕免费| 亚洲日产乱码一二三区别| 久久精品国产亚洲Aⅴ蜜臀色欲| 久久精品国产免费观看|