memcache的item key和序列化

dbhelper發表於2015-03-01
 

一個完整的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命令來間接實現。

來源

登入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;

}

來源:

有時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+)


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8494287/viewspace-1444355/,如需轉載,請註明出處,否則將追究法律責任。

相關文章