<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協議  回復  更多評論
      


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


    網站導航:
     
    主站蜘蛛池模板: 亚洲成?v人片天堂网无码| 久久久久精品国产亚洲AV无码| 亚洲人成色77777| 精品亚洲aⅴ在线观看| 亚洲激情黄色小说| 中日韩亚洲人成无码网站| 色偷偷亚洲第一综合| 日本中文字幕免费高清视频| 国产曰批免费视频播放免费s| 国产精品美女自在线观看免费| 精品国产日韩亚洲一区| 亚洲日韩中文字幕| 国产成人精品亚洲| 日韩人妻无码精品久久免费一| 日本无卡码免费一区二区三区| 亚洲男人天堂av| 色偷偷亚洲第一综合| 日本最新免费网站| 亚洲情XO亚洲色XO无码| 亚洲爆乳成av人在线视菜奈实 | 久久综合九色综合97免费下载| 免费在线观看a级毛片| 亚洲无线一二三四区| 免费国产叼嘿视频大全网站| 国产99视频免费精品是看6 | 亚洲午夜久久久久久噜噜噜| 亚洲AV无码男人的天堂| 免费国产作爱视频网站| 亚洲AV日韩精品久久久久久久 | 久久久久亚洲精品影视| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 日本妇人成熟免费中文字幕| 国产亚洲人成无码网在线观看 | 国产一级淫片免费播放| 亚洲熟妇无码AV不卡在线播放| 91网站免费观看| 亚洲春色另类小说| 成年女人色毛片免费看| 在线a亚洲老鸭窝天堂av高清| 91在线视频免费91| MM1313亚洲精品无码久久|