谈谈缓存

缓存无处不在,比如磁盘缓存、Web 缓存、网络缓存、分布式缓存,等等,这些是我们在开发系统软件和互联网服务时常常要用到的技术。

缓存的意义?

简单来说,缓存是存储数据的一个临时场所,由于获取原始数据的代价太大了,所以,我们会把一些使用频繁的数据放到一个更容易读取,操作更快的池子(一般是内存)里,为这些数据分门别类,打上标签,这样用户发来请求的时候,我们先在缓存池里进行快速检索,如果拿到数据就直接返回给用户;如果没有,再去数据库或其他介质获取原始数据返给用户,同时把该数据打上标签,放入缓存池。

网络世界也是一样,如果没有缓存,用户所有的请求都会直接穿透层层网络,打到数据库和磁盘 IO 上,随着数据量的增加,用户每次请求的时间会越来越长,这样的后果是,磁盘不爽,数据库不爽,用户不爽。

原理

既然缓存这么重要,拥有大量用户的互联网应用都应该增加缓存服务了,那么是不是搞个 Hashtable 就可以了呢?我们可以先了解一些缓存的术语。

缓存命中

用户发起了一个热点数据的请求,系统接收到这个请求之后,就需要根据用户的数据信息(key)去缓存池里寻找数据,如果根据用户提供的 Key 找到了这个条目,并返回给用户,这个过程叫一次缓存命中。

如果在缓存里没找到需要的数据,在缓存空间还有空闲的情况下,系统会去原始数据源(一般是数据库)获取信息,返回给用户,并把数据条目存储到缓存中,以备不时之用。如果缓存空间已经达到上限,那么就要根据缓存替代策略,把旧的数据对象销毁,把新的对象放入缓存池。

对于缓存服务的设计来说,命中率高的缓存系统,性能越好,命中率高,消耗的时间和资源越少。所以缓存服务并不是简单的搭建一个 Memcached、Ehcache 或 Redis 就可以了,相关的技术,应用在合适的业务场景中,才能最大化的利用缓存的价值。

缓存成本

上述场景中,缓存没有命中的时候,系统会从原始数据源中获取数据,一般是数据库或文件系统,然后再把数据放入缓存池中。这个过程需要的时间和空间,就是缓存成本。

一般为了避免缓存成本过高,系统初始化的时候,会同时进行缓存池的初始化,把我们需要的,已知的数据尽可能多的提前放入缓存池,这样可以最大程度的提升缓存命中率,降低缓存成本。但不适用在数据量大的场景,例如双11秒杀。因为不能在较短时间内遍历缓存key,而且初始化缓存请求过多,也将给后端机器造成压力。

缓存失效

当缓存中的数据需要更新的时候,说明缓存中的数据已经失效了,这时候就需要有相关的服务进行实时的数据更新,同时要保证数据的一致性,不能让系统拿到已经失效的数据到处去招摇撞骗,这种情况,系统和用户的内心,都是拒绝的。

大家知道在高并发下会缓存失效,会带来后台服务压力增大,解决这种问题有好多方法,大家可以参考下网上应对Memcached缓存失效,导致高并发查询DB的几种思路

替代策略

编程新手一般会觉得内存是可以无限使用了,看到服务器上标着 64G 内存这样金光闪闪的配置之后,他们会觉得「广阔天地,大有可为」,于是在系统里 new 出了一个又一个的 Hashtable,然后不停的往里面加入数据读出数据,事实上,如果系统简单,这样做一时半会还真出不了系统问题。如果是系统级的缓存服务,要考虑的事情就比较多了。

每个缓存产品,一般都会有一个类似 maxmemory 的最大内存使用参数,这个参数肯定是小于物理内存的。一旦缓存数据达到上限,而又出现缓存没有命中的情况时,系统就会踢出一些老弱病残的缓存数据,加入新条目。判断老弱病残的标准是什么呢?这就是替代策略。最理想的做法当然是把最没用的数据踢出去,但是,做到最理想永远是最难的,就像你永远想找到团队里最没用的那一个将其淘汰掉,但执行的时候总是极其艰难。

常用算法

常用的一些算法包括:FIFO、LFU、LRU、LRU2、ARC等。

FIFO 就是先进先出,一种非常简单的算法,缓存数据达到上限的时候,最先进入缓存的数据会最先被踢出去。很多老员工看到这一条都义愤填膺,所以,这个算法注定是不被喜欢的,但是由于它简单直接,很多开发者喜欢。嗯,有些企业老板也比较喜欢。Second Chance 和 CLock 是基于 FIFO 的改进,算法更加先进合理,也更复杂,Google「缓存算法 CLock」等。

LFU 的全称是 Least Frequently Used,最少使用算法,系统会为每个对象计算使用频率,最不常用的缓存对象会被踢走,简单粗暴。

这里看到一个形象的比喻:

缺点是,一个传统工业时代曾经被重用过的老员工,在互联网时代没用了,由于其前期使用频率很高,吃老本,所以数据会一直保存在缓存系统中,反而是后起之秀,往往会遭遇到误杀的不公正待遇。

LRU 的全称是 Least Recently Used,也就是最近最少使用算法。基本思路是,如果一个数据最近一段时间被使用的频率很少,那将来被用到的可能性也会很低。

LRU2ARC 都是基于 LRU 的改进

很多我们耳熟能详的缓存产品,比如 Memcached、Redis、Ehcache、OSCache等,都参考了类似的算法,要么进行加强,要么进行简化,目标就是提升缓存的命中率,降低缓存成本。

总结

缓存原理就是这些, 但是在实际工作中远没这么简单,特别在大数据、高并发情况问题就更多了。“Talk is cheap. Show me the code”,抱歉没有,以后争取写一些demo…

JasonThink wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!