分布式缓存系统 Memcached CAS协议
Memcached在1.2.4版本后新增了CAS(Check and Set)协议,主要用于并发控制:memcached中同一个item同时被多个线程(多个客户端)更改的并发问题。CAS协议最本质的东西——版本号,即将每个item都关联一个全局唯一的编号,从而利用该唯一的编号来判断item数据在某个线程操作期间有无被其他的线程所更改(每次更改版本号都会改变,因此可作为判断的标识)。
如果不采用CAS,则有如下的情景:
第一步,A取出数据对象X;
第二步,B取出数据对象X;
第三步,B修改数据对象X,并将其放入缓存;
第四步,A修改数据对象X,并将其放入缓存。
我们可以发现,第四步中会产生数据写入冲突。
如果采用CAS协议,则是如下的情景。
第一步,A取出数据对象X,并获取到CAS-ID1;
第二步,B取出数据对象X,并获取到CAS-ID2;
第三步,B修改数据对象X,在写入缓存前,检查CAS-ID与缓存空间中该数据的CAS-ID是否一致。结果是“一致”,就将修改后的带有CAS-ID2的X写入到缓存。
第四步,A修改数据对象Y,在写入缓存前,检查CAS-ID与缓存空间中该数据的CAS-ID是否一致。结果是“不一致”,则拒绝写入,返回存储失败。
可以通过重试,或者其他业务逻辑解决第四步设置失败的问题。
具体的,在Memcached中,每个key关联都一个64-bit长度的long型惟一数值,表示该key对应value的版本号。这个数值由Memcached server产生,从1开始,且同一Memcached server中不会重复。在两种情况下这个版本数值会加1:新增一个key-value对 和 对某已有key对应的value值更新成功。删除item,而版本值不会减小。
可由如下例子看出:
MemcachedClient client = new MemcachedClient();
client.set("fKey", "fValue");
//第一次set, 在Memcached server中会维护fKey对应的value的版本号,假设是548;
client.set("fKey", "sValue");
//再次set,则这个fKey对应的value的版本号变为549;
CASValue casValue = client.gets("fKey");
//这样就可以得到对应key的cas版本号和实际value(各个Memcached client都有类似的对象表示,名字可能不一样,但效果类同),如 casValue.getValue = "sValue",casValue.getCas=549;
注:get命令返回给定key的value值。 而gets则会返回给定key的value和cas版本号值。如下图:

其中,先set 一次name,再gets name的返回值为:“VALUE name 0 3 2", 然后再进行一次set name, 这时gets name的返回值为”VALUE name 0 3 3“,最后一个字段由2变为了3,即版本号因为set更改了value值而被增加了一,而get name的返回值为”VALUE name 0 3“,与gets的返回值相比,少最后的版本号的字段。
CAS协议在并发控制中的具体应用:
一个memcached server在有多个额客户端时,分析下多个client并发set同一个key的场景。如clientA想把当前key的value set为"x",且操作成功;clientB却把当前key的value值由"x"覆盖set为"y",这时clientA再根据key去取value时得到"y"而不是期望的"x",它使用这个值,但不知道这个值已经被其它线程修改过,就可能会出现问题。
而CAS协议正是用于解决这种并发修改问题。有线程试图修改当前key-value对的value时,先由gets方法得到item的版本号,操作完成提交数据时,则先比较获取的版本号与当前item key中的版本号是否一致,如果是相同的,则提交数据,完整set等更改操作。反之,如果不一致,则说明在该线程对item操作过程中,这个key-value对被其它线程更改过(当然也就更改了版本号),于是放弃此次修改(乐观锁概念)。
Memcached默认是打开cas属性的,每次执行更改操作后,存储数据时,都会生成其cas值并和item一起尝试这存储(是否存储成功,需要有版本号cas值是否一致来决定)如果操作成功则更改原版本号为该cas值,否则放弃本次操作。在进行gets操作会返回系统生成的cas值。
看下在存储item时的操作函数stor_item的相关代码:
//为新的item生成cas值
uint64_t get_cas_id(void)
{
static uint64_t cas_id = 0;
return ++cas_id;
}
//执行cas存储时执行的判断逻辑,
else if (ITEM_get_cas(it) == ITEM_get_cas(old_it))//版本号cas值一致
{
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.slab_stats[old_it->slabs_clsid].cas_hits++;
pthread_mutex_unlock(&c->thread->stats.mutex);
item_replace(old_it, it, hv);//执行存储逻辑
stored = STORED;
}
else //版本号cas值不一致,不进行实际的存储
{
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.slab_stats[old_it->slabs_clsid].cas_badval++; //更新统计信息
pthread_mutex_unlock(&c->thread->stats.mutex);
if (settings.verbose > 1)
{
//打印错误日志
fprintf(stderr, "CAS: failure: expected %llu, got %llu\n",
(unsigned long long) ITEM_get_cas(old_it),
(unsigned long long) ITEM_get_cas(it));
}
stored = EXISTS;
}
当因为cas值冲突,而不能完成对item的更改操作时,可以通过比如重试等方式,待没有其他线程同时来更改该item时,则能顺利完成更改操作。
分布式缓存系统 Memcached CAS协议的更多相关文章
- 分布式缓存系统 Memcached 整体架构
分布式缓存系统 Memcached整体架构 Memcached经验分享[架构方向] Memcached 及 Redis 架构分析和比较
- [Memcached]分布式缓存系统Memcached在Asp.net下的应用
Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached ...
- 分布式缓存系统Memcached在Asp.net下的应用
Memcached 是一个高性能的分布式内存对象缓存系统.用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来降低读取数据库的次数,从而提高动态.数据库驱动站点的速度. Memcache ...
- 分布式缓存系统 Memcached 快速入门
Memcached介绍 官网地址 Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提 ...
- 分布式缓存系统Memcached简介与实践
缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够灵 ...
- 分布式缓存系统Memcached简介与实践(.NET memcached client library)
缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够灵 ...
- (转)C# 中使用分布式缓存系统Memcached
转自:http://blog.csdn.net/devgis/article/details/8212917 缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了 ...
- 分布式缓存系统Memcached简介与以及在.net下的实践(转)
缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够灵 ...
- php分布式缓存系统 Memcached 入门
Memcached 是一个分布式的缓存系统, 但是 Memcachd 到底是什么意思,有什么作用呢?缓存一般用来保存一些经常被存取的数据和资源(例如:浏览器会将访问过的网页会话缓存起来),因为通过缓存 ...
随机推荐
- Java实现的一个小说采集程序
被标题吸引进来的不要骂我. 只是一个简单的实现,随手写了来下载一部喜欢的小说的.示例中的小说只是示例,不是我的菜. 使用了jsoup.挺好用的一个工具. 有需要的话,参考下自己改吧.挺简单的,是吧. ...
- 在线演示demo
*{display:none} 仿微博添加和删除动画 body{} input,button,select,textarea{outline:none;} .sdiv{width:400px;} .b ...
- 门户diy实现翻页功能的方法
1.打开 \source\class\block\portal\block_article.php 文件 找到 function getdata($style, $parameter) 修改为 fun ...
- Apache Tomcat 服务因 0 (0x0) 服务性错误而停止
操作系统:Windows Server 2003 SP2/win7 64JDK 版本为 6.0 update 6 安装在 D:\Java 目录下Tomcat 版本为 6.0.16 安装 D:\Tomc ...
- python 爬虫003-正则表达式简单介绍
正则表达式,简单的说就是用一个“字符串”来描述一个特征,然后去验证另外一个“字符串”是否符合这个特征. 正则表达式在线测试工具 http://tool.chinaz.com/regex 实例一,判断字 ...
- 创建CMD启动环境
我们可以用一个cmd文件,通过doskey命令模拟linux下的alias,指定一些我们习惯的命令名,比如: env.cmd @echo off doskey alias=doskey /macros ...
- ipmitool 查看硬件信息
[root@75-6-25-yf-core ~]# cat /var/log/mcelog MCE 0HARDWARE ERROR. This is *NOT* a software problem! ...
- opencv:图像的掩码操作
示例代码: #include <opencv.hpp> using namespace cv; int main() { Mat src = imread("005.jpg&qu ...
- json结构更改的方法 把date有数据的分类
data=[ { "content": "如何走进智障儿童的内心", "title": "如何走进智障儿童的内心", & ...
- 【android】下载文件至本应用程序的file目录或者sdcard
一.判断是否有sdcard卡 //判断是否有SD卡 //ture:有SD卡 //false:没有SD卡 public boolean avaiableMedia(){ String status ...