初一看,你肯定會覺得作者在忽悠人吧,200 行 C 代碼居然可以實現插件式 NOSQL 存儲服務器?是的,200 行 C 代碼確實可以做到,但只能是一個極其簡單的代碼框架而已,不過感興趣的同鞋可以在此基礎上進行擴展。
引言
好了不多說,究竟什么才是插件式( Pluggable )的存儲服務器呢,想必大家都用過 MySQL吧,它就是一款典型的插件式關系型數據服務器,MySQL 下面比較有名的存儲引擎有 MyISAM, InnoDB, MEMORY 等。另外大名鼎鼎的 Memcached 的開發版(1.6版,即官方代碼倉庫中的 engine-pu 分支)也開始支持插件式存儲引擎,這意味着以后你可以使用 Memcached 的其他存儲引擎了,而不是僅僅將數據緩存在內存中。
首先插件式存儲服務器的一種可能的設計架構圖如下:

按照典型的分層設計模式,最上層是網絡連接層(Connectin Layer),其次是中間處理層,包括 Cache 和 各種 Buffers、協議分析器、以及其他工具,再是公共存儲抽象服務層,然后最下層是各種存儲引擎,上圖中列舉了內存式,sqlite,leveldb等存儲引擎,當然你也可以實現自己的存儲引擎。
1、網絡連接層(Connection Layer):負責處理網絡連接,該層主要考慮如何高效地處理大量並發網絡連接,讀者可以閱讀 Memcached 1.6 的源碼來了解更多相關的技術。
2、中間處理層(Median Handle Layer):該層的功能包括 Cache 和各種 Buffers 的管理,協議解析,可能的查詢優化,以及其他的工具。
3、公共存儲抽象服務層(Common Storage Abstract Service Layer):該層主要和下層的存儲引擎進行交互。
4、存儲引擎層(Storage Layer):各種存儲引擎的實現。
不過,為了保持本文簡介和直觀,我們在此只關注第3、4層,即公共存儲抽象服務層(Common Storage Abstract Service Layer)和 存儲引擎層(Storage Layer),

本文以下部分將用簡短的 C 代碼解釋如何實現公共存儲抽象服務層(Common Storage Abstract Service Layer)和存儲引擎層(Storage Layer),同時為了簡單起見,我們沒有實現自己的存儲引擎,而是借助 sqlite 和 leveldb 作為持久化的存儲引擎,另外 in-memory 使用 uthash 作為存儲后端。
實現細節
文件描述
├── csas.c ├── csas.h ├── example-engine.c ├── example-engine.h ├── inmemory-engine.c ├── inmemory-engine.h ├── leveldb-engine.c ├── leveldb-engine.h ├── main.c ├── Makefile ├── sqlite-engine.c └── sqlite-engine.h
本文涉及的代碼一共 12 個文件(c文件、h文件、Makefile),其中各個文件功能如下:
- csas.h, csas.c:Common Storage Abstract Service(CSAS)實現文件。
- example-engine.c, example-engine.h:如何實現自己的存儲引擎,即自定義存儲引擎需要實現的接口。
- inmemory-engine.c, inmemory-engine.h:內存式存儲引擎,基於 uthash。
- leveldb-engine.c, leveldb-engine.h:持久化存儲引擎,基於 google 的 Leveldb。
- sqlite-engine.c, sqlite-engine.h:持久化存儲引擎,基於 sqlite。
- main.c:測試文件。
- Makefile:Makefile 文件。
/* * ============================================================================= * * Filename: csas.h * * Description: common storage abstract service. * * Created: 11/24/2012 03:16:19 PM * * Author: Fu Haiping (forhappy), haipingf@gmail.com * Company: ICT ( Institute Of Computing Technology, CAS ) * * ============================================================================= */ #ifndef CSAS_H #define CSAS_H typedef struct engine_s_ engine_t; typedef struct csas_context_s_ csas_context_t; typedef struct engine_operation_s_ engine_operation_t; struct csas_context_s_ { csas_context_t *next; engine_t *engine; /* other stuff here... */ }; struct engine_s_ { char *engine_name; unsigned int version; engine_operation_t *engine_ops; /* more stuff about storage engine here... */ }; struct engine_operation_s_ { int (*put)(engine_t *engine, const char *key, unsigned int key_len, const char *value, unsigned int value_len); char * (*get)(engine_t *engine, const char *key, unsigned int key_len, unsigned int *value_len); int (*delete)(engine_t *engine, const char *key, unsigned int key_len); int (*unsupported_operation)(engine_t *engine); }; csas_context_t * csas_init(engine_t *engine); void csas_destory(csas_context_t *context); int csas_put(csas_context_t *context, const char *key, unsigned int key_len, const char *value, unsigned int value_len); char * csas_get(csas_context_t *context, const char *key, unsigned int key_len, unsigned int *value_len); int csas_delete(csas_context_t *context, const char *key, unsigned int key_len); #endif /* CSAS_H */
/* * ============================================================================= * * Filename: csas.c * * Description: common storage abstract service. * * Created: 11/24/2012 03:16:38 PM * * Author: Fu Haiping (forhappy), haipingf@gmail.com * Company: ICT ( Institute Of Computing Technology, CAS ) * * ============================================================================= */ #include <assert.h> #include <stdlib.h> #include "csas.h" csas_context_t * csas_init(engine_t *engine) { csas_context_t *context = (csas_context_t *)malloc(sizeof(csas_context_t)); context->engine = engine; context->next = NULL; return context; } void csas_destory(csas_context_t *context) { assert(context != NULL); if (context->engine != NULL) { free(context->engine); } free(context); } int csas_put(csas_context_t *context, const char *key, unsigned int key_len, const char *value, unsigned int value_len) { int ret = -1; ret = context->engine->engine_ops->put(context->engine, key, key_len, value, value_len); return ret; } char * csas_get(csas_context_t *context, const char *key, unsigned int key_len, unsigned int *value_len) { char *retstr = NULL; retstr = context->engine->engine_ops->get(context->engine, key, key_len, value_len); return retstr; } int csas_delete(csas_context_t *context, const char *key, unsigned int key_len) { int ret = -1; ret = context->engine->engine_ops->delete(context->engine, key, key_len); }
那么如何實現自己的存儲引擎呢,你只要按照以下示例代碼實現存儲引擎相關的接口即可,目前只需要實現如下接口:
#ifndef EXAMPLE_ENGINE_H #define EXAMPLE_ENGINE_H #include "csas.h" extern engine_t * engine_example_init(); #endif /* EXAMPLE_ENGINE_H */
/* * ============================================================================= * * Filename: example-engine.c * * Description: example storage engine. * * Created: 11/24/2012 03:49:34 PM * * Author: Fu Haiping (forhappy), haipingf@gmail.com * Company: ICT ( Institute Of Computing Technology, CAS ) * * ============================================================================= */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "example-engine.h" static int put(engine_t *engine, const char *key, unsigned int key_len, const char *value, unsigned int value_len) { printf("hook call to example put.\n"); return 0; } static char * get(engine_t *engine, const char *key, unsigned int key_len, unsigned int *value_len) { printf("hook call to example engine get.\n"); return NULL; } static int delete(engine_t *engine, const char *key, unsigned int key_len) { printf("hook call to example engine delete.\n"); } engine_t * engine_example_init(void) { engine_t *engine = (engine_t *) malloc(sizeof(engine_t)); engine_operation_t *engine_ops = (engine_operation_t *) malloc(sizeof(engine_operation_t)); const char *engine_name = "example engine v0.1"; unsigned int engine_name_len = strlen(engine_name); unsigned int version = 0x1; engine->engine_name = malloc(sizeof(char) * (engine_name_len + 1)); memset(engine->engine_name, 0, (engine_name_len + 1)); strncpy(engine->engine_name, engine_name, engine_name_len); engine->version = version; engine_ops->put = put; engine_ops->get = get; engine_ops->delete = delete; engine->engine_ops = engine_ops; }
測試代碼如下:
/* * ============================================================================= * * Filename: main.c * * Description: main routine. * * Created: 11/24/2012 05:58:22 PM * * Author: Fu Haiping (forhappy), haipingf@gmail.com * Company: ICT ( Institute Of Computing Technology, CAS ) * * ============================================================================= */ #include <stdio.h> #include "leveldb-engine.h" #include "sqlite-engine.h" int main(int args, const char *argv[]) { engine_t *engine_leveldb = engine_leveldb_init(); engine_t *engine_sqlite = engine_sqlite_init(); csas_context_t *context_leveldb = csas_init(engine_leveldb); csas_context_t *context_sqlite = csas_init(engine_sqlite); csas_put(context_leveldb, "hello", 5, "world", 5); csas_put(context_sqlite, "hello", 5, "world", 5); }
(待續) 以上是本文前一部分內容,接下來會慢慢完善的,這個小項目的代碼整理后會放到 github 上,感興趣的同學可以一起討論討論。:-)
