一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數據庫技術|

服務器之家 - 數據庫 - Redis - Redis定時任務原理的實現

Redis定時任務原理的實現

2022-03-07 16:58心城以北 Redis

本文主要是基于 redis 6.2 源碼進行分析定時事件的數據結構和常見操作,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

本文主要是基于 redis 6.2 源碼進行分析定時事件的數據結構和常見操作。

數據結構

在 redis 中通過 aeTimeEvent 結構來創建定時任務事件,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Time event structure */
typedef struct aeTimeEvent {
    // 標識符
    long long id; /* time event identifier. */
    // 定時納秒數
    monotime when;
    // 定時回調函數
    aeTimeProc *timeProc;
    // 注銷定時器時候的回調函數
    aeEventFinalizerProc *finalizerProc;
    void *clientData;
    struct aeTimeEvent *prev;
    struct aeTimeEvent *next;
    int refcount; /* refcount to prevent timer events from being
             * freed in recursive time event calls. */
} aeTimeEvent;

常見操作

1. 創建定時事件

redis 中最重要的定時函數且是周期執行的函數,使用的是 serverCron 函數。在 redis 中由于定時任務比較少,因此并沒有嚴格的按照過期時間來排序的,而是按照 id自增 + 頭插法 來保證基本有序。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
  serverPanic("Can't create event loop timers.");
  exit(1);
}
 
//創建定時器對象
long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
        aeTimeProc *proc, void *clientData,
        aeEventFinalizerProc *finalizerProc)
{
    long long id = eventLoop->timeEventNextId++;
    aeTimeEvent *te;
 
    te = zmalloc(sizeof(*te));
    if (te == NULL) return AE_ERR;
    te->id = id;
    te->when = getMonotonicUs() + milliseconds * 1000;
    te->timeProc = proc;
    te->finalizerProc = finalizerProc;
    te->clientData = clientData;
    te->prev = NULL;
    // 頭插法 
    te->next = eventLoop->timeEventHead;
    te->refcount = 0;
    if (te->next)
        te->next->prev = te;
    eventLoop->timeEventHead = te;
    return id;
}

Redis定時任務原理的實現

2. 觸發定時事件

redis 中是采用 IO 復用來進行定時任務的。

查找距離現在最近的定時事件,見 usUntilEarliestTimer

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
?
/* How many microseconds until the first timer should fire.
 * If there are no timers, -1 is returned.
 *
 * Note that's O(N) since time events are unsorted.
 * Possible optimizations (not needed by Redis so far, but...):
 * 1) Insert the event in order, so that the nearest is just the head.
 *    Much better but still insertion or deletion of timers is O(N).
 * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).
 */
static int64_t usUntilEarliestTimer(aeEventLoop *eventLoop) {
    aeTimeEvent *te = eventLoop->timeEventHead;
    if (te == NULL) return -1;
 
    aeTimeEvent *earliest = NULL;
    while (te) {
        if (!earliest || te->when < earliest->when)
            earliest = te;
        te = te->next;
    }
 
    monotime now = getMonotonicUs();
    return (now >= earliest->when) ? 0 : earliest->when - now;
}
 
?

這里時間復雜度可能比較高,實際中需要結合具體場景使用。

更新剩余過期時間,想想為啥呢?因為我們前面提到過,IO 復用有可能因為 IO 事件返回,所以需要更新。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
  usUntilTimer = usUntilEarliestTimer(eventLoop);
 
if (usUntilTimer >= 0) {
  tv.tv_sec = usUntilTimer / 1000000;
  tv.tv_usec = usUntilTimer % 1000000;
  tvp = &tv;
} else {
  if (flags & AE_DONT_WAIT) {
    // 不等待
    tv.tv_sec = tv.tv_usec = 0;
    tvp = &tv;
  } else {
    /* Otherwise we can block */
    tvp = NULL; /* wait forever */
  }
}

3. 執行定時事件

一次性的執行完直接刪除,周期性的執行完在重新添加到鏈表。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/* Process time events */
static int processTimeEvents(aeEventLoop *eventLoop) {
  int processed = 0;
  aeTimeEvent *te;
  long long maxId;
 
  te = eventLoop->timeEventHead;
  maxId = eventLoop->timeEventNextId-1;
  monotime now = getMonotonicUs();
  
  // 刪除定時器
  while(te) {
    long long id;
        
    // 下一輪中對事件進行刪除
    /* Remove events scheduled for deletion. */
    if (te->id == AE_DELETED_EVENT_ID) {
      aeTimeEvent *next = te->next;
      /* If a reference exists for this timer event,
             * don't free it. This is currently incremented
             * for recursive timerProc calls */
      if (te->refcount) {
        te = next;
        continue;
      }
      if (te->prev)
        te->prev->next = te->next;
      else
        eventLoop->timeEventHead = te->next;
      if (te->next)
        te->next->prev = te->prev;
      if (te->finalizerProc) {
        te->finalizerProc(eventLoop, te->clientData);
        now = getMonotonicUs();
      }
      zfree(te);
      te = next;
      continue;
    }
    
    if (te->id > maxId) {
      te = te->next;
      continue;
    }
 
    if (te->when <= now) {
      int retval;
 
      id = te->id;
      te->refcount++;
      // timeProc 函數返回值 retVal 為時間事件執行的間隔
      retval = te->timeProc(eventLoop, id, te->clientData);
      te->refcount--;
      processed++;
      now = getMonotonicUs();
      if (retval != AE_NOMORE) {
        te->when = now + retval * 1000;
      } else {
        // 如果超時了,那么標記為刪除
        te->id = AE_DELETED_EVENT_ID;
      }
    }
    // 執行下一個
    te = te->next;
  }
  return processed;
}

總結

優點:實現簡單
缺點:如果定時任務很多,效率比較低。

到此這篇關于Redis定時任務原理的實現的文章就介紹到這了,更多相關Redis定時任務內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://juejin.cn/post/7072016644272291847

延伸 · 閱讀

精彩推薦
  • Redisredis實現排行榜功能

    redis實現排行榜功能

    排行榜在很多地方都能使用到,redis的zset可以很方便地用來實現排行榜功能,本文就來簡單的介紹一下如何使用,具有一定的參考價值,感興趣的小伙伴們...

    乘月歸5022021-08-05
  • Redisredis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    redis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    這篇文章主要給大家介紹了關于redis中如何使用lua腳本讓你的靈活性提高5個逼格的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具...

    一線碼農5812019-11-18
  • RedisRedis如何實現數據庫讀寫分離詳解

    Redis如何實現數據庫讀寫分離詳解

    Redis的主從架構,能幫助我們實現讀多,寫少的情況,下面這篇文章主要給大家介紹了關于Redis如何實現數據庫讀寫分離的相關資料,文中通過示例代碼介紹...

    羅兵漂流記6092019-11-11
  • RedisRedis全量復制與部分復制示例詳解

    Redis全量復制與部分復制示例詳解

    這篇文章主要給大家介紹了關于Redis全量復制與部分復制的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Redis爬蟲具有一定的參考學習...

    豆子先生5052019-11-27
  • Redisredis 交集、并集、差集的具體使用

    redis 交集、并集、差集的具體使用

    這篇文章主要介紹了redis 交集、并集、差集的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友...

    xiaojin21cen10152021-07-27
  • RedisRedis的配置、啟動、操作和關閉方法

    Redis的配置、啟動、操作和關閉方法

    今天小編就為大家分享一篇Redis的配置、啟動、操作和關閉方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    大道化簡5312019-11-14
  • Redis詳解Redis復制原理

    詳解Redis復制原理

    與大多數db一樣,Redis也提供了復制機制,以滿足故障恢復和負載均衡等需求。復制也是Redis高可用的基礎,哨兵和集群都是建立在復制基礎上實現高可用的...

    李留廣10222021-08-09
  • RedisRedis 事務知識點相關總結

    Redis 事務知識點相關總結

    這篇文章主要介紹了Redis 事務相關總結,幫助大家更好的理解和學習使用Redis,感興趣的朋友可以了解下...

    AsiaYe8232021-07-28
主站蜘蛛池模板: 扒开双腿疯狂进出爽爽动态图 | 色综合91久久精品中文字幕 | 奇米777四色精品综合影院 | 亚洲天堂在线视频观看 | 热巴在公交车h文 | 草莓秋葵菠萝蜜绿巨人污 | 欧美一区二区三区在线观看免费 | 日本aaaa级| 色在线亚洲视频www 色欲麻豆国产福利精品 | 免费理伦片在线观看全网站 | 亚洲国产精品一区二区三区久久 | 亚洲欧美日韩精品久久亚洲区 | 91国语自产拍在线观看 | 亚洲欧美视频在线播放 | 青草青草久热精品视频在线网站 | 精品四虎 | 好男人好资源在线观看免费 | 给我免费观看的视频在线播放 | 亚洲国产精品久久久久久网站 | 日韩精品成人a在线观看 | 免费aⅴ片 | chinese男同志videos| 国产成人a v在线影院 | 深夜在线看 | 蜜桃影像传媒破解版 | 日韩一 | 黄动漫车车好快的车车a | 国产小视频在线免费 | 成人亚洲欧美日韩在线观看 | 免费一级欧美片在线观看 | 欧美成人aa | 999精品视频这里只有精品 | 国产91精品露脸国语对白 | 精品免费国产 | 色婷婷天天综合在线 | www.爱操 | 午夜成私人影院在线观看 | 色字当头 | 国产亚洲精品激情一区二区三区 | 趴好撅高打屁股sp调教h | 奇米网在线 |