【Memcache】常见问题总结

参考:http://www.cnblogs.com/jiaosq/p/5812250.html

memcached的基本设置

启动Memcache的服务器端

进入cmd命令行,输入telnet 127.0.0.1 11211(memcached默认端口为11211)

stats:使用stats命令查看当前memcache服务器的状态

# /usr/local/bin/memcached -d -m 10 -u root -l 192.168.0.200 -p 12000 -c 256 -P /tmp/memcached.pid

-d选项是启动一个守护进程,
-m是分配给Memcache使用的内存数量,单位是MB,我这里是10MB,
-u是运行Memcache的用户,我这里是root,
-l是监听的服务器IP地址,如果有多个地址的话,我这里指定了服务器的IP地址192.168.0.200,
-p是设置Memcache监听的端口,我这里设置了12000,最好是1024以上的端口,
-c选项是最大运行的并发连接数,默认是1024,我这里设置了256,按照你服务器的负载量来设定,
-P是设置保存Memcache的pid文件,我这里是保存在 /tmp/memcached.pid,

如果要结束Memcache进程,执行:

# kill `cat /tmp/memcached.pid`

为什么要运行 memcached ?

如果网站的高流量很大并且大多数的访问会造成数据库高负荷的状况下,使用 memcached 能够减轻数据库的压力。

适用memcached的业务场景?

1)如果网站包含了访问量很大的动态网页,因而数据库的负载将会很高。由于大部分数据库请求都是读操作,那么memcached可以显著地减小数据库负载。

2)如果数据库服务器的负载比较低但CPU使用率很高,这时可以缓存计算好的结果( computed objects )和渲染后的网页模板(enderred templates)。

3)利用memcached可以缓存 session数据 、临时数据以减少对他们的数据库写操作。

4)缓存一些很小但是被频繁访问的文件。

5)缓存Web ‘services’(非IBM宣扬的Web Services,译者注)或RSS feeds的结果.。

不适用memcached的业务场景?

1)缓存对象的大小大于1MB

Memcached本身就不是为了处理庞大的多媒体(large media)和巨大的二进制块(streaming huge blobs)而设计的。

2)key的长度大于250字符

3)虚拟主机不让运行memcached服务

如果应用本身托管在低端的虚拟私有服务器上,像vmware, xen这类虚拟化技术并不适合运行memcached。Memcached需要接管和控制大块的内存,如果memcached管理的内存被OS或 hypervisor交换出去,memcached的性能将大打折扣。

4)应用运行在不安全的环境中

Memcached为提供任何安全策略,仅仅通过telnet就可以访问到memcached。如果应用运行在共享的系统上,需要着重考虑安全问题。

5)业务本身需要的是持久化数据或者说需要的应该是database

能够遍历memcached中所有的item吗?

不能,这个操作的速度相对缓慢且阻塞其他的操作(这里的缓慢时相比memcached其他的命令)。memcached所有非调试(non-debug)命令,例如add, set, get, fulsh等无论memcached中存储了多少数据,它们的执行都只消耗常量时间。任何遍历所有item的命令执行所消耗的时间,将随着memcached中数据量的增加而增加。当其他命令因为等待(遍历所有item的命令执行完毕)而不能得到执行,因而阻塞将发生。

memcached是怎么工作的?

Memcached的高性能源于两阶段哈希(two-stage hash)结构。Memcached就像一个巨大的、存储了很多<key,value>对的哈希表。通过key,可以存储或查询任意的数据。 客户端可以把数据存储在多台memcached上。当查询数据时,客户端首先参考节点列表计算出key的哈希值(阶段一哈希),进而选中一个节点;客户端将请求发送给选中的节点,然后memcached节点通过一个内部的哈希算法(阶段二哈希),查找真正的数据(item)并返回给客户端。从实现的角看,memcached是一个非阻塞的、基于事件的服务器程序。

memcached最大的优势是什么?

Memcached最大的好处就是它带来了极佳的水平可扩展性,特别是在一个巨大的系统中。由于客户端自己做了一次哈希,那么我们很容易增加大量memcached到集群中。memcached之间没有相互通信,因此不会增加 memcached的负载;没有多播协议,不会网络通信量爆炸(implode)。

memcached和MySQL的query cache相比,有什么优缺点?

缺点:

1)相比MySQL的query cache,把memcached引入应用中需要不少的工作量。MySQL的query cache,可以自动地缓存SQL查询的结果,被缓存的SQL查询可以被反复、快速的执行。

优点:

1)当修改表时,MySQL的query cache会立刻被刷新(flush)。当写操作很频繁时,MySQL的query cache会经常让所有缓存数据都失效。

2)在多核CPU上,MySQL的query cache会遇到扩展问题(scalability issues)。在多核CPU上,query cache会增加一个全局锁(global lock), 由于需要刷新更多的缓存数据,速度会变得更慢。

3)在MySQL的query cache中,是不能存储任意的数据的(只能是SQL查询结果)。利用memcached,我们可以搭建出各种高效的缓存。比如,可以执行多个独立的查询,构建出一个用户对象(user object),然后将用户对象缓存到memcached中。而query cache是SQL语句级别的,不可能做到这一点。在小的网站中,query cache会有所帮助,但随着网站规模的增加,query cache的弊将大于利。

4)query cache能够利用的内存容量受到MySQL服务器空闲内存空间的限制。给数据库服务器增加更多的内存来缓存数据,固然是很好的。但是,有了memcached,只要您有空闲的内存,都可以用来增加memcached集群的规模,然后您就可以缓存更多的数据。

memcached的cache机制是怎样的?

Memcached主要的cache机制是LRU(最近最少用)算法+超时失效。当您存数据到memcached中,可以指定该数据在缓存中可以呆多久Which is forever, or some time in the future。如果memcached的内存不够用了,过期的slabs会优先被替换,接着就轮到最老的未被使用的slabs。

memcached如何实现冗余机制?

不实现!Memcached应该是应用的缓存层,从设计本身来京就不带有任何冗余机制。如果一个memcached节点失去了所有数据,应该可以从数据源(比如数据库)再次获取到数据。应用系统应该可以容忍节点的失效。如果担心节点失效会大大加重数据库的负担,那么可以采取一些办法。比如您可以 增加更多的节点 (来减少丢失一个节点的影响),热备节点 (在其他节点down了的时候接管IP)等等。

memcached如何处理容错的?

在节点失效的情况下,集群没有必要做任何容错处理。如果发生了节点失效,应对的措施完全取决于用户。

节点失效时,下面列出几种方案供您选择:

1)忽略它! 在失效节点被恢复或替换之前,还有很多其他节点可以应对节点失效带来的影响。

2)把失效的节点从节点列表中移除。做这个操作千万要小心!在默认情况下(余数式哈希算法),客户端添加或移除节点,会导致所有的缓存数据不可用!因为哈希参照的节点列表变化了,大部分key会因为哈希值的改变而被映射到(与原来)不同的节点上。

3)启动热备节点,接管失效节点所占用的IP。这样可以防止哈希紊乱(hashing chaos)。

4)如果希望添加和移除节点,而不影响原先的哈希结果,可以使用一致性哈希算法(consistent hashing)。

5)两次哈希(reshing)。当客户端存取数据时,如果发现一个节点down了,就再做一次哈希(哈希算法与前一次不同),重新选择另一个节点(需要注意的时,客户端并没有把down的节点从节点列表中移除,下次还是有可能先哈希到它)。如果某个节点时好时坏,两次哈希的方法就有风险了,好的节点和坏的节点上都可能存在脏数据(stale data)。

如何将memcached中item批量导入导出?

不应该这样做!Memcached是一个非阻塞的服务器。任何可能导致memcached暂停或瞬时拒绝服务的操作都应该值得深思熟虑。向memcached中批量导入数据往往不是您真正想要的!想象看,如果缓存数据在导出导入之间发生了变化,您就需要处理脏数据了;如果缓存数据在导出导入之间过期了,您又怎么处理这些数据呢?因此,批量导出导入数据并不像想象中的那么有用。不过在一个场景倒是很有用。如果您有大量的从不变化 的数据,并且希望缓存很快热(warm)起来,批量导入缓存数据是很有帮助的。

但是我确实需要把memcached中的item批量导出导入,怎么办??

如果需要批量导出和导入,最可能的原因一般是重新生成缓存数据需要消耗很长的时间或者数据库坏了让您饱受痛苦。

如果一个memcached节点down了让您很痛苦,那么必须对数据库做一些优化工作。比如处理”惊群”问题( memcached节点都失效了,反复的查询让数据库不堪重负)或者存在优化不好的查询等。Memcached 并不是逃避优化查询的借口和方案。

这里给出一些提示:

使用MogileFS(或者CouchDB等类似的软件)在存储item,把item计算出来并dump到磁盘上。MogileFS可以很方便地覆写item,并提供快速地访问。甚至可以把MogileFS中的item缓存在memcached中,这样可以加快读取速度。 MogileFS+Memcached的组合可以加快缓存不命中时的响应速度,提高网站的可用性。

重新使用MySQL。MySQL的 InnoDB主键查询速度非常快。如果大部分缓存数据都可以放到VARCHAR字段中,那么主键查询的性能将更好。从memcached中按key查询几乎等价于

MySQL的主键查询:将key 哈希到64-bit的整数,然后将数据存储到MySQL中。您可以把原始(不做哈希)的key存储都普通的字段中,然后建立二级索引来加快查询…key被动地失效,批量删除失效的key,等等

memcached是如何做身份验证的?

没有身份认证机制!memcached是运行在应用下层的软件(身份验证应该是应用上层的职责)。memcached的客户端和服务器端之所以是轻量级的,部分原因就是完全没有实现身份验证机制。这样,memcached可以很快地创建新连接,服务器端也无需任何配置。如果您希望限制访问,您可以使用防火墙做端口ip限制,或者让memcached监听unix domain socket。

memcached能接受的key的最大长度是多少?

memcached能接受的key的最大长度是250个字符。需要注意的是,250是memcached服务器端内部的限制。如果使用的Memcached客户端支持”key的前缀”或类似特性,那么key(前缀+原始key)的最大长度是可以超过250个字符的。推荐使用较短的key,这样可以节省内存和带宽。

memcached对item的过期时间有什么限制?

item对象的过期时间最长可以达到30天。memcached把传入的过期时间(时间段)解释成时间点后,一旦到了这个时间点,memcached就把item置为失效状态,这是一个简单但obscure的机制。

memcached最大能存储多大的单个item?

memcached最大能存储1MB的单个item。如果需要被缓存的数据大于1MB,可以考虑在客户端压缩或拆分到多个key中。

为什么单个item的大小被限制在1M byte之内?

简单的回答:因为内存分配器的算法就是这样的。

详细的回答:

1)Memcached的内存存储引擎,使用slabs来管理内存。内存被分成大小不等的slabs chunks(先分成大小相等的slabs,然后每个slab被分成大小相等chunks,不同slab的chunk大小是不相等的)。chunk的大小依次从一个最小数开始,按某个因子增长,直到达到最大的可能值。如果最小值为400B,最大值是1MB,因子是1.20,各个slab的chunk的大小依次是:slab1 - 400B;slab2 - 480B;slab3 - 576B …slab中chunk越大,它和前面的slab之间的间隙就越大。因此,最大值越大,内存利用率越低。Memcached必须为每个slab预先分配内存,因此如果设置了较小的因子和较大的最大值,会需要为Memcached提供更多的内存。

2)不要尝试向memcached中存取很大的数据,例如把巨大的网页放到mencached中。因为将大数据load和unpack到内存中需要花费很长的时间,从而导致系统的性能反而不好。如果确实需要存储大于1MB的数据,可以修改slabs.c:POWER_BLOCK的值,然后重新编译memcached;或者使用低效的malloc/free。另外,可以使用数据库、MogileFS等方案代替Memcached系统。

可以在不同的memcached节点上使用大小不等的缓存空间吗?如果这么做之后,memcached能够更有效地使用内存吗?

Memcache客户端仅根据哈希算法来决定将某个key存储在哪个节点上,而不考虑节点的内存大小。因此,可以在不同的节点上使用大小不等的内存作为缓存空间。但是一般可以这样做:拥有较多内存的节点上可以运行多个memcached实例,每个实例使用的内存跟其他节点上的实例相同。

什么是二进制协议,是否需要关注?

二进制协议尝试为端提供一个更有效的、可靠的协议,减少客户端/服务器端因处理协议而产生的CPU时间。根据Facebook的测试,解析ASCII协议是memcached中消耗CPU时间最多的环节。

memcached的内存分配器是如何工作的?为什么不适用malloc/free!?为何要使用slabs?

实际上,这是一个编译时选项。默认会使用内部的slab分配器,而且确实应该使用内建的slab分配器。最早的时候,memcached只使用malloc/free来管理内存。然而,这种方式不能与OS的内存管理以前很好地工作。反复地malloc/free造成了内存碎片,OS最终花费大量的时间去查找连续的内存块来满足malloc的请求,而不是运行memcached进程。slab分配器就是为了解决这个问题而生的。内存被分配并划分成chunks,一直被重复使用。因为内存被划分成大小不等的slabs,如果item的大小与被选择存放它的slab不是很合适的话,就会浪费一些内存。

memcached是原子的吗?

所有的被发送到memcached的单个命令是完全原子的。如果您针对同一份数据同时发送了一个set命令和一个get命令,它们不会影响对方。它们将被串行化、先后执行。即使在多线程模式,所有的命令都是原子的。然是,命令序列不是原子的。如果首先通过get命令获取了一个item,修改了它,然后再把它set回memcached,系统不保证这个item没有被其他进程(process,未必是操作系统中的进程)操作过。memcached 1.2.5以及更高版本,提供了gets和cas命令,它们可以解决上面的问题。如果使用gets命令查询某个key的item,memcached会返回该item当前值的唯一标识。如果客户端程序覆写了这个item并想把它写回到memcached中,可以通过cas命令把那个唯一标识一起发送给memcached。如果该item存放在memcached中的唯一标识与您提供的一致,写操作将会成功。如果另一个进程在这期间也修改了这个item,那么该item存放在memcached中的唯一标识将会改变,写操作就会失败。

使用不同的客户端库,可以访问到memcached中相同的数据吗?

从技术上说,是可以的。但是可能会遇到下面三个问题:

1)不同的库采用不同的方式序列化数据。举个例子,perl的Cache::Memcached使用Storable来序列化结构复杂的数据(比如hash references, objects, 等)。其他语言的客户端库很可能不能读取这种格式的数据。如果您要存储复杂的数据并且想被多种客户端库读取,那么您应该以简单的string格式来存储,并且这种格式可以被JSON、XML等外部库解析。

2)从某个客户端来的数据被压缩了,从另一个客户端来的却没被压缩。

3)各个客户端库可能使用不同的哈希算法(阶段一哈希)。在连接到多个memcached服务器端的情况下,客户端库根据自身实现的哈希算法把key映射到某台memcached上。正是因为不同的客户端库使用不同的哈希算法,所以被Perl客户端库映射到memcached A的key,可能又会被Python客户端库映射到memcached B,等等。Perl客户端库还允许为每台memcached指定不同的权重(weight),这也是导致这个问题的一个因素。

什么时候失效的数据项会从缓存中删除?

memcached 使用懒失效,当客户端请求数据项时, memcached 在返回数据前会检查失效时间来确定数据项是否已经失效。同样地,当添加一个新的数据项时,如果缓存已经满了, memcached 就会先替换失效的数据项,然后才是缓存中最少使用的数据项。

在设计应用时,可以通过Memcached缓存那些内容?

1)缓存简单的查询结果: 查询缓存存储了给定查询语句对应的整个结果集,最合适缓存那些 经常被用到,但不会改变的 SQL 语句对查询到的结果集,比如载入特定的过滤内容。

$key = md5('SELECT * FROM rest_of_sql_statement_goes_here');

if ($memcache->get($key)) {

      ` return $memcache->get($key);`

}else {

    ` // Run the query and transform the result data into your final dataset form`

    ` $result = $query_results_mangled_into_most_likely_an_array`

     ` $memcache->set($key, $result, TRUE, 86400); // Store the result of the query for a day`

    ` return $result;`

}

记住,如果查询语句对应的结果集改变,该结果集不会展现出来。这种方法不总是有用,但它确实让工作变得比较快。

2)缓存简单的基于行的查询结果: 基于行的缓存会检查缓存数据key的列表,那些在缓存中的行可以直接被取出,不在缓存中的行将会从数据库中取出并以唯一的键为标识缓存起来,最后加入到最终的数据集中返回。随着时间的推移,大多数数据都会被缓存,这也意味着相比与数据库,查询语句会更多地从 memcached 中得到数据行。如果数据是相当静态的,我们可以设置一个较长的缓存时间。

基于行的缓存模式对下面这种搜索情况特别有用 :数据集本身很大或是数据集是从多张表中得到,而数据集取决于查询的输入参数但是查询的结果集之间的有重复部分。比如,如果你有用户 A , B , C , D , E 的数据集。你去点击一张显示用户 A , B , E 信息的页面。首先, memcached 得到 3 个不同的键,每个对应一个用户去缓存中查找,全部未命中。然后就到数据库中用 SQL 查询得到 3 个用户的数据行,并缓存他们。现在,你又去点击另一张显示显示 C , D , E 信息的页面。当你去查找 memcached 时, C , D 的数据并没有被命中,但我们命中了 E 的数据。然后从数据库得到 C , D 的行数据,缓存在 memcached 中。至此以后,无论这些用户信息怎样地排列组合,任何关于 A , B , C , D , E 信息的页面都可以从 memcached 得到数据了。

3)缓存的不只是 SQL 数据,可以缓存最终完成的部分显示页面,以节省CPU计算时间例如正在制作一张显示用户信息的页面,你可能得到一段关于用户的信息(姓名,生日,家庭住址,简介),然后你可能会将 XML 格式的简介信息转化为 HTML 格式或做其他的一些工作。相比单独存储这些属性,你可能更愿意 存储经过渲染的数据块 。那时你就可以简单地取出被预处理后的 HTML 直接填充在页面中,这样节省了宝贵的 CPU 时间。

预热你的缓存

如果你有一个很高访问率的站点,并且你正想加入故障恢复功能或是其他全新的功能,你最终可能会碰到空缓存的问题。一开始缓存是空的,然后一大群人点击你的站点,在填充缓存的过程中,你的数据库可能会承受不住压力。为了解决这一问题,你可以试试任何可行的方法来 ” 温暖 ” 你的Memcached。方法:可以写一些脚本来缓存通用的页面;也可以写一个命令行工具来填充缓存。你可以在高峰时刻在缓存里填充一些内容。

memcached的优势是什么?

1) 分布式 。2)相对应用服务器的内存而言,可以进行单点访问。3)性能强。

不太适合采用Memcached缓存的情况?

1) 如果Value特别大,不太适合。默认编译下Memcache只支持1M的Value。事实上由于存在序列化反序列化的过程,所以从实践的角度来说也不建 议把非常大的数据保存在Memcache中。Memcache适合面向输出的内容缓存,而不是面向处理的数据缓存,也就是不太适合把大块数据放进去拿出来 处理之后再放回去,而是适合拿出来就直接给输出了或是拿出来不需要处理直接使用。

2) 如果不允许过期,不太适合。Memcache在默认情况下最大30天过期,而且在内存达到使用限制后它也会回收最近最少使用的数据。

memcached 不支持命名空间。以下提供几种模仿命名空间的方式:

1)用键的前缀模仿命名空间:在真实的键之前加入有意义的前缀。

2)用命名空间删除数据项:尽管 memcached 不支持使用任何类型的通配符或命名空间来完成删除操作,但是可以采用一些技巧来替代:

<?php  
//生成一个用来保存 namespace 的 key  
$ns_key = $memcache->get("foo_namespace_key");  

//如果 key 不存在,则创建,默认使用当前的时间戳作为标识
if($ns_key===false) $memcache->set("foo_namespace_key",time());  

//根据 namespace_key 生成真正的 key,确保是唯一的key值  
$my_key = "foo_".$ns_key.$otherParms; 

//然后利用拼接的my_key值设置你需要缓存的各种数据
$memcache->set($my_key,$value,false,expire); 

//或者key值获得以前存储的缓存
$memcaceh->get($my_key);


//需要删除整个 namespace 里的对象的时候,如:更改数据库或者删除某些信息后
//将ns_key的值改变,则以后在访问缓存时,以前时间的将永远不会别访问到,以此来实现批量删除缓存 
 $memcache->set("foo_namespace_key",time());

清除部分缓存数据的过程?

可采用命名空间(在Memcache的语境下可采用Key前缀的办法代替,例如设置Key为“子系统名称+实体名+实体的ID”)的办法来实现,用以区分不同类型的缓存内容,以便在需要的时候可以清除某一类缓存。

Value的组织问题?

主要涉及被缓存的数据的颗粒度,比如要保存一个数据表,是一行数据保存在一个键值还是统一保存为一个键值。如果数据保存的粒度很小的话最好是在获取的时候能够批量获取,在保存的时候也能够批量保存,也就是说对于跨网络的调用次数越少越好。

memcache中Key的约定和命名规范?

第一种:一般是项目名称+字符常量(实体名或表名等)+返回PO的id(或者唯一标示都可以)。这种方法代码一般会嵌入到Service中,从而破坏service的业务逻辑,耦合性较高。可以考虑在action层与service层中间加入一层,来降低耦合性。

第二种:可以用spring aop来拦截你要缓存的service,唯一key可以通过类名+方法名+参数名等来组成;这种方法适用于分模块开发 ,因为调用的都是同一个类中的方法,但是拦截器也会在一定程度上影响性能。但是可以提高开发的效率,还有就是不会破坏service层的业务逻辑。

第三种:用sql语句+id(或者查询条件)。这种方法不是很好。

一致性Hash算法

目的有两点:一是节点变动后其他节点受影响尽可能小;二是节点变动后数据重新分配尽可能均衡 。

猜你喜欢

转载自blog.csdn.net/weixin_42632372/article/details/81738615