关键词搜索

源码搜索 ×
×

漫话Redis源码之六十三

发布2022-01-16浏览858次

详情内容

多数人对LRU应该很熟悉吧。这里就是LRU的近似实现算法,其原理跟LRU差不错。

大家在准备笔试面试的时候,肯定遇到过LRU相关的问题。

  1. /* LRU approximation algorithm
  2. *
  3. * Redis uses an approximation of the LRU algorithm that runs in constant
  4. * memory. Every time there is a key to expire, we sample N keys (with
  5. * N very small, usually in around 5) to populate a pool of best keys to
  6. * evict of M keys (the pool size is defined by EVPOOL_SIZE).
  7. *
  8. * The N keys sampled are added in the pool of good keys to expire (the one
  9. * with an old access time) if they are better than one of the current keys
  10. * in the pool.
  11. *
  12. * After the pool is populated, the best key we have in the pool is expired.
  13. * However note that we don't remove keys from the pool when they are deleted
  14. * so the pool may contain keys that no longer exist.
  15. *
  16. * When we try to evict a key, and all the entries in the pool don't exist
  17. * we populate it again. This time we'll be sure that the pool has at least
  18. * one key that can be evicted, if there is at least one key that can be
  19. * evicted in the whole database. */
  20. /* Create a new eviction pool. */
  21. void evictionPoolAlloc(void) {
  22. struct evictionPoolEntry *ep;
  23. int j;
  24. ep = zmalloc(sizeof(*ep)*EVPOOL_SIZE);
  25. for (j = 0; j < EVPOOL_SIZE; j++) {
  26. ep[j].idle = 0;
  27. ep[j].key = NULL;
  28. ep[j].cached = sdsnewlen(NULL,EVPOOL_CACHED_SDS_SIZE);
  29. ep[j].dbid = 0;
  30. }
  31. EvictionPoolLRU = ep;
  32. }
  33. /* This is an helper function for performEvictions(), it is used in order
  34. * to populate the evictionPool with a few entries every time we want to
  35. * expire a key. Keys with idle time bigger than one of the current
  36. * keys are added. Keys are always added if there are free entries.
  37. *
  38. * We insert keys on place in ascending order, so keys with the smaller
  39. * idle time are on the left, and keys with the higher idle time on the
  40. * right. */
  41. void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evictionPoolEntry *pool) {
  42. int j, k, count;
  43. dictEntry *samples[server.maxmemory_samples];
  44. count = dictGetSomeKeys(sampledict,samples,server.maxmemory_samples);
  45. for (j = 0; j < count; j++) {
  46. unsigned long long idle;
  47. sds key;
  48. robj *o;
  49. dictEntry *de;
  50. de = samples[j];
  51. key = dictGetKey(de);
  52. /* If the dictionary we are sampling from is not the main
  53. * dictionary (but the expires one) we need to lookup the key
  54. * again in the key dictionary to obtain the value object. */
  55. if (server.maxmemory_policy != MAXMEMORY_VOLATILE_TTL) {
  56. if (sampledict != keydict) de = dictFind(keydict, key);
  57. o = dictGetVal(de);
  58. }
  59. /* Calculate the idle time according to the policy. This is called
  60. * idle just because the code initially handled LRU, but is in fact
  61. * just a score where an higher score means better candidate. */
  62. if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) {
  63. idle = estimateObjectIdleTime(o);
  64. } else if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
  65. /* When we use an LRU policy, we sort the keys by idle time
  66. * so that we expire keys starting from greater idle time.
  67. * However when the policy is an LFU one, we have a frequency
  68. * estimation, and we want to evict keys with lower frequency
  69. * first. So inside the pool we put objects using the inverted
  70. * frequency subtracting the actual frequency to the maximum
  71. * frequency of 255. */
  72. idle = 255-LFUDecrAndReturn(o);
  73. } else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {
  74. /* In this case the sooner the expire the better. */
  75. idle = ULLONG_MAX - (long)dictGetVal(de);
  76. } else {
  77. serverPanic("Unknown eviction policy in evictionPoolPopulate()");
  78. }
  79. /* Insert the element inside the pool.
  80. * First, find the first empty bucket or the first populated
  81. * bucket that has an idle time smaller than our idle time. */
  82. k = 0;
  83. while (k < EVPOOL_SIZE &&
  84. pool[k].key &&
  85. pool[k].idle < idle) k++;
  86. if (k == 0 && pool[EVPOOL_SIZE-1].key != NULL) {
  87. /* Can't insert if the element is < the worst element we have
  88. * and there are no empty buckets. */
  89. continue;
  90. } else if (k < EVPOOL_SIZE && pool[k].key == NULL) {
  91. /* Inserting into empty position. No setup needed before insert. */
  92. } else {
  93. /* Inserting in the middle. Now k points to the first element
  94. * greater than the element to insert. */
  95. if (pool[EVPOOL_SIZE-1].key == NULL) {
  96. /* Free space on the right? Insert at k shifting
  97. * all the elements from k to end to the right. */
  98. /* Save SDS before overwriting. */
  99. sds cached = pool[EVPOOL_SIZE-1].cached;
  100. memmove(pool+k+1,pool+k,
  101. sizeof(pool[0])*(EVPOOL_SIZE-k-1));
  102. pool[k].cached = cached;
  103. } else {
  104. /* No free space on right? Insert at k-1 */
  105. k--;
  106. /* Shift all elements on the left of k (included) to the
  107. * left, so we discard the element with smaller idle time. */
  108. sds cached = pool[0].cached; /* Save SDS before overwriting. */
  109. if (pool[0].key != pool[0].cached) sdsfree(pool[0].key);
  110. memmove(pool,pool+1,sizeof(pool[0])*k);
  111. pool[k].cached = cached;
  112. }
  113. }
  114. /* Try to reuse the cached SDS string allocated in the pool entry,
  115. * because allocating and deallocating this object is costly
  116. * (according to the profiler, not my fantasy. Remember:
  117. * premature optimization bla bla bla. */
  118. int klen = sdslen(key);
  119. if (klen > EVPOOL_CACHED_SDS_SIZE) {
  120. pool[k].key = sdsdup(key);
  121. } else {
  122. memcpy(pool[k].cached,key,klen+1);
  123. sdssetlen(pool[k].cached,klen);
  124. pool[k].key = pool[k].cached;
  125. }
  126. pool[k].idle = idle;
  127. pool[k].dbid = dbid;
  128. }
  129. }


 

相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载