Redisのデータベースはソースコードの読み取りを実現する
13318 ワード
lookupKey:指定したキーを検索し、対応する値を返す場合
lookupKeyRead:まずkeyが期限切れであるかどうかを確認し、データベースから値を取り出し、ヒット/不明情報を更新します.
lookupKeyWrite
lookupKeyReadOrReply:読み込み操作を実行するためにデータベースからキーを返す値を検索します.
lookupKeyWriteOrReply:書き込み操作を実行するためにデータベースからキーを返す値を検索します.
dbAdd:キー値対keyとvalをデータベースに追加してみます.
setKey:高レベルのSET操作関数.
dbExists:キーキーキーがデータベースに存在するか、戻り1が存在するか、戻り0が存在しないかを確認します.
dbRandomKey:ランダムにデータベースからキーを取り出し、文字列オブジェクトとしてこのキーを返します.
dbDelete:指定したキー、キーの値、およびキーの有効期限をデータベースから削除します.
EmptyDb:サーバのすべてのデータを空にします.
selectDb:クライアントのターゲットデータベースをidで指定したデータベースに切り替える
SignalModifiedKey:データベース内のキーが変更されるたびに、signalModifiedKey()関数が呼び出されます.
SignalFlushedDb:データベースが空になるたびにsignalFlushDb()が呼び出されます.
flushdbCommand:クライアントが指定したデータベースを空にする
flushallCommand:サーバ内のすべてのデータベースを空にします.
delCommand:クライアントが指定したキーを削除する
existsCommand:キーが存在するかどうかを確認します
selectCommand:クライアントの現在のデータベースを変更する
randomkeyCommand:キーを返します
keysCommand:クライアントが指定したモードに一致するすべてのキーを返します.
removeExpire:キーキーの削除の有効期限
setExpire:キーキーの有効期限をwhenに設定
getExpire:指定したkeyの有効期限を返します.
propagateExpire:期限切れを付属ノードとAOFファイルに伝播します.
expireIfNeeded:keyが期限切れになっているかどうかを確認し、そうであればデータベースから削除します.
robj *lookupKey(redisDb *db, robj *key) {
//
dictEntry *de = dictFind(db->dict,key->ptr);
//
if (de) {
//
robj *val = dictGetVal(de);
/* Update the access time for the ageing algorithm.
* Don't do it if we have a saving child, as this will trigger
* a copy on write madness. */
// ( , copy-on-write )
if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)
val->lru = LRU_CLOCK();
//
return val;
} else {
//
return NULL;
}
}
lookupKeyRead:まずkeyが期限切れであるかどうかを確認し、データベースから値を取り出し、ヒット/不明情報を更新します.
robj *lookupKeyRead(redisDb *db, robj *key) {
robj *val;
// key
expireIfNeeded(db,key);
//
val = lookupKey(db,key);
// /
if (val == NULL)
server.stat_keyspace_misses++;
else
server.stat_keyspace_hits++;
//
return val;
}
lookupKeyWrite
robj *lookupKeyWrite(redisDb *db, robj *key) {
//
expireIfNeeded(db,key);
// key
return lookupKey(db,key);
}
lookupKeyReadOrReply:読み込み操作を実行するためにデータベースからキーを返す値を検索します.
robj *lookupKeyReadOrReply(redisClient *c, robj *key, robj *reply) {
//
robj *o = lookupKeyRead(c->db, key);
//
if (!o) addReply(c,reply);
return o;
}
lookupKeyWriteOrReply:書き込み操作を実行するためにデータベースからキーを返す値を検索します.
robj *lookupKeyWriteOrReply(redisClient *c, robj *key, robj *reply) {
robj *o = lookupKeyWrite(c->db, key);
if (!o) addReply(c,reply);
return o;
}
dbAdd:キー値対keyとvalをデータベースに追加してみます.
void dbAdd(redisDb *db, robj *key, robj *val) {
//
sds copy = sdsdup(key->ptr);
//
int retval = dictAdd(db->dict, copy, val);
// ,
redisAssertWithInfo(NULL,key,retval == REDIS_OK);
// ,
if (server.cluster_enabled) slotToKeyAdd(key);
}
setKey:高レベルのSET操作関数.
void setKey(redisDb *db, robj *key, robj *val) {
//
if (lookupKeyWrite(db,key) == NULL) {
dbAdd(db,key,val);
} else {
dbOverwrite(db,key,val);
}
incrRefCount(val);
//
removeExpire(db,key);
//
signalModifiedKey(db,key);
}
dbExists:キーキーキーがデータベースに存在するか、戻り1が存在するか、戻り0が存在しないかを確認します.
int dbExists(redisDb *db, robj *key) {
return dictFind(db->dict,key->ptr) != NULL;
}
dbRandomKey:ランダムにデータベースからキーを取り出し、文字列オブジェクトとしてこのキーを返します.
robj *dbRandomKey(redisDb *db) {
dictEntry *de;
while(1) {
sds key;
robj *keyobj;
//
de = dictGetRandomKey(db->dict);
//
if (de == NULL) return NULL;
//
key = dictGetKey(de);
// ,
keyobj = createStringObject(key,sdslen(key));
//
if (dictFind(db->expires,key)) {
// , ,
if (expireIfNeeded(db,keyobj)) {
decrRefCount(keyobj);
continue; /* search for another key. This expired. */
}
}
// ( )
return keyobj;
}
}
dbDelete:指定したキー、キーの値、およびキーの有効期限をデータベースから削除します.
int dbDelete(redisDb *db, robj *key) {
/* Deleting an entry from the expires dict will not free the sds of
* the key, because it is shared with the main dictionary. */
//
if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
//
if (dictDelete(db->dict,key->ptr) == DICT_OK) {
// ,
if (server.cluster_enabled) slotToKeyDel(key);
return 1;
} else {
//
return 0;
}
}
EmptyDb:サーバのすべてのデータを空にします.
long long emptyDb(void(callback)(void*)) {
int j;
long long removed = 0;
//
for (j = 0; j < server.dbnum; j++) {
//
removed += dictSize(server.db[j].dict);
//
dictEmpty(server.db[j].dict,callback);
//
dictEmpty(server.db[j].expires,callback);
}
// ,
if (server.cluster_enabled) slotToKeyFlush();
//
return removed;
}
selectDb:クライアントのターゲットデータベースをidで指定したデータベースに切り替える
int selectDb(redisClient *c, int id) {
// id
if (id < 0 || id >= server.dbnum)
return REDIS_ERR;
// ( )
c->db = &server.db[id];
return REDIS_OK;
}
SignalModifiedKey:データベース内のキーが変更されるたびに、signalModifiedKey()関数が呼び出されます.
void signalModifiedKey(redisDb *db, robj *key) {
touchWatchedKey(db,key);
}
SignalFlushedDb:データベースが空になるたびにsignalFlushDb()が呼び出されます.
void signalFlushedDb(int dbid) {
touchWatchedKeysOnFlush(dbid);
}
flushdbCommand:クライアントが指定したデータベースを空にする
void flushdbCommand(redisClient *c) {
server.dirty += dictSize(c->db->dict);
//
signalFlushedDb(c->db->id);
// dict expires
dictEmpty(c->db->dict,NULL);
dictEmpty(c->db->expires,NULL);
// ,
if (server.cluster_enabled) slotToKeyFlush();
addReply(c,shared.ok);
}
flushallCommand:サーバ内のすべてのデータベースを空にします.
void flushallCommand(redisClient *c) {
//
signalFlushedDb(-1);
//
server.dirty += emptyDb(NULL);
addReply(c,shared.ok);
// RDB ,
if (server.rdb_child_pid != -1) {
kill(server.rdb_child_pid,SIGUSR1);
rdbRemoveTempFile(server.rdb_child_pid);
}
// RDB
if (server.saveparamslen > 0) {
/* Normally rdbSave() will reset dirty, but we don't want this here
* as otherwise FLUSHALL will not be replicated nor put into the AOF. */
// rdbSave() dirty
// FLUSHALL ,
// rdbSave() dirty
int saved_dirty = server.dirty;
rdbSave(server.rdb_filename);
server.dirty = saved_dirty;
}
server.dirty++;
}
delCommand:クライアントが指定したキーを削除する
void delCommand(redisClient *c) {
int deleted = 0, j;
//
for (j = 1; j < c->argc; j++) {
//
expireIfNeeded(c->db,c->argv[j]);
//
if (dbDelete(c->db,c->argv[j])) {
// ,
signalModifiedKey(c->db,c->argv[j]);
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,
"del",c->argv[j],c->db->id);
server.dirty++;
// deleted
deleted++;
}
}
//
addReplyLongLong(c,deleted);
}
existsCommand:キーが存在するかどうかを確認します
void existsCommand(redisClient *c) {
// , ,
//
expireIfNeeded(c->db,c->argv[1]);
//
if (dbExists(c->db,c->argv[1])) {
addReply(c, shared.cone);
} else {
addReply(c, shared.czero);
}
}
selectCommand:クライアントの現在のデータベースを変更する
void selectCommand(redisClient *c) {
long id;
//
if (getLongFromObjectOrReply(c, c->argv[1], &id,
"invalid DB index") != REDIS_OK)
return;
if (server.cluster_enabled && id != 0) {
addReplyError(c,"SELECT is not allowed in cluster mode");
return;
}
//
if (selectDb(c,id) == REDIS_ERR) {
addReplyError(c,"invalid DB index");
} else {
addReply(c,shared.ok);
}
}
randomkeyCommand:キーを返します
void randomkeyCommand(redisClient *c) {
robj *key;
//
if ((key = dbRandomKey(c->db)) == NULL) {
addReply(c,shared.nullbulk);
return;
}
addReplyBulk(c,key);
decrRefCount(key);
}
keysCommand:クライアントが指定したモードに一致するすべてのキーを返します.
void keysCommand(redisClient *c) {
dictIterator *di;
dictEntry *de;
//
sds pattern = c->argv[1]->ptr;
int plen = sdslen(pattern), allkeys;
unsigned long numkeys = 0;
void *replylen = addDeferredMultiBulkLength(c);
// , ( )
di = dictGetSafeIterator(c->db->dict);
allkeys = (pattern[0] == '*' && pattern[1] == '\0');
while((de = dictNext(di)) != NULL) {
sds key = dictGetKey(de);
robj *keyobj;
//
if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {
//
keyobj = createStringObject(key,sdslen(key));
//
if (expireIfNeeded(c->db,keyobj) == 0) {
addReplyBulk(c,keyobj);
numkeys++;
}
decrRefCount(keyobj);
}
}
dictReleaseIterator(di);
setDeferredMultiBulkLength(c,replylen,numkeys);
}
removeExpire:キーキーの削除の有効期限
int removeExpire(redisDb *db, robj *key) {
/* An expire may only be removed if there is a corresponding entry in the
* main dict. Otherwise, the key will never be freed. */
//
redisAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);
//
return dictDelete(db->expires,key->ptr) == DICT_OK;
}
setExpire:キーキーの有効期限をwhenに設定
void setExpire(redisDb *db, robj *key, long long when) {
dictEntry *kde, *de;
/* Reuse the sds from the main dict in the expire dict */
//
kde = dictFind(db->dict,key->ptr);
redisAssertWithInfo(NULL,key,kde != NULL);
//
de = dictReplaceRaw(db->expires,dictGetKey(kde));
//
// , INT String
dictSetSignedIntegerVal(de,when);
}
getExpire:指定したkeyの有効期限を返します.
long long getExpire(redisDb *db, robj *key) {
dictEntry *de;
/* No expire? return ASAP */
//
// ,
if (dictSize(db->expires) == 0 ||
(de = dictFind(db->expires,key->ptr)) == NULL) return -1;
/* The entry was found in the expire dict, this means it should also
* be present in the main dict (safety check). */
redisAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);
//
return dictGetSignedIntegerVal(de);
}
propagateExpire:期限切れを付属ノードとAOFファイルに伝播します.
void propagateExpire(redisDb *db, robj *key) {
robj *argv[2];
// DEL key
argv[0] = shared.del;
argv[1] = key;
incrRefCount(argv[0]);
incrRefCount(argv[1]);
// AOF
if (server.aof_state != REDIS_AOF_OFF)
feedAppendOnlyFile(server.delCommand,db->id,argv,2);
//
replicationFeedSlaves(server.slaves,db->id,argv,2);
decrRefCount(argv[0]);
decrRefCount(argv[1]);
}
expireIfNeeded:keyが期限切れになっているかどうかを確認し、そうであればデータベースから削除します.
int expireIfNeeded(redisDb *db, robj *key) {
//
mstime_t when = getExpire(db,key);
mstime_t now;
//
if (when < 0) return 0; /* No expire for this key */
/* Don't expire anything while loading. It will be done later. */
// ,
if (server.loading) return 0;
/* If we are in the context of a Lua script, we claim that time is
* blocked to when the Lua script started. This way a key can expire
* only the first time it is accessed and not in the middle of the
* script execution, making propagation to slaves / AOF consistent.
* See issue #1525 on Github for more information. */
now = server.lua_caller ? server.lua_time_start : mstime();
/* If we are running in the context of a slave, return ASAP:
* the slave key expiration is controlled by the master that will
* send us synthesized DEL operations for expired keys.
*
* Still we try to return the right information to the caller,
* that is, 0 if we think the key should be still valid, 1 if
* we think the key is expired at this time. */
// replication
// key
//
//
//
if (server.masterhost != NULL) return now > when;
// , ,
/* Return when this key has not expired */
// , 0
if (now <= when) return 0;
/* Delete the key */
server.stat_expiredkeys++;
// AOF
propagateExpire(db,key);
//
notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,
"expired",key,db->id);
//
return dbDelete(db,key);
}