成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Redis超時問題分析匯總

運維 系統運維 Redis
Redis在分布式應用中占據著越來越重要的地位,短短的幾萬行代碼,實現了一個高性能的數據存儲服務。最近dump中心的cm8集群出現過幾次redis超時的情況,但是查看redis機器的相關內存都沒有發現內存不夠,或者內存發生交換的情況,查看redis源碼之后,發現在某些情況下redis會出現超時的狀況,相關細節如下。

Redis在分布式應用中占據著越來越重要的地位,短短的幾萬行代碼,實現了一個高性能的數據存儲服務。最近dump中心的cm8集群出現過幾次redis超時的情況,但是查看redis機器的相關內存都沒有發現內存不夠,或者內存發生交換的情況,查看redis源碼之后,發現在某些情況下redis會出現超時的狀況,相關細節如下。

1. 網絡。Redis的處理與網絡息息相關,如果網絡出現閃斷則容易發生redis超時的狀況。如果出現這種狀況首先應查看redis機器網絡帶寬信息,判斷是否有閃斷情況發生。

2. 內存。redis所有的數據都放在內存里,當物理內存不夠時,linux os會使用swap內存,導致內存交換發生,這時如果有redis調用命令就會產生redis超時。這里可以通過調整/proc/sys/vm/swappiness參數,來設置物理內存使用超過多少就會進行swap。

  1. int rdbSaveBackground(char *filename) { 
  2.     pid_t childpid; 
  3.     long long start; 
  4.   
  5.     if (server.rdb_child_pid != -1) return REDIS_ERR; 
  6.     serverserver.dirty_before_bgsave = server.dirty; 
  7.     server.lastbgsave_try = time(NULL); 
  8.     start = ustime(); 
  9.     if ((childpid = fork()) == 0) { 
  10.         int retval; 
  11.         /* Child */ 
  12.         if (server.ipfd > 0) close(server.ipfd); 
  13.         if (server.sofd > 0) close(server.sofd); 
  14.         retval = rdbSave(filename); 
  15.         if (retval == REDIS_OK) { 
  16.             size_t private_dirty = zmalloc_get_private_dirty(); 
  17.             if (private_dirty) { 
  18.                 redisLog(REDIS_NOTICE, 
  19.                     "RDB: %zu MB of memory used by copy-on-write", 
  20.                     private_dirty/(1024*1024)); 
  21.             } 
  22.         } 
  23.         exitFromChild((retval == REDIS_OK) ? 0 : 1); 
  24.     } else { 
  25.         /* Parent */ 
  26.         server.stat_fork_time = ustime()-start; 
  27.         if (childpid == -1) { 
  28.             server.lastbgsave_status = REDIS_ERR
  29.             redisLog(REDIS_WARNING,"Can't save in background: fork: %s", 
  30.                 strerror(errno)); 
  31.             return REDIS_ERR; 
  32.         } 
  33.         redisLog(REDIS_NOTICE,"Background saving started by pid %d",childpid); 
  34.         server.rdb_save_time_start = time(NULL); 
  35.         server.rdb_child_pid = childpid
  36.         updateDictResizePolicy(); 
  37.         return REDIS_OK; 
  38.     } 
  39.     return REDIS_OK; /* unreached */ 

 

程序1

另外還有一些特殊情況也會導致swap發生。當我們使用rdb做為redis集群持久化時可能會發生物理內存不夠的情況(aof持久化只是保持支持不斷的追加redis集群變化操作,不太容易引起swap)。當使用rdb持久化時,如程序1所示主進程會fork一個子進程去dump redis中所有的數據,主進程依然為客戶端服務。此時主進程和子進程共享同一塊內存區域, linux內核采用寫時復制來保證數據的安全性。在這種模式下如果客戶端發來寫請求,內核將該頁賦值到一個新的頁面上并標記為寫,在將寫請求寫入該頁面。因此,在rdb持久化時,如果有其他請求,那么redis會使用更多的內存,更容易發生swap,因此在可以快速恢復的場景下盡量少使用rdb持久化可以將rdb dump的條件設的苛刻一點,當然也可以選擇aof,但是aof也有他自身的缺點。另外也可以使用2.6以后的主從結構,將讀寫分離,這樣不會出現server進程上又讀又寫的情景發生 3. Redis單進程處理命令。Redis支持udp和tcp兩種連接,redis客戶端向redis服務器發送包含redis命令的信息,redis服務器收到信息后解析命令后執行相應的操作,redis處理命令是串行的具體流程如下。首先服務端建立連接如程序2所示,在創建socket,bind,listen后返回文件描述符:

 

  1. server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr); 

 

程序2

對于redis這種服務來說,它需要處理成千上萬個連接(***達到655350),需要使用多路復用來處理多個連接。這里redis提供了epoll,select, kqueue來實現,這里在默認使用epoll(ae.c)。拿到listen函數返回的文件描述符fd后,redis將fd和其處理acceptTcpHandler函數加入到事件驅動的鏈表中.實際上在加入事件隊列中,程序4事件驅動程序將套接字相關的fd文件描述符加入到epoll的監聽事件中。

  1.  if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE, 
  2.         acceptTcpHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.ipfd file event."); 
  3.   
  4. int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, 
  5.         aeFileProc *proc, void *clientData) 
  6.     if (fd >= eventLoop->setsize) { 
  7.         errno = ERANGE
  8.         return AE_ERR; 
  9.     } 
  10.     aeFileEvent *fe = &eventLoop->events[fd]; 
  11.   
  12.     if (aeApiAddEvent(eventLoop, fd, mask) == -1) 
  13.         return AE_ERR; 
  14.     fe->mask |= mask; 
  15.     if (mask & AE_READABLE) fe->rfileProc = proc
  16.     if (mask & AE_WRITABLE) fe->wfileProc = proc
  17.     fe->clientDataclientData = clientData; 
  18.     if (fd > eventLoop->maxfd) 
  19.         eventLoop->maxfd = fd; 
  20.     return AE_OK; 

 

程序3

 

  1. static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {  
  2.     aeApiState *state = eventLoop->apidata;  
  3.     struct epoll_event ee;  
  4.     /* If the fd was already monitored for some event, we need a MOD  
  5.      * operation. Otherwise we need an ADD operation. */  
  6.     int op = eventLoop->events[fd].mask == AE_NONE ?  
  7.             EPOLL_CTL_ADD : EPOLL_CTL_MOD;  
  8.     ee.events = 0;  
  9.     mask |= eventLoop->events[fd].mask; /* Merge old events */  
  10.     if (mask & AE_READABLE) ee.events |= EPOLLIN;  
  11.     if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;  
  12.     ee.data.u64 = 0; /* avoid valgrind warning */  
  13.     ee.data.fd = fd;  
  14.     if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;  
  15.     return 0;  
  16. }  

 

程序4

在初始話完所有事件驅動后,如程序5所示主進程根據numevents = aeApiPoll(eventLoop, tvp)獲得io就緒的文件描述符和其對應的處理程序,并對fd進行處理。大致流程是accept()->createclient()->readQueryFromClient()。其中readQueryFromClient()讀取信息中的redis命令-> processInputBuffer()->call()***完成命令。

  1. void aeMain(aeEventLoop *eventLoop) { 
  2.     eventLoop->stop = 0
  3.     while (!eventLoop->stop) { 
  4.         if (eventLoop->beforesleep != NULL) 
  5.             eventLoop->beforesleep(eventLoop); 
  6.         aeProcessEvents(eventLoop, AE_ALL_EVENTS); 
  7.     } 
  8. int aeProcessEvents(aeEventLoop *eventLoop, int flags) 
  9. {------------------------------- 
  10.  numevents = aeApiPoll(eventLoop, tvp); 
  11.         for (j = 0; j < numevents; j++) {             aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd]; 
  12.             int mask = eventLoop->fired[j].mask; 
  13.             int fd = eventLoop->fired[j].fd; 
  14.             int rfired = 0
  15.   
  16.             /* note the fe->mask & mask & ... code: maybe an already processed 
  17.              * event removed an element that fired and we still didn't 
  18.              * processed, so we check if the event is still valid. */ 
  19.             if (fe->mask & mask & AE_READABLE) { 
  20.                 rfired = 1
  21.                 fe->rfileProc(eventLoop,fd,fe->clientData,mask); 
  22.             } 
  23.             if (fe->mask & mask & AE_WRITABLE) { 
  24.                 if (!rfired || fe->wfileProc != fe->rfileProc) 
  25.                     fe->wfileProc(eventLoop,fd,fe->clientData,mask); 
  26.             } 
  27.             processed++; 
  28.         } 

 

程序5

從上述代碼可以看出redis利用ae事件驅動結合epoll多路復用實現了串行式的命令處理。所以一些慢命令例如sort,hgetall,union,mget都會使得單命令處理時間較長,容易引起后續命令time out.所以我們***需要從業務上盡量避免使用慢命令,如將hash格式改為kv自行解析,第二增加redis實例個數,每個redis服務器調用盡量少的慢命令。

責任編輯:黃丹 來源: 搜索技術博客
相關推薦

2014-07-01 11:18:37

Android Stu問題匯總

2022-09-28 14:13:03

Linux工具

2010-09-15 09:20:40

2011-08-12 09:52:35

iPhone開發TableviewUITextField

2010-11-25 11:15:11

MySQL查詢超時

2021-11-15 12:42:25

C# 定位gRPC

2011-04-02 10:29:20

Linux工具

2009-09-22 09:22:03

.NET常見問題

2012-02-06 10:37:07

Java

2013-08-21 14:57:42

objective-c問題

2010-05-13 13:27:23

2009-07-22 08:59:01

Windows xp升Windows 7系統升級

2017-05-17 15:09:46

Linux分析性能工具

2010-10-14 09:15:20

MySQL查詢

2010-02-03 16:32:13

2010-07-21 09:29:33

Perl常見問題

2020-12-28 11:08:18

MySQL數據庫服務器

2010-08-17 09:16:23

2020-10-18 12:00:27

前端開發架構

2020-09-21 14:15:58

Redis數據庫命令
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美精品黄 | 亚洲国产成人精品一区二区 | 98久久| 精品欧美一区二区三区久久久 | 成人午夜在线 | 成人欧美一区二区三区黑人孕妇 | 久久一视频 | 欧美精品一区二区在线观看 | 精品国产亚洲一区二区三区大结局 | 国产一区二区三区亚洲 | 欧美男人的天堂 | 日韩看片 | 一级午夜aaa免费看三区 | 懂色中文一区二区三区在线视频 | 免费高清av | 涩涩视频在线观看 | 一区二区伦理电影 | 韩国久久精品 | 中文字幕精品一区 | 成人精品一区亚洲午夜久久久 | 黑人巨大精品欧美黑白配亚洲 | 亚洲视频欧美视频 | 黄色大片在线视频 | 国产九九九 | 亚洲三级在线 | 一级黄色短片 | 国产成人免费视频网站视频社区 | 东京av男人的天堂 | 中文字幕一区二区三区乱码图片 | 婷婷在线免费 | 中文字幕在线二区 | av色站| 国产一区二区在线视频 | 四虎永久影院 | 日本黄色免费大片 | 欧美日韩美女 | 一区中文字幕 | 综合久久色 | 亚洲精品一区在线观看 | 国产资源在线观看 | 国产一区二区麻豆 |