【039期】头条面试

}

// move the accessed node to the head;

this.moveToHead(node);

return node.value;

}

public void set(String key, int value) {

DLinkedNode node = cache.get(key);

if(node == null){

DLinkedNode newNode = new DLinkedNode();

newNode.key = key;

newNode.value = value;

this.cache.put(key, newNode);

this.addNode(newNode);

++count;

if(count > capacity){

// pop the tail

DLinkedNode tail = this.popTail();

this.cache.remove(tail.key);

–count;

}

}else{

// update the value.

node.value = value;

this.moveToHead(node);

}

}

/**

* Always add the new node right after head;

*/

private void addNode(DLinkedNode node){

node.pre = head;

node.post = head.post;

head.post.pre = node;

head.post = node;

}

/**

* Remove an existing node from the linked list.

*/

private void removeNode(DLinkedNode node){

DLinkedNode pre = node.pre;

DLinkedNode post = node.post;

pre.post = post;

post.pre = pre;

}

/**

* Move certain node in between to the head.

*/

private void moveToHead(DLinkedNode node){

this.removeNode(node);

this.addNode(node);

}

// pop the current tail.

private DLinkedNode popTail(){

DLinkedNode res = tail.pre;

this.removeNode(res);

return res;

}

}

那么问题的后半部分,是 Redis 如何实现,这个问题这么问肯定是有坑的,那就是redis肯定不是这样实现的。

Redis的LRU实现

如果按照HashMap和双向链表实现,需要额外的存储存放 next 和 prev 指针,牺牲比较大的存储空间,显然是不划算的。所以Redis采用了一个近似的做法,就是随机取出若干个key,然后按照访问时间排序后,淘汰掉最不经常使用的,具体分析如下:

为了支持LRU,Redis 2.8.19中使用了一个全局的LRU时钟,server.lruclock,定义如下,

#define REDIS_LRU_BITS 24

unsigned lruclock:REDIS_LRU_BITS; /* Clock for LRU eviction */

默认的LRU时钟的分辨率是1秒,可以通过改变REDIS_LRU_CLOCK_RESOLUTION宏的值来改变,Redis会在serverCron()中调用updateLRUClock定期的更新LRU时钟,更新的频率和hz参数有关,默认为100ms一次,如下,

#define REDIS_LRU_CLOCK_MAX ((1<<REDIS_LRU_BITS)-1) /* Max value of obj->lru */

#define REDIS_LRU_CLOCK_RESOLUTION 1 /* LRU clock resolution in seconds */

void updateLRUClock(void) {

server.lruclock = (server.unixtime / REDIS_LRU_CLOCK_RESOLUTION) &

REDIS_LRU_CLOCK_MAX;

}

server.unixtime是系统当前的unix时间戳,当 lruclock 的值超出REDIS_LRU_CLOCK_MAX时,会从头开始计算,所以在计算一个key的最长没有访问时间时,可能key本身保存的lru访问时间会比当前的lrulock还要大,这个时候需要计算额外时间,如下,

/* Given an object returns the min number of seconds the object was never

* requested, using an approximated LRU algorithm. */

unsigned long estimateObjectIdleTime(robj *o) {

if (server.lruclock >= o->lru) {

return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;

} else {

return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) *

RED 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 IS_LRU_CLOCK_RESOLUTION;

}

}

Redis支持和LRU相关淘汰策略包括,

  • volatile-lru 设置了过期时间的key参与近似的lru淘汰策略

  • allkeys-lru 所有的key均参与近似的lru淘汰策略

当进行LRU淘汰时,Redis按如下方式进行的,

/* volatile-lru and allkeys-lru policy */

else if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||

server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)

{

for (k = 0; k < server.maxmemory_samples; k++) {

sds thiskey;

long thisval;

robj *o;

de = dictGetRandomKey(dict);

thiskey = dictGetKey(de);

/* When policy is volatile-lru we need an additional lookup

* to locate the real key, as dict is set to db->expires. */

if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)

de = dictFind(db->dict, thiskey);

o = dictGetVal(de);

thisval = estimateObjectIdleTime(o);

/* Higher idle time is better candidate for deletion */

if (bestkey == NULL || thisval > bestval) {

bestkey = thiskey;

bestval = thisval;

}

}

}

Redis会基于server.maxmemory_samples配置选取固定数目的key,然后比较它们的lru访问时间,然后淘汰最近最久没有访问的key,maxmemory_samples的值越大,Redis的近似LRU算法就越接近于严格LRU算法,但是相应消耗也变高,对性能有一定影响,样本值默认为5。

总结

看来,虽然一个简单的概念,在工业界的产品中,为了追求空间的利用率,也会采用权衡的实现方案。

作者: 文西

blog.csdn.net/hopeztm/article/details/79547052

往期精选  点击标题可跳转

[【029期】面试官:数据量很大的情况下,对于分页查询你有什么优化方案吗?](()

[【030期】面试官问:MySQL发生死锁有哪些原因,怎么避免?](()

[【031期】面试官问:为什么 StringBuilder 线程不是安全的?](()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值