【译】从草稿开始写一个 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阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容