概念html
redis是一個key-value存儲系統。和Memcached相似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)和zset(有序集合)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操做,並且這些操做都是原子性的。在此基礎上,redis支持各類不一樣方式的排序。與memcached同樣,爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件,而且在此基礎上實現了master-slave(主從)同步。redis
開始算法
這裏我使用的是redis-2.2.2.tar.gz版本的redis(下載地址)首先要對它進行安裝,這裏我選擇使用cygwin工具進行安裝,加入我把該壓縮包放在F盤下,使用cygwin工具進行shell命令:shell
1
2
3
|
$
tar
xzf redis-2.2.2.
tar
.gz
$
cd
redis-2.2.2
$
make
|
這裏的make實際上操做的是Makefile文件,Makefile按類型、功能、模塊分別放在若干個目錄中,makefile定義了一系列的規則來指定,哪些文件須要先編譯,哪些文件須要後編譯,哪些文件須要從新編譯,甚至於進行更復雜的功能操做,由於makefile就像一個shell腳本同樣,其中也能夠執行操做系統的命令。數據庫
瀏覽下Redis根目錄中的Makefile文件:緩存
經過make命令能夠執行「cd src && make all」。而此時的make all實際上已經開始執行src目錄中的Makefile文件。這個文件比較複雜,大體就是將一系列的c文件以及h文件連接起來,經過cc/gcc編譯器將文件生成目標文件o,接着將相應的o目標文件在經過編譯器生成exe文件,當你編譯完畢後,在src的目錄上將產生5個exe文件:bash
redis-benchmark.exe:用於作性能測試;網絡
redis-check-aof.exe:更新日誌檢查;數據結構
redis-check-dump.exe:用於本地數據庫檢查;app
redis-cli.exe:客戶端程序;
redis-server.exe:服務端程序;
具體用法這裏很少說了,能夠參考(http://www.cnblogs.com/daizhj/articles/1956681.html)
如今來看下src包含的文件(我按照首字母順序來說):
adlist.h/adlist.c:用於對list的定義,它是個雙向鏈表結構,從頭文件能夠找到:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// list節點
typedef
struct
listNode {
struct
listNode *prev;
struct
listNode *next;
void
*value;
} listNode;
// list迭代器
typedef
struct
listIter {
listNode *next;
int
direction;
} listIter;
// list數據結構
typedef
struct
list {
listNode *head;
listNode *tail;
void
*(*dup)(
void
*ptr);
void
(*
free
)(
void
*ptr);
int
(*match)(
void
*ptr,
void
*key);
unsigned
int
len;
} list;
|
在ListNode節點下包含prev指針和next指針,說明它經過指針將節點進行雙向連接。
而且從adlist.h的頭文件能夠找到很是豐富的方法聲明,包括list建立,list釋放,list頭部/尾部添加節點等等,具體在後面的系列會作出介紹。
ae.h/ae.c:用於Redis的事件處理,包括句柄事件和超時事件。
在ae.c中的頭部能夠發現:
1
2
3
4
5
6
7
8
9
|
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE
#include "ae_kqueue.c"
#else
#include "ae_select.c"
#endif
#endif
|
在網絡相關操做中,定義了一組公共操做接口:aeApiCreate,aeApiFree,aeApiAddEvent,aeApiDelEvent,aeApiPoll,aeApiName方法。在ae_epoll.c、ae_kqueue.c和ae_select.c中,分別實現了基於epoll/kqueue和select系統調用的接口。系統調用的選擇順序依次爲epoll,kqueue,select。
anet.h/anet.c:這兩個文件很是重要,做爲Server/Client通訊的基礎封裝,包括anetTcpServer,anetTcpConnect,anetTcpAccept,anetRead,anetWrite等等方法。
aof.c:aof,全稱爲append only file,做用就是記錄每次的寫操做,在遇到斷電等問題時能夠用它來恢復數據庫狀態。可是他不是bin的,而是text的。一行一行,寫得很規範.若是你是一臺redis,那你也能人肉經過它恢復數據。
config.h/config.c:用於將配置文件redis.conf文件中的配置讀取出來的屬性經過程序放到server對象中。在main函數(server服務主入口點處)能夠發現裏面調用loadServerConfig(char *filename)方法,這個方法就是使用config.c裏面的方法實現。具體會在後面的系列中詳細介紹。
db.c:對於Redis內存數據庫的相關操做。
debug.c:用於調試使用。
dict.h/dict.c:也是很重要的兩個文件,主要對於內存中的hash進行管理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
typedef
struct
dictEntry {
void
*key;
void
*val;
struct
dictEntry *next;
} dictEntry;
typedef
struct
dictType {
unsigned
int
(*hashFunction)(
const
void
*key);
void
*(*keyDup)(
void
*privdata,
const
void
*key);
void
*(*valDup)(
void
*privdata,
const
void
*obj);
int
(*keyCompare)(
void
*privdata,
const
void
*key1,
const
void
*key2);
void
(*keyDestructor)(
void
*privdata,
void
*key);
void
(*valDestructor)(
void
*privdata,
void
*obj);
} dictType;
typedef
struct
dict {
dictType *type;
void
*privdata;
dictht ht[2];
int
rehashidx;
/* rehashing not in progress if rehashidx == -1 */
int
iterators;
/* number of iterators currently running */
} dict;
|
這裏dictEntry做爲一個dict字段結構,裏面包括key以及value,已經指向下一個dictEntry的指針。dictType做爲一些dict的操做結構。dict做爲一個hash結構。後面的文章會具體介紹。
fmacros.h:用於Mac下的兼容性處理。
help.h:輔助於命令的提示信息,做用於redis-cli.exe可執行文件中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
struct
commandHelp {
char
*name;
char
*params;
char
*summary;
int
group;
char
*since;
} commandHelp[] = {
{
"APPEND"
,
"key value"
,
"Append a value to a key"
,
1,
"1.3.3"
},
{
"AUTH"
,
"password"
,
"Authenticate to the server"
,
8,
"0.08"
},
{
"BGREWRITEAOF"
,
"-"
,
"Asynchronously rewrite the append-only file"
,
9,
....
};
|
intset.h/intset.c:整數範圍內的使用set,幷包含相關set操做。
lzf.h/lzf_c.c/lzf_d.c/lzfP.h:對於本地數據庫的保存,使用的是LZF壓縮算法,很神奇,算法只有200-300行的代碼。
multi.c:用於事務處理操做。請看這樣的一個例子:
經過執行exec,能夠提交整個事務過程,若是你想撤銷整個事務過程,你可使用discard命令:
能夠發現get age已經取不到值了,說明discard命令讓事務失效。
networking.c:網絡協議傳輸方法定義相關的都放在這個文件裏面了。包括讓Client鏈接上Server,讓Slave掛接到Master,已經Server/Client之間的信息交互的實現等等。
object.c:用於建立和釋放redisObject對象,redisObject結構爲:
1
2
3
4
5
6
7
8
9
10
11
12
|
typedef
struct
redisObject {
unsigned type:4;
unsigned storage:2;
/* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */
unsigned encoding:4;
unsigned lru:22;
/* lru time (relative to server.lruclock) */
int
refcount;
void
*ptr;
/* VM fields are only allocated if VM is active, otherwise the
* object allocation function will just allocate
* sizeof(redisObjct) minus sizeof(redisObjectVM), so using
* Redis without VM active will not have any overhead. */
} robj;
|
pqsort.h/pqsort.c/sort.c:關於排序算法,sort.c具體做爲Redis場景下的排序實現。
pubsub.c:用於訂閱模式的實現,有點相似於Client廣播發送的方式。
rdb.c:對於Redis本地數據庫的相關操做,默認文件是dump.rdb(經過配置文件得到),包括的操做包括保存,移除,查詢等等。
redis-benchmark.c:用於redis性能測試的實現。請看main方法如下設置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
config.debug = 0;
config.numclients = 50;
config.requests = 10000;
config.liveclients = 0;
config.el = aeCreateEventLoop();
aeCreateTimeEvent(config.el,1,showThroughput,NULL,NULL);
config.keepalive = 1;
config.donerequests = 0;
config.datasize = 3;
config.randomkeys = 0;
config.randomkeys_keyspacelen = 0;
config.quiet = 0;
config.loop = 0;
config.idlemode = 0;
config.latency = NULL;
config.clients = listCreate();
config.hostip =
"127.0.0.1"
;
config.hostport = 6379;
config.hostsocket = NULL;
parseOptions(argc,argv);
config.latency = zmalloc(
sizeof
(
long
long
)*config.requests);
|
默認性能測試中的客戶端數量爲50個,並行發送的請求有10000條,你也能夠經過redis-benchmark命令行參數進行設置。
redis-check-aof.c:用於更新日誌檢查的實現。
redis-check-dump.c:用於本地數據庫檢查的實現。
redis-cli.c:客戶端程序的實現。具體會在後面的文章詳細介紹。
redis.h/redis.c:服務端程序的實現。具體會在後面的文章詳細介紹。
release.h/release.c:用於發佈使用。
replication.c:用於主從數據庫的複製操做的實現。
sds.h/sds.c:用於對字符串的定義,從頭文件能夠找到:
1
2
3
4
5
6
|
//字符串
struct
sdshdr {
int
len;
int
free
;
char
buf[];
};
|
還能夠看到對於字符串的相關操做,包括複製,鏈接,清零等等。
sha1.h/sha1.c:有關於sha算法的實現。
solarisfixes.h:Solaris系統的兼容性實現。
syncio.c:用於同步Socket和文件I/O操做。
t_hash.c/t_list.c/t_set.c/t_string.c/t_zset.c:hash,list,set,string,zset在Server/Client中的應答操做。主要經過redisObject進行類型轉換。
testhelp.h:一個C風格的小型測試框架。
util.c:關於通用工具的方法實現。
version.h:Redis版本號定義。
vm.c:關於虛擬內存的管理實現。
zipmap.h/zipmap.c:zipmap是一個相似於hash的存儲對象。在新建一個hash對象時開始是用zipmap(又稱爲small hash)來存儲的。這個zipmap其實並非hash table可是zipmap相比正常的hash實現能夠節省很多hash自己須要的一些元數據存儲開銷,若是field或者value的大小超出必定限制後,redis會在內部自動將zipmap替換成正常的hash實現。
ziplist.h/ziplist.c:ziplist是一個相似於list的存儲對象。它的原理相似於zipmap。
zmalloc.h/zmalloc.c:關於Redis的內存分配的封裝實現。
下一篇我會介紹下redis-server以及redis-cli的源碼實現。