【譯】從草稿開始寫一個 MySQL 存儲引擎(一)

原文: Writing a MySQL storage engine from scratch

本文主要描述了如何寫一個MySQL 存儲引擎 - 一個可以持久保存 MySQL 表數據的插件

簡介

這篇文章總結了我在寫一個新的 MySQL 存儲引擎時學到的一些事情。MySQL 的存儲引擎是一個能夠提供 在磁盤中存儲數據,并且能夠提供對數據的鏈接的插件。存儲引擎通常被作為 key/value 數據庫實現,但是并不一定是這樣。Oracle 的 MySQL 提供了多種存儲引擎,并且所有的存儲引擎都能夠被重新編寫。默認的存儲引擎是 InnoDB,一種 key/value 庫。其他的存儲引擎有的能夠寫入 csv 文件(‘Tina’)或者專門用來歸檔數據('archive').Maria DB,作為一個分支,是一個更加先進并且提供了 Cassandra NoSQL 數據庫接口的存儲引擎,Percona 的 TokuDB key/value 庫 和 xtradb,則是 InnoDB 的改進版本。

總的來說,MySQL 有13種存儲引擎,MariaDB 有大約20 種。但是這些仍然不夠,讓我們再來添加一種。

下載源碼以及第一次接觸

你能夠在 GitHub 上面下載 MySQL 的源碼,我的工作版本是 5.7.12。

git clone https://github.com/mysql/mysql-server.git

cd mysql-server

git checkout mysql-5.7-12

讓我們簡單看一下代碼的整體結構。文件和目錄的數量有點多,但是大部分和我們的工作無關,只有少部分的目錄值得我們仔細查看:

include 包含全局 include 文件,你能在里面找到 mysql.h,其中包含了很多重要的宏和聲明。

sql 保護了 sql 相關的代碼:解析器,查詢引擎等。

sql/field.h Field class 描述了一個 MySQL 行。

sql/item.h Item class 是一個解析器生成的抽象語法樹的節點。

sql/sql_*.cc 這些文件實現了實際上的 SQL 操作。

sql/handler.h 存儲引擎的基類

storage 這個目錄包含了所有的存儲引擎。

storage/innobase InnoDB 存儲引擎

storage/example 一個存儲引擎實例項目。

通過瀏覽這些代碼,我們可以清晰地看出 mysql 代碼的基礎是相對較老的。雖然大部分的代碼是通過 C++ 實現的,但是實際上僅僅是 “c 和 類”的實現方式。更多的高級 c++ 特性,例如 模板,exceptions 或者標準庫都沒有被使用。連接列表被實現成包裹在 "void *" 中的結構體對象(my_list.h)。mysql 還有它自己的 string 實現(string.c) 而不是使用 std::string。RAII 也沒有被使用。更甚的是,你能夠找到很多用于清理內存分配資源的 goto 語句。

功能的實現會非常的長(超過 1000 行代碼,并且包含了多層的 if 語句 mysql_prepare_create_table),并且有一些類也非常的大。很多方法被存儲在基類中而不是繼承類,并且類中存儲了很多的狀態。

MySQL 用了幾種內存自動分配(memroot_allocator.h),它們不是標準的實現。很多類使用自己的內存分配來重寫了操作符 new 和 delete。

同時MySQL 的代碼沒有統一的編碼風格。首行縮進都有多種風格,結構體的命名風格也有多種方式(。。。),空格的使用也非常隨意(。。。)。

我沒有和 Oracle 或者 Mysql AB 工作的人討論過,但是我們可以基于這些代碼看出他們的合作風格。缺乏同一的代碼風格意味著每一位工程師都能選擇他最熟悉的方式。代碼風格有很大的差異,甚至在同一個文件中,每個人也可以對任意部分的代碼進行修改。我曾經在做過類似的工作,所以我明白這其中有非常多的工作需要做。

如果 MySQL希望吸引新的開發者,“c 加 類”的使用,不使用標準庫并且非常長且復雜的功能實現都是需要重構的。

編譯,安裝,運行,debug

讓我們繼續安裝。如果你運行了之前的步驟,那么接下來你需要檢查 mysql-server 目錄中的代碼。我們將在這個目錄中運行 cmake 來生成 Makefile。我們同時將會創建一個 Debug build,雖然它會有一點慢,但是在 debug 的過程中我們會用到它。

cd mysql-server

cmake -DCMAKE_BUILD_TYPE=Debug

make -j 5

sudo make install

你的編譯文件現在安裝在了 /usr/local/mysql 中。你能通過在子目錄中運行 cmake 來生成獨立的 Debug 和 Release builds,并且你能夠選擇不同的安裝目錄,運行 “cmake --help”來獲得選項的列表。在這個文章中我僅僅會保持所有事情盡可能簡單。

為了成功啟動 MySQL 客戶端,我們還需要做以下事情。我們需要創建一個數據目錄(用來存放 表)并且初始化它們(注意你可能需要改變下面代碼的文件路徑):

mkdir /home/alan/tmp/mysql-data # must be empty

cd /usr/local/mysql/bin

./mysqld --initialize --datadir=/home/alan/tmp/mysql-data

最后一個命令將會生成一個 root 密碼。記下它,接下來會用到。我將我的開發環境設置為空的 root 密碼 - 在測試時它將會更加安全。你可以使用接下來的命令重新設置密碼(用上面生成的 root 密碼來替換 <PASSWORD>):

-- start the server

./mysqld --datadir=/home/alan/tmp/mysql-data

-- in a separate terminal we can now change the password

./mysqladmin password --user=root --password=<password>

</password>

然后我們可以創建一個新的數據庫:

./mysqladmin create test --user=root --password

客戶端能夠被正常啟動,并且之后你將可以創建表,插入數據等等。

./mysql --user=root test

逐步構建一個新的存儲引擎

存儲引擎被實現為一個動態的庫(一個 .so 文件),并且他的源文件被存儲在 msyql-server/storage 目錄中。如果你僅僅希望玩一下那么你可以修改已有的 'example ' 存儲引擎。我選擇通過以下步驟創建我自己的 'upscaledb'。

cd mysql-server/storage

-- copy the 'ha_example' directory to 'ha_upscaledb'

cp -r ha_example ha_upscaledb

-- rename the files

cd ha_upscaledb

mv ha_example.h ha_upscaledb.h

mv ha_example.cc ha_upscale.cc

最有一步就是,使用你最喜歡的 IDE 來將 'example' 替換為 'upscaledb'以及 'EXAMPLE' 替換為 'UPSCALEDB'。不要忘記同樣更改你的 CMakeLists.txt 。然后進入 mysql-server 的根目錄并且再一次運行 'cmake' 和 ‘make’ 命令。新的 upscaledb 引擎現在構建好了,它的文件名是 mysql-server/storage/upscaledb/ha_upscaledb.so。

現在我們需要提示 MySQL 關于我們的新存儲引擎。首先我們創建一個 安裝目錄 到新的 .so 文件的 symbolic link(軟連接?)。通過這個連接我們的服務器將會總是使用最新版本的 .so 文件。不論我們何時修改了代碼,我們只需要簡單的重新編譯存儲引擎并且重啟 MySQL 服務器就行了。

cd /usr/local/mysql/lib/plugin

sudo ln -s ~/prj/msyql-server/storage/upscaledb/ha_upscaledb.so ha_upscaledb.so

最后的步驟就是更新 MySQL 的內部系統表。我們能夠使用 MySQL 的客戶端完成這些。同時確保 MySQL 的服務器端仍在運行。

cd /usr/local/mysql/bin

./mysql --user=root test

mysql> INSTALL PLUGIN upscaledb SONAME 'ha_upscaledb.so';

現在你能夠使用最新的存儲引擎并且嘗試構建表 (create table test(value INTEGER) ENGINE=upscaledb;)。現在我們的存儲引擎只是一個框架并且沒有任何的實現。我們將會得到一個報錯。在我們開始增添程序邏輯之前我會教你如何 Debug MySQL 服務器。接下來的代碼將會在 gdb 中啟動服務器,并且將會讓 gdb 獲取所有的信號。(i.e. ctrl-c 可以結束 debugger)。

cd /usr/local/mysql/bin

gdb --args ./mysqld --gdb --datadir=/home/alan/tmp/mysql-data

嘗試在你的存儲引擎中的 'create()' 方法中設置一個斷點,并且運行一次上述的 CREATE TABLE 命令。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容