什么是memcache
memcache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视 频、文件以及数据库检索的结果等。Memcache是danga.com的一个项目,最早是为 LiveJournal 服务的,最初为了加速 LiveJournal 访问速度而开发的,后来被很多大型的网站采用。目前全世界不少人使用这个缓存项目来构建自己大负载的网站,来分担数据库的压力。
为什么要遍历
目前,用到memcache的公司和网站也越来越多。Memcache的客户端操作一般都只提供了get,set等简单的操作,这些操作都是非常高效的。 虽然memcache是个key-value存储的系统,但是在某些时候,我们可能需要遍历memcache的数据。
如何遍历memcache
stats命令
memcache的stats命令包括:
- stats
- stats reset
- stats malloc
- stats maps
- stats sizes
- stats slabs
- stats items
- stats cachedump slab_id limit_num
- stats detail [on|off|dump]
通过命令完成遍历
通过这些stats命令我们就可以完成memcache存储的内容的遍历,OK,下面我们通过telnet直接连接到memcache通过这些命令来完成相关的操作。
telnet到192.168.15.225(局域网测试机器)的memcache服务器
执行stats items命令,可以看到出现 很多的items行。
贴上一段php实现的遍历memcache数据的代码,其他语言可以参考代码自己实现。
- <?php
- $host='localhost';
- $port=11211;
- $mem=new Memcache();
- $mem->connect($host,$port);
- $items=$mem->getExtendedStats ('items');
- $items=$items["$host:$port"]['items'];
- foreach($items as $key=>$values){
- $number=$key;
- $str=$mem->getExtendedStats ("cachedump",$number,0);
- $line=$str["$host:$port"];
- if( is_array($line) && count($line)>0){
- foreach($line as $key=>$value){
- echo "<pre>".$key.'=>';
- print_r($mem->get($key));
- echo "</pre><br/>";
- }
- }
- }
- ?>
Memcached如何实现高性能批量删除,插入
Memcached 的批量删除,向来是 Memcached 使用者很头疼的事情,因为 Memcached 采取的缓存方案是哈希表结构,所以没有办法实现 delete from tablename where key like ‘%XX%’ 类似这样的批量删除功能。所以不得不自己采取一些策略实现批量删除。常见的删除方案主要有以下两种:
第一种方案:通过记录 KEY 到 DB 然后循环删除。
其实这也是最常规的删除方法,最容易想到,可能大部分人首先都会这么考虑。具体方法如下:
1. 添加业务数据时,在向 Memcached 缓存 set 数据的时候,将其 key 记录到数据库(或内存中,或文件中,由于数据量大,内存中基本不可行),也就是每 set 一条数据到 Memcached 缓存中,就向记录 key 的数据表(这类专门用于辅助删除缓存的表可以按业务分类建表,为了方便,我们下文简称 keytable, 因为不可能所有的 key 都用一张表,即使按业务分类建,数据量也是很庞大的) insert 一条记录。数据表结构可以设计为这两个字段: key( 对应缓存 key), id( 对应业务实体 id) ,这里的存储 id 主要是方便批量查询出需要从缓存删除的 KEY 时方便 , 也可以根据实际业务设计。
2. 删除业务数据时,首先从数据库的业务数据表删除数据,然后从 keytable 中查询出 keyList. 查询语句类似, select key from keytable where 条件 ,再然后循环 keyList 从 Memcached 缓存中 remove 掉相应的记录,最后删除 keytable表中相应的记录,删除语句类似 delete key from keytable where 条件.
这种方法的优点:方案设计思路简单,符合常规思维,而且能达到精确删除的目的。
这种方法的缺点:删除代价比较高,需要数据库或其他存储介质辅助,而且实际应用中,缓存量是很大的,会导致大量频繁的对 keytable 表的 insert 操作,性能问题将会很严重,需要根据具体情况运用。
第二种方案 : 利用 Memcached 的 LRU 算法进行懒清理
这种方案主要是利用 Memcached 缓存根据 LRU 算法进行定期自动清理不用或少用的缓存,通过对 key 增加版本管理来实现。
该方案的具体方法如下(拿商品数据举例说明):
1. 对缓存中的每个 key 进行版本管理,比如缓存商品数据的 key 为 product_10001.0, 这里的 product 表示商品这类业务数据, product_10001 表示具体的商品对象 10001 , 0 表示版本号。
2. 在 Memcached 中维护一版本记录数据,这个很简单,比如整个系统只有商品,订单,用户这三类缓存数据,则在Memcached 中只维护三个 key ,类似 product_version,book_version,user_version. 他们的取值范围为 0-99 之间循环,之所有循环是防止数据版本多了无限增大,管理麻烦,而且数字大了占用空间大(要对应到每个 key 的值)。
3. 删除商品数据时,首先从数据库删除对应商品数据的记录。然后更新对应商品的版本: product_version = product_version+1 (记得当 product_version=99 时, product_version=0 )。
4. 从 Memcached 中 get 数据时,需要 get 两次,首次按 get 对应业务数据的版本号,如 product_version ,然后根据数据本身的 key+ 最新的 product_version 作为真正的 key 去取数据。这样删除了的数据自动为过期数据, Memcached 会根据 LRU 算法清理。
5. 向 Memcached 中 set 数据时,也是先取最新的 product_version ,然后将数据本身的 key+product_version 作为 key保存数据到缓存。
这种方案的优点:减少了批量删除从 Memcached 真正删除数据的麻烦。利用 Memcached 自身的特点解决删除问题,符合 Memcached 的设计思想。
这种方案的缺点: a. 每次 set 数据都要,先 get 版本,然后 set, 性能上差不多降低了 1 倍,虽然性能方面还是不错。不过这个问题好像没有办法解决,原先考虑在客户端缓存版本,因为版本毕竟不是时时都变,但是考虑到版本数据时多进程间共享数据,所以不能这样做,每次必须从 Memcached 中取版本。否则难以保证是最新的。 b. 这种方案对内存上有点浪费,虽然 Memcached 会根据 LRU 算法清理低版本的垃圾数据,但是毕竟不是实时清理,所以必然浪费内存,所以内存不足的情况下,要仔细考虑采取此方案。