看一粒沙中的世界,
一朵野花中的天堂。
把无限握于掌中,
把永恒握于瞬间。
——
威廉
•
布莱克
开始讨论缓存之前
,
让我们先来讨论讨论另外一个问题
:
理论和实践
.
从
ahuaxuan
接触的程序员来看
,
有的程序员偏实践
,
有的程序员偏理论
,
但
是这都是不好的行为
,
理论和实践同样重要
,
我们在做很多核心的算法的时候
,
没有理论根本无从下手
,
而在我们多年的实践中
,
不总结理论就不能加深自己的理
解
.
所以理论和实践同等重要
.
缓存是当今各种软件或者硬件系统中不可缺少的技术之一
,
所以对每个程序员来说都显得异常重要
,
对
ahuaxuan
来说亦是如此
.
如果说用
dfa
实现文字过滤是从理论到实践
,
那么本文便是从实践中总结出得理论
.
在讨论缓存功能之前
,
我们首先来了解一下缓存这个东西本身
.ahuaxuan
根据自己的经验把缓存问题细分为
4
类小问题
.
1
缓存为什么要存在
?
2
缓存可以存在于什么地方
?
3
缓存有哪些属性
?
4
缓存介质
?
搞清楚这
4
个问题
,
那么我们就可以随意的通过应用的场景来判断使用何种缓存了
.
1.
缓存为什么要存在
?
一般情况下
,
一个网站
,
或者一个应用
,
它的一般形式是
,
浏览器请求应用服务器
,
应用服务器做一堆计算后再请求数据库
,
数据库收到请求后再作一堆计
算后把数据返回给应用服务器
,
应用服务器再作一堆计算后把数据返回给浏览器
.
这个是一个标准流程
.
但是随着互连网的普及
,
上网的人越来越多
,
网上的信息量
也越来越多
,
在这两个越来越多的情况下
,
我们的应用需要支撑的并发量就越来越多
.
然后我们的应用服务器和数据库服务器所做的计算也越来越多
,
但是往往我们
的应用服务器资源是有限的
,
数据库每秒中接受请求的次数也是有限的
(
谁叫俺们的硬盘转速有限呢
).
如果利用有限的资源来提供尽可能大的吞吐量呢
,
一个办
法
:
减少计算量
,
缩短请求流程
(
减少网络
io
或者硬盘
io),
这时候缓存就可以大展手脚了
.
缓存的基本原理就是打破上图中所描绘的标准流程
,
在这个标准流
程中
,
任何一个环节都可以被切断
.
请求可以从缓存里取到数据直接返回
.
这样不但节省了时间
,
提高了响应速度
,
而且也节省了硬件资源
.
可以让我们有限的硬件
资源来服务更多的用户
.
2
缓存可以存在于什么地方
?
Java 代码
数据库 à 分过层的 app- à 浏览器和 app 之间 --- à 浏览器 ---
数据库 à 分过层的 app- à 浏览器和 app 之间 --- à 浏览器 ---
在上图中
,
我们可以看到一次请求的一般流程
,
下面我们重新绘制这张图
,
让我们的结构稍微复杂一点点
.
(
将
app
分层
)
数据库
à
分过层的
app-
à
浏览器和
app
之间
---
à
浏览器
---
理论上来将
,
请求的任何一个环节都是缓存可以作用的地方
.
第一个环节
,
浏览器
,
如果数据存在浏览器上
,
那么对用户来说速度是最快的
,
因为这个时候
根本无需网络请求
.
第二个环节
,
浏览器和
app
之间
,
如果缓存加在这个地方
,
那么缓存对
app
来说是透明的
.
而且这个缓存中存放的是完整的页面
.
第三个节
点
,app
中本身就有几个层次
,
那么缓存也可以放在不同的层次上
,
这一部分是情况或者场景比较复杂的部分
.
选择缓存时需要谨慎
.
第四个环节
,
数据库中也可
以有缓存
,
比如说
mysql
的
querycache.
那么也就是说在整个请求流程的任何一点
,
我们都可以加缓存
.
但是是所有的数据都可以放进缓存的吗
.
当然不是
,
需要放进缓存的数据总是有一些特征的
,
要清楚的判断数据是否可以被缓存
,
可以被怎样缓存就必须要从数据的变化特征下手
.
数据有哪些变化特征
?
最简单的就是两种
,
变和不变
.
我们都知道
,
不会变化的数据不需要每次都进行计算
.
问题是难道所有的数据理论上来讲都会变化
,
变化是世界永恒的主题
.
也就是说我们把数据分为变和不变两种是不对的
,
那么就让我们再加一个条件
:
时间
.
那么我们就可以把数据特征总结为一段时间内变或者
不变
.
那么根据这个数据特征
,
我们就可以在合适的位置和合适的缓存类型中缓存该数据
.
3
缓存有哪些属性
从面向对象的角度来看
,
缓存就是一个对象
,
那么是对象
,
必然有属性
.
那么下面我们来探讨一下缓存有哪些属性
.
以下列举我们常用到的
3
个属性
.
(1)
命中率
命中率是指请求缓存次数和缓存返回正确结果次数的比例
.
比例越高
,
就证明缓存的使用率越高
.
命中率问题是缓存中的一个非常重要的问题
,
我们都希望自己缓存的命中率能达到
100%,
但是往往事与愿违
,
而且缓存命中率是衡量缓存有效性的重要指标
.
(2)
最大元素
缓存中可以存放得最大元素得数量
,
一旦缓存中元素数量超过这个值
,
那么将会起用缓存清空策略
,
根据不同的场景合理的设置最大元素值往往可以一定程度上提高缓存的命中率
.
从而更有效的时候缓存
.
(3)
清空策略
1 FIFO
,
first in first out
,最先进入缓存得数据在缓存空间不够情况下
(
超出最大元素限制时
)
会被首先清理出去
2 LFU
,
Less Frequently Used
,一直以来最少被使用的元素会被被清理掉。这就要求缓存的元素有一个
hit
属性,在缓存空间不够得情况下
,hit
值最小的将会被清出缓存。
2 LRU
,
Least Recently Used
,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
4
缓存介质
从硬件介质上来将无非就是两种
,
内存和硬盘
(
对应应用层的程序来讲不用考虑寄存器等问题
).
但是往往我们不会从硬件上来划分
,
一般的划分方法是从技术上划分
,
可以分成几种
,
内存
,
硬盘文件
.
数据库
.
(1)
内存
.
将缓存放在内存中是最快的选择
,
任何程序直接操作内存都比操作硬盘要快的多
,
但是如果你的数据要考虑到
break down
的问题
,
因为放在内存中的数据我们称之为没有持久话的数据
,
如果硬盘上没有备份
,
机器
down
机之后
,
很难或者无法恢复
.
(2)
硬盘
.
一般来说
,
很多缓存框架会结合使用内存和硬盘
,
比如给内存分配的空间有满了之后
,
会让用户选择把需要退出内存空间的数据持久化到硬盘
.
当然也选择直接把数据放一份到硬盘
(
内存中一份
,
硬盘中一份
,down
机也不怕
).
也有其他的缓存是直接把数据放到硬盘上
.
(3)
数据库
.
说到数据库
,
可能有的人会想
,
之前不是讲到要减少数据库查询的次数
,
减少数据库计算的压力吗
,
现在怎么又用数据库作为缓存的介质了呢
.
这是因为数
据库又很多种类型
,
比如
berkleydb,
这种
db
不支持
sql
语句
,
没有
sql
引擎
,
只是
key
和
value
的存储结构
,
所以速度非常的快
,
在当代一
般的
pc
上
,
每秒中十几
w
次查询都是没有问题的
(
当然这个是根据业务特征来决定的
,
如果您访问的数据在分布上是均匀的
,
那
ahuaxuan
可不能保证这个
速度了
).
除了缓存介质之外
,ahuaxuan
根据缓存和应用的耦合程度将其划分为
local cache
和
remote cache.
Local cache
是指包含在应用之中的缓存组件
.
而
remote cache
指和应用解耦在应用之外的缓存组件
.
典型的
local cache
有
ehcache,oscache,
而
remote cache
有大名鼎鼎的
memcached.
Localcache
最大的优点是应用和
cache
的时候是在同一个进程内部
,
请求缓存非常快速
,
完全不需要网络开销等
.
所以单应用
,
不需要集群
或者集群情况下
cache node
不需要相互通知的情况下使用
local cache
比较合适
.
这也是
java
中
ehcache
和
oscache
这么流行的原因
.
但是
Local cache
是有一定的缺点的
,
一般这种缓存框架
(
比如
java
中的
ehcache
或者
oscache)
都是
local cache.
也就是跟着应用程序走的
,
多个应用程序无法直接共享缓存
,
应用集群的情况下这个问题更加明显
,
当然也有的缓存组件提供了集群节点相互通知缓存
更新的功能
,
但是由于这个是广播
,
或者是环路更新
,
在缓存更新频繁的情况下会导致网络
io
开销非常大
,
严重的时候会影响应用的正常运行
.
而且如果缓存中数
据量较大得情况下使用
localcache
意味着每个应用都有一份这么大得缓存
,
着绝对是对内存的浪费
.
所以这个情况下
,
往往我们会选择
remote cache,
比如
memcached.
这样集群或者分布式的情况下各个应用都可以共享
memcached
中的数据
,
这些应用都通过
socket
和基于
tcp/ip
协议上层的
memcached
协议直接连接到
memcached,
有一个
app
更新了
memcached
中的值
,
所有的应用都能拿到最新的
值
.
虽然这个时候多了很多了网络上的开销
,
但是往往这种方案要比
localcache
广播或环路更新
cache
节点要普遍的多
,
而且性能也比后者高
.
由于
数据只需要保存一份
,
所以也提高了内存的使用率
.
通过以上分析可以看出
,
不管是
local cache,
还是
remote cache
在缓存领域都有自己的一席之地
,
所以
ahuaxuan
建议在选择或者使用缓存时一定要根据缓存的特征和我们的业务场景准确判断使用何种缓存
.
这样才能充分发挥缓存的功能
.
Ahuaxuan
认为
,
缓存的使用是架构师的必备技能
,
好的架构师能够根据数据的类型
,
业务的场景来准确的判断出使用何种类型的缓存
,
并且如何使
用这种类型的缓存
.
在缓存的世界里也没有银弹
,
目前还没有一种缓存可以解决任何的业务场景或者数据类型
,
如果这种技术出现了
,
那架构师就又更不值钱了
.
呵
呵
.
本文是
ahuaxuan
从自己的实践中总结出来的一些小小的心得
,
未参考任何文章
,,
所以可能未必好
,
未必全面
,
未必令您满意
,
欢迎拍砖
.
最后说一说写这篇文章的初衷
,
周末有人让我说说我对缓存的理解
,
我的回答的是对缓存的理解无法用一句话来表述
,
起码写
5
篇文章
.
那本文只是第一篇
.
待续
-------------