<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)航:
     
    主站蜘蛛池模板: 亚洲午夜一区二区电影院| 羞羞漫画登录页面免费| 成人免费无码视频在线网站| 亚洲精品国产第一综合99久久| 亚洲区小说区图片区| 97在线视频免费公开观看| 亚洲人成人伊人成综合网无码| 国产综合精品久久亚洲| 永久免费av无码网站韩国毛片| 九九免费观看全部免费视频| 久久久久久亚洲精品成人| 波多野结衣免费视频观看| 99re在线精品视频免费| 污污污视频在线免费观看| 亚洲av无码不卡久久| 亚洲精品卡2卡3卡4卡5卡区| 大地资源二在线观看免费高清| 国产精品成人免费观看| 亚洲无码一区二区三区| 国产亚洲福利精品一区| 啊灬啊灬别停啊灬用力啊免费看| 色欲A∨无码蜜臀AV免费播 | 成人免费看黄20分钟| 最新亚洲成av人免费看| 亚洲AV电影天堂男人的天堂| 亚洲色av性色在线观无码| 国产精品亚洲mnbav网站| 免费看www视频| 18观看免费永久视频| 最新亚洲成av人免费看| 无码AV动漫精品一区二区免费| 亚洲中文无码亚洲人成影院| 91情国产l精品国产亚洲区| 国产91精品一区二区麻豆亚洲| 免费无遮挡无码视频网站| 亚洲一区二区免费视频| 久久国产精品成人免费| 国产精品无码永久免费888| 日本精品久久久久久久久免费| 亚洲色成人四虎在线观看| 亚洲av无码一区二区三区观看|