LRU算法/最近最久未使用算法 与Clock算法

一 LRU算法

LRU算法在操作系统的内存管理,MySQL页管理,redis的缓存管理中都有使用到,这是一种通用的淘汰算法。

如下图所示,假设缓存中有三个槽,而磁盘中有A~E五份数据,再假设用户访问数据的顺序为:ABCDBCAEACACEBEC

由于缓存中的内容最初是空的,所以前三次访问刚好会把缓存填满

那么在第四次访问D时,就需要淘汰缓存中ABC三个数据中的某一个,腾出空间在存放D,那么应该淘汰哪一个呢

最直接也是最完美的做法是看看以后用户会访问哪些数据。

这在里,用户访问的顺序是ABC D BC……,由于第5,6次分别是访问BC,说明BC在不久的将来马上就会被访问到,而A暂时不会,所以淘汰A是最好的选择

这种淘汰策略是最优的,因为它可以保证缓存不命中的次数最少,然而,它却是不可能实现的,因为我们需要看看“未来”会发生什么,当用户访问D时,我们根本没法知道他接下来两次会访问BC。

这种情况下要怎么办呢?虽然我们没法知道未来会发生什么,但是我们可以用历史去预测未来。

举个例子,假如我们统计一下历史,发现每次用户访问了D之后,一定会访问BC,那么,我们就可以根据这个规律判断出来,即然目前用户访问了D,那么用户未来很大概率会访问BC,因此可以将A淘汰掉。

也就是说,我们需要找出历史的规律,然后来预测未来。

而我们伟大的前辈们,早就给出了这种规律,那就是大名鼎鼎的局部性原理:

最近访问过的数据,在未来的不久也会访问

例如这个例子,ABCDBC 用户访问D的前两次是访问BC,那么我们就可以认为用户在接下来应该也会访问BC,而A是最近最久都没有被访问过的,所以它是未来最不可能被访问的。因此,我们可以淘汰A

这个就是LRU算法,即最近最久未使用算法。总的来说就是一句话:

回顾一下历史,找出最近最久没有使用到的那个缓存数据,将其淘汰

而这种算法最重要的就是用历史去预测未来,以及局部性原理。

另外值得一提的是,局部性原理包括两部分内容,上面说到的,只是时间的局部性,还有空间的局部性:

假如某份数据被访问到了,那么其相邻的数据也将很快被访问到。

二 Clock算法

LRU算法缓存淘汰算法虽然好,但是它的时间复杂度一定是o(n),因为需要遍历历史的数据。

这种时间复杂度,某些情况下还好,但是对于操作系统这种,还是太高了。

Clock算法是LRU的一种近似,它的性能比LRU要好很多。

如下图所示,假设某系统有12个缓存槽,编号为1-12:

红色表示未被访问,绿色表示被访问过

Clock算法逻辑如下:

  1. 每个缓存槽都有一个对应的状态,每当这个缓存槽中的数据被访问后,将状态至为 1,否则为0 (图中用绿色表示状态为1,红色表示状态为0)
  2. 使用一个指针A指向下一个将要被淘汰的位置
  3. 每次需要淘汰缓存中,从A指针开始,顺时针遍历,找到第一个状态为0的槽,将其淘汰
  4. 而B指针会定时顺时针遍历,把所有的缓存槽的状态为都重置为0

举个例子,例如上面的图,假如现在需要淘汰一个缓存槽,则A指针从1号槽位开始遍历,找到第一个未被访问的槽位为3号,则将3号淘汰即可。

而B指针,可能以一定速度,例如5秒/次,将所有的缓存槽状态全重置为0。

算法想法

由于B指针会以一定的频率将所有缓存槽的状态置为0,所以当A指针遍历时,如果遇到了状态为1的槽位,则说明此槽位最近被访问过

例如B指针以5秒/次的频率清零所有状态,则当A指针遍历遇到状态为1的槽位时,说明此槽位5秒内被访问过,而状态位为0的位说明5秒内都没有被访问过。

因此,每次A遍历时,只需要找到下一个状态为0的槽,将其淘汰即可,因为这个槽最近都没有被使用到。

你可能感兴趣的:(LRU算法/最近最久未使用算法 与Clock算法)