程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> UNABLE TO PURGE A RECORD(二),unablepurge

UNABLE TO PURGE A RECORD(二),unablepurge

編輯:MySQL綜合教程

UNABLE TO PURGE A RECORD(二),unablepurge


      上一篇文章說明了bug出現的原因和原理分析,要修復bug似乎已經水到渠成了,但遠沒有這麼簡單,只因為“並發”。要修復問題,首先要做的第一件事情是穩定的復現問題。由於數據庫系統是一個並發系統,並且這個bug只有一定的概率出現,更說明了多個線程在一定的執行序列情況下才會出現這個bug。在沒有用戶請求的情況下,mysql自身的線程就很多,比如主線程,IO線程,監控線程,監聽線程等;有用戶請求的情況下,還會分配工作線程為用戶服務。我們只找和bug相關的線程:purge線程,IO線程,工作線程。Purge線程主要工作是執行purge操作,產生IBUF_OP_DELETE操作;工作線程則主要執行DELETE,INSERT語句,產生IBUF_OP_INSERT和IBUF_OP_DELETE_MARK操作;IO線程則是IBUF的merge操作。為達到重現bug的目的,我們需要產生IBUF_OP_DELETE_MARK,IBUF_OP_INSERT和IBUF_OP_DELETE序列。由於這裡面涉及到purge線程和用戶線程,因此涉及一個協同問題。如何協同?

DEBUG_SYNC

       DEBUG_SYNC是mysql源碼中自帶的宏,通過信號的方式實現多個線程間的同步。假設存在兩個線程thread-1(A,B),thread-2(a,b),A,B和a,b分別是thread-1和thread-2的執行順序。為了得到AaBb執行序列,可以通過DEBUG_SYNC實現。

1) 源代碼中,設置同步點

Session-1(thread-1)

Session-2(thread-2)

A

a

DEBUG_SYNC(“after_A”)

DEBUG_SYNC(“after_a”)

B

b

DEBUG_SYNC(“after_B”)

 

 

2) Session設置

Session-1(thread-1)

Session-2(thread-2)

set debug_sync="after_A signal stepA wait_for  stepa";

set debug_sync=”after_B singal stepB”;

set debug_sync = "now wait_for stepA ";

set debug_sync = "after_a signal stepa wait_for stepB";

A

a

B

b

      通過在代碼中設置同步點,以及在會話中設置同步關系即可以實現在並發環境下穩定地得到AaBb序列。注意到2)中的藍色字體表示同步點,紅色字體signal和 wait_for表示動作。比如“after_A signal stepA wait_for  stepa”表示執行到同步點after_A後,發出信號after_A,並且等待stepa信號的到來。另外,其中now是一個特殊的同步點,表示執行到指定的位置後等待。debug_sync似乎已經完美地解決了同步問題,但問題是,通過debug_sync同步,需要會話設置等待或發出信號動作,如果需要和後台線程同步怎麼辦?而這個bug恰好又需要和後台線程purge,以及IO線程同步。別著急,有需求,就一定有解,mysql還有另外一種協同方法,DBUG_EXECUTE_IF。

DBUG_EXECUTE_IF

      DBUG_EXECUTE_IF原理就很簡單了,就是在某一點執行一定的操作,至於是什麼操作則沒有限制。顯然,我們可以在某一個點執行sleep函數,雖然無法做到嚴格的同步,但也基本能滿足需求。DEBUG_EXECUTE_IF的基本格式為:DEBUG_EXECUTE_IF(key, code),比如為了得到AaBb序列,可以通過DBUG_EXECUTE_IF實現

1)  源代碼中,設置操作點

Session-1(thread-1)

Session-2(thread-2)

 

DEBUG_EXECUTE_IF (“before_a”,{my_sleep(1)})

A

a

DEBUG_EXECUTE_IF (“after_A”,{my_sleep(3)})

DEBUG_SYNC(“after_b”, {my_sleep(5)})

B

b

DEBUG_SYNC(“after_B”, {my_sleep(7)})

 

2) Session設置

這裡為了讓後台線程也起作用,可以設置全局debug變量,若只想對本session起作用,可以設置會話debug變量。設置後,執行到對應的操作點時,則會額外執行DEBUG_EXECUTE_IF裡面的代碼。通過sleep方式來同步是一種取巧的方式,在實際情況中,需要不斷調整sleep的時間,盡可能保證能按照預設的順序執行。設置如下:紅色部分是加的代碼。

set global debug=”+d,before_a, after_A, after_B, after_b”;

實踐

       對於我們這個案例而言,為了得到指定的IBUF序列(IBUF_OP_DELETE_MARK,IBUF_OP_INSERT和IBUF_OP_DELETE),首先保證操作頁面不出現在buffer_pool中;其次,執行INSERT操作產生的IBUF_OP_INSERT必需先於PURGE執行,最後,在整個過程中頁面不能被MERGE,否則也將功虧一篑。對於第一點,可以通過將buffer_pool設置地比較小,然後掃描一個大表達到清理buffer_pool的目的;對於第二點,由於執行IBUF緩存的函數是IBUF_INSERT,針對類型為IBUF_OP_DELETE,強制其等待;對於第三點,在執行merge ibuf的地方,也強制其等待,並且比PURGE等待的時間要更長。具體設置點如下:

/*storage/innobase/ibuf/ibuf0ibuf.cc: ibuf_insert*/
ibuf_insert
{
    DBUG_EXECUTE_IF("sleep_ibuf_insert",{
    if (op == IBUF_OP_DELETE)    
      my_sleep(3000000);
    });

  ......
 ibuf_insert_low();
}

/* storage/innobase/ibuf/ibuf0ibuf.cc:ibuf_merge_pages*/
ibuf_merge_pages
{
   ......
  DBUG_EXECUTE_IF("sleep_ibuf_merge",{my_sleep(5000000);});
 
   buf_read_ibuf_merge_pages(sync, space_ids, space_versions, page_nos, *n_pages);//merge操作
}

總結

    在整個重現bug的過程中,其實還有很多細節,因為代碼中總是會有很多if else的判斷,以及特定標記位導致的特殊操作,因此打日志和debug很重要,通過打日志,了解大體執行流程;通過debug來了解細節,從而能掌握關鍵路徑,在關鍵路徑上設置合適的同步點,則能在並發環境下得到想要的執行序列。

參考文檔

http://www.gpfeng.com/?p=461

http://hedengcheng.com/?p=238

http://www.cnblogs.com/cchust/p/4544518.html

 

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved