整體思路:
1.使用開源學堂在線編程環境開發MiniOB編譯環境
2.使用vscode進行代碼調試和開發以及上傳到倉庫
MiniOB源碼:https://github.com/oceanbase/miniob
MiniOB文檔:MiniOB 介紹 - MiniOB
數據庫大賽官網:OceanBase 社區
訓練營:OceanBase 社區
一、新建github倉庫以及將倉庫內代碼CLONE到本地
前提條件:已注冊 Gitee 賬號,Gitee 官網地址:https://gitee.com。
創建私有倉庫:
登錄 Gitee 平臺,選擇新建倉庫。
輸入倉庫信息,單擊 創建。設置為私有倉庫后其他人無法查看到你的代碼。
賦權官方測試賬號
對于私有倉庫,默認情況下其他人看不到,同樣 OceanBase 測試后臺也無法拉取到代碼,這時想要提交測試,需要先給 OceanBase 的官方測試賬號增加一個權限。
官方測試賬號為:oceanbase-ce-game-test
選擇管理>倉庫成員管理>觀察者。
選擇直接添加,搜索官方測試賬號。
添加完成后,單擊 提交。
上述完成了一系列新建倉庫的操作,接下來建立本地項目與遠程倉儲的同步。
第一步新建文件夾,我此時新建的文件夾為MINIOB。
第二步打開所創建的文件夾。CLONE倉庫到新建文件夾下(此時建立連接成功)
接下來打開obtest文件夾,獲取miniob源碼(獲取地址:https://github.com/oceanbase/miniob),解壓后粘貼到obtest文件夾下:
# 進入到obtest目錄,刪除.git目錄,清除已有的git信息
cd obtest
rm -rf .git
# 重新初始化 git 信息,并將代碼提交到自己的倉庫
git init
git add .
git commit -m 'init' ?# 提交所有代碼到本地倉庫
# 將代碼推送到遠程倉庫
git remote add origin https://gitee.com/nwd_320/obtest.git # 注意替換命令中的信息為自己的庫信息
git branch -M main
git push -u origin main
打開gittee新建的倉庫,如下圖所示:
二、搭建開發環境
訪問開源學堂網址:云燕實驗室
進入課程學習界面后,大家可以在左側課程目錄中找到對應的實驗,然后點擊對應的目錄項,查看對應的實驗的文檔。
點擊上方啟動環境即可啟動開發環境,每次實驗的開發環境中的內容是會持久化保存在云端。
點擊實驗工具中的vscode來進行開發,將項目在瀏覽器中打開。
輸入git clone https://gitee.com/nwd_320/obtest.git# 注意替換命令中的信息
安裝相應的插件:
新建兩個空文檔(launch.json和tasks.json)
將下列分別粘入文件中,記得保存:
launch.json:
{// Use IntelliSense to learn about possible attributes.// Hover to view descriptions of existing attributes.// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387"version":?"0.2.0","configurations":?[{"type":?"cppdbg","request":?"launch","name":?"Debug","program":?"${workspaceFolder}/${defaultBuildTask}/bin/observer","args":?["-f",?"${workspaceFolder}/etc/observer.ini",?"-P",?"cli"],"cwd":?"${workspaceFolder}/${defaultBuildTask}/","internalConsoleOptions":?"openOnSessionStart","osx":?{"MIMode":?"lldb","externalConsole":true}},{"type":?"lldb","request":?"launch","name":?"LLDB","program":?"${workspaceFolder}/${defaultBuildTask}/bin/observer","args":?["-f",?"${workspaceFolder}/etc/observer.ini",?"-P",?"cli"],"cwd":?"${workspaceFolder}/${defaultBuildTask}/"}]}
Tasks.json:
{// See https://go.microsoft.com/fwlink/?LinkId=733558// for the documentation about the tasks.json format"version":?"2.0.0","tasks":?[{"label":?"init","type":?"shell","command":?"sudo -E env PATH=$PATH bash ${workspaceFolder}/build.sh init"},{"label":?"build_debug","type":?"shell","command":?"bash build.sh debug","problemMatcher":?[],"group":?{"kind":?"build","isDefault":?true}},{"label":?"build_release","type":?"shell","command":?"bash build.sh release"},{"label":?"gen_parser","type":?"shell","command":?"cd ${workspaceFolder}/src/observer/sql/parser && bash gen_parser.sh"}]}
如下圖所示(這里需要等待一會兒):
構建完畢后,將build_debug中的compile_commands.json文件復制到miniob目錄中,隨便打開一個cpp文件,就可以看到clangd開始工作。
用F5進行調試,關于如何vscode如何調試,可以參考相關的資料:cpp-debug。修改launch.json文件中program和args來調試不同的可執行文件。
打開命令行輸入如下信息:
接下來提交到自己的gittee倉庫中:
注:首先打開服務器,再去打開客戶端。
1.在MiniOB代碼目錄下,運行下面命令來編譯MiniOB
bash build.sh debug
2.進入build_debug目錄
cd build_debug/
3.在MiniOB代碼目錄下,運行下面命令來啟動 MiniOB
./bin/observer
4.打開另一個終端,進入build_debug目錄,運行下面命令來啟動MiniOB client
./bin/obclient
三、miniob訓練營提交(basic測試)
進入miniob訓練營官網,選擇basic。
點擊進入
接下來到官網提交代碼,首先找到倉庫地址
我進行簡單提交如圖所示:
四、搭建開發環境MiniOB框架
1.如圖所示左邊是客戶端,右邊是服務端。客戶端發起命令,通過網絡通訊和服務端的網絡模塊去通訊收發命令。服務端的網絡模塊收到SQL(字符串形式存儲)請求后,交給詞法(lex_sql.l)/語法(yacc_sql.y)解析模塊(Parser模塊)(它的代碼在src/observer/sql/parser中。),經過詞法解析和語法解析模塊將SQL請求字符串轉換成帶有語法結構信息的內存數據結構(語法樹)。
2.再轉發給下一個執行緩存模塊Plan Cache,執行計劃緩存模塊會將該SQL第一次生成的執行計劃緩存在內存中,后續的執行可以反復執行這個計劃,避免了重復查詢優化的過程。如果命中,直接將查詢計劃交給 Executor進行執行。
3.若Plan Cache未命中,直接再轉發給Resolver模塊。Resolver模塊會將判斷模塊解析出來的語法樹,進一步細化后轉換成真實的對象(STMT)。比如查詢一張表,會把表名轉換成具體的表對象;如果是查詢某個字段,會轉換成對應的字段名;如果是select *,會把 * 轉換成對應表的各個字段。除此之外還會做一些預檢,比如查詢一張不存在的表,就會提前返回錯誤。
4.把處理的結果給到Transformer(邏輯優化階段)。分析用戶 SQL 的語義,并根據內部的規則或代價模型,將用戶SQL改寫為與之等價的其他形式,并將其提供給后續的優化器做進一步的優化。Transformer的工作方式是在原Statement Tree上做等價變換,變換的結果仍然是一棵Statement Tree。
5.經過這個規則轉換(Transformer)后,交給優化模塊(Optimizer)。優化模塊會根據一些條件找到更好的查詢路徑。比如查詢數據,需要先判斷直接使用索引查詢,速度更快,還是通過全表遍歷查詢速度更快。因為有時部分因素或條件會導致索引查詢更慢,所以優化模塊會做一些判斷,選擇較好的查詢計劃,再轉發給下一個執行模塊。
6.執行模塊(Executor)(相關代碼在src/observer/sql/executor中)會按照查詢計劃去執行,訪問索引、Buffer Pool、記錄管理、以及底層的模塊。然后把查詢的結果返回給網絡模塊。網絡模塊再通過 Socket 返回給客戶端。
其中:
元數據管理(Meta Data):記錄當前的數據庫、表、字段和索引元數據信息;
存儲引擎(Storage Engine):負責數據的存儲和檢索;包括:【Record Manager:組織記錄一行數據在文件中如何存放。Buffer Pool:文件跟內存交互的關鍵組件。B+Tree:索引結構。】
五、Drop-table實驗
刪除表。清除表相關的資源。
注意:要刪除所有與表關聯的數據,不僅僅是在create table時創建的資源,還包括索引等數據。
我們可以根據create table的方式模仿實現Drop-table。可以使用日志文件輔助發現問題在哪。如下圖所示。可以發現創建statement失敗,是在Resolver模塊中。無需從Parser模塊開始修改。
實現具體細節可參考:
https://gitee.com/nwd_320/obtest/commit/9dca2fef06f1e5c76d579aa637664989f38fa86b
測試集:
test/case/test/primary-drop-table.test
Create?table就是新建表,比如在create table t時,會新建一個t.table文件,同時為了存儲數據也會新建一個t.data文件存儲下來。同時創建索引的時候,也會創建記錄索引數據的文件。
因此刪除表,就需要刪除t.table文件、t.data文件和關聯的索引文件。同時由于buffer pool的存在,在新建表和插入數據的時候,會寫入buffer pool緩存。所以drop table,不僅需要刪除文件,也需要清空buffer pool,防止在數據沒落盤的時候,再建立同名表,仍然可以查詢到數據。如果建立了索引,比如t_id on t(id),那么也會新建一個t_id.index文件,也需要刪除這個文件。這些東西全部清空,那么就完成了drop table。存儲方式如下圖所示。
(1)新增SQL類型只需添加對應分支和Stmt子類(Resolver模塊)
在src/observer/sql/stmt/stmt.cpp添加drop_table相關分支
在src/observer/sql/stmt/目錄下新增drop_table_stmt.cpp(模仿create_table_stmt.cpp)
在src/observer/sql/stmt/目錄下新增drop_table_stmt.h(模仿create_table_stmt.h)
(2)增加drop_table執行器(Executor模塊)
在src/observer/sql/executor/command_executor.cpp添加drop_table相關分支
在src/observer/sql/executor/目錄下新建drop_table_executor.cpp(同上)
在src/observer/sql/executor/目錄下新建drop_table_executor.h(同上)
(3)找到對應的Db對象,并且實現表對象的邏輯刪除
在src/observer/storage/db/db.cpp添加drop_table函數
在src/observer/storage/db/db.h添加drop_table定義
(4)刪除.table文件和.data文件,這個表對應的索引文件,以及DiskBufferPool中的相關資源:
在src/observer/storage/table/table.cpp添加drop函數(包括刪除.table文件,刪除.data文件以及包括調用刪除索引文件以及緩存的函數)。
在src/observer/storage/table/table.h添加drop定義
在src/observer/storage/index/bplus_tree.cpp添加desdroy函數(刪除索引文件)
在src/observer/storage/index/bplus_tree.h添加desdroy函數定義
在src/observer/storage/index/bplus_tree_index.cpp添加desdroy函數(刪除索引)
在src/observer/storage/index/bplus_tree_index.cpp添加desdroy函數定義
在src/observer/storage/buffer/disk_buffer_pool.cpp添加remove_file()函數(刪除緩存)
在src/observer/storage/buffer/disk_buffer_pool.h添加remove_file()函數定義
如上圖所示功能實現成功。上傳至私人倉庫。提交至訓練營。如下圖所示
六、DATE實驗
要求實現日期類型字段。date測試不會超過2038年2月,不會小于1970年1月1號。注意處理非法的date輸入,需要返回FAILURE。
當前已經支持了int、char、float類型,在此基礎上實現date類型的字段。
這道題目需要從詞法解析開始,一直調整代碼到執行階段,還需要考慮DATE類型數據的存儲。
注意:
- 需要考慮date字段作為索引時的處理,以及如何比較大小;
- 這里限制了日期的范圍,所以簡化了溢出處理的邏輯,測試數據中也刪除了溢出日期,比如沒有 2040-01-02;
- 需要考慮閏年。
實現具體細節可參考:
https://gitee.com/nwd_320/obtest/commit/f0e0054d743cda3f9cc8b451a832af2438f6758a
測試集:
test/case/test/primary-date.test
實現DATE類型可以模仿別的數據類型實現。我們采用日志文件可以發現,DATE類型從Parser模塊就未定義。如下圖所示。
(1)新增DATE類型,支持語法分析和詞法分析(Parser模塊)
在src/observer/sql/parser/lex_sql.l添加DATE類型(詞法分析)
在src/observer/sql/parser/yacc_sql.y添加DATE類型(語法分析)
注意:
此時在src/observer/sql/parser/目錄下,執行以下命令:
./gen_parser.sh
將會生成詞法分析代碼 lex_sql.h 和 lex_sql.cpp,語法分析代碼 yacc_sql.hpp 和 yacc_sql.cpp。
由于當前沒有把lex_sql.l和yacc_sql.y加入CMakefile.txt中,所以修改這兩個文件后,需要手動生成c代碼,然后再執行編譯。
(2)元數據管理的修改
在src/observer/common/type/attr_type.cpp添加date類型
在src/observer/common/type/attr_type.h添加date類型
在src/observer/common/type/date_type.cpp目錄下新建date_type.cpp(模仿現有類型)
在src/observer/common/type/date_type.h目錄下新建date_type.cpp(模仿現有類型)
在src/observer/common/type/data_type.cpp添加DATE實例化對象(模仿現有類型)
在src/observer/common/type/char_type.cpp添加DATE類型轉換
在src/observer/common/value.h添加DATE類型標識(模仿現有類型)
在src/observer/common/value.cpp添加DATE類型(模仿現有類型)
(3)Date的合法性判斷
在src/common/time/datetime.cpp添加判斷DATE合法性函數
在src/common/time/datetime.h添加判斷DATE合法性函數定義
功能實現成功。上傳至私人倉庫。提交至訓練營。如下圖所示