Memcached CAS详解

Memcached CAS详解

什么是cas

Memcached于1.2.4版本新增CAS协议,类同于Java并发包中CAS(Compare and Set)原子操作,用来处理同一item被多个线程更改过程的并发问题.

基本原理

基本原理非常简单,简而言之就是”版本号”.每个存储的数据对象都有一个版本号.在Memcached中,每个key关联有一个64bit长度的long型唯一数值,表示该key对应value的版本号.

这个数值由Memcached产生,从1开始,且同一Memcached不会重复,在两种情况下这个版本数值会加,即新增与更新,而删除item版本值不会减小.

未使用cas协议
1. A取出数据对象x保存至本地
2. B取出数据对象x保存至本地
3. B修改x为数据对象y,并将其放入缓存
4. A修改x为数据对象z,并将其放入缓存
5. B想取回对象,发现不是y而是z,此处会发生数据写入冲突.

使用cas协议
1. A取出数据对象x,并获取到CAS-ID
2. B取出数据对象x,并获取到CAS-ID
3. B修改数据对象y,在写入缓存前,发现CAS-ID与缓存空间中该数据的CAS-ID是一致,就将修改后的带有CAS-ID2的y写入到缓存.
4. A修改数据对象z,在写入缓存前,发现CAS-ID与缓存空间中该数据的CAS-ID2不一致,则拒绝写入,返回存储失败.

示例代码

这里我们没有使用常用的spymemcached,而是使用更稳定,性能更好的java-memcached-client

public static void main(String[] args) {

    String[] serverlist = {"localhost:11211"};

    // 初始化memcached
    SockIOPool pool = SockIOPool.getInstance("test");
    pool.setServers(serverlist);

    pool.setInitConn(5);
    pool.setMinConn(5);
    pool.setMaxConn(50);

    pool.setNagle(false);
    pool.initialize();

    // 获取实例
    MemCachedClient mc = new MemCachedClient("test");
    String key = "abc";
    out.println("store:" + mc.set(key, "test string"));

    Bench b1 = new Bench(key, 2000);
    Bench b2 = new Bench(key, 5000);

    b1.start();
    b2.start();

}

private static class Bench extends Thread {

    String key;

    long sleep;

    public Bench(String key, long sleep) {
        this.key = key;
        this.sleep = sleep;
    }

    @Override
    public void run() {
        MemCachedClient mc = new MemCachedClient("test");

        // 此方法不同于get方法 获取MemcachedItem对象
        MemcachedItem item = mc.gets(key);
        out.println(
            Thread.currentThread().getName() + " value:" + item.getValue() + " cas:" + item.getCasUnique());
        try {
            Thread.sleep(sleep);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        out.println(mc.cas(key, item.getValue() + String.valueOf(sleep), item.getCasUnique()));
    }
}

当然,默认情况下如果出现写入冲突,memcached是不会将最后的值写入,所以如果需要保证强一致性,还是需要使用do while循环,保证最后的值一定被更新至memcached中.

原文地址

阿土’s Blog—— http://www.pigg.co/memcached-cas-detail.html

发表评论

电子邮件地址不会被公开。