什么是caching?

我经常听到有人遇到了性能问题x,他们通过caching解决了这个问题。

或者,在你的程序代码中如何做x,y,z会伤害你的caching能力。

即使在最新的播客之一,杰夫·阿特伍德谈到如何caching某些价值的快速检索。

“caching”和“caching”这两个词似乎有些模棱两可,导致我在不同的情况下对它的含义感到困惑。 无论你是指应用程序或数据库caching,CPU等,以及这意味着什么。

什么是caching,什么是不同的types?

从上下文中,我可以了解它,将经常检索的值存储到主内存中,并快速查看它的访问权限。 但是,究竟是什么呢?

这个词似乎被用在许多不同的上下文中(cpu,数据库,应用程序等),而且我真的希望清除它。

在您的应用程序caching如何工作与数据库caching之间有区别吗?

当有人说他们发现了一段会伤害caching的代码,并在修复之后,它提高了他们的应用程序的速度,他们在说什么?

程序的caching是自动完成的吗? 你如何让值caching在你的程序中? 我经常阅读本网站的用户说,他们在应用程序中caching了一个价值,我坐在这里,想知道他们的意思。

另外,当有人谈论数据库caching时,这意味着什么? 这只是他们在数据库中启用的function吗? 你必须显式caching值或数据库select哪些caching给你?

我如何开始caching项目来提高性能?

你可以给我一些如何开始在应用程序中caching值的例子吗? 或者再次,这是已经完成的东西,在引擎盖下,我只需要以特定的方式写我的代码,以允许“caching”?

数据库caching怎么样,我该如何开始呢? 我听说过像memcache这样的东西。 这种types的工具是否需要在数据库中caching?

我期待着在应用程序和数据库之间caching区别,它们是如何使用以及如何在两种情况下实现的。

caching只是将数据存储在高性能存储(通常是内存)中的数据,或者是显式地或隐式地从中检索数据。

让我解释。 内存访问速度比文件,远程URL(通常),数据库或任何其他您喜欢的外部信息存储更快。 因此,如果使用这些外部资源之一的行为是显着的,那么您可以从caching中获益以提高性能。

克努特曾经说过早的优化是万恶的根源。 那么,就我而言,过早caching是所有头痛的根源。 直到遇到问题才解决问题。 你做出的每一个决定都要付出一定的代价,你现在要付出代价来支付,然后再付钱去改变它,所以你可以拖延做更多的事情,改变你的系统。

所以首先确定你确实有一个问题,它在哪里 。 分析,日志logging和其他forms的性能testing将在这里帮助你。 我不能强调这一步有多重要。 我看到人们“优化”一些不成问题的次数是惊人的。

好的,所以你有一个性能问题。 说你的网页正在运行一个需要很长时间的查询。 如果这是一个阅读,那么你有很多select:

  • 将查询作为单独的进程运行,并将结果放入caching中。 所有页面只是访问caching。 您可以根据需要随时更新caching版本(每天一次,每周一次,每5秒一次)。
  • 通过持久性提供程序,ORM或其他方式透明地caching。 当然,这取决于你使用的是什么技术。 以Hibernate和Ibatis为例,支持查询结果caching;
  • 如果结果不在caching中(或者它是“陈旧的”,意味着它的计算时间比指定的“年龄”更早),并且将其放入caching中,请让页面运行查询。 如果两个(或更多)单独的进程都决定需要更新结果,那么最终同时运行同一(昂贵的)查询八次,这会产生并发问题。 您可以处理这个lockingcaching,但是这会产生另一个性能问题。 您也可以回退到您的语言的并发方法(例如Java 5并发API)。

如果它是一个更新(或更新发生,需要反映在您的读取caching),那么它有点复杂,因为在caching中具有旧的值并且在数据库中具有更新的值是不好的,这样您可以提供页面对数据的看法不一致。 但广义而言,有四种方法:

  • 更新caching,然后将请求排队以更新相关存储;
  • 通过caching写入:caching提供者可以提供一种机制来保持更新并阻止调用者直到做出改变; 和
  • 后写caching:与直写caching相同,但不阻塞调用者。 更新发生asynchronous和分开; 和
  • 持久化作为服务模型:这假设你的caching机制支持某种可观察性(即caching事件监听器)。 基本上,一个完全独立的进程 – 调用者不知道 – 监听caching更新,并在必要时保持它们。

您select的上述哪种方法将取决于您的要求,您正在使用的是什么技术以及许多其他因素(例如,是否需要群集和故障转移支持?)。

比这更难以具体说明,并在不知道有关问题的更多细节(如是否有问题)的情况下为您提供指导。

您很可能会阅读有关Web应用程序上下文中的caching。 由于Web的性质,caching可以带来很大的性能差异。

考虑以下:

网页请求到达Web服务器,Web服务器将请求传递给应用程序服务器,应用程序服务器执行一些呈现页面的代码,该代码需要转到数据库以dynamic检索数据。

这种模式不能很好地扩展,因为随着页面请求数量的增加,服务器必须一遍又一遍地为每个请求做同样的事情。

如果Web服务器,应用程序服务器和数据库位于不同的硬件上并通过networking彼此进行通信,则会变得更加严重。

如果你有大量的用户点击这个页面,那么每次请求都不要一直到数据库。 相反,你采取不同层次的caching。

结果集caching

结果集caching将数据库查询的结果与查询一起存储在应用程序中。 每当网页生成查询时,应用程序就会检查结果是否已被caching,如果是,则将其从内存数据集中提取出来。 应用程序仍然需要呈现该页面。

组件caching

一个网页是由不同的组件组成的 – 小页面,或任何你可能想要调用它们。 组件caching策略必须知道用什么参数来请求组件。 例如,网站上的一个“最新消息”栏使用用户的地理位置或偏好来显示本地新闻。 因此,如果一个位置的消息被caching,那么该组件不需要被渲染,并且可以从caching中取出。

页面caching

caching整个页面的一个策略是将查询string和/或头部参数与完全渲染的HTML一起存储。 文件系统足够快 – 对于Web服务器来说,读取文件的成本要比对应用程序服务器调用页面的成本要低。 在这种情况下,发送相同查询string的每个用户都将获得相同的caching内容。

智能组合这些caching策略是为大量并发用户创build真正可扩展的Web应用程序的唯一方法。 正如您可以很容易地看到的,这里的潜在风险是,如果caching中的某个内容不能唯一标识它的关键字,那么人们将开始看到错误的内容。 这可能会变得非常复杂,特别是当用户有会话并且有安全上下文时。

我知道有两个意思。


一个是应用程序caching 。 这是当数据从某个地方(例如从networking)缓慢获取或计算速度慢时,应用程序caching数据的一个副本(以便它不需要再次获取或重新计算:它是已经被caching)。 实现一个caching需要一些额外的应用软件(逻辑来使用caching)和额外的内存(在其中存储caching的数据)。

正如你在这里引用的那样,这就是“caching”

从上下文中,我可以了解它,将经常检索的值存储到主内存中,并快速查看它的访问权限。


另一个是CPUcaching ,在这篇维基百科文章中有描述。 CPUcaching自动发生。 如果你从很less的内存中读取很多内容,那么CPU可以从caching中读取大部分的内容。 OTOH如果你从大量的内存中读取,它不能全部放入caching中,CPU必须花费更多的时间处理较慢的内存。

正如你在这里引用的那样,这就是“caching”

当有人说他们发现了一段会伤害caching的代码,并在修复之后,它提高了他们的应用程序的速度,他们在说什么?

这意味着他们find了一种重新排列代码的方法,以减lessCache未命中的次数 。


至于数据库caching ,我不知道。

有几个问题。

一,是粒度。 你的应用程序可以有非常好的caching水平,超过了数据库的水平。 例如,数据库可能简单地caching数据页面,而不一定是特定的行。

另一件事是应用程序可以存储数据在其“本机”格式,而数据库显然只caching在其内部格式。

简单的例子。

假设你在数据库中有一个用户,它由列: USERIDFIRSTNAMELASTNAME 。 很简单。

您希望将用户USERID=123加载到您的应用程序中。 有什么步骤?

  1. 发出数据库调用
  2. parsing请求( SELECT * FROM USER WHERE USERID = ?
  3. 规划请求(即系统如何获取数据)
  4. 从磁盘中获取数据
  5. 将数据从数据库stream式传输到应用程序
  6. 将数据库数据转换为应用程序数据(例如, USERID为一个整数,比如string的名称。

数据库caching可能会caching步骤2和步骤3(这是一个语句caching,所以它不会parsing或重新计算查询),并caching实际的磁盘块。

所以,这是关键。 您的用户USER ID 123名称JESSE JAMES 。 你可以看到这不是很多数据。 但数据库正在caching磁盘块。 你有索引块( 123上),然后是数据块(实际的数据,所有其他行适合该块)。 那么名义上来说,60-70字节的数据实际上对数据库的缓冲和数据影响可能是4K-16K(取决于块大小)。

光明的一面? 如果你需要附近的另一行(比如USER ID = 124 ),那么索引的可能性很高,而且数据已经被caching了。

但是,即使使用这种caching,您仍然需要花费成本将数据通过networking传输(除非使用本地数据库,否则就是回送),并且您正在“解组”数据。 即将其从数据库位转换为语言位,转换为应用程序位。

现在,一旦应用程序获得其USER ID 123 ,它就将值填入一个长寿命的哈希映射中。

如果应用程序再次需要它,它将查看本地地图,应用程序caching,并保存查找,电汇传输和编组成本。

应用程序caching的黑暗面是同步。 如果有人进来并执行UPDATE USER SET LASTNAME="SMITH" WHERE USERID=123 ,则应用程序不会“知道”,因此caching很脏。

那么,处理这种关系的一些细节就会使应用程序与数据库保持同步。

拥有大量的数据库caching对于“热”数据集的大型查询非常好。 你拥有的内存越多,你所拥有的“热门”数据就越多。 到目前为止,如果您可以将整个DBcaching到RAM中,则可以消除将数据从磁盘移动到RAM缓冲区的I / O(至less是读取)延迟。 但是,你仍然有运输和编组成本。

应用程序可以更具select性,比如caching更多有限的数据子集(DB只是caching块),并且使数据“更接近”应用程序,从而获得更好的性能。

不好的一面是,并不是所有东西都被caching在应用程序中。 数据库往往比应用程序更高效地存储数据。 对于应用程序caching的数据,您也缺less“查询”语言。 大多数人只需通过一个简单的密钥caching,然后从那里进入。 很容易findUSER ID 123 ,更难“所有用户命名JESSE”。

数据库caching往往是“免费的”,你设置一个缓冲区号,DBMS处理其余的。 低影响,减less总体I / O和磁盘延迟。

应用程序caching就是应用程序特定的。

它对于孤立的“静态”数据非常有效。 这很容易。 加载一堆东西在启动时查找表,并重新启动应用程序,如果他们改变。 这很容易做到。

之后,复杂性开始增加,因为你添加了“脏”的逻辑等。

只要你有一个数据API,你可以增量caching。

所以,只要你打电话到getUser(123)而不是打DB,那么你以后可以回来添加caching到getUser而不影响你的代码。

所以,我总是在每个人的代码中build议某种数据访问层,以提供那个抽象和拦截层。

caching是采取长或cpu密集型algorithm的结果并保存答案,以便您不必再次运行algorithm,只需重新使用结果。

caching概念在这里是一个超载的术语。 我不熟悉数据库caching的细节。

在应用程序中有两个用途的用语。

当有人说他们发现了一段会伤害caching的代码,并在修复之后,它提高了他们的应用程序的速度,他们在说什么?

在这种情况下,他们正在参考CPUcaching。

CPU高速caching是CPU内存,比RAM快得多,但没有随机存取。 CPU决定加载到caching中可能会有点复杂。 参见Ulrich Dreppers 每个程序员都应该知道关于内存的大量细节。

注意CPUcaching可以加快处理速度 – 只需要多加注意一下物理内存中相对于彼此放置的位置,以及何时可能使用它们。

一个例子(也可能是可维护性的反模式)是,你有一个结构数组,你做了很多的结构成员循环,你可能会更好地服务于一个结构的字段都是数组。 如果你正在循环的数据在内存中是连续的,那么在不扰乱caching的情况下你有更好的机会。

所有types的东西都会影响caching使用效率 – 对caching中的代码进行分支预测,数据结构和访问模式的大小和alignment,何时何地声明将要放入堆栈的局部variables。

应用程序编程这个术语的其他常见用法可以通过称为memoization的东西来完成。 维基百科页面上的因子示例解释了比我所能做的更好的事情。

数据库中的高速caching通常是数据库的一个function,它由数据库自动pipe理。 应用程序中的caching将因平台而异。

对象caching是一种机制,您可以使用这种机制将常用对象放入内存中,这样您就不必花费成本来检索数据并重新创build它们。 这通常通过代码进行pipe理,并根据您使用的caching解决scheme而有所不同。

有分布式caching解决scheme,涉及在多个服务器上设置服务,为您提供各种caching服务器场。 这提供了可扩展性和冗余。 客户端可以通过networking请求caching的信息。 再次,这是您的代码中的手动过程。 分布式caching提供程序的一个例子是memcached:

http://www.danga.com/memcached/

一个特定types的caching的例子是asp.netcaching。 Asp.net支持几种caching。 有传统的对象caching(可用于各种.net应用程序,而不仅仅是网站)。 还有cachingfunction,允许您configuration页面和用户控件来自动caching他们的输出。 这不会caching数据,它将caching最终结果(页面的HTML),并在用户请求具有与之前用户相同的查询string参数的相同页面时提供该数据。

这可能比你想象的要容易 – 这就是为什么人们试图closures它。

它只是将值存储在内存中,而不是每次都返回数据库。

有很多方法可以这样做,但这个概念本身是微不足道的。

编辑:它也可以在任何级别完成 – 任何需要很长时间的东西都可以caching到某个地方,以便更快地获得。

caching不一定只适用于“经常检索”的值,而是通过减less重新计算次数来节省时间。 想到一个简单的例子就是计算斐波纳契数列 。 最简单的recursion实现看起来像这样(在伪代码中):

 function f(n) if n < 2 then return n; return f(n - 1) + f(n - 2) 

这可以通过caching来改善,以防止重新计算已知的值:

 fib_cache = {} function f(n) if n < 2 then return n; if fib_cache.contains(n) then return fib_cache[n] fib_cache[n] = f(n - 1) + f(n - 2) return fib_cache[n]