memcache的item key和序列化

 

一个完整的item长度是键长+值长+后缀长+item结构大小(32字节),item操作就是根据这个长度来计算slabclassid的。 
+---------------------------------------+ 
| key-value | cas | suffix | item head | 
+---------------------------------------+

其中suffix的格式是 flag vlen flagit_flagvlenvalue的长度;

key默认最大长度为250,需要修改源码才能扩展;

#define KEY_MAX_LENGTH 250

Item最大长度默认为1M,可通过启动选项-I修改;

-I  Override the size of each slab page. Adjusts max item size (default: 1mb, min: 1k, max: 128m)

MC通过hash表维护item的查找,添加以及删除等操作,以查找为例,其API如下:

item *assoc_find(const char *key, const size_t nkey) {

    //采用hash算法将传入的key转换,然后从hash table中查找;

    uint32_t hv = hash(key, nkey, 0);

    item *it;

    unsigned int oldbucket;

 

    //倘若hash表正在扩容,须判断此时hash bucket的是在old还是在new table;通过 hv & hashmask(hashpower - 1) 或者hv & hashmask(hashpower)找出对应的hash table下标,

    if (expanding && (oldbucket = (hv & hashmask(hashpower - 1))) >= expand_bucket)

    {

        it = old_hashtable[oldbucket];

    } else {

        it = primary_hashtable[hv & hashmask(hashpower)];

    }

 

    item *ret = NULL;

int depth = 0;

    //为应对hash冲突,每个hash bucket都有一个链表,通过遍历该链表确定要找到的key

    while (it) {

        if ((nkey == it->nkey) && (memcmp(key, ITEM_key(it), nkey) == 0)) {

            ret = it;

            break;

        }

        it = it->h_next;

        ++depth;

    }

MEMCACHED_ASSOC_FIND(key, nkey, depth);

//向客户端返回item

    return ret;

}

注:hash表默认分配2^16bucket,当item数量 > bucket*1.5时扩容,每次扩容为原来的2倍;先申请相应内存,然后启动线程同步原hash表中数据;

MC只维护了hash tableslab,因此没有内置方法来遍历其下所有item,可通过stats命令来间接实现。

来源http://www.darkcoding.net/software/memcached-list-all-keys/

登录MCtelnet 127.0.0.1 11211

查看所有slab信息

stats items

STAT items:3:number 1

STAT items:3:age 498

STAT items:22:number 1

STAT items:22:age 498

END

注:这里的322slab id

依次列举各个slab,将100替换为0则表示列举所有key

stats cachedump 3 100

ITEM views.decorators.cache.cache_header..cc7d9 [6 b; 1256056128 s]

END

stats cachedump 22 100

ITEM views.decorators.cache.cache_page..8427e [7736 b; 1256056128 s]

END

现在可通过get key命令获取每个key对应的item内容。



Key的选择

应该根据业务类型而定,除了常见的数据命名,还可以选择如下方式:

封装SQL作为key

下面这个例子就是通过Cache缓存查询数据库结果的场景,把sql+userId作为一个key,相同用户第二次查询时就可以从缓存中直接提取:

sql1 = "SELECT * FROM user WHERE user_id = ?"

key  = ‘SQL:‘ . user_id . ‘:‘ . md5sum(sql1)

if (defined result = memcli:get(key)) {    

  return result

}

else {       

  handler = run_sql(sql1, user_id)      

  t[info] = handler:turn_into_an_array

  memcli:set(key, t, 5 * 60)       

  return t

}

2  Get-By-Group-Key

很多客户端在处理multiget多服务器请求时采用串行方式,会导致性能严重下降,解决办法:保证Multiget中的键只出现在一台服务器。 

譬如,用户名字(user:foo:name),用户年龄(user:foo:age)等数据在散列到多台服务器上时,不应按照完整的键名(user:foo:nameuser:foo:age)来散列的,而应按照特殊的键(foo)来散列的,这样就保证了相关的键只出现在一台服务器上。

PHPmemcache客户端提供

Memcached::getMultiByKey — Retrieve multiple items from a specific server

Memcached::setMultiByKey — Store multiple items on a specific server

来源:http://blog.51yip.com/php/729.html



序列化

序列化 (Serialization)将对象转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。

除了stringint/longmemcache还可以存储object类型,但是存储前需要将其先进行序列化,读取时再进行反序列化;

MC客户端默认自动开启了序列化和反序列化,也可以在set前使用marshal或其他工具进行序列化;

注:由于不同的Client实现的序列化方式不同,所以如果不同的语言使用同一个memcached来存取数据,可能会造成数据不一致的问题。

分别以pythonjava为例,

Python的客户端工具memcache.py

if isinstance(val, str):

  pass

elif isinstance(val, int):

  flags |= Client._FLAG_INTEGER

  val = "%d" % val

# force no attempt to compress this silly string.

  min_compress_len = 0

elif isinstance(val, long):

  flags |= Client._FLAG_LONG

  val = "%d" % val

# force no attempt to compress this silly string.

  min_compress_len = 0

else:

  flags |= Client._FLAG_PICKLE

  file = StringIO()

  pickler = self.pickler(file, protocol=self.pickleProtocol)

  if self.persistent_id:

    pickler.persistent_id = self.persistent_id

    pickler.dump(val)

    val = file.getvalue()

JAVA spymemcached client

byte[] b=null;

int flags=0;

if(o instanceof String) {

  b=encodeString((String)o);

} else if(o instanceof Long) {

  b=tu.encodeLong((Long)o);

  flags |= SPECIAL_LONG;

} else {

  b=serialize(o);

  flags |= SERIALIZED;

}

来源:http://www.tech126.com/memcached-client-serialize/

有时MC客户端自带的序列化算法可能不够高效,导致MC耗用更多的内存来存储,此时可以选择第三方工具来封装客户端序列化机制,譬如googleprotobuf

如下面这个例子http://my.oschina.net/pzh0819/blog/110460

登录MC通过stat slabs命令查看slab运行情况,大多数item都存储在STAT 15,chunk_size2320(2K),但是业务数据只是个Element对象,转化为xml长度仅仅只有700字节,从这可以看出memcache(客户端版本:net.spy memcached-2.4.2)客户端自带的序列化使存储的数据翻了将近3倍。通过将Element对象转为xml字符串存储到MC,缓存由以前的6G+减小到当前的2G(数据条数:300W+)


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。