mysql embeded 用法_Embedded Mysql | 學步園

引言

關於Mysql,大家都已經非常熟悉了。但是這種C/S的數據服務不適合於一些輕量級的應用,很多情況下,我們希望能使用一個進程級的存儲引擎,供我們查詢一些關係數據,而非使用一套臃腫的服務。當然這時我們可以選擇SqlLite,Berkeley DB,Access等輕量級的存儲件,但是都有學習成本,我們能否採用我們已經非常熟悉的Mysql介面來實現呢?答案是肯定的,很多人可能不知道:Mysql其實可以將服務以library的形式編譯,供程序單獨使用,這樣我們的程序相當於內聯了一套Mysql服務。

編譯

我們以linux下的編譯為例介紹編譯Embedded Mysql的步驟。

Mysql 5.0版本的編譯

在Mysql 5.0及之前版本,都是採用autobuild工具編譯,config時的參數可以如下配置:

./configure --prefix=/home/work/mysql --with-embedded-server--with-mysqld-ldflags--with-mysqld-ldflags=-all-static--enable-thread-safe-client --enable-local-infile

--with-extra-charsets=complex--disable-shared

上面的參數中:

--prefix=/home/work/mysql 指定安裝位置,可以根據自己的需要設置到其他目錄。

--with-embedded-server是編譯embedded mysql的關鍵,這個參數默認是不開啟的,因此一般只會編譯出libmysqlclient.a(.so)這個客戶端API庫,而開啟此參數後,會多生成一個libmysqld.a(.so)的庫,這個庫同時集成了mysql服務與客戶端API。

--with-mysqld-ldflags=-all-static與--disable-shared兩個參數表示只編譯靜態庫,如果需要使用動態庫,也可以去掉這兩個參數。

-enable-thread-safe-client表示客戶端API應該保持線程安全(但貌似Mysql 5.0有bug,即使開啟此參數,有時也非線程安全)。

--with-extra-charsets=complex 表示支持大字符集。

configure完成之後:

$ make

$ make install

即可以完成編譯。

Mysql 5.5之後的版本編譯

mysql 5.5之後的版本都遷移到採用cmake進行編譯,因此機器需要先安裝cmake,關於cmake的安裝,在此不做介紹,可以參考:

安裝cmake之後,可以用下面的命令編譯

cmake .   -DWITH_EMBEDDED_SERVER=1 -DCMAKE_INSTALL_PREFIX=/home/work/mysql  -DCMAKE_BUILD_TYPE=Release

上面的參數中:

-DCMAKE_INSTALL_PREFIX指定安裝位置,與5.0中的--prefix對應。

-DDWITH_EMBEDDED_SERVER表示將編譯Embedded Mysql庫,與5.0中的--with-embedded-server對應。

-DCMAKE_BUILD_TYPE表示生成庫的方式,它有以下幾種選項:

1. RelWithDebInfo 帶調試信息,開啟編譯優化的庫,編譯參數為-O2 -g

2. Debug 帶調試信息,不做任何編譯優化的庫,編譯參數-g

3. Release 不帶調試信息,編譯優化的庫,編譯參數-O2

4. MinSizeRel 不帶調試信息,最小size的庫,編譯參數-Os

Mysql5.5之後的版本編出的庫都是線程安全的,所以不需要 類似-enable-thread-safe-client的參數

是否帶調試信息對生成出庫的大小影響很大,例如Debug庫編譯出來有70多兆,而Release編譯出來只有不到20MB,如果不需要調試,建議已Release編譯。

Embedded Mysql使用方法

我們編寫如下代碼(test.cpp)進行測試:

#include

#include

#include

#include

int main()

{

MYSQL* sql;

MYSQL_STMT* stmt;

MYSQL_BIND bind;

unsigned* indice;

int ret;

int result;

unsigned long length;

char query1[] = "create database if not exists mydb";

char query2[] = "use mydb";

char query3[] = "create table if not exists mytable (id int not null, col int not

null)";

char query4[] = "insert into mytable (id, col) values (1,100)";

char query5[] = "select col from mytable where id = ?";

char *server_options[] = { "","--defaults-file=my.cnf","--basedir=./"};

int num_elements = sizeof(server_options)/ sizeof(char *);

if(mysql_server_init(num_elements, server_options, NULL))

{

puts("failed to initialize server");

return 1;

}

sql = mysql_init(NULL);

if(!mysql_real_connect(sql, NULL , NULL , NULL , NULL, 3306, NULL, 0))

printf("failed to connect\n");

if(mysql_query(sql, query1))

printf("failed to create database:%s\n", mysql_error(sql));

ret = mysql_query(sql, query2);

if(ret)

printf("failed to use database:%s\n", mysql_error(sql));

ret = mysql_query(sql, query3);

if(ret)

printf("failed to create table:%s\n", mysql_error(sql));

ret = mysql_query(sql, query4);

if(ret)

printf("failed to insert record:%s\n", mysql_error(sql));

stmt = mysql_stmt_init(sql);

if(!stmt)

printf("failed to init stmt:%s\n", mysql_error(sql));

ret = mysql_stmt_prepare(stmt, query5, strlen(query5));

if(ret)

printf("failed to prepare:%s\n", mysql_stmt_error(stmt));

result = 1;

memset(&bind, 0 , sizeof(bind));

bind.buffer_type = MYSQL_TYPE_LONG;

bind.is_unsigned = (my_bool)0;

bind.is_null = 0;

bind.buffer = (char*)&result;

bind.buffer_length = 4;

bind.length = &length;

ret = mysql_stmt_bind_param(stmt, &bind);

if(ret)

printf("failed to bind param:%s\n", mysql_stmt_error(stmt));

ret = mysql_stmt_execute(stmt);

if(ret)

printf("failed to execute stmt:%s\n", mysql_stmt_error(stmt));

memset(&bind, 0 , sizeof(bind));

bind.buffer_type = MYSQL_TYPE_LONG;

bind.is_unsigned = (my_bool)0;

bind.is_null = 0;

bind.buffer = (char*)&result;

bind.buffer_length = 4;

bind.length = &length;

ret = mysql_stmt_bind_result(stmt, &bind);

if(ret)

printf("failed to bind result:%s\n", mysql_stmt_error(stmt));

ret = mysql_stmt_store_result(stmt);

if(ret)

printf("failed to store result:%s\n", mysql_stmt_error(stmt));

ret = mysql_stmt_fetch(stmt);

if(ret)

printf("failed to fetch result:%s\n", mysql_stmt_error(stmt));

mysql_stmt_close(stmt);

mysql_close(sql);

mysql_server_end();

return 0;

}

從代碼我們可以看出,使用embedded mysql的方法與使用一般的mysql API基本一致,但是需要在使用前後初始化庫與清理環境,分別調用mysql_server_init()與mysql_server_end()這兩個函數。其中,mysql_server_init中可以指定mysql服務的配置文件my.cnf以及數據生成的位置(也可以不在init中配置數據目錄,在my.cnf文件中配置數據目錄)。

相比正式的mysql服務,embedded mysql的my.cnf只支持[server]與[embedded]這兩節配置,我們可以在當前目錄下創建一個類似下面的my.cnf:

[server]

language=/home/work/mysql/share/english

datadir==./data

[embedded]

language==/home/work/mysql/share/english

注意:上面的配置文件中,datadir指定mysql數據保存的位置(注意:該目錄必須在程序啟動前已存在,如果不存在需要提前手工創建,否則會啟動失敗)。language這個選項相當重要,它指定的是服務端與客戶端各種錯誤信息的資源文件。我們可以在mysql安裝目錄的share目錄下找到多種語言的資源文件,一般採用share/english/errmsg.sys即可(既可以直接將language指向這個目錄,也可以將這個文件拷貝至當前目錄下,然後將language指向目錄設為./)。

配置好my.cnf之後,我們如下編譯:

$ g++ test.cpp -o a.out  -I/home/work/mysql/include -L/home/work/mysql/lib -lmysqld

如果編譯了動態鏈接庫,生成的執行文件可能不能正常運行,需要在LD_LIBRARY_PATH中導出libmysqld.so的位置:

$ export  LD_LIBRARY_PATH=/home/work/mysql/lib:$LD_LIBRARY_PATH

編譯生成a.out,執行即可查詢。

注意事項

embedded mysql目前只能使用inno-db引擎,它會在datadir指定的目錄下生成資料庫文件,每個database用一個目錄保存,我們也可以將這些資料庫目錄拷貝到真正的mysql服務的數據目錄中,使用mysql工具進行查詢(注意:如果這麼操作,mysql服務必須重啟)。

關於Mysql 5.5的bug

本節是我在使用embedded mysql過程中發現的一些bug,這些bug至少在Mysql 5.5.15版本中存在,我已向Mysql社區反映,在官方修復之前,建議在編譯之前修改下面一些文件:

bug1:會導致多線程下的內存泄漏

在libmysqld/lib_sql.cc源代碼的256行:

stmt->fields= mysql->fields;

之後添加:

free_root(&stmt->mem_root, MYF(0));

bug2:會導致多線程下的內存泄漏

在libmysqld/lib_sql.cc的356行(stmt->result= *data;語句之前)加:

if(stmt->result.alloc.free != (*data).alloc.free ||

stmt->result.alloc.pre_alloc != (*data).alloc.pre_alloc)

{

free_root(&stmt->result.alloc, MYF(0));

}

bug3:會導致某些查詢失敗

在libmysqld/lib_sql.cc的346行:

if (res)

{

NET *net= &stmt->mysql->net;

set_stmt_errmsg(stmt, net);

DBUG_RETURN(1);

}

之後,添加下面兩行代碼:

else if (stmt->mysql->status==MYSQL_STATUS_GET_RESULT)

stmt->mysql->status= MYSQL_STATUS_STATEMENT_GET_RESULT;

結論

使用Embedded Mysql方案可以儘可能少的改動我們的代碼,兼容Mysql服務,實現輕量級的關係資料庫存儲。

其他

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值