目录
- 一、 memcached 介绍
- 1.1 简介
- 1.2 特性
- 1.3 优点
- 1.4 缺点
- 1.5 实现原理
- 1.6 适用场景
- 1.7 不适用场景
- 1.8 与 Redis 比较相同点
- 1.9 与 Redis 比较不同点
- 二、对 java 的支持
- 2.1 Memcached-java-client 客户端
- 2.2 Spymemcached 客户端
- 2.3 XMemcached 客户端
- 三、下载安装
- 3.1 windows环境安装
- 3.1.1 memcached <1.4.5 版本安装
- 3.1.2 memcached >= 1.4.5 版本安装
- 3.2 CentOS环境安装
- 3.2.1 查找memcached
- 3.2.2 安装 memcached
- 3.2.3 验证安装memcached
- 3.2.4 修改memcached配置文件
- 3.2.5 重启memcached服务
- 3.2.6 使用memcached-tool检测memcached服务
- 四、使用 Memcached-java-client
- 4.1 添加 maven 依赖
- 4.2 修改 application.properties 配置信息
- 4.3 编写 memcache 配置类
- 4.4 编写测试方法
- 4.5 测试
- 五、使用 SpyMemcached 方式
- 5.1 添加 maven 依赖
- 5.2 修改 application.properties 配置信息
- 5.3 编写 SpyMemcacheConfig 配置类
- 5.4 编写测试方法
- 5.5 测试
- 六、使用 XMemcached 方式
- 6.1 添加 maven 依赖
- 6.2 修改 application.properties 配置信息
- 6.3 编写 XMemcacheConfig 配置类
- 6.4 编写测试方法
- 6.5 测试
- 七、常见面试题
一、 memcached 介绍
1.1 简介
Memcached 是一个高性能的分布式内存对象缓存系统
,用于动态 Web 应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。
Memcached 基于一个存储键/值对的 HashMap,在内存中对任意的数据(比如字符串、对象等)使用 key-value 存储,数据可以来自数据库调用、API 调用,或者页面渲染的结果。其守护进程(daemon )是用 C 写的,但是客户端可以用任何语言来编写,并通过 Memcached 协议与守护进程通信。
1.2 特性
- 本质上就是一个内存 key-value 缓存
- 协议简单,使用的是基于文本行的协议
不支持数据的持久化
,服务器关闭之后数据全部丢失
- Memcached 简洁而强大,便于快速开发,上手较为容易
- 互不通信的 Memcached 之间具有分布特征 ,没有安全机制
1.3 优点
Memcached 可以利用多核优势,单实例吞吐量极高,可以达到几十万 QPS(取决于 key/value 的字节大小以及服务器硬件性能,日常环境中 QPS 高峰大约在 4-6w 左右)。适用于最大程度扛量。
支持直接配置为 session handle
1.4 缺点
- 只支持简单的 key-value 数据结构,不像 Redis 可以支持丰富的数据类型
- 无法进行持久化,数据不能备份,只能用于缓存使用,且重启后数据全部丢失
- 无法进行数据同步,不能将 Memcached 中的数据迁移到其他 Memcached 实例中
- Memcached 内存分配采用 Slab Allocation 机制管理内存,value 大小分布差异较大时会造成内存利用率降低,并引发低利用率时依然出现踢出等问题。需要用户注重 value 设计。
1.5 实现原理
Memcached 处理的原理是每一个 key/value,key 会通过一个 hash 表转换成 hash 的 key,便于查找对比以及尽可能的做到散列。同时 Memcached 用的是一个二级散列,通过一个 hash 表来维护。
Memcached 有两个核心组件:服务端**(server)和客户端(client)**。
在一个 Memcached 组件查询中,client 先通过 key 的 hash 值来确定 key 在 server 端的位置,当 server 端确定后,客户端就会发送一个查询请求给对应的 server 端。让它来查找出确切数据,因为这之间没有交互以及多播协议,因此 Memcached 带给网络的影响最小。
1.6 适用场景
- 利用 Memcached 可以缓存 session 数据 、临时数据以减少对他们的数据库写操作
- 缓存一些很小但是被频繁访问的文件
- 经常被读取并且实时性要求不强可以等到自动过期的数据。
- 变化频繁,查询频繁,但是不需要入库的场景读多写少的场景中用于页面缓存
1.7 不适用场景
- 缓存的数据需要持久化
- key 的长度大于 250 字符
- 变化频繁且需要入库
- 过大的数据不适宜放在 Memcached 中
1.8 与 Redis 比较相同点
- 都是基于内存的数据库系统,最大存储量是根据机器内存大小而定
- 都有不同的过期策略,分布式数据的备份可以设置一主多从,也可以一主一从(Master-Slave)
- 都支持 key-value 数据缓存
1.9 与 Redis 比较不同点
- 数据持久化支持:Redis 虽然是基于内存的存储系统,但是它本身是支持内存数据的持久化的,而且提供两种主要的持久化策略:RDB 快照和 AOF 日志。而 Memcached 是不支持数据持久化操作的。
- 灾难恢复:Memcached 挂掉后,数据不可恢复。 Redis 数据丢失后可以通过 AOF 恢复
- IO 方面:Redis 使用的单线程 IO 复用网络模型, 而 Memcached 多线程非阻塞 IO 复用模型
- 数据支持类型:Redis 支持 key-value 数据类型,还有 list、set、zset、hash 等数据结构,而 Memcached 只支持 key-value 数据
- Value 值大小不同:Redis 最大可以达到 512mb;Memcached 只有 1mb。
- 数据一致性:Memcached 提供了 CAS 命令,可以保证多个并发访问操作同一份数据的一致性问题。Redis 没有提供 CAS 命令,并不能保证这点,不过 Redis 提供了事务的功能,可以保证一串命令的原子性,中间不会被任何操作打断。
二、对 java 的支持
可以使用三种不同的 Java 客户端来支持 Memcached, 分别为 Memcached-java-client
、Spymemcached
和 XMemcached
。我们分别介绍下这三种客户端。
2.1 Memcached-java-client 客户端
Memcached-java-client
客户端推出较早,应用最广泛,阻塞式 IO,稳定性高,高并发下性能低。较早推出的 Memcached JAVA 客户端 API,应用广泛,运行比较稳定,使用阻塞 IO,不支持 CAS 操作,已停止更新。
2.2 Spymemcached 客户端
Spymemcached
客户端时基于 JDK 的 Concurrent 和 NIO,存取速度高,并发性能高,支持 CAS 操作,已停止更新。
2.3 XMemcached 客户端
XMemcached
客户端同样基于 NIO 实现,减少了线程创建和切换的开销,这一点在高并发下特别明显,一直更新。推荐使用。
三、下载安装
3.1 windows环境安装
官网上并未提供 Memcached 的 Windows 平台安装包,我们可以使用以下链接来下载,你需要根据自己的系统平台及需要的版本号点击对应的链接下载即可:
- 32位系统 1.2.5版本:http://static.jyshare.com/download/memcached-1.2.5-win32-bin.zip
- 32位系统 1.2.6版本:http://static.jyshare.com/download/memcached-1.2.6-win32-bin.zip
- 32位系统 1.4.4版本:http://static.jyshare.com/download/memcached-win32-1.4.4-14.zip
- 64位系统 1.4.4版本:http://static.jyshare.com/download/memcached-win64-1.4.4-14.zip
- 32位系统 1.4.5版本:http://static.jyshare.com/download/memcached-1.4.5-x86.zip
- 64位系统 1.4.5版本:http://static.jyshare.com/download/memcached-1.4.5-amd64.zip
在 1.4.5 版本以前 memcached 可以作为一个服务安装,而在 1.4.5 及之后的版本删除了该功能。因此我们以下介绍两个不同版本 1.4.4 及 1.4.5的不同安装方法:
3.1.1 memcached <1.4.5 版本安装
1)解压下载的安装包到指定目录。
2)在 1.4.5 版本以前 memcached 可以作为一个服务安装,使用管理员权限运行以下命令:
c:\memcached\memcached.exe -d install
注意:你需要使用真实的路径替代 c:\memcached\memcached.exe。
3)然后我们可以使用以下命令来启动和关闭 memcached 服务:
c:\memcached\memcached.exe -d start
c:\memcached\memcached.exe -d stop
4)如果要修改 memcached 的配置项, 可以在命令行中执行 regedit.exe
命令打开注册表并找到 "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\memcached"
来进行修改。
如果要提供 memcached 使用的缓存配置 可以修改 ImagePath 为:
"c:\memcached\memcached.exe" -d runservice -m 512
-m 512
意思是设置 memcached 最大的缓存配置为512M。
此外我们还可以通过使用 "c:\memcached\memcached.exe -h"
命令查看更多的参数配置。
5、如果我们需要卸载 memcached ,可以使用以下命令:
c:\memcached\memcached.exe -d uninstall
3.1.2 memcached >= 1.4.5 版本安装
1)解压下载的安装包到指定目录。
2)在 memcached1.4.5 版本之后,memcached 不能作为服务来运行,需要使用任务计划中来开启一个普通的进程,在 window 启动时设置 memcached自动执行。
我们使用管理员身份执行以下命令将 memcached 添加来任务计划表中:
schtasks /create /sc onstart /tn memcached /tr "'c:\memcached\memcached.exe' -m 512"
注意:你需要使用真实的路径替代 c:\memcached\memcached.exe。
注意:-m 512 意思是设置 memcached 最大的缓存配置为512M。
注意:我们可以通过使用 “c:\memcached\memcached.exe -h” 命令查看更多的参数配置。
3)如果需要删除 memcached 的任务计划可以执行以下命令:
schtasks /delete /tn memcached
3.2 CentOS环境安装
3.2.1 查找memcached
yum search memcached
该命令可以查询yum库中有关memcached的安装包信息,以下是搜寻结果截图:
3.2.2 安装 memcached
yum -y install memcached
该命令用来安装memcached,不必关心其中各个安装包之间的依赖。命令执行结果如下图所示:
(备注:如果执行yum –y install memcached报网络解析异常,则说明当前无法联网,或者说明dns服务器没有正确配置,如果因为dns没有配置原因,可以通过 vi /etc/resolv.conf
添加 nameserver 192.98.18.20(dns服务器地址) 来配置dns映射)
3.2.3 验证安装memcached
memcached –h
该命令主要的功能是提供memcached的帮助信息,罗列memcached命令有哪些接口以及接口参数。具体信息参考截图:
3.2.4 修改memcached配置文件
chkconfig --level 2345 memcached on
执行该命令将memcached
加入linux服务。添加成功以后,你可以使用service memcached start|stop|restart
等命令来启动、关闭、重启memcached服务。
修改memcached的参数,主要涉及memcached启动绑定的端口、用户、最大连接、最大内存等,命令:vi /etc/sysconfig/memcached
命令响应结果如截图:
通过vi命令即可修改上面各个参数。PORT,服务启动端口。USER,服务启动用户。MAXCONN,服务配置支持最大连接数。CACHESIZE,服务支持的最大内存,以M为单位。
3.2.5 重启memcached服务
修改上面的参数以后,需要重启memcached服务,不然参数是无法立即生效的。重启服务,只需执行:service memcached restart
。服务重启以后,即可使用memcached-tool来检测memcached服务状态。
3.2.6 使用memcached-tool检测memcached服务
执行memcached-tool 127.0.0.1:11211 stats
即可检测memcached的服务状态。如果报错,连接不上,那么说明memcached没有正确安装或者没有启动。执行正常,内容如截图所示:
四、使用 Memcached-java-client
4.1 添加 maven 依赖
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
</dependency>
<dependency>
<groupId>com.whalin</groupId>
<artifactId>Memcached-Java-Client</artifactId>
<version>3.0.2</version>
</dependency>
4.2 修改 application.properties 配置信息
# memcached config
memcache.servers=127.0.0.1:11211
memcache.failover=true
memcache.initConn=100
memcache.minConn=20
memcache.maxConn=1000
memcache.maintSleep=50
memcache.nagel=false
memcache.socketTO=3000
memcache.aliveCheck=true
4.3 编写 memcache 配置类
/**
* @author hanson
* @date 2024/3/21 15:19
*/
@Configuration
public class MemcacheConfig {
@Value("${memcache.servers}")
private String[] servers;
@Value("${memcache.failover}")
private boolean failover;
@Value("${memcache.initConn}")
private int initConn;
@Value("${memcache.minConn}")
private int minConn;
@Value("${memcache.maxConn}")
private int maxConn;
@Value("${memcache.maintSleep}")
private int maintSleep;
@Value("${memcache.nagel}")
private boolean nagel;
@Value("${memcache.socketTO}")
private int socketTO;
@Value("${memcache.aliveCheck}")
private boolean aliveCheck;
@Bean
public SockIOPool sockIOPool () {
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(failover);
pool.setInitConn(initConn);
pool.setMinConn(minConn);
pool.setMaxConn(maxConn);
pool.setMaintSleep(maintSleep);
pool.setNagle(nagel);
pool.setSocketTO(socketTO);
pool.setAliveCheck(aliveCheck);
pool.initialize();
return pool;
}
@Bean
@ConditionalOnBean(SockIOPool.class)
public MemCachedClient memCachedClient(){
return new MemCachedClient();
}
}
4.4 编写测试方法
@Autowired
private MemCachedClient memCachedClient;
@Test
void memCacheJavaClient() throws InterruptedException {
// 放入缓存
boolean flag = memCachedClient.set("a", 1);
System.out.println(flag);
// 取出缓存
Object a = memCachedClient.get("a");
System.out.println(a);
// 3s后过期
memCachedClient.set("b", "2", new Date(3000));
Object b = memCachedClient.get("b");
System.out.println(b);
Thread.sleep(3000);
b = memCachedClient.get("b");
System.out.println(a);
System.out.println(b);
}
4.5 测试
输出结果如图所示:
五、使用 SpyMemcached 方式
5.1 添加 maven 依赖
<dependency>
<groupId>net.spy</groupId>
<artifactId>spymemcached</artifactId>
<version>2.12.2</version>
</dependency>
5.2 修改 application.properties 配置信息
memcache.ip=127.0.0.1
memcache.port=11211
5.3 编写 SpyMemcacheConfig 配置类
@Configuration
public class SpyMemcacheConfig implements CommandLineRunner{
@Value("${memcache.ip}")
private String ip;
@Value("${memcache.port}")
private int port;
private MemcachedClient client = null;
@Override
public void run(String... args) throws Exception {
try {
client = new MemcachedClient(new InetSocketAddress(ip,port));
} catch (IOException e) {
}
}
public MemcachedClient getClient() {
return client;
}
}
5.4 编写测试方法
@Autowired
private SpyMemcacheConfig spyMemcacheConfig;
@Test
void spyMemCached() throws InterruptedException {
/*这个过期时间单位是秒,最大值是60*60*24*30*/
spyMemcacheConfig.getClient().set("spyMemcachedKey",1,"Hanson");
System.out.println("基于spyMemcached实现,现在的值为 "+spyMemcacheConfig.getClient().get("spyMemcachedKey"));
Thread.sleep(2000);
System.out.println("1秒后缓存内容清除,现在的值为: "+spyMemcacheConfig.getClient().get("spyMemcachedKey"));
}
5.5 测试
六、使用 XMemcached 方式
6.1 添加 maven 依赖
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.5</version>
</dependency>
6.2 修改 application.properties 配置信息
# memcached服务器集群(格式为host:port,多个服务器之间用空格隔开)
memcached.server=127.0.0.1:11211 10.86.54122:11211
# 接口操作的默认超时时间,可以被接口覆盖
memcached.opTimeout=3000
# 池子大小
memcached.poolSize=10
# 是否开启失败模式,默认为false
memcached.failureMode=false
# 是否使用memcached缓存
memcached.enabled=true
6.3 编写 XMemcacheConfig 配置类
@Configuration
public class XMemcacheConfig {
@Value("${memcached.server}")
private String server;
@Value("${memcached.opTimeout}")
private Integer opTimeout;
@Value("${memcached.poolSize}")
private Integer poolSize;
@Value("${memcached.failureMode}")
private boolean failureMode;
@Value("${memcached.enabled}")
private boolean enabled;
@Bean(name = "memcachedClientBuilder")
public MemcachedClientBuilder getBuilder() {
MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder(server);
// 内部采用一致性哈希算法
memcachedClientBuilder.setSessionLocator(new KetamaMemcachedSessionLocator());
// 操作的超时时间
memcachedClientBuilder.setOpTimeout(opTimeout);
// 采用二进制传输协议(默认为文本协议)
memcachedClientBuilder.setCommandFactory(new BinaryCommandFactory());
// 设置连接池的大小
memcachedClientBuilder.setConnectionPoolSize(poolSize);
// 是否开起失败模式
memcachedClientBuilder.setFailureMode(failureMode);
return memcachedClientBuilder;
}
/**
* 由Builder创建memcachedClient对象,并注入spring容器中
* @param memcachedClientBuilder
* @return
*/
@Bean(name = "memcachedClient")
public MemcachedClient getClient(@Qualifier("memcachedClientBuilder") MemcachedClientBuilder memcachedClientBuilder) {
MemcachedClient client = null;
try {
client = memcachedClientBuilder.build();
} catch(Exception e) {
e.printStackTrace();
}
return client;
}
}
6.4 编写测试方法
@Autowired
private MemcachedClient memcachedClient;
@Test
void xMemCached(){
try {
// 新增操作
memcachedClient.set("XMemcacheKeyOne",0,"Hanson");
System.out.println((String) memcachedClient.get("XMemcacheKeyOne"));
// 删除操作
memcachedClient.delete("XMemcacheKeyOne");
System.out.println((String) memcachedClient.get("XMemcacheKeyOne"));
// 设置存活时间
memcachedClient.set("XMemcacheKeyTwo",1,"李四");
Thread.sleep(2000);
System.out.println((String)memcachedClient.get("XMemcacheKeyTwo"));
// 更新操作
memcachedClient.set("XMemcacheKeyThree",0,"Huang");
System.out.println((String)memcachedClient.get("XMemcacheKeyThree"));
memcachedClient.set("XMemcacheKeyThree",0,"Hanson Huang");
System.out.println((String)memcachedClient.get("XMemcacheKeyThree"));
} catch (Exception e) {
e.printStackTrace();
}
}
6.5 测试
七、常见面试题
问题 1:memchaced 中的一致性哈希算法是怎样工作的?
在计算一致性哈希时采用一般采用如下步骤:
1)首先求出 memcached 服务器(节点)的哈希值,并将其配置到 0~2^32-1
的圆上。
2) 然后采用同样的方法求出存储数据的键的哈希值,并映射到相同的圆上。
3)然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过 2^32-1 仍然找不到服务器,就会保存到第一台 memcached 服务器上。
点评:
传统的哈希算法最常见的是哈希取模算法,即集群中可用机器节点数量为 N,那么 key 值为 K 的的数据请求会路由到 hash (K) mod N 对应的节点。这种算法简单,但并不适用于分布式系统。在分布式系统中,每个节点都可能失效,且节点会随时动态增减,如果用 hash 取模算法,会导致节点增减时的大量缓存无法命中,瞬间穿透缓存,给下游 DB 等系统带来极高的负载,甚至引起宕机
。
一致哈希和哈希的具体区别见文章《何为一致性哈希?一致性哈希和哈希有什么区别?一文深入理解一致性哈希》
问题 2:redis 和 memcached 的区别是什么?
1)数据类型支持不同。 memcached
仅支持简单的 key-value
结构的数据类型,复杂的对象需要客户端自己处理;Redis
支持的数据类型要丰富得多。最为常用的数据类型主要由五种:String、Hash、List、Set 和 Sorted Set
。
2)持久化支持不同,memcached 不支持数据持久存储 ,数据一直在内存中;redis 支持数据落地持久化存储,可以将内存中的数据保持在磁盘中,因此在某一时刻,redis 中并不是所有的数据都一直存储在内存中;
3)分布式支持不同。memcached 一般是在客户端通过一致性哈希等算法来实现分布式存储; Redis 更偏向于在服务器端构建分布式存储,支持主从复制等集群模式;
4)内存管理不同。memcached 默认使用 Slab Allocation 机制管理内存,其主要思想是按照预先规定的大小呈阶梯状分配好,会根据接收到数据的大小选择一个最合适的 Slab Class 进行存储,该方法可以避免内存碎片问题,可也不可避免地出现一定的空间浪费;redis 则采用包装过的 mallc/free 来分配内存,什么时候需要什么时候分配,更简单一些。
5)应用场景不同。 memcached 是一个分布式内存对象缓存系统(distributed memory object caching system,),旨在通过减轻数据库负载来加速动态 Web 应用程序;redis 是内存中的数据结构存储系统(in-memory data structure store),它可以用作数据库、缓存和消息中间件。
点评:
这个题目也是半开放式的,这类题目之前也出现得比较多了,回答可能会有好几个差异点,但其实除了重要的差异点外,其他差异点多一个少一个不会影响大局,关键还是要有自己的理解,不要表现得像死记硬背。
当然本题目的问法比较直白,在实际上的面试中可能会换一种问法,比如说 “你们的系统当时为什么选择 redis/memcached 作为缓存方案,为什么没有选择 memcached/redis” 等等,本质上是想考察做技术选型背后的逻辑。通常来讲:redis 比 memcached 的功能多一些,实现也更复杂。 不过 memcached 更专注于保存 key-value 数据(这已经能满足大多数使用场景了),而 redis 提供更丰富的数据结构及其他的一些功能。
问题3:适用memcached的业务场景?
1)如果网站包含了访问量很大的动态网页,因而数据库的负载将会很高。由于大部分数据库请求都是读操作,那么memcached可以显著地减小数据库负载。
2)如果数据库服务器的负载比较低但CPU使用率很高,这时可以缓存计算好的结果( computed objects )和渲染后的网页模板(enderred templates)。
3)利用memcached可以缓存session数据、临时数据以减少对他们的数据库写操作。
4)缓存一些很小但是被频繁访问的文件。
5)缓存Web 'services’或RSS feeds的结果.。
问题4:不适用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
问题5:能够遍历memcached中所有的item吗?
不能,这个操作的速度相对缓慢且阻塞其他的操作(这里的缓慢时相比memcached其他的命令)。memcached所有非调试(non-debug)命令,例如add, set, get, fulsh等无论memcached中存储了多少数据,它们的执行都只消耗常量时间。任何遍历所有item的命令执行所消耗的时间,将随着memcached中数据量的增加而增加
。当其他命令因为等待(遍历所有item的命令执行完毕)而不能得到执行,因而阻塞将发生。
问题6: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集群的规模,然后您就可以缓存更多的数据。
问题7:memcached和服务器的local cache(比如PHP的APC、mmap文件等)相比,有什么优缺点?
1)首先,local cache面临着严重的内存限制,能够利用的内存容量受到(单台)服务器空闲内存空间的限制。
2)local cache有一点比memcached和query cache都要好,那就是它不但可以存储任意的数据,而且没有网络存取的延迟。因此,local cache的数据查询更快。考虑把highly common的数据放在local cache中吧。如果每个页面都需要加载一些数量较少的数据,可以考虑把它们放在local cached。
3)local cache缺少集体失效(group invalidation)的特性。在memcached集群中,删除或更新一个key会让所有的观察者觉察到。但是在local cache中, 我们只能通知所有的服务器刷新cache(很慢,不具扩展性)或者仅仅依赖缓存超时失效机制。
问题8:memcached如何处理容错的?
在节点失效的情况下,集群没有必要做任何容错处理。如果发生了节点失效,应对的措施完全取决于用户。节点失效时,下面列出几种方案供您选择:
1)忽略它! 在失效节点被恢复或替换之前,还有很多其他节点可以应对节点失效带来的影响。
2)把失效的节点从节点列表中移除。做这个操作千万要小心!在默认情况下(余数式哈希算法),客户端添加或移除节点,会导致所有的缓存数据不可用!因为哈希参照的节点列表变化了,大部分key会因为哈希值的改变而被映射到(与原来)不同的节点上。
3)启动热备节点,接管失效节点所占用的IP。这样可以防止哈希紊乱(hashing chaos)。
4)如果希望添加和移除节点,而不影响原先的哈希结果,可以使用一致性哈希算法(consistent hashing)。
5)两次哈希(reshing)。当客户端存取数据时,如果发现一个节点down了,就再做一次哈希(哈希算法与前一次不同),重新选择另一个节点(需要注意的时,客户端并没有把down的节点从节点列表中移除,下次还是有可能先哈希到它)。如果某个节点时好时坏,两次哈希的方法就有风险了,好的节点和坏的节点上都可能存在脏数据(stale data)。
问题9:memcached是如何做身份验证的?
没有身份认证机制!memcached是运行在应用下层的软件(身份验证应该是应用上层的职责)。memcached的客户端和服务器端之所以是轻量级的,部分原因就是完全没有实现身份验证机制。这样,memcached可以很快地创建新连接,服务器端也无需任何配置。如果您希望限制访问,您可以使用防火墙,或者让memcached监听unix domain socket
。
问题10:memcached能接受的key的最大长度是多少?
memcached能接受的key的最大长度是250个字符
。需要注意的是,250是memcached服务器端内部的限制。如果使用的Memcached客户端支持"key的前缀"或类似特性,那么key(前缀+原始key)的最大长度是可以超过250个字符的。推荐使用较短的key,这样可以节省内存和带宽。
问题11:memcached对item的过期时间有什么限制?
item对象的过期时间最长可以达到30天
。memcached把传入的过期时间(时间段)解释成时间点后,一旦到了这个时间点,memcached就把item置为失效状态。
问题12:memcached最大能存储多大的单个item?
memcached最大能存储1MB
的单个item。如果需要被缓存的数据大于1MB,可以考虑在客户端压缩或拆分到多个key中。
问题13:为什么单个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系统。
问题14:memcached的内存分配器是如何工作的?为什么不适用malloc/free!?为何要使用slabs?
实际上,这是一个编译时选项。默认会使用内部的slab分配器,而且确实应该使用内建的slab分配器。最早的时候,memcached只使用malloc/free来管理内存。然而,这种方式不能与OS的内存管理以前很好地工作。反复地malloc/free造成了内存碎片,OS最终花费大量的时间去查找连续的内存块来满足malloc的请求,而不是运行memcached进程。slab分配器就是为了解决这个问题而生的。内存被分配并划分成chunks,一直被重复使用。因为内存被划分成大小不等的slabs,如果item的大小与被选择存放它的slab不是很合适的话,就会浪费一些内存。
问题15: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中的唯一标识将会改变,写操作就会失败。
问题16:什么时候失效的数据项会从缓存中删除?
memcached 使用懒失效,当客户端请求数据项时, memcached 在返回数据前会检查失效时间来确定数据项是否已经失效。同样地,当添加一个新的数据项时,如果缓存已经满了, memcached 就会先替换失效的数据项,然后才是缓存中最少使用的数据项。
问题17:在设计应用时,可以通过Memcached缓存那些内容?
1)缓存简单的查询结果:查询缓存存储了给定查询语句对应的整个结果集,最合适缓存那些经常被用到,但不会改变的 SQL 语句对查询到的结果集,比如载入特定的过滤内容。记住,如果查询语句对应的结果集改变,该结果集不会展现出来。这种方法不总是有用,但它确实让工作变得比较快。
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 时间。
文章代码地址GitHub
觉得有用的话还请来个三连!!!