工作原理:
Memcached处理
![](/icons/73536de.gif)
原子是每
![](/icons/73536yi.gif)
个(key
![](/icons/73536dou.gif)
value)对(以下简称kv对)
![](/icons/73536dou.gif)
key会通过
![](/icons/73536yi.gif)
个hash算法转化成hash-key
![](/icons/73536dou.gif)
便于查找、对比以及做到尽可能
![](/icons/73536de.gif)
散列
![](/icons/73536dou2.gif)
同时
![](/icons/73536dou.gif)
memcached用
![](/icons/73536de.gif)
是
![](/icons/73536yi.gif)
个 2级散列
![](/icons/73536dou.gif)
通过
![](/icons/73536yi.gif)
张大hash表来维护
![](/icons/73536dou2.gif)
Memcached有两个核心组件组成:服务端(ms)和客户端(mc)
![](/icons/73536dou.gif)
在
![](/icons/73536yi.gif)
个memcached
![](/icons/73536de.gif)
查询中
![](/icons/73536dou.gif)
mc先通过计算key
![](/icons/73536de.gif)
hash值来确定kv对所处在
![](/icons/73536de.gif)
ms位置
![](/icons/73536dou2.gif)
当ms确定后
![](/icons/73536dou.gif)
客户端就会发送
![](/icons/73536yi.gif)
个查询请求给对应
![](/icons/73536de.gif)
ms
![](/icons/73536dou.gif)
让它来查找确切
![](/icons/73536de.gif)
数据
![](/icons/73536dou2.gif)
![](/icons/73536yinwei.gif)
这的间没有交互以及多播协议
![](/icons/73536dou.gif)
所以 memcached交互带给网络
![](/icons/73536de.gif)
影响是最小化
![](/icons/73536de.gif)
![](/icons/73536dou2.gif)
举例介绍说明:考虑以下这个场景
![](/icons/73536dou.gif)
有 3个mc分别是X
![](/icons/73536dou.gif)
Y
![](/icons/73536dou.gif)
Z
![](/icons/73536dou.gif)
还有 3个ms分别是A
![](/icons/73536dou.gif)
B
![](/icons/73536dou.gif)
C:
设置kv对
X想设置key=”foo”,value=”seattle”
X拿到ms列表
![](/icons/73536dou.gif)
并对key做hash转化
![](/icons/73536dou.gif)
根据hash值确定kv对所存
![](/icons/73536de.gif)
ms位置
B被选中了
X连接上B
![](/icons/73536dou.gif)
B收到请求
![](/icons/73536dou.gif)
把(key=”foo”,value=”seattle”)存了起来
获取kv对
Z想得到key=”foo”
![](/icons/73536de.gif)
value
Z用相同
![](/icons/73536de.gif)
hash算法算出hash值
![](/icons/73536dou.gif)
并确定key=”foo”
![](/icons/73536de.gif)
值存在B上
Z连接上B
![](/icons/73536dou.gif)
并从B那边得到value=”seattle”
其他任何从X
![](/icons/73536dou.gif)
Y
![](/icons/73536dou.gif)
Z
![](/icons/73536de.gif)
想得到key=”foo”
![](/icons/73536de.gif)
值
![](/icons/73536de.gif)
请求都会发向B
![](/icons/73536class.gif)
="alignnone size-medium wp-image-85" height=423 alt="" src="http://www.crazycoder.cn/WebFiles/20091/cb3c679f-f42a-4e38-952a-40475b08c2b9.jpg" width=355>
Memcached服务器(ms)
内存分配默认情况下
![](/icons/73536dou.gif)
ms是用
![](/icons/73536yi.gif)
个内置
![](/icons/73536de.gif)
叫“块分配器”
![](/icons/73536de.gif)
组件来分配内存
![](/icons/73536de.gif)
![](/icons/73536dou2.gif)
舍弃c
![](/icons/73536jiajia.gif)
标准
![](/icons/73536de.gif)
malloc/free
![](/icons/73536de.gif)
内存分配
![](/icons/73536dou.gif)
而采用块分配器
![](/icons/73536de.gif)
主要目
![](/icons/73536de.gif)
是为了避免内存碎片
![](/icons/73536dou.gif)
否则操作系统要花费更多时间来查找这些逻辑上连续
![](/icons/73536de.gif)
内存块(实际上是断开
![](/icons/73536de.gif)
)
![](/icons/73536dou2.gif)
用了块分配器
![](/icons/73536dou.gif)
ms会轮流
![](/icons/73536de.gif)
对内存进行大块
![](/icons/73536de.gif)
分配
![](/icons/73536dou.gif)
并不断重用
![](/icons/73536dou2.gif)
当然由于块
![](/icons/73536de.gif)
大小各不相同
![](/icons/73536dou.gif)
当数据大小和块大小不太相符
![](/icons/73536de.gif)
情况下
![](/icons/73536dou.gif)
还是有可能导致内存
![](/icons/73536de.gif)
浪费
![](/icons/73536dou2.gif)
同时
![](/icons/73536dou.gif)
ms对key和data都有相应
![](/icons/73536de.gif)
限制
![](/icons/73536dou.gif)
key
![](/icons/73536de.gif)
长度不能超过250字节
![](/icons/73536dou.gif)
data也不能超过块大小
![](/icons/73536de.gif)
限制 --- 1MB
![](/icons/73536dou2.gif)
![](/icons/73536yinwei.gif)
mc所使用
![](/icons/73536de.gif)
hash算法
![](/icons/73536dou.gif)
并不会考虑到每个ms
![](/icons/73536de.gif)
内存大小
![](/icons/73536dou2.gif)
理论上mc会分配概率上等量
![](/icons/73536de.gif)
kv对给每个ms
![](/icons/73536dou.gif)
这样如果每个ms
![](/icons/73536de.gif)
内存都不太
![](/icons/73536yi.gif)
样
![](/icons/73536dou.gif)
那可能会导致内存使用率
![](/icons/73536de.gif)
降低
![](/icons/73536dou2.gif)
所以
![](/icons/73536yi.gif)
种替代
![](/icons/73536de.gif)
解决方案是
![](/icons/73536dou.gif)
根据每个ms
![](/icons/73536de.gif)
内存大小
![](/icons/73536dou.gif)
找出他们
![](/icons/73536de.gif)
最大公约数
![](/icons/73536dou.gif)
然后在每个ms上开n个容量=最大公约数
![](/icons/73536de.gif)
instance
![](/icons/73536dou.gif)
这样就等于拥有了多个容量大小
![](/icons/73536yi.gif)
样
![](/icons/73536de.gif)
子ms
![](/icons/73536dou.gif)
从而提供整体
![](/icons/73536de.gif)
内存使用率
缓存Cache策略当ms
![](/icons/73536de.gif)
hash表满了的后
![](/icons/73536dou.gif)
新
![](/icons/73536de.gif)
插入数据会替代老
![](/icons/73536de.gif)
数据
![](/icons/73536dou.gif)
更新
![](/icons/73536de.gif)
策略是LRU(最近最少使用)
![](/icons/73536dou.gif)
以及每个kv对
![](/icons/73536de.gif)
有效时限
![](/icons/73536dou2.gif)
Kv对存储有效时限是在mc端由app设置并作为参数传给ms
![](/icons/73536de.gif)
![](/icons/73536dou2.gif)
同时ms采用是偷懒替代法
![](/icons/73536dou.gif)
ms不会开额外
![](/icons/73536de.gif)
进程来实时监测过时
![](/icons/73536de.gif)
kv对并删除
![](/icons/73536dou.gif)
而是当且仅当
![](/icons/73536dou.gif)
新来
![](/icons/73536yi.gif)
个插入
![](/icons/73536de.gif)
数据
![](/icons/73536dou.gif)
而此时又没有多余
![](/icons/73536de.gif)
空间放了
![](/icons/73536dou.gif)
才会进行清除动作
缓存Cache数据库查询现在memcached最流行
![](/icons/73536de.gif)
![](/icons/73536yi.gif)
种使用方式是缓存Cache数据库查询
![](/icons/73536dou.gif)
下面举
![](/icons/73536yi.gif)
个简单例子介绍说明:
App需要得到userid=xxx
![](/icons/73536de.gif)
用户信息
![](/icons/73536dou.gif)
对应
![](/icons/73536de.gif)
查询语句类似:
“SELECT * FROM users WHERE userid = xxx”
App先去问cache
![](/icons/73536dou.gif)
有没有“user:userid”(key定义可预先定义约束好)
![](/icons/73536de.gif)
数据
![](/icons/73536dou.gif)
如果有
![](/icons/73536dou.gif)
返回数据;如果没有
![](/icons/73536dou.gif)
App会从数据库中读取数据
![](/icons/73536dou.gif)
并
![](/icons/73536diaoyong.gif)
cache
![](/icons/73536de.gif)
add
![](/icons/73536hanshu.gif)
![](/icons/73536dou.gif)
把数据加入cache中
![](/icons/73536dou2.gif)
当取
![](/icons/73536de.gif)
数据需要更新
![](/icons/73536dou.gif)
app会
![](/icons/73536diaoyong.gif)
cache
![](/icons/73536de.gif)
update
![](/icons/73536hanshu.gif)
![](/icons/73536dou.gif)
来保持数据库和cache
![](/icons/73536de.gif)
数据同步
![](/icons/73536dou2.gif)
从上面
![](/icons/73536de.gif)
例子我们也可以发现
![](/icons/73536dou.gif)
![](/icons/73536yi.gif)
旦数据库
![](/icons/73536de.gif)
数据发现变化
![](/icons/73536dou.gif)
我们
![](/icons/73536yi.gif)
定要及时更新cache中
![](/icons/73536de.gif)
数据
![](/icons/73536dou.gif)
来保证app读到
![](/icons/73536de.gif)
是同步
![](/icons/73536de.gif)
正确数据
![](/icons/73536dou2.gif)
当然我们可以通过定时器方式记录下cache中数据
![](/icons/73536de.gif)
失效时间
![](/icons/73536dou.gif)
时间
![](/icons/73536yi.gif)
过就会激发事件对cache进行更新
![](/icons/73536dou.gif)
但这的间总会有时间上
![](/icons/73536de.gif)
延迟
![](/icons/73536dou.gif)
导致app可能从 cache读到脏数据
![](/icons/73536dou.gif)
这也被称为狗洞问题
![](/icons/73536dou2.gif)
(以后我会专门描述研究这个问题)
数据冗余和故障预防从设计角度上
![](/icons/73536dou.gif)
memcached是没有数据冗余环节
![](/icons/73536de.gif)
![](/icons/73536dou.gif)
它本身就是
![](/icons/73536yi.gif)
个大规模
![](/icons/73536de.gif)
高性能cache层
![](/icons/73536dou.gif)
加入数据冗余所能带来
![](/icons/73536de.gif)
只有设计
![](/icons/73536de.gif)
复杂性和提高系统
![](/icons/73536de.gif)
开支
![](/icons/73536dou2.gif)
当
![](/icons/73536yi.gif)
个ms上丢失了数据的后
![](/icons/73536dou.gif)
app还是可以从数据库中取得数据
![](/icons/73536dou2.gif)
不过更谨慎
![](/icons/73536de.gif)
做法是在某些ms不能正常工作时
![](/icons/73536dou.gif)
提供额外
![](/icons/73536de.gif)
ms来支持cache
![](/icons/73536dou.gif)
这样就不会
![](/icons/73536yinwei.gif)
app从cache中取不到数据而
![](/icons/73536yi.gif)
下子给数据库带来过大
![](/icons/73536de.gif)
负载
![](/icons/73536dou2.gif)
同时为了减少某台ms故障所带来
![](/icons/73536de.gif)
影响
![](/icons/73536dou.gif)
可以使用“热备份”方案
![](/icons/73536dou.gif)
就是用
![](/icons/73536yi.gif)
台新
![](/icons/73536de.gif)
ms来取代有问题
![](/icons/73536de.gif)
ms
![](/icons/73536dou.gif)
当然新
![](/icons/73536de.gif)
ms还是要用原来ms
![](/icons/73536de.gif)
IP地址
![](/icons/73536dou.gif)
大不了数据重新装载
![](/icons/73536yi.gif)
遍
![](/icons/73536dou2.gif)
另外
![](/icons/73536yi.gif)
种方式
![](/icons/73536dou.gif)
就是提高你ms
![](/icons/73536de.gif)
节点数
![](/icons/73536dou.gif)
然后mc会实时侦查每个节点
![](/icons/73536de.gif)
状态
![](/icons/73536dou.gif)
如果发现某个节点长时间没有响应
![](/icons/73536dou.gif)
就会从mc
![](/icons/73536de.gif)
可用server列表里删除
![](/icons/73536dou.gif)
并对server节点进行重新hash定位
![](/icons/73536dou2.gif)
当然这样也会造成
![](/icons/73536de.gif)
问题是
![](/icons/73536dou.gif)
原本key存储在B上
![](/icons/73536dou.gif)
变成存储在C上了
![](/icons/73536dou2.gif)
所以此方案本身也有其弱点
![](/icons/73536dou.gif)
最好能和“热备份”方案结合使用
![](/icons/73536dou.gif)
就可以使故障造成
![](/icons/73536de.gif)
影响最小化
Memcached客户端(mc)Memcached客户端有各种语言
![](/icons/73536de.gif)
版本供大家使用
![](/icons/73536dou.gif)
包括java
![](/icons/73536dou.gif)
c
![](/icons/73536dou.gif)
php
![](/icons/73536dou.gif)
.net等等
![](/icons/73536dou.gif)
具体可参见memcached api page[2]
![](/icons/73536dou2.gif)
大家可以根据自己项目
![](/icons/73536de.gif)
需要
![](/icons/73536dou.gif)
选择合适
![](/icons/73536de.gif)
客户端来集成
缓存Cache式
Web应用
架构有了缓存Cache
![](/icons/73536de.gif)
支持
![](/icons/73536dou.gif)
我们可以在传统
![](/icons/73536de.gif)
app层和db层的间加入cache层
![](/icons/73536dou.gif)
每个app服务器都可以绑定
![](/icons/73536yi.gif)
个mc
![](/icons/73536dou.gif)
每次数据
![](/icons/73536de.gif)
读取都可以从ms中取得
![](/icons/73536dou.gif)
如果没有
![](/icons/73536dou.gif)
再从db层读取
![](/icons/73536dou2.gif)
而当数据要进行更新时
![](/icons/73536dou.gif)
除了要发送update
![](/icons/73536de.gif)
sql给db层
![](/icons/73536dou.gif)
同时也要将更新
![](/icons/73536de.gif)
数据发给mc
![](/icons/73536dou.gif)
让mc去更新ms中
![](/icons/73536de.gif)
数据
![](/icons/73536dou2.gif)
![](/icons/73536class.gif)
="alignnone size-medium wp-image-86" height=459 alt="" src="http://www.crazycoder.cn/WebFiles/20091/8a5c6773-a907-4a8a-a428-2c7a17270267.jpg" width=556>
假设今后我们
![](/icons/73536de.gif)
数据库可以和ms进行通讯了
![](/icons/73536dou.gif)
那可以将更新
![](/icons/73536de.gif)
任务统
![](/icons/73536yi.gif)
交给db层
![](/icons/73536dou.gif)
每次数据库更新数据
![](/icons/73536de.gif)
同时会自动去更新ms中
![](/icons/73536de.gif)
数据
![](/icons/73536dou.gif)
这样就可以进
![](/icons/73536yi.gif)
步减少app层
![](/icons/73536de.gif)
逻辑复杂度
![](/icons/73536dou2.gif)
如下图:
![](/icons/73536class.gif)
="alignnone size-medium wp-image-87" height=459 alt="" src="http://www.crazycoder.cn/WebFiles/20091/afd76f69-b4ef-42fa-8fd4-1f34cf02f8b6.jpg" width=556>
不过每次我们如果没有从cache读到数据
![](/icons/73536dou.gif)
都不得不麻烦数据库
![](/icons/73536dou2.gif)
为了最小化数据库
![](/icons/73536de.gif)
负载压力
![](/icons/73536dou.gif)
我们可以部署数据库复写
![](/icons/73536dou.gif)
用slave数据库来完成读取操作
![](/icons/73536dou.gif)
而master数据库永远只负责 3件事:1.更新数据;2.同步slave数据库;3.更新cache
![](/icons/73536dou2.gif)
如下图:
![](/icons/73536class.gif)
="alignnone size-medium wp-image-96" height=417 alt="" src="http://www.crazycoder.cn/WebFiles/20091/2a768611-4d1a-4f46-9d63-2a98653258f7.jpg" width=555>
以上这些缓存Cache式web架构在实际应用中被证明是能有效并能极大地降低数据库
![](/icons/73536de.gif)
负载同时又能提高web
![](/icons/73536de.gif)
运行性能
![](/icons/73536dou2.gif)
当然这些架构还可以根据具体
![](/icons/73536de.gif)
应用环境进行变种
![](/icons/73536dou.gif)
以达到区别硬件条件下性能
![](/icons/73536de.gif)
最优化
未来
憧憬Memcached
![](/icons/73536de.gif)
出现可以说是革命性
![](/icons/73536de.gif)
![](/icons/73536dou.gif)
第
![](/icons/73536yi.gif)
次让我们意识到可以用内存作为存储媒介来大规模
![](/icons/73536de.gif)
缓存Cache数据以提高
![](/icons/73536chengxu.gif)
![](/icons/73536de.gif)
性能
![](/icons/73536dou2.gif)
不过它毕竟还是比较新
![](/icons/73536de.gif)
东西
![](/icons/73536dou.gif)
还需要很多有待优化和改进
![](/icons/73536de.gif)
地方
![](/icons/73536dou.gif)
例如:
如何利用memcached实现cache数据库
![](/icons/73536dou.gif)
让数据库跑在内存上
![](/icons/73536dou2.gif)
这方面
![](/icons/73536dou.gif)
tangent software 开发
![](/icons/73536de.gif)
memcached_engine[3]已经做了不少工作
![](/icons/73536dou.gif)
不过现在
![](/icons/73536de.gif)
版本还只是处于实验室阶段
![](/icons/73536dou2.gif)
如何能方便有效
![](/icons/73536de.gif)
进行批量key清理
![](/icons/73536dou2.gif)
![](/icons/73536yinwei.gif)
现在key是散列在区别
![](/icons/73536de.gif)
server上
![](/icons/73536de.gif)
![](/icons/73536dou.gif)
所以对某类key进行大批量清理是很麻烦
![](/icons/73536de.gif)
![](/icons/73536dou2.gif)
![](/icons/73536yinwei.gif)
memcached 本身是
![](/icons/73536yi.gif)
个大hash表
![](/icons/73536dou.gif)
是不具备key
![](/icons/73536de.gif)
检索功能
![](/icons/73536de.gif)
![](/icons/73536dou2.gif)
所以memcached是压根不知道某
![](/icons/73536yi.gif)
类
![](/icons/73536de.gif)
key到底存了多少个
![](/icons/73536dou.gif)
都存在哪些server上
![](/icons/73536dou2.gif)
而这类功能在实际应用中却是经常用到
参考[1]. Memcached website: http://danga.com/memcached/
[2]. Memcached API Page: http://danga.com/memcached/apis.bml
[3]. memcached_engine: http://tangent.org/506/memcache_engine.html
TAG:
应用
![](/icons/73536chengxu.gif)
WEB
Web
高性能
构建
Memcached