1. 程式人生 > >MySQL服務與儲存引擎間的介面

MySQL服務與儲存引擎間的介面

MySQL定義了一系列抽象儲存引擎API,以支援外掛式儲存引擎架構。在歷史版本中,這些介面被為"table handler"。我們這裡所說的儲存引擎(Storage Engine),是指資料的儲存/讀取相關的邏輯模組。而儲存引擎API(table handler)是指Storage Engine與MySQL優化器間的介面。比如,新增一個儲存引擎---Storage Engine,如何實現MySQL服務層對儲存儲存引擎訪問呢?MySQL服務訪問的儲存引擎是介面就是抽象儲存引擎API,新儲存引擎只需要實現相應的抽象儲存引擎API介面,就可以實現MySQL Service對儲存引擎的訪問。
MySQL外掛式儲存引擎架構,促使
MySQL以更加開放的姿態,接受更多的儲存引擎。抽象儲存引擎介面是在v3.22提升到v3.23時引入的設計。在快速整合InnoDB儲存引擎階段中起了很大的作用。眾所周知,InnoDB為MySQL帶來了穩定事務支援、多版本、行鎖。只要知道怎麼讀寫記錄,任何東西都可以很快的整合到MySQL中。

抽象儲存引擎API介面是通過抽象類handler來實現,handler類提供諸如開啟/關閉table、掃表、查詢Key資料、寫記錄、刪除記錄等基礎操作方法。每一個儲存引擎通過繼承handler類,實現以上提到的方法,在方法裡面實現對底層儲存引擎的讀寫介面的轉調。從5.0版本開始,為了避免在初始化、事務提交、儲存事務點、回滾操作時需要操作one-table例項,引入了handlerton結構讓儲存引擎提供了發生上面提到操作時的鉤子操作。

API以Handler類的虛擬函式的方式存在,可在程式碼庫下的./sql/handler.h中檢視詳細資訊。可在Handler類的註釋中看到描述:

/**
  The handler class is the interface for dynamically loadable
  storage engines. Do not add ifdefs and take care when adding or
  changing virtual functions to avoid vtable confusion

  Functions in this class accept and return table columns data. Two data
  representation formats are used:
  1. TableRecordFormat - Used to pass [partial] table records to/from
     storage engine
  2. KeyTupleFormat - used to pass index search tuples (aka "keys") to
     storage engine. See opt_range.cc for description of this format.
  TableRecordFormat
  =================
  [Warning: this description is work in progress and may be incomplete]
  The table record is stored in a fixed-size buffer:
    record: null_bytes, column1_data, column2_data, ...
  //篇幅原因,略去部分內容。
*/
class handler : public Sql_alloc
{
    //篇幅原因,不列出具體程式碼。讀者可直接在原始碼檔案./sql/handler.h中找到具體內容。
}

Handler類繼承自Sql_alloc類,Sql_alloc沒有任何成員變數,純粹過載了new和delete操作。所以Handler類分配記憶體是可以從連線相關的記憶體池來分配。而delete操作時不做任何事情。記憶體的釋放只會在mysys/my_alloc.c的free_root()呼叫發生。無需顯式釋放,在語句執行之後清理。

每一個table描述符對應一個handler的例項。注意針對同一個table可能會被開啟多次的情況,這時候會出現多個handler例項。5.0版本引入index_merge方法後導致了一些有趣的方式,以前多個handler例項只會在table cache中拷貝描述符產生,引入Index_merge之後,handler例項可能會在優化階段被建立。

下面我將分類描述部分儲存引擎API。
建立、開啟和關閉表:

通過函式create來建立一個table:
/**
  *name:要建立的表的名字
  *from:一個TABLE型別的結構,要建立的表的定義,跟MySQL Server已經建立好的tablename.frm檔案內容是匹配的
  *info:一個HA_CREATE_INFO型別的結構,包含了客戶端輸入的CREATE TABLE語句的資訊
*/

int create(const char *name, TABLE *form, HA_CREATE_INFO *info);


通過函式open來開啟一個table:
/**
  mode包含以下兩種
  O_RDONLY  -  Open read only
  O_RDWR    -  Open read/write
*/

int open(const char *name, int mode, int test_if_locked);


通過函式close來關閉一個table:
int close(void);


對錶加鎖:
當客戶端呼叫LOCK TABLE時,通過external_lock函式加鎖:
int ha_example::external_lock(THD *thd, int lock_type)

全表掃描:
//初始化全表掃描
virtual int rnd_init (bool scan);
//從表中讀取下一行
virtual int rnd_next (byte* buf);


通過索引訪問table內容
//使用索引前呼叫該方法
int ha_foo::index_init(uint keynr, bool sorted) 
//使用索引後呼叫該方法
int ha_foo::index_end(uint keynr, bool sorted)
//讀取索引第一條內容
int ha_index_first(uchar * buf);
//讀取索引下一條內容
int ha_index_next(uchar * buf);
//讀取索引前一條內容
int ha_index_prev(uchar * buf);
//讀取索引最後一條內容
int ha_index_last(uchar * buf);
//給定一個key基於索引讀取內容
int index_read(uchar * buf, const uchar * key, uint key_len,
          enum ha_rkey_function find_flag)

事務處理
//開始一個事務
int my_handler::start_stmt(THD *thd, thr_lock_type lock_type)
//回滾一個事務
int (*rollback)(THD *thd, bool all); 
//提交一個事務
int (*commit)(THD *thd, bool all);

handlerton 4.1之前的版本,從handler類繼承是唯一一個儲存引擎可以與核心結構互動的途徑。
當優化器需要針對儲存引擎做一些事情的時候,只可能呼叫當前table相關的handler例項的介面方法。但是隨著整合多種儲存引擎的發展,盡是靠handler方法來互動的形式是不太夠的,因此,handlerton概念誕生。

handlerton是一個幾乎全是回撥方法指標的C結構體。定義在sql/handler.h
回撥函式在某一事件發生時針對具體的儲存引擎被呼叫(事務提交/儲存事務點/連線關閉等)

如何編寫自己的儲存引擎

在MySQL的官方文件上,有對於編寫自己的儲存引擎的指導文件,連結如下。
作為編寫自己儲存引擎的開始,你可以檢視MySQL原始碼庫中的一個EXAMPLE儲存引擎,它實現了必須要實現的儲存引擎API,可以通過複製它們作為編寫我們自己儲存引擎的開始:

sed -e s/EXAMPLE/FOO/g -e s/example/foo/g ha_example.h > ha_foo.h
sed -e s/EXAMPLE/FOO/g -e s/example/foo/g ha_example.cc > ha_foo.cc

如何新增第三方儲存引擎

5.1之後版本新增變得簡單多。可以根據"blackhole"儲存引擎的模式來改造
1、建立目錄 storage/xx-csv,實際檔案ha_csv.h(cc)移至該目錄
2、Makefile.am 放入storage/xx-csv
3、configure.in 改寫MYSQL_STORAGE_ENGINE巨集
4、autoconf & automake & ./configure --prefix=/usr --with-plugins=xx-csv & make

mysql_declare_plugin巨集註冊引擎

讀寫支援的儲存引擎需要面對更多問題,多個handler例項會被建立,發現多個寫例項的存在會導致寫入位置發生一些奇怪的現象,這或許會讓我們感到驚訝。其他儲存引擎通過共享底層的表描述符結構體來解決這個問題。

sql/examples/ha_tina.cc更為詳細的引擎實現方式