前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >再聊缓存技术

再聊缓存技术

作者头像
后端技术探索
发布2018-08-10 10:31:00
6270
发布2018-08-10 10:31:00
举报
文章被收录于专栏:后端技术探索后端技术探索

一:缓存的概念

对于现在的各种系统来说,缓存的应用无处不在。如果能合理的利用缓存,整个系统的性能将会得到大大的提高,Web开发尤其如此。一般高并发大访问量的应用,主要压力都在服务器端,所以服务器端的性能至关重要,缓存的使用,很多时候是有决定性影响的。

在这里明确一下我所说的缓存,它不是一个软件系统,不是一段程序,也不是一个存储空间,而是一种处理方式。缓存的目的是为了提高程序性能,减少程序的执行时间。当然缓存也有相应的代价,一般是牺牲空间换时间。

本文介绍下图所示各层缓存的使用:

二:客户端以及网络协议的缓存

从客户端到服务器端的数据交换,都要用到网络协议,Web 开发最常见的就是 HTTP/HTTPS 协议。通常由客户端发起一个 HTTP 请求,建立一个到服务器指定端口(默认是80端口)的 TCP 连接。http 服务器则在那个端口监听客户端发送过来的请求。一旦收到请求,服务器向客户端发回状态码和响应消息。在请求和响应的过程中,缓存就可以发挥作用了。

在 HTTP 协议里,通过设置 Cache-Control 等一些header信息,就能实现数据缓存在浏览器端的目的。浏览器会根据 Cache-Control 设置的缓存失效时间,来决定是否直接用本地缓存数据,以减少对服务器端的 http 请求。 还有 Last-Modified 和 ETag 是条件请求(Conditional Request)相关的两个字段。客户端请求服务器时,会发送一个验证请求询问服务器页面是否已经更改,在 HTTP 头里面带上” ETag”和”If Modify Since”头。服务器根据这些信息判断是否有更新信息,如果没有,就返回HTTP 状态码 304(Not Modify);如果有更新,返回状态码 200 和更新的页面内容,并且携带新的 ”ETag” 和 ”Last-Modified”。使用这个机制,能够减少数据的传输和服务器资源的消耗,不过仍然会产生一个 HTTP 请求,而 Cache-Control 实现的缓存不会产生 HTTP 请求。

下面是 PHP 代码设置 HTTP header 信息,来控制缓存的例子:

$now = time();$t = 3600;header("Cache-Control: max-age=" . $t . ", private");header("Expires: " . gmdate("D, d M Y H:i:s", $now + $t) . " GMT");header("Last-Modified: " . gmdate("D, d M Y H:i:s", $now) . " GMT");header("ETag: app-" . $now);header("Pragma: cache");

当然,这种缓存一般适用于静态页面,或者数据变化不频繁的情况下。如下图,就是百度的"关于百度"(http://home.baidu.com )页面的缓存设置:

另外浏览器端也可以通过 cookie,localStorage 等,把一些数据存在客户端磁盘来作为缓存,以减少对后端数据的依赖,提高性能。

三:服务端应用程序中的缓存

应用程序里的缓存是我们最常用的缓存。一般来说,系统常会部署一些分布式存储系统来作为缓存的载体。常见的有 Memcached,Redis 等 key-value 存储系统。应用程序经常把某些耗时长的程序的运算结果,存储起来以备下次使用,降低下次访问的耗时。下图是一个简单的缓存使用场景,把Mongo数据库的数据缓存在Redis里:

偶尔还会使用多级缓存,例如既把远程数据库的数据,缓存在本地磁盘,也会把本地磁盘的数据缓存在内存里,来加速服务器端程序的响应。

实际业务中,一般经常把一些对读取量很大,实时性要求不高的数据缓存起来,如热门商品基本信息,排行榜等。常见代码如下, 缓存有数据就直接返回数据,否则重新取数据返回,并写入缓存:

$lc = CLocalCache::getInstance();if ($result = $lc->get('hot_data')){ return $result;}else{ $result = get_data(); $lc->set('hot_data',$result, 60); return $result;}

还有一种情况是,把整个动态页面当作静态html文件缓存起来,而不是仅仅缓存某部分数据。某些框架就有这种模板的页面缓存功能,例如 Smarty 模板引擎。直接将变化不是很频繁的页面静态化,定期更新,静态文件的请求速度和消耗资源都远小于动态程序。

四:运行环境缓存

运行环境缓存指的是程序代码之外的缓存,下面介绍两种使用较多的:

Web 服务器缓存:常见的 Web 服务器,如 Nginx ,Apache 都有自己的缓存模块。Nginx 的 http_proxy 模块,可以实现类似于Squid的缓存功能。Nginx 对客户已经访问过的内容在 Nginx 服务器本地建立副本,这样在一段时间内再次访问该数据,就不需要通过 Nginx 服务器再次向后端服务器发出请求,所以能够减少 Web服务器与后端服务器之间的网络流量,减轻网络拥塞,同时还能减小数据传输延迟,提高用户访问速度。

OPCode 缓存: 这里是针对 PHP 来说,大家都知道 PHP 是一种脚本语言,每次执行程序时,解释器需要对脚本代码进行分析,将它们生成可以直接运行的中间代码,也称为操作码,即 OPCode。OPCode 缓存能避免重复转换操作码的过程,减少 CPU 和内存开销。

如上图所示,PHP 的执行过程默认是中间的步骤,如果有 OPCode 缓存,就可以走最上面的步骤,省去解析脚本代码的过程了。

PHP 支持 Opcode 缓存的扩展大家也不陌生,有 Xcache、APC 等

五: 数据库缓存

对于 PHP 语言开发的应用程序来说,后端存储大多使用了 MySQL, 因此 MySQL 对系统性能有非常大的影响。

MySQL 读取数据时,可以从硬盘的数据文件中获取数据,也可以从数据库缓存中读取数据。从内存中读取要比从硬盘上速度要快好几百倍,故现在绝大部分应用系统,都会最大程度的使用缓存(内存中的一个存储区域),来提高系统的运行效率。MYSQL的查询缓存用于缓存select查询结果,并在下次接收到同样的查询请求时,且数据库里的数据没有发生变更,便不再执行实际查询处理而直接返回结果,有这样的查询缓存能提高查询的速度,使查询性能得到优化。

可以用如下语句来查看 MySQL 的查询缓存设置:

SHOW VARIABLES LIKE '%query_cache%';

+------------------------------+----------+

| Variable_name | Value |

+------------------------------+----------+

| have_query_cache | YES |

| query_cache_limit | 1048576 | 如果单个查询结果大于这个值,则不Cache

| query_cache_min_res_unit | 4096 | 每次给QC结果分配内存的大小

| query_cache_size | 33554432 |

| query_cache_type | ON |

| query_cache_wlock_invalidate | OFF |

+------------------------------+----------+

query_cache_type表示是否开启了查询缓存,query_cache_size 默认是32M,可以适当调大一些。query_cache_limit设置单条查询最大可以缓存的数据量,默认1M。

如何查看 MySQL 的 query_cache 使用效率如何呢,可以用下列语句:

show status like '%Qcache%';

+-------------------------+----------+

| Variable_name | Value |

+-------------------------+----------+

| Qcache_free_blocks | 160 | 目前还处于空闲状态的 Query Cache中内存 Block 数目,数目大说明可能有碎片。FLUSH QUERY CACHE会对缓存中的碎片进行整理,从而得到一个空闲块。

| Qcache_free_memory | 23147296 | 缓存中的空闲内存总量。

| Qcache_hits | 52349 | 缓存命中次数。

| Qcache_inserts | 8827 | 缓存失效次数。

| Qcache_lowmem_prunes | 0 | 缓存出现内存不足并且必须要进行清理以便为更多查询提供空间的次数。这个数字最好长时间来看;如果这个数字在不断增长,就表示可能碎片非常严重,或者内存很少。

| Qcache_not_cached | 2446 | 没有被cache和不适合进行缓存的查询的数量,通常是由于这些查询不是SELECT语句以及由于query_cache_type设置的不会被Cache的查询。如show,use,desc

| Qcache_queries_in_cache | 5234 | 当前被cache的SQL数量。

| Qcache_total_blocks | 10796 | 缓存中块的数量。

+-------------------------+----------+

show global status like 'Com_select';

+---------------+-------+

| Variable_name | Value |

+---------------+-------+

| Com_select | 12592 |

+---------------+-------+

com_select等于qcache_inserts(缓存失效) + qcache_not_cache(没有缓存) + 权限检查错误的查询。

可以根据上面的数据,来查看我们的缓存设置是否合理:

缓存碎片率 = Qcache_free_blocks / Qcache_total_blocks * 100%

如果碎片率太高,比如20% ,可以FLUSH QUERY CACHE整理缓存碎片,或者试试减小query_cache_min_res_unit,如果你的查询都是小数据量的话。

查询缓存利用率 = (query_cache_size – Qcache_free_memory) / query_cache_size * 100%

查询缓存利用率在25%以下的话说明query_cache_size设置的过大,可适当减小;查询缓存利用率在80%以上而且Qcache_lowmem_prunes > 50的话说明query_cache_size可能有点小,要不就是碎片太多。

Mysql的查询缓存命中率,一般用如下数据进行计算:

命中率 ≈ qcache_hits / (qcache_hits + com_select)

有一些提高命中率的技巧:1:查询和存储的字符集相同 。2:SQL语句尽量固定(SQL语句避免随机数,秒数等) 。3 加大缓存空间 。4:适当分表,动静分离。

Mysql 还有一些其它缓存设置,比如打开表缓存(table_cache),临时表缓存(tmp_table_size)等,这些缓存机制经过合理配置后都能提高系统性能。

六:总结

缓存对于我们系统性能的影响无处不在,合理利用缓存是提升性能的一大法宝。本文总结了各个层次的缓存应用。当然实际用到的缓存并不仅仅只有这些,例如还有静态文件的CDN缓存,网络请求的DNS缓存等。缓存的使用,是我们实际工作中必须掌握的技能。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-04-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 nginx 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档