什么时候需要自己搭建缓存服务

都说“程序等于数据结构加算法”,在软件的运行时刻,其实是数据加进程,进程离数据越“近”,越能得到高速的读写性能。

Cpu的计算速度通常不是软件执行的瓶颈,io的速度会更大地影响软件执行的速度,从“近”到“远”,一般有以下几个场景:

  • 访问内存
  • 访问磁盘
  • 访问网络

软件设计的性能提升,都是想办法使进程更靠近数据,减少数据在慢速介质中的传输

传统软件的client/server结构,就是基于这个思路,让计算在服务端完成,只把计算结果传输到客户端,这样可以减少数据的传输的耗时。反过来,当然也可以把数据传输到客户端,由客户端计算,来节省服务器的cpu消耗,但一般情况下,很少有只需要传送少量数据而需要大量计算的场景,而采用后一种方案。

传统的数据库软件,都会提供存储过程和触发器这样的编程方法,使计算更“靠近”数据,也是提高性能的良方。现代的海量数据系统,比如hadoop,数据分布在许多节点上,在执行计算时,也是把代码发布到数据节点上,在每个节点分别计算,最后汇总结果,这个过程称为mapreduce,执行计算时,都是访问节点本地的数据,汇总时才使用到网络传输。

回到我们的应用软件,因为访问内存比访问磁盘快,所以我们使用redis来做缓存,redis是共享内存的存储,在实际使用中,通常和应用服务器不在同一台物理机器上,也就是说,应用系统访问redis,还是要经过网络传输。

  • 所以应用程序访问数据库的延时是:网络延时+磁盘延时
  • 应用程序访问redis缓存的延时是:网络延时+内存延时

所以,即使是访问缓存,也要求在完成一个请求(当然,今天我们讨论的是web应用)时,访问缓存的次数尽量地少。根据实际的情况,如果是一个需要返回结果给调用方结果的同步请求,需要访问缓存6、7次,会大大降低性能,这时,说明你的进程离数据不够“近”。

这时我们有两个选择,要么给redis开发插件,使redis支持“存储过程”,使计算在redis的进程内完成。要么自己实现缓存功能,也就是在你的应用进程内,实现redis的功能。在大多数情况下,后者更容易实现,通常只需要在内存中构造一个dictionary对象,支持按key访问value就可以了。这样,我们其实构造了一个local cache,我们构造local cashe的目的非常明确,当应用的计算,需要访问多次缓存数据时(这时使用缓存的性能优势已经不明显),我们在应用中构造自己的缓存,使得访问外部缓存系统变成访问本地内存,来提高性能。

在实践中,构造本地缓存,需要考虑到以下几个方面

  • 使用集群,每个集群内的缓存数据,都是相同的,外部请求不管落到哪个服务器,都是访问本机的内存来获得数据
  • 数据同步,集群内的每个服务器,数据同步是独立完成的,数据源的变化会通知到每一台服务器。在某个时间点,不同的服务器内数据允许有不同,但最终数据一致
  • 读写分离的api实现,读操作,可以多线程并发执行。写操作,由专门的线程执行,也就是使用actor模型,由某个actor执行所有的数据修改操作
  • 冷启动,因为需要加载某些表的所有记录到内存中,通常会需要几十秒到数分钟,需要有 拉出集群 – 加载数据 – 拉进集群 的发布流程
  • 数据过期策略,如果有数据过期的需求,需要设计一个后台线程,定期检查所有的缓存元素,把过期的数据清理掉

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>