新聞中心
Redis源碼剖析:從源碼到對照表

在湘潭等地區(qū),都構建了全面的區(qū)域性戰(zhàn)略布局,加強發(fā)展的系統(tǒng)性、市場前瞻性、產品創(chuàng)新能力,以專注、極致的服務理念,為客戶提供成都網站建設、網站制作 網站設計制作定制網站設計,公司網站建設,企業(yè)網站建設,品牌網站建設,成都全網營銷推廣,成都外貿網站制作,湘潭網站建設費用合理。
Redis是一個開源的、高性能的、支持多種數(shù)據(jù)結構的NoSQL數(shù)據(jù)庫,它是由Salvatore Sanfilippo開發(fā)的,C語言編寫。Redis在工業(yè)界得到了廣泛的應用,被譽為高性能的“鍵值存儲器”或“鍵值數(shù)據(jù)庫”。
由于Redis的應用廣泛,源碼的閱讀也變得重要,本文將從源碼的角度,剖析Redis數(shù)據(jù)庫的內部原理,幫助讀者更好地了解Redis的工作過程,并提供對照表以加深記憶。
Redis源碼剖析
Redis數(shù)據(jù)庫的源碼主要由以下幾部分組成:網絡、多線程、內存分配、持久化、訂閱和發(fā)布、客戶端等。下面通過一些核心源碼的分析,讓讀者了解Redis數(shù)據(jù)庫內部結構。
網絡
Redis是基于Socket通信的,Socket通信分為分叉處理和非分叉處理兩種方式,其中非分叉處理的效率更好,且可靠性更高。
以下是Redis網絡連接的核心代碼:
/* Set the socket non-blocking. We have to do the same in Redis, but since
* socket operations are serialized on the event loop, it's better to
* set the socket in non-blocking mode in the accept() call, to get
* better performance inside the event loop itself. */
anetNonBlock(NULL, connfd);
/* Set TCP keep alive option to detect dead peers. */
anetKeepAlive(NULL, connfd, server.tcpkeepalive);
/* Create a client that is by default not yet registered in an event loop. */
c = createClient(connfd);
/* Check if the user is authenticated. */
if (server.requirepass) {
c->flags |= CLIENT_PENDING_WRITE;
c->auth_callback = connCreateAuthCallback(connfd, conn);
aeCreateFileEvent(server.el, connfd, AE_WRITABLE, sendAuthReply, c);
return c;
}
在上面的代碼中,anetNonBlock()函數(shù)設置連接的套接字為非阻塞模式,并且anetKeepAlive()函數(shù)設置TCP長連接和心跳機制。如果需要密碼認證,Redis數(shù)據(jù)庫會將連接添加到Redis事件服務器,等待認證消息到達。
多線程
Redis數(shù)據(jù)庫中,多線程主要用于實現(xiàn)數(shù)據(jù)的并發(fā)讀寫操作,并提高數(shù)據(jù)庫的并發(fā)性能。Redis使用了一種簡單的多線程架構,并使用了讀寫鎖來確保數(shù)據(jù)的安全性。
以下是Redis數(shù)據(jù)庫的多線程鎖保證代碼:
/* Acquire the lock for the specified object. This function is used in
* blocking operations, and has a timeout. */
int lockThreadedIO(const char *func, robj *key, long long timeout, aeEventLoop *el) {
connection *conn = el->ioconn;
/* Don't wt to a negative timeout. */
if (timeout
/* Create the lock key as a string object. */
robj *lockkey = createObject(OBJ_STRING,
sdscatprintf(sdsempty(), "threaded-io-lock:%s:%s",
func, (char*)key->ptr));
/* Lock the lock. */
while (timeout >= 0) {
/* Try to create the lock key with a timeout. */
long long start = ustime();
int64_t res = setnxOne(conn,lockkey,shared.cone,NULL);
/* Check if we acquired the lock. */
if (res == 1) {
decrRefCount(lockkey);
return 0;
}
/* Wt for a little bit, then try agn. */
if (timeout > THREAD_IDELTIME_CAP) {
uint64_t delay = (rand()%1000)*1000 + 500000; /* Max 1.5 sec */
delay = (delay * timeout) / THREAD_IDELTIME_CAP;
if (delay
if (delay > timeout) delay = timeout;
usleep(delay);
timeout -= (ustime()-start)/1000;
} else {
usleep(THREAD_MIN_SLEEP);
timeout -= THREAD_MIN_SLEEP/1000;
}
/* Check for SIGTERM or SIGINT signals. */
stopThreadedIOIfNeeded();
}
decrRefCount(lockkey);
errno = ETIMEDOUT;
return -1;
}
這段代碼使用了讀寫鎖,確保線程之間的數(shù)據(jù)安全。對于讀取操作,線程可以使用共享讀鎖,允許多個線程同時訪問相同的數(shù)據(jù)。但對于寫操作,線程必須使用獨占寫鎖,以確保線程之間不會干擾數(shù)據(jù)的修改。
內存分配
Redis數(shù)據(jù)庫的內存管理策略采用了基于Unix機制的內存分配,包括mmap、sbrk以及malloc等。
以下是Redis內存分配策略代碼:
/* Macro to revert back a couple of lines of code when a malloc() fls, to
* repeat the operation with a smaller request. */
#define REDIS_MALLOC_FL_CLEAN(ptr,size) do { \
zfree(ptr); \
ptr = zmalloc(size); \
if (!ptr) oom("malloc()",size); \
} while(0);
/* Allocate or realloc 'oldptr' so that it can contn at least
* 'minsize' bytes of data. */
void *zrealloc(void *oldptr, size_t size) {
void *ptr = realloc(oldptr,size);
if (!ptr) oom("realloc()",size);
return ptr;
}
/* Allocate 'size' bytes of memory, and return a pointer to the allocated
* memory. */
void *zmalloc(size_t size) {
void *ptr = malloc(size+PREFIX_SIZE);
/* Even on flure to allocate we set the prefix in order to get predictable
* behavior from zfree. */
if (!ptr) oom("malloc()",size);
*((size_t*)ptr) = size;
return (char*)ptr+PREFIX_SIZE;
}
在這段代碼中,Redis的內存分配策略主要是通過宏定義完成,使用zmalloc()為Redis分配內存,并設置前綴以確保內存分配的可預測性。同時,Redis代碼中還包括了內存溢出的處理代碼,確保Redis在分配內存時不會因為內存溢出而崩潰。
持久化
Redis數(shù)據(jù)庫支持數(shù)據(jù)持久化,可以將數(shù)據(jù)保存在磁盤中,以防止宕機和數(shù)據(jù)丟失。Redis的數(shù)據(jù)持久化主要分為rdb持久化和aof持久化兩種方式。
以下是Redis數(shù)據(jù)庫的rdb持久化代碼:
/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success. */
int rdbSave(char *filename) {
atomicSet(server.rdb_child_pid,-1);
if ((server.bgrewritechildpid != -1 || server.aof_child_pid != -1 ||
server.pipe_conns != NULL) &&
server.rdb_child_pid == -1)
{
errno = EAGN;
return REDIS_ERR;
}
int fd;
ssize_t nwritten;
FILE *fp;
/* Open the output file. */
if ((fp = fopen(filename,"w")) == NULL) {
redisLog(REDIS_WARNING,"Fled opening .rdb for saving: %s", strerror(errno));
return REDIS_ERR;
}
fd = fileno(fp);
if (server.rdb_compression) {
if (setCompressionAlgorithm(fd)
fclose(fp);
return REDIS_ERR;
}
}
/* BgSave */
beforeRDBSave();
if (rdbSaveRio(fd,&server.rdb_save Rio) == -1) {
fclose(fp);
return REDIS_ERR;
}
afterRDBSave();
fflush(fp);
if (ferror(fp)) {
fclose(fp);
return REDIS_ERR;
}
nwritten = server.rdb_save Rio .processedBytes();
if (server.rdb_checksum) {
unsigned char digest[10];
memset(digest,0,sizeof(digest));
/* Note that we are just obtning the digest of the first database.
* It's not really a good way, but redis-check-rdb will spit out a
* warning to the user. */
rio cksum;
rioInitWithFlag(&cksum,RIO_CHECKSUM);
rdbSaveRio(&cksum,NULL);
/* Append the checksum to the RDB file. */
memcpy(digest,cksum.updateChecksum(NULL,0
香港服務器選創(chuàng)新互聯(lián),2H2G首月10元開通。
創(chuàng)新互聯(lián)(www.cdcxhl.com)互聯(lián)網服務提供商,擁有超過10年的服務器租用、服務器托管、云服務器、虛擬主機、網站系統(tǒng)開發(fā)經驗。專業(yè)提供云主機、虛擬主機、域名注冊、VPS主機、云服務器、香港云服務器、免備案服務器等。
網站欄目:Redis源碼剖析從源碼到對照表(redis源碼對照表)
文章網址:http://m.5511xx.com/article/dhscppi.html


咨詢
建站咨詢
