<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间 &#187; 缓存</title>
	<atom:link href="https://www.phppan.com/tag/%e7%bc%93%e5%ad%98/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sat, 25 Apr 2026 00:56:17 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>聊下缓存</title>
		<link>https://www.phppan.com/2024/05/talk-about-cache/</link>
		<comments>https://www.phppan.com/2024/05/talk-about-cache/#comments</comments>
		<pubDate>Fri, 24 May 2024 08:03:04 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=2204</guid>
		<description><![CDATA[在当今的互联网应用中，缓存作为一种提高系统性能的关键技术，扮演着至关重要的角色。无论是日常浏览网页、使用 AP [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="color: #424b5d;" data-tool="mdnice编辑器">在当今的互联网应用中，缓存作为一种提高系统性能的关键技术，扮演着至关重要的角色。无论是日常浏览网页、使用 APP，还是企业级应用的后台处理，缓存的存在无处不在。那么，什么是缓存？我们应该如何有效地利用缓存来优化系统性能呢？</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><span class="wx_text_underline">什么是缓存？</span></p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">从本质上来看，<strong>缓存是将数据暂时存储在比原始数据源更快的存储介质中，以便快速访问</strong>。其主要目的是减少数据访问的延迟，提高系统的性能，从而提升用户体验。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">从一个 Web 请求的链路来看，主要有以下几种类型的缓存：</p>
<ol class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;"><strong>浏览器缓存</strong>：浏览器本地的缓存，包括内存缓存和磁盘缓存。可以通过 HTTP 响应头控制缓存策略，如 Cache-Control、Expires 等。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>DNS 缓存</strong>：本地机器或 DNS 服务器上对域名解析结果的缓存，可以加快后续对同一域名的访问速度。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>CDN 缓存</strong>：CDN 的边缘节点上的缓存，可以让用户从距离最近的节点获取资源，减轻服务器压力，提高响应速度。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>Web 服务器缓存</strong>：如 Nginx、Apache 等 Web 服务器自带的缓存功能，或者专门的缓存服务器如 <span class="wx_search_keyword_wrap" style="color: var(--weui-link);">Varnish<i class="wx_search_keyword"></i></span> 等。对请求进行缓存，可减轻后端服务器的负载。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>应用层缓存</strong>：在应用程序中自己实现的缓存，如使用 Redis、Memcached 等内存型数据库对数据进行缓存，或者在代码中实现对特定数据的缓存。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>数据库缓存</strong>：数据库本身的查询缓存，如 MySQL 的 <span class="wx_search_keyword_wrap" style="color: var(--weui-link);">Query Cache<i class="wx_search_keyword"></i></span>。会对 SELECT 语句及其结果进行缓存。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>操作系统缓存</strong>：操作系统级别的文件系统缓存，如 Linux 的 Page Cache。可以加快对磁盘上同一文件的重复读取速度。</section>
</li>
</ol>
<p style="color: #424b5d;" data-tool="mdnice编辑器">这些缓存按照其位置和类型，可以分为客户端缓存、网络缓存和服务端缓存。合理地利用各种缓存，可以显著提升 Web 应用的性能和用户体验。同时也要注意缓存的更新策略，以免数据不一致问题的出现。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">1 客户端缓存</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><span class="wx_text_underline">客户端缓存是指存储在客户端本地的缓存数据，主要是浏览器缓存。浏览器缓存是最常见的客户端缓存形式，它可以显著减少网络传输，加快页面加载速度，提升用户体验。</span></p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">浏览器缓存主要包括以下两种类型：</p>
<ol class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;">内存缓存：</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">内存缓存是指存储在内存中的缓存数据，读取速度非常快。</section>
</li>
<li>
<section style="color: #424b5d;">但是内存缓存的生命周期较短，会在浏览器关闭时被清除。</section>
</li>
<li>
<section style="color: #424b5d;">内存缓存一般用于存储当前页面中已经下载的资源，如页面文档、图片、脚本、样式表等。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;">磁盘缓存：</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">磁盘缓存是指存储在磁盘上的缓存数据，读取速度相对内存缓存慢一些，但是容量更大。</section>
</li>
<li>
<section style="color: #424b5d;">磁盘缓存的生命周期更长，可以在浏览器关闭后继续保留。</section>
</li>
<li>
<section style="color: #424b5d;">磁盘缓存主要用于存储那些可能在将来的请求中重复使用的资源，如静态图片、脚本、样式表等。</section>
</li>
</ul>
</li>
</ol>
<p style="color: #424b5d;" data-tool="mdnice编辑器">浏览器缓存的工作原理涉及到两个重要的概念：<strong>强缓存和协商缓存</strong>。</p>
<ol class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;"><strong>强缓存</strong>：</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">当资源在缓存有效期内时，浏览器会直接从缓存中读取，<strong>不会向服务器发送请求</strong>。</section>
</li>
<li>
<section style="color: #424b5d;">强缓存由 HTTP 响应头中的 Cache-Control 或 Expires 字段控制。都是表示资源的缓存有效时间。</section>
</li>
<li>
<section style="color: #424b5d;">Expires 是 HTTP 1.0 的规范，值是一个 GMT 格式的时间点字符串。缺点是失效时间是一个绝对时间,服务器时间与客户端时间偏差较大时会导致缓存混乱。</section>
</li>
<li>
<section style="color: #424b5d;">Cache-Control 是 HTTP 1.1 的规范，一般常用该字段的 max-age 值来进行判断，它是一个相对时间。</section>
</li>
<li>
<section style="color: #424b5d;">常见的应用场景有静态资源缓存，如 CSS、JavaScript、图片等，可以设置较长的缓存时间。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>协商缓存</strong>：</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;"><strong>协商缓存是由服务器来确定缓存资源是否可用</strong>，协商缓存会向服务器发送请求，询问资源是否有更新。</section>
</li>
<li>
<section style="color: #424b5d;">协商缓存由 HTTP 响应头中的 Last-Modified/If-Modified-Since 或 ETag/If-None-Match 字段控制。</section>
</li>
<li>
<section style="color: #424b5d;">服务器根据资源的最后修改时间或内容生成的唯一标识（<span class="wx_search_keyword_wrap" style="color: var(--weui-link);">ETag<i class="wx_search_keyword"></i></span>）来判断资源是否有更新。</section>
</li>
<li>
<section style="color: #424b5d;">如果资源没有更新，服务器会返回 304 状态码，告诉浏览器可以直接从缓存中读取。</section>
</li>
<li>
<section style="color: #424b5d;">常见的应用场景有动态内容缓存，如新闻、博客文章等，以及 API 响应缓存。</section>
</li>
</ul>
</li>
</ol>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在以上提到的这些缓存控制标签中，优先级：<strong>Cache-Control &gt; Expires &gt; ETag &gt; Last-Modified</strong></p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">浏览器在处理缓存时，会优先执行强缓存策略，如果强缓存失效，则执行协商缓存策略。通过合理设置 HTTP 缓存头部，可以有效利用浏览器缓存，减少不必要的网络传输，提升 Web 应用的性能。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">这里想多聊一点关于 stale-while-revalidate 和 stale-if-error。它们都是 HTTP Cache-Control 响应头的扩展指令，用于控制浏览器如何处理陈旧的缓存资源，是两个略小众的指令。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><strong>1. stale-while-revalidate</strong></p>
<blockquote style="color: var(--weui-fg-1);" data-tool="mdnice编辑器">
<p style="color: #424b5d;">语法：Cache-Control: max-age=, stale-while-revalidate=</p>
</blockquote>
<p style="color: #424b5d;" data-tool="mdnice编辑器">stale-while-revalidate 指令用于指定在缓存过期后，允许客户端在异步 revalidate 的同时，继续使用陈旧的缓存资源的最长时间。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><strong>工作原理</strong>：</p>
<ul class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;">在 max-age 时间内，浏览器直接从缓存中获取资源，不会发送请求。</section>
</li>
<li>
<section style="color: #424b5d;">当缓存过期后，在 stale-while-revalidate 指定的时间内，浏览器会发送请求到服务器进行revalidate，但同时会立即返回陈旧的缓存资源给客户端使用。</section>
</li>
<li>
<section style="color: #424b5d;">如果 revalidate 成功（即服务器返回304 Not Modified），则缓存更新，并且下次请求会直接从缓存中获取。</section>
</li>
<li>
<section style="color: #424b5d;">如果 revalidate 失败（即服务器返回 200 或其他状态码），则缓存更新为新的响应内容，并且下次请求会直接从缓存中获取新内容。</section>
</li>
</ul>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><strong>使用场景</strong>：</p>
<ul class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;">适用于更新不太频繁，但又希望用户总是能看到最新内容的资源，如 CSS、JavaScript 等。</section>
</li>
<li>
<section style="color: #424b5d;">提高了用户体验，避免因等待revalidate而延迟页面呈现。</section>
</li>
</ul>
<blockquote style="color: var(--weui-fg-1);" data-tool="mdnice编辑器">
<p style="color: #424b5d;">示例：Cache-Control: max-age=600， stale-while-revalidate=30<br />
说明：资源可以在 600 秒内直接从缓存中获取，在接下来的30秒内，虽然缓存已过期，但浏览器仍可以显示陈旧的缓存资源，同时在后台进行异步revalidate。</p>
</blockquote>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><strong>2. stale-if-error</strong></p>
<blockquote style="color: var(--weui-fg-1);" data-tool="mdnice编辑器">
<p style="color: #424b5d;">语法：Cache-Control: max-age=， stale-if-error=</p>
</blockquote>
<p style="color: #424b5d;" data-tool="mdnice编辑器">stale-if-error 指令用于指定在发生错误（如网络错误、服务器错误等）时，允许客户端使用陈旧的缓存资源的最长时间。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><strong>工作原理：</strong></p>
<ul class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;">在 max-age 时间内，浏览器直接从缓存中获取资源，不会发送请求。</section>
</li>
<li>
<section style="color: #424b5d;">当缓存过期后，浏览器会发送请求到服务器获取最新资源。</section>
</li>
<li>
<section style="color: #424b5d;">如果请求过程中发生错误，并且在 stale-if-error 指定的时间内，浏览器会返回陈旧的缓存资源给客户端使用。</section>
</li>
<li>
<section style="color: #424b5d;">如果请求成功，则缓存更新，并且下次请求会直接从缓存中获取。</section>
</li>
</ul>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><strong>使用场景：</strong></p>
<ul class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;">适用于更新频率较高，但又希望在请求出错时能显示旧内容，而不是错误页面的资源，如用户生成的内容、新闻文章等。</section>
</li>
<li>
<section style="color: #424b5d;">提高了用户体验，避免因请求错误而显示错误页面。</section>
</li>
</ul>
<blockquote style="color: var(--weui-fg-1);" data-tool="mdnice编辑器">
<p style="color: #424b5d;">示例：Cache-Control: max-age=600， stale-if-error=1200<br />
说明：资源可以在 600 秒内直接从缓存中获取，在接下来的 1200 秒内，如果请求发生错误，浏览器仍可以显示陈旧的缓存资源。</p>
</blockquote>
<p style="color: #424b5d;" data-tool="mdnice编辑器">这两个指令可以单独使用，也可以组合使用，以实现更灵活的缓存控制策略。它们的目的都是在一定条件下允许使用过期的缓存，以提高性能和用户体验，同时还能保证一定的数据更新。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">需要注意的是，这两个指令都是 HTTP Cache-Control 响应头的扩展，并非所有浏览器都支持。目前，Chrome、Firefox 等主流浏览器已经实现了对这两个指令的支持。在实际应用中，还需要进行充分的测试和评估，以确保缓存策略的有效性和可靠性。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><strong>并且尽管浏览器已经支持了，但是服务器也需要正确地设置响应头才能生效。</strong></p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">2 服务器缓存</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><strong>服务器缓存是指将数据临时存储在服务器的内存或磁盘上，以便后续的请求可以直接从缓存中获取数据，而不必每次都重新生成或计算数据</strong>。服务器缓存的目的是提高服务器的响应速度，减少数据处理的开销，从而提升整个系统的性能和吞吐量。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">服务器缓存可以分为多个层次和类型，包括：</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">1.<strong>Web 服务器缓存</strong>：在 Web 服务器上配置的缓存机制，如：</p>
<ul class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;">Nginx 的 <span class="wx_search_keyword_wrap" style="color: var(--weui-link);">FastCGI<i class="wx_search_keyword"></i></span> 缓存、Proxy 缓存等。</section>
</li>
<li>
<section style="color: #424b5d;">Apache 的 mod_cache 模块。</section>
</li>
</ul>
<ol class="list-paddingleft-1" style="color: #000000;" start="2" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;"><strong>应用层缓存</strong>：在应用程序中实现的缓存机制，通常使用内存缓存技术，如：</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">对象缓存：将频繁访问的数据对象缓存在内存中，如 Java 的 Guava Cache 等。</section>
</li>
<li>
<section style="color: #424b5d;">查询缓存：将数据库查询的结果缓存起来，下次相同的查询可以直接从缓存中获取，如 Hibernate Second Level Cache。</section>
</li>
<li>
<section style="color: #424b5d;">页面缓存（Page Caching）：将动态生成的网页内容缓存起来，下次请求可以直接返回缓存的页面。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>数据库缓存</strong>：在数据库系统中实现的缓存机制，如 MySQL Query Cache</section>
</li>
<li>
<section style="color: #424b5d;"><strong>分布式缓存</strong>：将数据缓存在独立的分布式缓存服务器上，供多个应用服务器共享使用，如常用的 Redis。</section>
</li>
</ol>
<p style="color: #424b5d;" data-tool="mdnice编辑器">这些服务器缓存的类型可以独立使用，也可以组合使用，形成多级缓存架构。不同类型的服务器缓存在系统架构中的位置不同，缓存的数据类型和粒度也不同，但它们的共同目标都是提高服务器的性能，加快数据访问速度。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在实际应用中，需要根据具体的业务场景和性能瓶颈，选择适当的服务器缓存策略。这包括合理设计缓存的粒度、缓存的过期策略、缓存的更新机制、缓存的容量规划等。同时也要注意缓存的一致性问题，确保缓存的数据与原始数据源保持同步，避免出现脏读或数据不一致的问题。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">服务器缓存是提高系统性能的重要手段之一。合理利用服务器缓存，可以显著减少数据处理的开销，提高服务器的响应速度，从而提升整个系统的吞吐量和并发处理能力。但同时也要权衡缓存的成本和收益，避免过度使用缓存而带来的内存开销和维护成本。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">3 缓存实现策略或模式</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">从应用程序角度来看，缓存的实现模式主要涉及如何在应用程序中使用和集成缓存，以提高数据访问的效率和性能。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">常用的缓存模式有以下几种：</p>
<ol class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;"><strong>旁路缓存（Cache Aside）</strong>：</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">应用程序先查询缓存，如果缓存中有数据，则直接返回。</section>
</li>
<li>
<section style="color: #424b5d;">如果缓存中没有数据，则从数据源（如数据库）查询，并将查询结果存入缓存，然后返回。</section>
</li>
<li>
<section style="color: #424b5d;">更新数据时，先更新数据源，然后再删除缓存，保证数据一致性。</section>
</li>
<li>
<section style="color: #424b5d;">优点：简单易懂，应用程序可以控制缓存的生命周期。</section>
</li>
<li>
<section style="color: #424b5d;">缺点：可能出现缓存和数据源不一致的情况，需要合理设计缓存的过期策略和并发策略。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>读写穿透（Read/Write Through）</strong>：</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">应用程序只与缓存交互，不直接访问数据源。</section>
</li>
<li>
<section style="color: #424b5d;">缓存服务负责与数据源交互，并将数据在缓存和数据源之间同步。</section>
</li>
<li>
<section style="color: #424b5d;">读取数据时，缓存服务先查询缓存，如果缓存中没有数据，则从数据源查询，并将结果存入缓存，然后返回。</section>
</li>
<li>
<section style="color: #424b5d;">更新数据时，缓存服务先更新缓存，然后再更新数据源，保证数据一致性。</section>
</li>
<li>
<section style="color: #424b5d;">优点：应用程序无需关心缓存和数据源的同步，缓存服务保证了数据一致性。</section>
</li>
<li>
<section style="color: #424b5d;">缺点：缓存服务需要实现与数据源的交互，增加了复杂性；写操作的性能可能较低，因为需要同时更新缓存和数据源。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>异步写入（Write Behind）</strong>：</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">应用程序只与缓存交互，不直接访问数据源。应用程序更新缓存，缓存服务在后台异步地将数据更新到数据源。</section>
</li>
<li>
<section style="color: #424b5d;">写入数据时，只更新缓存，并将更新操作加入队列。</section>
</li>
<li>
<section style="color: #424b5d;">异步线程或进程从队列中取出更新操作，并批量写入数据源。</section>
</li>
<li>
<section style="color: #424b5d;">优点：写入操作的性能很高，因为只需要更新缓存；数据源的写入可以批量进行，提高效率。</section>
</li>
<li>
<section style="color: #424b5d;">缺点：缓存和数据源之间可能存在一定的延迟，需要合理设计队列的大小和刷新策略；如果缓存服务崩溃，可能导致数据丢失，因此需要着重考虑缓存服务的可靠性</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>预刷新（Refresh Ahead）</strong>：</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">定期或在特定条件下，异步地从数据源加载数据到缓存中。</section>
</li>
<li>
<section style="color: #424b5d;">可以通过定时任务、事件触发或者智能预测等方式来触发预刷新操作。</section>
</li>
<li>
<section style="color: #424b5d;">优点：避免了缓存 miss 导致的性能下降，提高了读取操作的响应速度。</section>
</li>
<li>
<section style="color: #424b5d;">缺点：需要额外的计算资源和存储空间来执行预刷新操作；如果预刷新的数据无法准确预测，可能会浪费资源。</section>
</li>
</ul>
</li>
</ol>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在业务场景中我们往往不局限于只使用某一种策略，可能会是使用以上多种模式，，根据不同的数据特点和访问模式，采用不同的策略。例如，对于读多写少的数据，可以使用「旁路缓存」或「读写穿透」策略；对于写多读少的数据，可以使用「异步写入」策略。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">计算机领域有个名言警句：</p>
<blockquote style="color: var(--weui-fg-1);" data-tool="mdnice编辑器">
<p style="color: #424b5d;">There are only two hard problems in Computer Science: cache invalidation, and naming things.（计算机领域只有有两大难题，「让缓存失效」和「给东西命名」）</p>
</blockquote>
<p style="color: #424b5d;" data-tool="mdnice编辑器">接下来我们聊一下缓存过期策略。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">4 缓存过期策略</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">缓存过期策略是指确定缓存数据何时失效并从缓存中移除的规则。合理的缓存过期策略可以帮助控制缓存的数据鲜度，并优化缓存的空间利用率。以下是一些常见的缓存过期策略:</p>
<ol class="list-paddingleft-2" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;"><strong><span class="wx_search_keyword_wrap" style="color: var(--weui-link);">TTL<i class="wx_search_keyword"></i></span> 策略</strong></section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">定义：TTL（Time To Live）策略为每个缓存条目设置一个固定的生存时间。当数据存入缓存时，指定一个过期时间。到达过期时间后，缓存条目将被自动移除，即使它在这段时间内没有被访问过。</section>
</li>
<li>
<section style="color: #424b5d;">优点：TTL 策略实现简单，易于配置，适用于对数据新鲜度有严格要求的场景。</section>
</li>
<li>
<section style="color: #424b5d;">缺点：容易导致缓存抖动，即频繁的缓存失效和重新加载，可能增加系统负载。</section>
</li>
<li>
<section style="color: #424b5d;">场景：TTL策略适用于那些数据变化频繁且需要确保数据新鲜度的场景。例如，实时新闻数据、股票价格、天气预报等。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>LRU 策略</strong></section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">定义：LRU（Least Recently Used）策略根据使用频率来决定缓存条目的去留。当缓存空间不足时，会移除最近最少使用的条目，以腾出空间存储新的数据。</section>
</li>
<li>
<section style="color: #424b5d;">优点：LRU 策略能有效利用缓存空间，适用于访问模式有局部性的场景。</section>
</li>
<li>
<section style="color: #424b5d;">缺点：实现较复杂，可能会增加缓存管理的开销，特别是在高并发环境下。</section>
</li>
<li>
<section style="color: #424b5d;">场景：LRU 策略广泛应用于需要频繁访问的大型数据集，例如 Web 服务器的页面缓存、数据库查询缓存等。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>LFU 策略</strong></section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">定义：LFU（Least Frequently Used）策略根据访问频率来决定缓存条目的去留。当缓存空间不足时，会移除访问频率最低的条目，以腾出空间存储新的数据。</section>
</li>
<li>
<section style="color: #424b5d;">优点：LFU 策略适用于访问频率有明显差异的场景，能有效缓存高频访问的数据。</section>
</li>
<li>
<section style="color: #424b5d;">缺点：实现复杂度较高，频繁更新访问计数可能会增加系统负载。</section>
</li>
<li>
<section style="color: #424b5d;">场景：LFU 策略适用于用户访问行为具有明显模式的应用，如推荐系统、热点新闻或视频的缓存。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>FIFO 策略</strong></section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">定义：FIFO（First In, First Out）策略按照条目加入缓存的顺序来决定去留。最早加入缓存的条目最先被移除，不考虑条目的使用频率或时间。</section>
</li>
<li>
<section style="color: #424b5d;">优点：FIFO 策略实现简单，适用于数据访问模式较为均匀的场景。</section>
</li>
<li>
<section style="color: #424b5d;">缺点：可能会导致热门数据被过早移除，不适合需要缓存热点数据的场景。</section>
</li>
<li>
<section style="color: #424b5d;">场景：FIFO 策略适用于缓存数据生命周期较短且频繁更新的场景，例如某些实时数据流的缓冲。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>ARC 策略</strong></section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">定义：ARC（Adaptive Replacement Cache）策略结合了 LRU 和 LFU 的优点，通过动态调整缓存策略来适应不同的访问模式。ARC 维护两个 LRU 列表，一个用于最近访问过的数据，另一个用于以前访问过的数据，并根据缓存命中情况在这两个列表之间调整权重。</section>
</li>
<li>
<section style="color: #424b5d;">优点：ARC策略能够自适应调整缓存替换策略，既考虑了最近使用的频率，又考虑了访问频率，从而提高缓存命中率。</section>
</li>
<li>
<section style="color: #424b5d;">缺点：实现复杂，需要维护多个列表和动态调整算法，可能增加缓存管理的开销。</section>
</li>
<li>
<section style="color: #424b5d;">场景：ARC 策略适用于访问模式多变且无法预知的场景，如混合型工作负载的缓存管理。它在需要高效利用缓存空间且保持高命中率的系统中表现尤为出色，例如数据库管理系统、操作系统的页面缓存等。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>SLRU 策略</strong></section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">定义：SLRU（Segmented Least Recently Used）是一种缓存替换算法，它是LRU（Least Recently Used）算法的一个变体。SLRU（Segmented LRU）策略将缓存分为两个段：一个是保护段（probation segment），另一个是优选段（protected segment）。新加入的条目首先进入保护段，如果条目在保护段中被再次访问，则移动到优选段。优选段中的条目如果再次被访问，则保持在优选段，否则会被移除。</section>
</li>
<li>
<section style="color: #424b5d;">优点：SLRU 策略通过分段管理缓存条目，既能保留最近访问的数据，也能保护多次访问的数据，提高缓存命中率。</section>
</li>
<li>
<section style="color: #424b5d;">缺点：实现复杂度较高，需要维护多个段和管理策略，可能增加系统开销。</section>
</li>
<li>
<section style="color: #424b5d;">场景：SLRU 策略适用于需要平衡最近访问和频繁访问需求的场景，例如Web浏览器的缓存管理、文件系统的缓存管理等。</section>
</li>
</ul>
</li>
</ol>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">5 一些注意事项</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在应用开发中使用缓存虽然可以显著提升系统性能和用户体验，但如果不当使用，也可能导致一些问题和陷阱。</p>
<ol class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;"><strong>缓存与数据源的一致性</strong>： 缓存数据和原始数据源之间的不一致是常见的问题之一。当数据被更新时，如果缓存没有同步更新，就会出现旧数据被重复使用的情况。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>缓存穿透</strong>：缓存穿透指查询不存在的数据时，请求直接穿过缓存访问数据库，如果这种请求非常频繁，将严重影响数据库的性能。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>缓存雪崩</strong>：缓存雪崩是指在缓存层面发生大规模的缓存失效，导致所有的请求都去打数据库，可能会因此使数据库压力过大而崩溃。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>缓存预热</strong>：系统启动后缓存是空的，直接面对大流量可能会导致短时间内数据库请求量激增。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>脏读问题</strong>：在分布式环境中，如果多个节点同时对缓存进行读写操作，可能会读到过期或不一致的数据。</section>
</li>
</ol>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">6 小结</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><strong>缓存不是解决性能问题的银弹，而是一种在适当的场景下能够显著提升系统响应速度和处理能力的工具</strong>。在实际应用中，缓存的引入需要仔细考虑其适用性、一致性问题、资源管理和安全性等多方面因素。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><strong>缓存最适合用于读操作远多于写操作的数据</strong>，以及那些数据更新不频繁、但需要快速访问的场景。然而，对于高度动态的数据，缓存可能不仅无法提供预期的性能提升，反而因为频繁的缓存更新和失效处理增加了额外的复杂性和开销。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在使用缓存的过程中，数据一致性是引入缓存时必须面对的一个挑战。无论是在单体应用还是分布式系统中，如何保证缓存中的数据与数据库中的数据保持一致，是设计缓存策略时必须仔细考虑的问题。不恰当的缓存策略可能导致数据过时或错误，影响业务的正确性。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">并且，缓存的管理和维护也是一项不可忽视的任务。正确的缓存大小、适宜的过期策略、有效的内存管理等都是确保缓存系统高效运作的关键。缓存过大可能会消耗过多的内存资源，影响系统的稳定性；缓存过小则可能无法发挥缓存的性能优势。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">缓存是一种强大的优化工具，但它并不适合所有情况。只有在正确的场景下，配合合适的策略和周到的管理，才能发挥出缓存的最大效能，帮助提升应用的性能和用户体验。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2024/05/talk-about-cache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTTP缓存算法</title>
		<link>https://www.phppan.com/2012/12/http-cache-algorithm/</link>
		<comments>https://www.phppan.com/2012/12/http-cache-algorithm/#comments</comments>
		<pubDate>Mon, 10 Dec 2012 00:26:46 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[HTTP协议]]></category>
		<category><![CDATA[HTTP权威指南]]></category>
		<category><![CDATA[HTTP缓存]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1768</guid>
		<description><![CDATA[HTTP协议缓存的目标是去除许多情况下对于发送请求的需求和去除许多情况下发送完整请求的需求。以不发送请求或减少 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>HTTP协议缓存的目标是去除许多情况下对于发送请求的需求和去除许多情况下发送完整请求的需求。以不发送请求或减少请求传输的数据量来优化整个HTTP架构，此目标的实现可以产生如下好处：</p>
<ul>
<li>减少网络传输的冗余信息量</li>
<li>缓解网络瓶颈的问题</li>
<li>降低对原始服务器的请求量</li>
<li>减少了传送距离，降低了因为距离而产生的时延</li>
</ul>
<p>缓存基本处理过程包括七个步骤。</p>
<ol>
<li>接收 &#8211; 缓存从网络中读取抵达的请求报文</li>
<li>解析 &#8211; 缓存对报文进行解析，提取出URL和各种首部</li>
<li>查询 &#8211; 缓存查看是否有本地副本可用，如果没有，就获取一份副本，并保存在本地</li>
<li>新鲜度检测 &#8211; 缓存查看已缓存副本是否足够新鲜，如果不是，就询问服务器是否有任何更新</li>
<li>创建响应 &#8211; 缓存会用新的首部和已缓存主体来构建一条响应报文</li>
<li>发送 &#8211; 缓存通过网络将响应发回给客户端</li>
<li>日志 &#8211; 缓存可选地创建一个日志文件条目来描述这个事务</li>
</ol>
<p>这里的缓存可以是本地客户端缓存，也可以是代理缓存之类的公共缓存。</p>
<h2>HTTP缓存模型</h2>
<p>HTTP缓存可以在不依赖服务器记住有哪些缓存拥有文档副本，而实现文档的一致。这些机制称为文档过期（document  expiration）和服务器再验证（server revalidation），也可以称它们为截止模型和证实模型。</p>
<p>截止模型是HTTP请求中带上标记文档的过期时间，HTTP协议中使用如下两个字段标记过期时间：</p>
<ul>
<li>Expires字段 &#8211; 指定一个绝对的过期日期。</li>
<li>Cache-control:max-age &#8211; 定义文档的最大使用期，从第一次生成文档到文档不再新鲜，无法使用为止，最大的合法生存时间（单位为s）</li>
</ul>
<p>仅仅使用截止模型还不够，即使文档过期了，也并不意味着当前文档和原始服务器的文档不一致了。此时就到证实模型大显身手的时候了。证实模型需要询问原始服务器文档是否发生了变化。其依赖于HTTP协议的如下字段：</p>
<ul>
<li>If-Modified-Since字段 &#8211;  如果从指定日期之后文档被修改了，就执行请求的方法。可以与Last-modified服务器响应首部配合使用。它告诉服务器只有在客户端缓存了对象的副本后，又服务器对其进行了修改的情况下，才在回复中发送此对象。如果服务器对象没有修改，返回304  Not Modified。如果服务器修改了此对象，发送此对象，返回200 OK。如果服务器删除了些对象，返回404 Not Found。</li>
<li>If-None-Match字段 &#8211; 服务器可以为文档提供特殊的标签（ETag），如果此标签与服务器的标签不一样，就会执行请求的方法。</li>
</ul>
<p>如果服务器应答中包括一个ETag，又包括一个Last-Mofidied值，则客户端在发送请求时使用两种证实机制，并且只有当两种证实机制都满足时才会返回304  Not Modified。</p>
<p>缓存在新鲜度检测时，只需要计算两个值：已缓存副本的使用期和已缓存副本的新鲜生存期。</p>
<h2>HTTP缓存使用期算法</h2>
<p>响应的使用期是服务器发布响应（或通过证实模型再验证）之后经过的总时间。使用期包括了因特网中传输的时间，在中间节点缓存的时间，以及在本地缓存中的停留时间。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">       <span style="color: #666666; font-style: italic;">/*
       * age_value 当代理服务器用自己的头部去响应请求时，Age标明实体产生到现在多长时间了。
       * date_value HTTP 服务器应答中的Date字段 原始服务器
       * request_time 缓存的请求时间
       * response_time 缓存获取应答的时间
       * now 当前时间
       */</span>
&nbsp;
      apparent_age <span style="color: #339933;">=</span> <span style="color: #990000;">max</span>（<span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> response_time <span style="color: #339933;">-</span> date_value）<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//缓存收到响应时响应的年龄 处理时钟偏差存在时，可能为负的情况</span>
&nbsp;
      corrected_received_age <span style="color: #339933;">=</span> <span style="color: #990000;">max</span>（apparent_age<span style="color: #339933;">,</span> age_value）<span style="color: #339933;">;</span>  <span style="color: #666666; font-style: italic;">//  容忍Age首部的错误</span>
&nbsp;
      response_delay <span style="color: #339933;">=</span> response_time <span style="color: #339933;">-</span> request_time<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// 处理网络时延，导致结果保守</span>
&nbsp;
      corrected_initial_age <span style="color: #339933;">=</span> corrected_received_age <span style="color: #339933;">+</span> response_delay<span style="color: #339933;">;</span>
&nbsp;
      resident_time <span style="color: #339933;">=</span> now <span style="color: #339933;">-</span> response_time<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// 本地的停留时间，即收到响应到现在的时间间隔</span>
&nbsp;
      current_age   <span style="color: #339933;">=</span> corrected_initial_age <span style="color: #339933;">+</span> resident_time<span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>因此，完整的使用期计算算法是通过查看Date首部和Age首部来判断响应已使用的时间，再记录其在本地缓存中的停留时间就是总的使用期。除此之外，HTTP协议对时钟偏差和网络时延进行了一补偿，特别是其对网络时延的补偿，可能会重复计算已使用的时间，从而使整个算法产生保守的结果。这种保守的效果时，如果出错了，算法只会使文档看起来比实际使用期要老，并引发再验证。</p>
<h2>HTTP缓存新鲜度算法</h2>
<p>通过已缓存文档的使用期，根据服务器和客户端限制来计算新鲜生存期，就可以确定已缓存的文档是否新鲜。已缓存文档的使用期在前面已经介绍过了，这小节我们来看看新鲜生存期的计算。</p>
<p>为了确定一条响应是保鲜的（fresh）还是陈旧的（stale），我们需要将其保鲜寿命（freshness  lifetime)和年龄(age)进行比较。年龄的计算见13.2.3节，本节讲解怎样计算保鲜寿命，以及判定一个响应是否已经过期。在下面的讨论中，数值可以用任何适于算术操作的形式表示。</p>
<p>与此相关的首部字段包括（按优先级从高到低）：  Cache-Control字段中“max-age”控制指令的值、Expires、Last-Modified、默认最小的生存期。用PHP代码体现如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">    <span style="color: #009933; font-style: italic;">/**
     * $heuristic 启发式过期值应不大于从那个时间开始到现在这段时间间隔的某个分数
     * $Max_Age_value_set  是否存在Max_Age值  Cache-Control字段中“max-age”控制指令的值
     * $Max_Age_value  Max_Age值
     * $Expires_value_set 是否存在Expires值
     * $Expires_value Expires值
     * $Date_value Date头部
     * $default_cache_min_lifetime 
     * $default_cache_max_lifetime
     */</span>
    <span style="color: #000000; font-weight: bold;">function</span> server_freshness_limit<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$Max_Age_value_set</span><span style="color: #339933;">,</span> <span style="color: #000088;">$Max_Age_value</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$Expires_value_set</span><span style="color: #339933;">,</span> <span style="color: #000088;">$Expires_value</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$Date_value</span><span style="color: #339933;">,</span> <span style="color: #000088;">$default_cache_min_lifetime</span><span style="color: #339933;">,</span> <span style="color: #000088;">$default_cache_max_lifetime</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #000088;">$factor</span> <span style="color: #339933;">=</span> <span style="color:#800080;">0.1</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//典型设置为10%</span>
&nbsp;
        <span style="color: #000088;">$heuristic</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//  启发式 默认为0</span>
&nbsp;
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$Max_Age_value_set</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>   <span style="color: #666666; font-style: italic;">// 优先级一为 Max_Age</span>
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$Max_Age_value</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #b1b100;">elseif</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$Expires_value_set</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>  <span style="color: #666666; font-style: italic;">//   优先级二为Expires</span>
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$Expires_value</span> <span style="color: #339933;">-</span> <span style="color: #000088;">$Date_value</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #b1b100;">elseif</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$Last_Modified_value_set</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #666666; font-style: italic;">//  优先级三为Last_Modified</span>
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>int<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$factor</span> <span style="color: #339933;">*</span> <span style="color: #990000;">max</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> <span style="color: #000088;">$Last_Modified_value</span> <span style="color: #339933;">-</span> <span style="color: #000088;">$Date_value</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000088;">$heuristic</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//  启发式</span>
        <span style="color: #009900;">&#125;</span><span style="color: #b1b100;">else</span><span style="color: #009900;">&#123;</span>  
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$default_cache_min_lifetime</span><span style="color: #339933;">;</span>
            <span style="color: #000088;">$heuristic</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//  启发式</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$heuristic</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">&gt;</span> <span style="color: #000088;">$default_cache_max_lifetime</span> ? <span style="color: #000088;">$default_cache_max_lifetime</span> <span style="color: #339933;">:</span> <span style="color: #000088;">$freshness_lifetime</span><span style="color: #339933;">;</span>
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">&lt;</span> <span style="color: #000088;">$default_cache_min_lifetime</span> ? <span style="color: #000088;">$default_cache_min_lifetime</span> <span style="color: #339933;">:</span> <span style="color: #000088;">$freshness_lifetime</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #b1b100;">return</span> <span style="color: #000088;">$freshness_lifetime</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>计算响应是否过期非常简单： response_is_fresh = (server_freshness_limit() &gt;  current_age)</p>
<p>以此为《HTTP权威指南》第七章读书笔记。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/12/http-cache-algorithm/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP缓存之APC-简介、存储结构和操作</title>
		<link>https://www.phppan.com/2012/06/php-opcode-cache-apc-1/</link>
		<comments>https://www.phppan.com/2012/06/php-opcode-cache-apc-1/#comments</comments>
		<pubDate>Mon, 04 Jun 2012 01:18:02 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[APC]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[opcode]]></category>
		<category><![CDATA[PHP缓存]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1700</guid>
		<description><![CDATA[APC简介 APC，全称是Alternative PHP Cache，官方翻译叫”可选PHP缓存”。它为我们提 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2>APC简介</h2>
<p>APC，全称是Alternative PHP Cache，官方翻译叫”可选PHP缓存”。它为我们提供了缓存和优化PHP的中间代码的框架。  APC的缓存分两部分:系统缓存和用户数据缓存。</p>
<ul>
<li><strong>系统缓存</strong> 它是指APC把PHP文件源码的编译结果缓存起来，然后在每次调用时先对比时间标记。如果未过期，则使用缓存的中间代码运行。默认缓存  3600s(一小时)。但是这样仍会浪费大量CPU时间。因此可以在php.ini中设置system缓存为永不过期(apc.ttl=0)。不过如果这样设置，改运php代码后需要重启WEB服务器。目前使用较多的是指此类缓存。</li>
<li><strong>用户数据缓存</strong> 缓存由用户在编写PHP代码时用apc_store和apc_fetch函数操作读取、写入的。如果数据量不大的话，可以一试。如果数据量大，使用类似memcache此类的更加专著的内存缓存方案会更好。</li>
</ul>
<p>在APC中我们也可以享受APC带来的缓存大文件上传进度的特性，需要在php.ini中将apc.rfc1867设为1，并且在表单中加一个隐藏域  APC_UPLOAD_PROGRESS，这个域的值可以随机生成一个hash，以确保唯一。之前的一篇文章<a href="http://www.phppan.com/2012/04/php-upload-progress/"><strong>PHP文件上传进度的实现原理</strong></a>中有对此更为细致的说明。</p>
<h2>APC与PHP内核的交互</h2>
<p>APC是作为一个扩展添加到PHP体系中的。因此，按照PHP的扩展规范，它会有PHP_MINIT_FUNCTION、PHP_MSHUTDOWN_FUNCTION、PHP_RINIT_FUNCTION、PHP_RSHUTDOWN_FUNCTION等宏定义的函数。在PHP_MINIT_FUNCTION(apc)中有调用apc_module_init中，并且在此函数中通过重新给zend_compile_file赋值以替换系统自带的编译文件过程，从而将APC自带的功能和相关数据结构插入到整个PHP的体系中。</p>
<p>这里会有一个问题，如果出现多个zend_compile_file的替换操作呢？在实际使用过程，这种情况会经常出现，比如当我们使用xdebug扩展时，又使用了apc，此时PHP是怎么处理的呢？不管是哪个扩展，在使用zend_compile_file替换时，都会有一个自己的compile_file函数（替换用），还有一个作用域在当前扩展的，一个旧的编译函数：old_compile_file。相当于每个扩展当中都保留了一个对于前一个编译函数的引用，形成一个单向链表。并且，所有最终的op_array都是在新的zend_compile_file中通过old_compile_file生成，即都会沿着这条单向链表，将编译的最终过程传递到PHP的zend_compile_file实现。在传递过程中，每经过一个节点，这些节点都会增加一些属于自己的数据结构，以实现特定的需求。</p>
<h3>APC内部存储结构</h3>
<p>在APC内部，对于系统缓存和用户缓存分别是以两个全局变量存储，从代码逻辑层面就隔离了两种缓存，当然，这两种存储的实现过程和数据结构是一样的，它们都是apc_cache_t类型，如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">&nbsp;
    <span style="color: #808080; font-style: italic;">/* {{{ struct definition: apc_cache_t */</span>
    <span style="color: #993333;">struct</span> apc_cache_t <span style="color: #009900;">&#123;</span>
        <span style="color: #993333;">void</span><span style="color: #339933;">*</span> shmaddr<span style="color: #339933;">;</span>                共享缓存的本地进程地址
        cache_header_t<span style="color: #339933;">*</span> header<span style="color: #339933;">;</span>       缓存头，存储在共享内存中
        slot_t<span style="color: #339933;">**</span> slots<span style="color: #339933;">;</span>               缓存的槽数组，存储在共享内存中
        <span style="color: #993333;">int</span> num_slots<span style="color: #339933;">;</span>                存储在缓存中的槽个数
        <span style="color: #993333;">int</span> gc_ttl<span style="color: #339933;">;</span>                   GC列表中槽的最大生存时间
        <span style="color: #993333;">int</span> ttl<span style="color: #339933;">;</span>                      如果对槽的访问时间大于这个TTL，需要则移除这个槽
        apc_expunge_cb_t expunge_cb<span style="color: #339933;">;</span>  <span style="color: #808080; font-style: italic;">/* cache specific expunge callback to free up sma memory */</span>
        uint has_lock<span style="color: #339933;">;</span>                为可能存在的造成同一进程递归锁而存在的标记 <span style="color: #808080; font-style: italic;">/* flag for possible recursive locks within the same process */</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
    <span style="color: #808080; font-style: italic;">/* }}} */</span></pre></td></tr></table></div>

<p>对于一个缓存，apc_cache_t类型的变量是其入口，它包含了这个缓存的一些全局信息。每个缓存都会有多个缓存槽，包含在slots字段中，slots的个数包含在num_slots字段，槽的过程时间控制在于ttl字段。对于用户缓存和系统缓存，默认情况下系统缓存数量为1000，实际上APC创建了1031个，也就是说默认情况下APC最少可以缓存1031个文件的中间代码。当然这个值还需要考虑内存大小，计算slot的key后的分布等等。更多的关于缓存的统计信息存储在header字段中，header字段结构为cache_header_t，如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">&nbsp;
<span style="color: #993333;">struct</span> cache_header_t <span style="color: #009900;">&#123;</span>
        apc_lck_t lock<span style="color: #339933;">;</span>             读写锁，独占阻塞缓存锁
        apc_lck_t wrlock<span style="color: #339933;">;</span>           写锁，为防止缓存爆满
        <span style="color: #993333;">unsigned</span> <span style="color: #993333;">long</span> num_hits<span style="color: #339933;">;</span>     缓存命中数
        <span style="color: #993333;">unsigned</span> <span style="color: #993333;">long</span> num_misses<span style="color: #339933;">;</span>   缓存未命中数
        <span style="color: #993333;">unsigned</span> <span style="color: #993333;">long</span> num_inserts<span style="color: #339933;">;</span>  插入缓存总次数
        <span style="color: #993333;">unsigned</span> <span style="color: #993333;">long</span> expunges<span style="color: #339933;">;</span>     清除的总次数
        slot_t<span style="color: #339933;">*</span> deleted_list<span style="color: #339933;">;</span>       指向被清除的槽的链表
        time_t start_time<span style="color: #339933;">;</span>          以上计数器被重置的时间
        zend_bool busy<span style="color: #339933;">;</span>             当apc在忙于清除缓存时告诉客户端此时状态的标记
        <span style="color: #993333;">int</span> num_entries<span style="color: #339933;">;</span>            统计的实体数
        <span style="color: #993333;">size_t</span> mem_size<span style="color: #339933;">;</span>            统计的被用于缓存的内存大小
        apc_keyid_t lastkey<span style="color: #339933;">;</span>        用户缓存最后一写入的key
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>一个缓存包含多个slots，每个slot都是一个slot结构体的变量，其结构如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">&nbsp;
    <span style="color: #993333;">struct</span> slot_t <span style="color: #009900;">&#123;</span>
        apc_cache_key_t key<span style="color: #339933;">;</span>        槽的key
        apc_cache_entry_t<span style="color: #339933;">*</span> value<span style="color: #339933;">;</span>   槽的值
        slot_t<span style="color: #339933;">*</span> next<span style="color: #339933;">;</span>               链表中的下一个槽
        <span style="color: #993333;">unsigned</span> <span style="color: #993333;">long</span> num_hits<span style="color: #339933;">;</span>     这个bucket的命中数<span style="color: #808080; font-style: italic;">/* number of hits to this bucket */</span>
        time_t creation_time<span style="color: #339933;">;</span>       槽的初始化时间
        time_t deletion_time<span style="color: #339933;">;</span>       槽从缓存被移除的时间 <span style="color: #808080; font-style: italic;">/* time slot was removed from cache */</span>
        time_t access_time<span style="color: #339933;">;</span>         槽的最后一次被访问的时间
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>每个槽包含一个key，以apc_cache_key_t结构体存储；包含一个值，以apc_cache_entry_t结构体存储。如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">&nbsp;
    <span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> apc_cache_key_t apc_cache_key_t<span style="color: #339933;">;</span>
    <span style="color: #993333;">struct</span> apc_cache_key_t <span style="color: #009900;">&#123;</span>
        apc_cache_key_data_t data<span style="color: #339933;">;</span>
        <span style="color: #993333;">unsigned</span> <span style="color: #993333;">long</span> h<span style="color: #339933;">;</span>              <span style="color: #808080; font-style: italic;">/* pre-computed hash value */</span>
        time_t mtime<span style="color: #339933;">;</span>                 <span style="color: #808080; font-style: italic;">/* the mtime of this cached entry */</span>
        <span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span> type<span style="color: #339933;">;</span>
        <span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span> md5<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">16</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>        <span style="color: #808080; font-style: italic;">/* md5 hash of the source file */</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>结构说明如下：</p>
<ul>
<li>data字段  apc_cache_key_data_t类型，一个联合体，存储key的关联信息，比如对于系统缓存，其可能会存储文件的路径或OS的文件device/inode；对于用户缓存可能会存储用户给定的标识或标识长度。</li>
<li>h字段 文件完整路径或用户给定的标识的hash值，使用的hash算法为PHP自带的time33算法；或者文件所在device和inode的和</li>
<li>mtime字段 缓存实体的修改时间</li>
<li>type字段 APC_CACHE_KEY_USER：用户缓存; APC_CACHE_KEY_FPFILE：系统缓存（有完整路径）;  APC_CACHE_KEY_FILE: 系统缓存（需要查找文件）</li>
<li>md5字段  文件内容的MD5值，这个字段与前面四个字段不同，它是可选项，可以通过配置文件的apc.file_md5启用或禁用。并且这个值是在初始化实体时创建的。看到这里源文件的md5值，想起之前做过一个关于MySQL数据表中访问路径查询的优化，开始时通过直接查询路径字段，在数据量达到一定级别时，出现了就算走索引还是会很慢的情况，各种方案测试后，采用了以新增一个关于访问路径的md5值查询解决。</li>
</ul>
<p>除了入口，APC在最终的数据存储上对于系统缓存和用户缓存也做了区分，在_apc_cache_entry_value_t分别对应file和user。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">&nbsp;
    <span style="color: #993333;">typedef</span> <span style="color: #993333;">union</span> _apc_cache_entry_value_t <span style="color: #009900;">&#123;</span>
        <span style="color: #993333;">struct</span> <span style="color: #009900;">&#123;</span>            
            <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>filename<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* absolute path to source file */</span>
            zend_op_array<span style="color: #339933;">*</span> op_array<span style="color: #339933;">;</span>     存储中间代码的op_array
            apc_function_t<span style="color: #339933;">*</span> functions<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* array of apc_function_t's */</span>
            apc_class_t<span style="color: #339933;">*</span> classes<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* array of apc_class_t's */</span>
            <span style="color: #993333;">long</span> halt_offset<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* value of __COMPILER_HALT_OFFSET__ for the file */</span>
        <span style="color: #009900;">&#125;</span> file<span style="color: #339933;">;</span>                         file结构体 系统缓存所用空间，包括文件名，，
        <span style="color: #993333;">struct</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>info<span style="color: #339933;">;</span>
            <span style="color: #993333;">int</span> info_len<span style="color: #339933;">;</span>
            zval <span style="color: #339933;">*</span>val<span style="color: #339933;">;</span>
            <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> ttl<span style="color: #339933;">;</span>           过期时间
        <span style="color: #009900;">&#125;</span> user<span style="color: #339933;">;</span>                         ser结构体 用户缓存所用空间
    <span style="color: #009900;">&#125;</span> apc_cache_entry_value_t<span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>如图所示：<br />
<div id="attachment_1703" style="width: 672px" class="wp-caption aligncenter"><a href="http://www.phppan.com/wp-content/uploads/2012/06/apc.jpg"><img src="http://www.phppan.com/wp-content/uploads/2012/06/apc.jpg" alt="APC缓存存储结构" title="apc" width="662" height="580" class="size-full wp-image-1703" /></a><p class="wp-caption-text">APC缓存存储结构</p></div></p>
<h3>初始化</h3>
<p>在APC扩展的模块初始化函数（PHP_MINIT_FUNCTION(apc)）中，APC会调用apc_module_init函数初始化缓存所需要的全局变量，如系统缓存则调用apc_cache_create创建缓存全局变量apce_cache，默认情况下会分配1031个slot所需要的内存空间，用户缓存也会调用同样的方法创建缓存，存储在另一个全局变量apc_user_cache，默认情况下会分配4099个内存空间。这里分配的空间的个数都是素数，在APC的代码中有一个针对不同数量的素数表primes(在apc_cache.c文件)。素数的计算是直接遍历素数表，找到表中第一个比需要分配的个数大的素数。</p>
<h3>缓存key生成规则</h3>
<p>APC的缓存中的每个slot都会有一个key，key是 apc_cache_key_t结构体类型，除了key相关的属性，关键是h字段的生成。  h字段决定了此元素落于slots数组的哪一个位置。对于用户缓存和系统缓存，其生成规则不同。</p>
<ul>
<li>用户缓存通过apc_cache_make_user_key函数生成key。通过用户传递进来的key字符串，依赖PHP内核中的hash函数（PHP的hashtable所使用的hash函数：zend_inline_hash_func），生成h值。</li>
<li>系统缓存通过apc_cache_make_file_key函数生成key。通过APC的配置项apc.stat的开关来区别对待不同的方案。在打开的情况下，即  apc.stat= On  时，如果被更新则自动重新编译和缓存编译后的内容。此时的h值是文件的device和inode相加所得的值。在关闭的情况下，即apc.stat=off时，当文件被修改后，如果要使更新的内容生效，则必须重启Web服务器。此时h值是根据文件的路径地址生成，并且这里的路径是绝对路径。即使你是使用的相对路径，也会查找PG(include_path)定位文件，以取得绝对路径，所以使用绝对路径会跳过检查，可以提高代码的效率。</li>
</ul>
<h3>添加缓存过程</h3>
<p>以用户缓存为例，apc_add函数用于给APC缓存中添加内容。如果key参数为字符串中，APC会根据此字符串生成key，如果key参数为数组，APC会遍历整个数组，生成key。根据这些key，APC会调用_apc_store将值存储到缓存中。由于这是用户缓存，当前使用的缓存为apc_user_cache。执行写入操作的是apc_cache_make_user_entry函数，其最终调用apc_cache_user_insert执行遍历查询和写入操作。与此对应，系统缓存使用apc_cache_insert执行写入操作，其最终都会调用_apc_cache_insert。</p>
<p>不管是用户缓存还是系统缓存，大体的执行过程类似，步骤如下：</p>
<ol>
<li>通过求余操作，定位当前key的在slots数组中的位置： cache-&gt;slots[key.h % cache-&gt;num_slots];</li>
<li>在定位到slots数组中的位置后，遍历当前key对应的slot链表，如果存在slot的key和要写入的key匹配或slot过期，清除当前slot。</li>
<li>在最后一个slot的后面插入新的slot。</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/06/php-opcode-cache-apc-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于缓存</title>
		<link>https://www.phppan.com/2011/07/about-cache/</link>
		<comments>https://www.phppan.com/2011/07/about-cache/#comments</comments>
		<pubDate>Mon, 18 Jul 2011 00:43:43 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[DNS缓存]]></category>
		<category><![CDATA[HTTP缓存]]></category>
		<category><![CDATA[数据库缓存]]></category>
		<category><![CDATA[查询缓存]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1422</guid>
		<description><![CDATA[在维基百科中有这样一段描述： 凡是位于速度相差较大的两种硬件之间的，用于协调两者数据传输速度差异的结构，均可称 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="text-indent: 2em;">在<a style="color: #1299da; text-decoration: underline;" href="http://zh.wikipedia.org/wiki/Cache">维基百科</a>中有这样一段描述： <strong>凡是位于速度相差较大的两种硬件之间的，用于协调两者数据传输速度差异的结构，均可称之为Cache。</strong> 从最初始的处理器与内存间的Cache开始，都是为了让数据访问的速度适应CPU的处理速度， 其基于的原理是内存中“程序执行与数据访问的局域性行为”。</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">处理器缓存</h2>
<p style="text-indent: 2em;">在处理器与物理内存间有三级缓存，如下图： <img src="http://images.cnblogs.com/cnblogs_com/mydomain/201106/201106122243022711.png" alt="" /></p>
<p style="text-indent: 2em;">这是《深入理解计算机系统》上的一张图，对于每一层，位于上层的更快更小的存储设备作为位于下层更大更慢的存储设备的缓存。 从物理内存往上越往处理器方向走，存储设备的成本越高，并且更小，访问的速度更快。 它们是作为存储在更大更慢的存储设备的缓存而存在，它们的作用在于协调处理器与物理内存间的传输数据不一致。 但是把内存单独拉出来说，它也是一种缓存，它的作用也是为了将硬盘或其它较慢存储介质中的数据更快的提供给处理器。</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">DNS缓存</h2>
<p style="text-indent: 2em;"><a style="color: #1299da; text-decoration: underline;" href="http://zh.wikipedia.org/zh-cn/DNS">DNS(域名解析系统)</a>缓存是指当第一次访问某个站点的时候，客户端会向DNS服务器发出解析请求， 然后把信息保存在本机的DNS缓存以备再次访问。启用DNS缓存能提高网络访问速度，相应地，计算机的安全性降低了。</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">HTTP协议缓存</h2>
<p style="text-indent: 2em;">HTTP为提高性能，减少网络传输的信息量，从而使用了缓存。 HTTP协议缓存的目标是去除许多情况下对于发送请求的的需求和去除许多情况下发送完整请求的需求。 在HTTP协议中使用截止模型和证实模型来实现缓存。 协议它只是协议，只是一种通行的建议和规范，关键还是看客户端的实现。 比较直观的体现是：当第一次去一个网站时加载会比较慢，但再次打开这个网站时速度会快很多。 这是由于基于HTTP协议，浏览器客户端将一些CSS，图片等文件都缓存在本地，从而不再需要从服务器读取。</p>
<p style="text-indent: 2em;">DNS缓存、HTTP协议缓存的作用与处理器缓存有一些不同，虽然也是协调数据传输速度的差异，但是其本质的差别是它已不再是纯粹的两种硬件之间的差异， 而是引入了网络的元素，换一种表示方式：<strong>凡是位于速度相差较大的两种实体之间的，用于协调两者数据传输速度差异的结构，均可称之为Cache。</strong></p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">数据库系统缓存</h2>
<p style="text-indent: 2em;">这里没有用数据库缓存是因为数据库与数据库系统在概念上不是一回事。数据库是“按照数据结构来组织、存储和管理数据的仓库”。 而数据库系统是一个实际可运行的存储、维护和应用系统提供数据的软件系统，是存储介质、处理对象和管理系统的集合体。 我们平时所说的数据库缓存是数据库系统所提供的缓存功能。</p>
<p style="text-indent: 2em;">以MySQL为例，我们使用最多的是查询缓存(Query Cache)。 它的实现过程不是很复杂，当客户端请求了一个查询后，MySQL通过特定的Hash算法生成一个标识用的hash值， 得MySQL计算完后，返回的结果集将与这个生成的hash值对应存放在内存中。若此缓存没有过期时， 下一次相同的请求过来时将直接返回结果，不再需要SQL解析，计算等操作。</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">PHP内存管理中的缓存</h2>
<p style="text-indent: 2em;">PHP内存管理中的缓存也是基于“程序执行与数据访问的局域性行为”的原理。 引入缓存，就是为了减少小块内存块的查询次数，为最近访问的数据提供更快的访问方式。 其实现过程主要包括以下的一些活动：</p>
<ul>
<li>标识缓存和缓存的大小限制，即何时使用缓存，在某些情况下可以以最少的修改禁用掉缓存</li>
<li>缓存的存储结构，即缓存的存放位置、结构和存放的逻辑</li>
<li>初始化缓存</li>
<li>获取缓存中内容</li>
<li>写入缓存</li>
<li>释放缓存或者清空缓存列表</li>
</ul>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">文件缓存</h2>
<p style="text-indent: 2em;">此处的文件缓存是指在应用开发过程中将一些中间结果存放在文件，以备下次使用。 如在PHP中一些模板系统的实现，以其规则编写了模板文件，生成中间的PHP文件，如果用户调用某个页面则直接访问PHP页面，而跳过了模板的解析过程</p>
<p style="text-indent: 2em;">以上三种缓存与前面也不一样，它实际上是将一些需要计算后的结果缓存，下次直接返回计算后的结果。依此，则缓存的表述可以再次修改为： <strong>凡是获取数据速度相差较大的两种实体之间的，用于协调两者数据传输速度差异的结构，均可称之为Cache。</strong></p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/07/about-cache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Yii框架中有点意思的文件缓存</title>
		<link>https://www.phppan.com/2011/01/yii-filecache/</link>
		<comments>https://www.phppan.com/2011/01/yii-filecache/#comments</comments>
		<pubDate>Fri, 21 Jan 2011 10:01:53 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Yii框架]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1228</guid>
		<description><![CDATA[Yii框架中有点意思的文件缓存 在Yii框架的framework/caching目录下是Yii框架的所有缓存操 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h1 style="font-weight: normal; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.4em; color: #006ea3; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-bottom: 10px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #aaaaaa;">Yii框架中有点意思的文件缓存</h1>
<p style="text-indent: 2em;">在Yii框架的framework/caching目录下是Yii框架的所有缓存操作。包括文件缓存，APC缓存、Db缓存，Memcache缓存等。 今天我们要说的是文件缓存。</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">【文件和类结构】</h2>
<p style="text-indent: 2em;">它总共包括CFileCache.php、CCache.php和接口文件。 缓存基类CCache抽象类实现了CApplicationComponent类，ICache, ArrayAccess接口。 实现类CFileCache继承自CCache基类。其它如Memcache等缓存也是继承这个基类。 在ICache接口中统一缓存接口，在CCache基类中统一对外的接口，以类似于模板方法模式的方式将对于增加，修改，删除操作延迟到子类实现。</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">【缓存方案】</h2>
<p style="text-indent: 2em;">指定缓存目录，以一条数据一个文件的方式存储。序列化使用PHP自带的serialize函数。 如果缓存数据过多，作者建议使用多级目录，多级目录会自动生成，默认为0，即当前目录，推荐使用3级以下的子目录。 对于缓存过期是以文件的修改时间为准（filemtime函数）。在获取数据时，如果文件过期，则取的数据为空。</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">【有点意思的地方】</h2>
<p style="text-indent: 2em;">个人觉得有点意思的地方在于其对于过期缓存的批量处理，在程序中作者称其为garbage collection。 看其实现代码：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #ff8400;">if</span><span style="color: #ffffff;">(</span><span style="color: #e0882f;">!</span><span style="color: #6d9cbe;">$this</span><span style="color: #e0882f;">-&gt;</span>_gced <span style="color: #e0882f;">&amp;&amp;</span> <a style="color: #1299da; text-decoration: none;" href="http://www.php.net/mt_rand"><span style="color: #e2392d;">mt_rand</span></a><span style="color: #ffffff;">(</span><span style="color: #1299da;">0</span><span style="color: #e0882f;">,</span><span style="color: #1299da;">1000000</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">&lt;</span><span style="color: #6d9cbe;">$this</span><span style="color: #e0882f;">-&gt;</span>_gcProbability<span style="color: #ffffff;">)</span>
<span style="color: #ffffff;">{</span>
    <span style="color: #6d9cbe;">$this</span><span style="color: #e0882f;">-&gt;</span><span style="color: #ffffff;">gc</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>    <span style="color: #bc9458; font-style: italic;">//  清除过期的缓存文件</span>
    <span style="color: #6d9cbe;">$this</span><span style="color: #e0882f;">-&gt;</span>_gced<span style="color: #e0882f;">=</span><span style="color: #cc7833;">true</span><span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">各位看官，你应该知道这段代码是啥意思了。 有点意思的地方是和PHP源码中对于session过期的处理。如下所示源码：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span>PS<span style="color: #ffffff;">(</span>mod_data<span style="color: #ffffff;">)</span> <span style="color: #e0882f;">&amp;&amp;</span> PS<span style="color: #ffffff;">(</span>gc_probability<span style="color: #ffffff;">)</span> <span style="color: #e0882f;">&gt;</span> <span style="color: #1299da;">0</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
    <span style="color: #1299da;">int</span> nrdels <span style="color: #e0882f;">=</span> <span style="color: #e0882f;">-</span><span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span>

    nrand <span style="color: #e0882f;">=</span> <span style="color: #ffffff;">(</span><span style="color: #1299da;">int</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">(</span><span style="color: #ffffff;">(</span><span style="color: #1299da;">float</span><span style="color: #ffffff;">)</span> PS<span style="color: #ffffff;">(</span>gc_divisor<span style="color: #ffffff;">)</span> <span style="color: #e0882f;">*</span> php_combined_lcg<span style="color: #ffffff;">(</span>TSRMLS_C<span style="color: #ffffff;">)</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span>nrand <span style="color: #e0882f;">&lt;</span> PS<span style="color: #ffffff;">(</span>gc_probability<span style="color: #ffffff;">)</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <span style="color: #bc9458; font-style: italic;">//  调用定义的垃圾收集方法</span>
        PS<span style="color: #ffffff;">(</span>mod<span style="color: #ffffff;">)</span><span style="color: #e0882f;">-&gt;</span>s_gc<span style="color: #ffffff;">(</span><span style="color: #e0882f;">&amp;</span>PS<span style="color: #ffffff;">(</span>mod_data<span style="color: #ffffff;">)</span><span style="color: #e0882f;">,</span> PS<span style="color: #ffffff;">(</span>gc_maxlifetime<span style="color: #ffffff;">)</span><span style="color: #e0882f;">,</span> <span style="color: #e0882f;">&amp;</span>nrdels TSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        ...
    <span style="color: #ffffff;">}</span>
<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">对比下变量名，对比下调用方式。 ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/01/yii-filecache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP缓存 Cache Lite源码总结</title>
		<link>https://www.phppan.com/2010/03/php-cache-lite/</link>
		<comments>https://www.phppan.com/2010/03/php-cache-lite/#comments</comments>
		<pubDate>Sat, 27 Mar 2010 02:56:55 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP应用]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=617</guid>
		<description><![CDATA[PHP缓存 Cache Lite源码总结 1、【设置参数的方法】 在构造方法中调用对象方法setOption设 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>PHP缓存 Cache Lite源码总结</p>
<p>1、【设置参数的方法】<br />
在构造方法中调用对象方法setOption设置类私有变量的值，从而完成对象的初始化操作。<br />
在setOption方法中通过判断$name是否为$availableOptions数组中的一员来设置初始值。<br />
感觉有些坏味道<br />
<br />
2、【缓存文件命名规则】<br />
命名规则对fileNameProtection参数有两种设置方法<br />
如果此参数为真，则对于$group和$id进行md5加密，否则直接使用这两个字段<br />
默认情况下使用md5加密后的名称<br />
如下所示代码：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
</pre></td><td class="code"><pre class="php" style="font-family:monospace;"> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_fileNameProtection<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
         <span style="color: #000088;">$suffix</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'cache_'</span><span style="color: #339933;">.</span><span style="color: #990000;">md5</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$group</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'_'</span><span style="color: #339933;">.</span><span style="color: #990000;">md5</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$id</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
 <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
         <span style="color: #000088;">$suffix</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'cache_'</span><span style="color: #339933;">.</span><span style="color: #000088;">$group</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'_'</span><span style="color: #339933;">.</span><span style="color: #000088;">$id</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>
3、【缓存路径设置规则】<br />
如果设置了hashedDirectoryLevel参数，则会在用户所给的缓存地址（cacheDir参数）后添加多层（hashedDirectoryLevel层）目录，所有的目录以cache_开头<br />
默认hashedDirectoryLevel的值为0，即不添加嵌套目录<br />
如下所示代码：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="php" style="font-family:monospace;">        <span style="color: #000088;">$root</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_cacheDir<span style="color: #339933;">;</span>    <span style="color: #666666; font-style: italic;">//    用户所给的缓存地址</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_hashedDirectoryLevel<span style="color: #339933;">&gt;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000088;">$hash</span> <span style="color: #339933;">=</span> <span style="color: #990000;">md5</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$suffix</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$i</span><span style="color: #339933;">=</span><span style="color: #cc66cc;">0</span> <span style="color: #339933;">;</span> <span style="color: #000088;">$i</span><span style="color: #339933;">&lt;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_hashedDirectoryLevel <span style="color: #339933;">;</span> <span style="color: #000088;">$i</span><span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #000088;">$root</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$root</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">'cache_'</span> <span style="color: #339933;">.</span> <span style="color: #990000;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$hash</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> <span style="color: #000088;">$i</span> <span style="color: #339933;">+</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">'/'</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>   
        <span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p></p>
<p>4、【基于内存的缓存】<br />
在缓存参数中我们可以看到有一个memoryCaching参数，此参数默认情况下为false，<br />
对于这个内存缓存，我有些疑惑：<br />
<strong><br />
1、基于apache2服务器的PHP页面，每次访问都会有一个apache线程处理这个请求，而在每个线程中，这些内存缓存都是以对象属性的形式存在，则在各线程间如何共享？这样存在的意义是什么？<br />
2、如果是在一次执行中进行缓存，那这样做的意义又有多大呢？<br />
</strong><br />
<br />
5、【过期时间的控制】<br />
在每次读取缓存时间都会调用_setRefreshTime方法刷新此前时间，<br />
<strong>$this->_refreshTime = time() &#8211; $this->_lifeTime;</strong><br />
然后在取数据时判断缓存文件的创建时间是否比_refreshTime大</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
</pre></td><td class="code"><pre class="php" style="font-family:monospace;">     <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">file_exists</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_file<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">@</span><span style="color: #990000;">filemtime</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_file<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_refreshTime<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                    <span style="color: #000088;">$data</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_read<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
               <span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>6、【_write函数中的坏味道】<br />
在_write函数和_setFileName函数之间有重复代码<br />
个人觉得可以将此提取出来。<br />
<br />
7、【caching参数的必要性】<br />
此参数控制全局的缓存的打开与关闭，在调试程序时十分有用<br />
<br />
8、【自动清除旧缓存的控制】<br />
automaticCleaningFactor参数控制是否自动清除旧缓存，<br />
如果此参数的值大于1则会进行自动清除，只是在程序中针对自动清除有一个随机数，<br />
可以理解为 1 / automaticCleaningFactor的机率进行自动清除旧缓存<br />
<br />
9、【&#8230;&#8230;】<br />
很轻便的缓存类，如果是简单应用，值得一试！<br />
<br />
EOF</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2010/03/php-cache-lite/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
