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

(接 從草稿開始寫一個 MySQL 存儲引擎(一))

增加功能

現(xiàn)在是時候來填充我們的 Handler 類中的各種方法了涡贱。詳細的描述每個方法將會花上很多時間堪夭。同時愕把,其中的一部分我暫時也不太清楚。但是我會給出一個概覽森爽,并且提供具體實現(xiàn)的鏈接恨豁。

意識到同一個表中運行了多種 Handler 的實例是一個非常重要的過程。實際的表數(shù)據(jù)因此需要存儲在不同的對象中(一個 'share')爬迟,然后 Handler 將會需要這個 Share橘蜜。如果你的數(shù)據(jù)存儲在一個原始文件中,那么這個文件的 Handle 將會成為 Share 的一個成員付呕,而不是 Handler 中的成員计福。

你能在 這兒 看到一個樣例的Share,以及我的 UpscaledbShare 徽职∠笥保可以看到 UpscaledbShare 存儲了實際的數(shù)據(jù) handles,以及額外關(guān)于數(shù)據(jù)庫的元數(shù)據(jù)姆钉。

創(chuàng)建表

ExampleHandler::createUpscaledbHandler::create 每當(dāng)創(chuàng)建表時都會被調(diào)用说订。之后,MySQL 將會在表上調(diào)用 open() 方法潮瓶。你的 create() 方法因此可以準(zhǔn)備表陶冷,但是并不一定需要打開它。

UpscaledbHandler::create() 的實現(xiàn)非常直接毯辅。首先它創(chuàng)建一個 upscaledb 環(huán)境埃叭,然后創(chuàng)建一個針對每個索引的數(shù)據(jù)庫。如果用戶沒有為對應(yīng)的表創(chuàng)立主鍵悉罕,那么將會生成一個索引赤屋。數(shù)據(jù)庫的配置文件依賴于每列的實際類型以及其他的一下參數(shù)(例如,是否唯一)壁袄。

打開表

ExampleHandler::openUpscaledbHandler::open 每當(dāng)打開表時都會被調(diào)用类早。如上面提到的,它將會在同一個表中發(fā)生多次嗜逻。因此你需要將你的實際數(shù)據(jù)存儲到 'Share' 中涩僻。

當(dāng)檢索到一個 'Share' 對象的指針后,UpscaledbHandler::open() 方法將會檢查 upscaledb 環(huán)境是否已經(jīng)打開。如果打開逆日,它將會立即返回嵌巷。如果沒有,它將會打開文件室抽,并且將環(huán)境的 Handle 存儲到 'Share' 中搪哪。

關(guān)閉表

ExampleHandler::closeUpscaledbHandler::close 方法被用來關(guān)閉表。如果你的表數(shù)據(jù)存儲在 Share 中坪圾,那么你將會使用引用計數(shù)來確定何時銷毀 Share 對象晓折。在我的 UpscaledbHandler 中我不會銷毀我的 Share,因為 Share 在之后的很長時間將會起作用兽泄。

插入行(INSERTing rows)

不論你何時調(diào)用 INSERT SQL 狀態(tài)漓概,你 handler 的 write_row() 方法都會被調(diào)用。它唯一的參數(shù)就是新的一行病梢,字節(jié)陣列中的序列胃珍。當(dāng)你調(diào)用 CREATE TABLE 狀態(tài)時,陣列會用不同的順序存儲實際上的列蜓陌。主鍵永遠是初始的堂鲜,后面跟隨著其他索引過的列,最后跟著未索引的列护奈。

這個字節(jié)陣列通常開始于一個(可選的)位映射缔莲,它描述了當(dāng)前列中的空值。后面跟隨著任何一個修改過長度的列霉旗,或者是變長的列(例如 TINYTEXT痴奏,MEDIUMTEXT, TEXT, LONGTEXT 或者是一個對應(yīng)的BLOB 列)。變長的列通常以一到兩個存儲了列的大小的字節(jié)開始厌秒,后面跟著相關(guān)的數(shù)據(jù)读拆。(這些數(shù)據(jù)能夠被存儲為分隔的塊;因此這個字節(jié)序列包含了一個對應(yīng)了實際數(shù)據(jù)的編碼指針鸵闪。)如果你將一行存進文件中檐晕,它將會壓縮變量長度為一個更加緊湊的格式,從而節(jié)約空間蚌讼。

我的 UpscaledbHandler 緩存了索引的域?qū)ο蟊倩遥瑥亩梢钥焖俳鈮核饕牧小O旅娴拇a可以用于解壓一個索引行的 key (‘index’ 是指索引的數(shù)字 ID篡石;主索引一直是0)

static inline ups_key_t
key_from_row(TABLE *table, const uchar *buf, int index)
{
  KEY_PART_INFO *key_part = table->key_info[index].key_part;
  uint16_t key_size = (uint16_t)key_part->length;
  uint32_t offset = key_part->offset;

  if (key_part->type == HA_KEYTYPE_VARTEXT1
          || key_part->type == HA_KEYTYPE_VARBINARY1) {
    key_size = buf[offset];
    offset += 1;
  }
  else if (key_part->type == HA_KEYTYPE_VARTEXT2
          || key_part->type == HA_KEYTYPE_VARBINARY2) {
    key_size = *(uint16_t *)&buf[offset];
    offset += 2;
  }

  ups_key_t key = ups_make_key((uchar *)buf + offset, key_size);
  return key;
}

當(dāng)解壓了檢索 key 之后芥喇,你可以在你的文件中存儲行數(shù)據(jù)。同時你將需要解決三個問題:

  1. 用戶沒有指定任何的索引或者主鍵凰萨。
  2. 用戶指定了主鍵但是沒有制定其他的索引继控。
  3. 用戶制定了主鍵和額外的索引械馆。
CREATE TABLE test (
    id         INT NOT NULL,
    last_name  CHAR(30) NOT NULL,
    first_name CHAR(30) NOT NULL,
    PRIMARY KEY (id),
    INDEX name (last_name, first_name)    -- creates a virtual index!
);

可以參照作者的相關(guān)實現(xiàn): UpscaledbHandler::write_row

<h3deleteing rows="">

在 Handler 中武通,DELETE SQL 命令以調(diào)用 delete_row()方法作為收尾霹崎。你需要確保 所有 的部分都被刪除了,不僅僅是主要的冶忱。同時尾菇,刪除主鍵是非常簡單的,因為 MySQL 將會使用一個數(shù)據(jù)庫游標(biāo)來定位它朗和。 可以參考 [UpscaledbHandler::delete_row()][https://github.com/cruppstahl/upscaledb-mysql/blob/d4c2744e612616efc2deeeaf4ccd3132959c0e14/storage/upscaledb/ha_upscaledb.cc#L1163-L1217] 的實現(xiàn)。

更新行(UPDATEing rows)

這是最復(fù)雜的一部分 - 至少如果你希望讓它能夠比較快的話簿晓。 updata_row() 方法接受兩個參數(shù):舊的行的值和新行的值眶拉。最原始的方法就是調(diào)用 delete_row() 來刪除原始的行,然后調(diào)用 write_row() 來寫入新行憔儿。這樣會運行的非常慢忆植,因為即使只更新一列,你實際上也更新了所有的列谒臼。只更新修改了的列會快的多朝刊。

游標(biāo)

對于大部分的任務(wù),MySQL 核心僅僅會創(chuàng)建一個數(shù)據(jù)庫游標(biāo)蜈缤,定位一個 key(不論是在主鍵或者第二檢索上)拾氓,然后在實際數(shù)據(jù)上工作的時候向前移動。下面是一些為了支撐游標(biāo)的功能你需要實現(xiàn)的一些方法底哥。

  • index_init(): creates a cursor for a secondary index
  • index_end(): can close the cursor
  • index_read_map(): positions the cursor on a row
  • index_next(): moves cursor to the next key, retrieves the row
  • index_prev(): moves cursor to the previous key, retrieves the row
  • index_last(): moves cursor to the last key, retrieves the row
  • index_first(): moves cursor to the first key, retrieves the row
  • index_next_same(): moves cursor to the next duplicate of the current key
  • rnd_init(): creates a cursor for the primary index
  • rnd_end(): closes the cursor
  • rnd_next(): moves cursor to the next key, retrieves the row
  • rnd_pos(): moves cursor to a specified row

其他的方法

接下來會提到一些其他重要的或者有趣的方法咙鞍。

rename_table():對表進行重命名(以及它所有的文件)。每當(dāng)你的 schema 改變時都會調(diào)用這個方法趾徽,例如续滋,當(dāng)你增加一列時。MySQL 會將所有的數(shù)據(jù)復(fù)制到一個臨時的表中孵奶,然后使用這個方法將你臨時表中的數(shù)據(jù)重命名到你原始的表中疲酌。

delete_table(): 刪除一個表(以及所有包含的文件)

table_flags(): 返回一個描述了你的 Handler 能力的 flag 集。這些 flag 很多都沒有很好的文檔了袁,并且很多都是針對 InnoDB 生成的文檔朗恳。我的猜測是只有 InnoDB 實現(xiàn)了所有的功能。

index_flags(): 返回一個描述了你的 Handler 能力的 flag 集载绿。例如僻肖,你的 index_prev() 和 index_next() 提供的功能。

總結(jié)

編寫自己的存儲引擎聽起來像一個很復(fù)雜的任務(wù)卢鹦,但是實際上并不是臀脏。你不需要實現(xiàn)我上面說的所有方法劝堪。對于有些方法,MySQL將會找出缺失的實現(xiàn)并且提供自己的方法揉稚,但是對于其他的秒啦,將會有一些報錯。ExampleHandler 本來是空的并且不提供任何功能搀玖。但是你能夠加載并且為它加上斷電余境,一個接一個地實現(xiàn)功能。如果你遇到了問題灌诅,那么你可以參考 MySQL 已有的存儲引擎或者是 MariaDB 芳来。mysql-internals 郵件列表里的開發(fā)者們也能提供一定的幫助。

一些關(guān)于有趣的存儲引擎的想法浮現(xiàn)在我腦海中:

· 一個將數(shù)據(jù)存儲在 HDFS 中的只增數(shù)據(jù)庫猜拾;使用者能夠使用 Spark 或者 Hadoop 的 map/reduce 方法來進行進一步的數(shù)據(jù)處理即舌。

· 基于 std::map 或者 std::multi_map 的存儲中的表。

· 使用 XML 文件后端的存儲引擎挎袜。

你還有什么其他的想法呢顽聂?

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市盯仪,隨后出現(xiàn)的幾起案子紊搪,更是在濱河造成了極大的恐慌,老刑警劉巖全景,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耀石,死亡現(xiàn)場離奇詭異,居然都是意外死亡爸黄,警方通過查閱死者的電腦和手機娶牌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來馆纳,“玉大人诗良,你說我怎么就攤上這事÷呈唬” “怎么了鉴裹?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長钥弯。 經(jīng)常有香客問我径荔,道長,這世上最難降的妖魔是什么脆霎? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任总处,我火速辦了婚禮,結(jié)果婚禮上睛蛛,老公的妹妹穿的比我還像新娘鹦马。我一直安慰自己胧谈,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布荸频。 她就那樣靜靜地躺著菱肖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旭从。 梳的紋絲不亂的頭發(fā)上稳强,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音和悦,去河邊找鬼退疫。 笑死,一個胖子當(dāng)著我的面吹牛鸽素,可吹牛的內(nèi)容都是我干的褒繁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼付鹿,長吁一口氣:“原來是場噩夢啊……” “哼澜汤!你這毒婦竟也來了蚜迅?” 一聲冷哼從身側(cè)響起舵匾,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谁不,沒想到半個月后坐梯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡刹帕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年吵血,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偷溺。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡蹋辅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挫掏,到底是詐尸還是另有隱情侦另,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布尉共,位于F島的核電站褒傅,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏袄友。R本人自食惡果不足惜殿托,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望剧蚣。 院中可真熱鬧支竹,春花似錦旋廷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至叹坦,卻和暖如春熊镣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背募书。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工绪囱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人莹捡。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓鬼吵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親篮赢。 傳聞我的和親對象是個殘疾皇子齿椅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容