缓存淘汰算法之LRU LFU 和 redis简单的讲解

缓存淘汰算法之LRU LFU 和 redis简单的讲解


晨讲人:尤恩

缓存机制是什么?有哪些?

  • 堆内存当中的字符串常量池。
    • “abc” 先在字符串常量池中查找,如果有,直接拿来用。如果没有则新建,然后再放入字符串常量池。
  • 堆内存当中的整数型常量池。
    • [-128 ~ 127] 一共256个Integer类型的引用,放在整数型常量池中。没有超出这个范围的话,直接从常量池中取。
  • 连接池(Connection Cache)
    • 这里所说的连接池中的连接是java语言连接数据库的连接对象:java.sql.Connection对象。
    • JVM是一个进程。MySQL数据库是一个进程。进程和进程之间建立连接,打开通道是很费劲的。是很耗费资源的。怎么办?可以提前先创建好N个Connection连接对象,将连接对象放到一个集合当中,我们把这个放有Connection对象的集合称为连接池。每一次用户连接的时候不需要再新建连接对象,省去了新建的环节,直接从连接池中获取连接对象,大大提升访问效率。
    • 连接池
      • 最小连接数
      • 最大连接数
      • 连接池可以提高用户的访问效率。当然也可以保证数据库的安全性。
  • 线程池
    • Tomcat服务器本身就是支持多线程的。
    • Tomcat服务器是在用户发送一次请求,就新建一个Thread线程对象吗?
      • 答:当然不是,实际上是在Tomcat服务器启动的时候,会先创建好N多个线程Thread对象,然后将线程对象放到集合当中,称为线程池。用户发送请求过来之后,需要有一个对应的线程来处理这个请求,这个时候线程对象就会直接从线程池中拿,效率比较高。
      • 所有的WEB服务器,或者应用服务器,都是支持多线程的,都有线程池机制。
  • 向ServletContext应用域中存储数据,也等于是将数据存放到缓存cache当中了。
  • !!!今日内容 redis

什么是Redis?

Redis(Remote Dictionary Server)是一款基于内存的开源键值对存储数据库,它支持多种数据结构,例如字符串、哈希表、列表、集合和有序集合等。Redis 注重性能,使用 C 语言编写并且所有的操作都发生在内存中,因此它非常快速。

Redis以内存作为数据存储介质,所以读写数据的效率极高,远远超过数据库。以设置和获取一个256字节字符串为例,它的读取速度可高达110000次/s,写速度高达81000次/s。

  • 举个例子叭!

    拿大型网站来举个例子,比如a网站首页一天有100万人访问,其中有一个板块为推荐新闻。要是直接从数据库查询,那么一天就要多消耗100万次数据库请求。上面已经说过,Redis支持丰富的数据类型,所以这完全可以用Redis来完成,将这种热点数据存到Redis(内存)中,要用的时候,直接从内存取,极大的提高了速度和节约了服务器的开销。

总之,Redis的应用是非常广泛的,而且极有价值,真是服务器中的一件利器,所以从现在开始,我们就来一步步学好它。

Redis中的LRU(最长时间)算法

LRU算法全称是:Least Recently Used,即:检查最近最少使用的数据。这个算法通常使用在内存淘汰策略中,用于将不常用的数据转移出内存,将空间腾给最近更常用的“热点数据”。

一般来讲,LRU将访问数据的顺序或时间和数据本身维护在一个容器当中。当访问一个数据时:

  1. 若该数据不在容器中,则设置该数据的优先级为最高并放入容器中。
  2. 若该数据在容器当中,则更新该数据的优先级至最高。

当数据的总量达到上限后,则移除容器中优先级最低的数据。下图是一个简单的LRU原理示意图:

img

如果我们按照7 0 1 2 0 3 0 4的顺序来访问数据,且数据的总量上限为3,则如上图所示,LRU算法会依次淘汰7 1 2这三个数据。

Redis中的LFU(最少次)算法

LFU算法:least frequently used,最近最不经常使用算法

对于每个条目,维护其使用次数 cnt最近使用时间 time

cache容量为 n,即最多存储n个条目。

那么当我需要插入新条目并且cache已经满了的时候,需要删除一个之前的条目。

删除的策略是:优先删除使用次数cnt最小的那个条目*,因为它最近最不经常使用,所以删除它。***

如果使用次数cnt最小值为min_cnt,这个min_cnt对应的条目有多个,那么在这些条目中删除最近使用时间time最早的那个条目(举个栗子:a资源和b资源都使用了两次,但a资源在5s的时候最后一次使用,b资源在7s的时候最后一次使用,那么删除a,因为b资源更晚被使用,所以b资源相比a资源来说,更有理由继续被使用,即时间局部性原理)。

类似LRU算法的想法,利用哈希表+链表

链表是负责按时间先后排序的。哈希表是负责O(1)时间查找key对应节点的。

img

img

LRU和LFU的区别

LRU和LFU都是内存管理的页面置换算法。

LRU:最近最少使用(最长时间)淘汰算法(Least Recently Used)。LRU是淘汰最长时间没有被使用的页面。

LFU:最不经常使用(最少次)淘汰算法(Least Frequently Used)。LFU是淘汰一段时间内,使用次数最少的

  • LRU算法适合:较大的文件比如游戏客户端(最近加载的地图文件);
  • LFU算法适合:较小的文件和零碎的文件比如系统文件、应用程序文件 ;
  • LRU消耗CPU资源较少,LFU消耗CPU资源较多。

操作系统之页面置换算法

/*
FIFO: 简单的先进先出,用队列模拟即可 prio 表示入队时间(小值先出)
LRU: 淘汰最久没有被访问的页面 prio 表示上一次入队的时间(小值先出)
LFU: 淘汰最少访问次数的页面 prio 表示被访问的次数
NRU: 四类 prio表示状态类数
    第0类:没有被访问,没有被修改。00
    第1类:没有被访问,已被修改。  01
    第2类:已被访问,没有被修改。  10
    第3类:已被访问,已被修改     11
*/

#include<bits/stdc++.h>
#define xcx(x) printf("ojbk %d\n",x)
using namespace std ;
const int PAGE_NUM = 20 ;
const int MEM_SIZE = 3 ;
const double NRU_LIMIT = 3 ;
bool in_mem[ PAGE_NUM+1 ] ;
struct page{
    int val, prio, pos ; // val:页数 pos:占内存位置
    page () {}
    page ( int val , int prio , int pos ) {
        this->val = val ;
        this->prio = prio ;
        this->pos = pos ;
    }
};

bool operator < ( const page &l , const page &r ) {
    if (  l.prio== r.prio ) {
        return l.pos < r.pos ;
    }
    return l.prio > r.prio ;
}
vector< int > CreatSeq ( int n ) { // 随机生成长度为 n 的访问序列
    vector< int > ret ;
    for( int i=0; i<n; i++ ) {
        ret.push_back( rand()%PAGE_NUM+1 ) ;
    }
    return ret ;
}
void Init ( vector< vector<int> > &ret , vector< bool > &is_miss , const vector< int > &seq ) {
    vector< int > e( MEM_SIZE ) ;
    memset( in_mem, false, sizeof(in_mem) ) ;
    is_miss.clear() ;   ret.clear() ;
    for( int i=0; i<seq.size(); i++ ) {
        is_miss.push_back( 0 ) ;
        ret.push_back( e ) ;
    }
}
vector< vector<int> > FIFO ( const vector< int > &seq , vector< bool > &is_miss ) {
    vector< vector<int> > ret ;
    Init( ret , is_miss , seq ) ;
    queue< page > q ;
    bool is_full = false ;
    int num = 0, mem_pos[ MEM_SIZE ] = {0} ;
    for( int i=0; i<seq.size(); i++ ) {
        if ( in_mem[seq[i]] == false ) { // 不在mem
            is_miss[i] = true ;
            if ( is_full == true ) { // mem已满,淘汰
                page temp = q.front() ;
                q.pop() ;   in_mem[ temp.val ] = false ;
                q.push( page( seq[i], i+1, temp.pos ) );    in_mem[ seq[i] ] = true ;
                mem_pos[temp.pos] = seq[i] ;
            }
            else { // mem未满,添加
                q.push( page( seq[i], i+1, num ) );
                in_mem[ seq[i] ] = true ;
                mem_pos[ num++ ] = seq[i] ;
                if ( num >= MEM_SIZE ) is_full = true ;
            }
        }
        ///存储当前状态
        for( int j=0; j<MEM_SIZE; j++ ) {
            ret[i][j] = mem_pos[j] ;
        }
    }
    return ret;
}
vector< vector<int> > LRU ( const vector< int > &seq , vector< bool > &is_miss ) {
    vector< vector<int> > ret ;
    Init( ret , is_miss , seq ) ;
    vector< page > q ;
    bool is_full = false ;
    int num = 0, mem_pos[ MEM_SIZE ] = {0} ;
    for( int i=0; i<seq.size(); i++ ) {
        if ( in_mem[seq[i]] == false ) { // 不在 mem
            is_miss[i] = true ;
            if ( is_full == true ) { // mem已满,淘汰
                page temp = q[0] ;
                q[0] = page( seq[i], i+1, temp.pos ) ;
                in_mem[ temp.val ] = false ;    in_mem[ seq[i] ] = true ;
                mem_pos[temp.pos] = seq[i] ;
            }
            else { // mem未满,添加
                q.push_back( page( seq[i], i+1, num ) );
                in_mem[ seq[i] ] = true ;
                mem_pos[ num++ ] = seq[i] ;
                if ( num >= MEM_SIZE ) is_full = true ;
            }
        }
        else { // 在 mem
            for( int i=0; i<q.size(); i++ ) {
                if ( q[i].val == seq[i] ) {
                    q[i].prio = i ;
                }
            }
        }
        sort( q.begin() , q.end() ) ;
        ///存储当前状态
        for( int j=0; j<MEM_SIZE; j++ ) {
            ret[i][j] = mem_pos[j] ;
        }
    }
    return ret;
}
vector< vector<int> > LFU ( const vector< int > &seq , vector< bool > &is_miss ) {
    vector< vector<int> > ret ;
    Init( ret , is_miss , seq ) ;
    vector< page > q ;
    bool is_full = false ;
    int num = 0, mem_pos[ MEM_SIZE ] = {0} ;
    for( int i=0; i<seq.size(); i++ ) {
        if ( in_mem[seq[i]] == false ) { // 不在 mem
            is_miss[i] = true ;
            if ( is_full == true ) { // mem已满,淘汰
                page temp = q[0] ;
                q[0] = page( seq[i], 1, temp.pos ) ;
                in_mem[ temp.val ] = false ;    in_mem[ seq[i] ] = true ;
                mem_pos[temp.pos] = seq[i] ;
            }
            else { // mem未满,添加
                q.push_back( page( seq[i], 1, num ) );
                in_mem[ seq[i] ] = true ;
                mem_pos[ num++ ] = seq[i] ;
                if ( num >= MEM_SIZE ) is_full = true ;
            }
        }
        else { // 在 mem
            for( int i=0; i<q.size(); i++ ) {
                if ( q[i].val == seq[i] ) {
                    q[i].prio++ ;
                    break ;
                }
            }
        }
        sort( q.begin() , q.end() ) ;
        ///存储当前状态
        for( int j=0; j<MEM_SIZE; j++ ) {
            ret[i][j] = mem_pos[j] ;
        }
    }
    return ret;
}
vector< vector<int> > NRU ( const vector< int > &seq , vector< bool > &is_miss ) {
    double T_start, T_end ;
    T_start = clock() ;
    vector< vector<int> > ret ;
    Init( ret , is_miss , seq ) ;
    vector< page > q ;
    bool is_full = false ;
    int num = 0, mem_pos[ MEM_SIZE ] = {0} , prio[ PAGE_NUM ] = {0} ;
    for( int i=0; i<seq.size(); i++ ) {
        if ( in_mem[seq[i]] == false ) { // 不在 mem
            is_miss[i] = true ;
            if ( is_full == true ) { // mem已满,淘汰
                page temp = q[0] ; in_mem[ temp.val ] = false ;
                q[0] =  page( seq[i], 0, temp.pos ) ;    in_mem[ seq[i] ] = true ;
                prio[ seq[i] ] |= 1 ; // 视为修改
                mem_pos[temp.pos] = seq[i] ;
            }
            else { // mem未满,添加
                q.push_back( page( seq[i], 0, num ) );
                prio[ seq[i] ] |= 2 ; // 视为访问
                in_mem[ seq[i] ] = true ;
                mem_pos[ num ] = seq[i] ;
                num++;
                if ( num >= MEM_SIZE ) is_full = true ;
            }
        }
        else { // 在 mem
            prio[ seq[i] ] |= 2 ; // 视为访问
        }
        if ( clock() - T_start > NRU_LIMIT ) { // 更新
            T_start = clock() ;
            for( int i=0; i<PAGE_NUM; i++ ) {
                prio[i] &= 1 ;
            }
        }
        for( int i=0; i<q.size(); i++ ) {
            q[i].prio = prio[ q[i].val ] ;
        }
        sort( q.begin() , q.end() ) ;
        ///存储当前状态
        for( int j=0; j<MEM_SIZE; j++ ) {
            ret[i][j] = mem_pos[j] ;
        }
    }
    return ret;
}
void PrintTable ( const vector< vector<int> > &ans , const vector< bool > &is_miss , string type ) {
    int num = 0 ;
    printf( "%s\n", type.c_str() ) ;
    for( int i=0; i<MEM_SIZE ; i++ ) {
        printf( "\tmem unit %d :\t", i ) ;
        for( int j=0; j<ans.size(); j++ ) {
            if ( ans[j][i] != 0 ) {
                printf( "%3d", ans[j][i] ) ;
            }
            else {
                printf( "   " ) ;
            }
        }
        printf( "\n" ) ;
    }
    printf( "\tis miss :\t") ;
    for( int i=0; i<is_miss.size(); i++ ) {
        if ( is_miss[i] == true ) {
            num++ ;
            printf( "  Y" ) ;
        }
        else {
            printf( "  N" ) ;
        }
    }
    printf( "\n miss num : %d \n", num ) ;
}
const string type[4] = { "FIFO", "LRU", "LFU", "NRU" };

int main() {
    vector< int > seq = CreatSeq( 19 ) ;
    printf( "page seq:\t" ) ;
    for( int i=0; i<seq.size(); i++ ) {
        printf( "%3d", seq[i] ) ;
    }
    printf( "\n" ) ;
    vector< bool > is_miss ;
///FIFO:
    vector< vector < int > > ans_FIFO = FIFO( seq , is_miss ) ;
    PrintTable( ans_FIFO , is_miss , type[0] ) ;
///LRU:
    vector< vector < int > > ans_LRU = LRU( seq , is_miss ) ;
    PrintTable( ans_LRU , is_miss , type[1] ) ;
///LFU:
    vector< vector < int > > ans_LFU = LFU( seq , is_miss ) ;
    PrintTable( ans_LFU , is_miss , type[2] ) ;
///NRU:
    vector< vector < int > > ans_NRU = NRU( seq , is_miss ) ;
    PrintTable( ans_NRU , is_miss , type[3] ) ;

    return 0;

}

运行结果:

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值