赞
踩
本文是笔者对Memcached这个高性能分布式缓存组件的实践案例,Memcached是一种高性能的分布式内存对象缓存系统,用于减轻数据库负载,加速动态Web应用,提高网站访问速度。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高应用程序的性能。
本文涉及到的源码gitee地址:源码地址
主要特点:
键值存储:Memcached 使用键值对存储模型,其中键是一个字符串,值可以是任何数据,通常用于存储数据库查询结果、网页或对象的序列化形式。
内存存储:数据存储在内存中,这比从硬盘读取数据要快得多。但是,这也意味着数据是非持久化的,如果 Memcached 或操作系统重启,数据会丢失。
LRU 缓存替换策略:当内存不足时,Memcached 使用最近最少使用(Least Recently Used,LRU)算法来自动删除不常用的缓存项,以便为新数据腾出空间。
简单协议:Memcached 使用一个简单的文本行协议,这使得它易于实现和集成。
跨平台:Memcached 可以在多种操作系统上运行,包括 Linux 和 BSD 等。
高可用性和可扩展性:Memcached 支持分布式部署,可以在多个服务器上运行,以增加容量和冗余。
轻量级和快速:由于其简单的架构和内存存储特性,Memcached 能够提供非常低的延迟和高吞吐量。
适用场景:
缓存数据库查询结果。
存储用户会话信息。
缓存计算结果或频繁访问的内容。
存储用户个性化数据。
不适用场景:
缓存的数据需要持久化
key 的长度大于 250 字符
变化频繁且需要存入数据库
过大的数据不适宜放在 Memcached 中
与 Redis 比较相同点:
都是基于内存的数据库系统,最大存储量是根据机器内存大小而定
都有不同的过期策略,分布式数据的备份可以设置一主多从,也可以一主一从(Master-Slave)
都支持 key-value 数据缓存
与 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 客户端来支持 Memcached, 分别为 Memcached-java-client、Spymemcached 和 XMemcached。
Memcached-java-client 客户端
Spymemcached 客户端
XMemcached 客户端
地址:memcached下载
把这个链接复制到新的标签页打开自动下载。下载后的压缩包解压如下
使用管理员权限打开cmd,进入此路径
笔者下载的是1.4.4版本
memcached <1.4.5 版本安装
解压下载的安装包到指定目录。
在 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
然后使用sc query memcached查看命令是否成功:
打开 服务列表发现memcached服务已安装,并且是开机自启动,后续无需手动再执行
memcached.exe -d start命令了
先创建一个Memcached-Project的springboot应用,创建好后去resources下把application.properties改为application.yml
为了方便,三种java客户端,我都使用这个项目来展示。
首先在pom引入如下依赖:
<!--Memcached-java-client-->
<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>
MemcachedConfig配置类:
package com.hulei.memcached.memcachedjavaclient; import com.whalin.MemCached.MemCachedClient; import com.whalin.MemCached.SockIOPool; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author hulei * Memcached-java-client的配置 */ @Configuration public class MemcachedConfig { @Value("${MemcachedJavaClient.memcached.servers}") private String[] servers; @Value("${MemcachedJavaClient.memcached.failover}") private boolean failover; @Value("${MemcachedJavaClient.memcached.initConn}") private int initConn; @Value("${MemcachedJavaClient.memcached.minConn}") private int minConn; @Value("${MemcachedJavaClient.memcached.maxConn}") private int maxConn; @Value("${MemcachedJavaClient.memcached.maintSleep}") private int maintSleep; @Value("${MemcachedJavaClient.memcached.nagel}") private boolean nagel; @Value("${MemcachedJavaClient.memcached.socketTO}") private int socketTO; @Value("${MemcachedJavaClient.memcached.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(); } }
yml配置:
MemcachedController测试类:
package com.hulei.memcached.memcachedjavaclient; import com.whalin.MemCached.MemCachedClient; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Date; @RestController public class MemcachedController { @Resource private MemCachedClient memCachedClient; @RequestMapping("/memcachedIndex") public String memcachedIndex() 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); return "abcd"; } }
浏览器输入地址:http://localhost:8080/memcachedIndex,观察IDEA控制台输出:
pom中引入依赖:
<!--SpyMemcached-->
<dependency>
<groupId>net.spy</groupId>
<artifactId>spymemcached</artifactId>
<version>2.12.3</version>
</dependency>
SpyMemcachedConfig配置类:
package com.hulei.memcached.spymemcached; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.spy.memcached.MemcachedClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.context.annotation.Configuration; import java.io.IOException; import java.net.InetSocketAddress; @Configuration @Slf4j public class SpyMemcachedConfig implements CommandLineRunner { @Value("${SpyMemcached.memcached.ip}") private String ip; @Value("${SpyMemcached.memcached.port}") private int port; @Getter private MemcachedClient client = null; @Override public void run(String... args) { try { client = new MemcachedClient(new InetSocketAddress(ip,port)); } catch (IOException e) { log.error("SpyMemcached初始化失败",e); throw new RuntimeException("SpyMemcached初始化失败"); } } }
yml配置:
SpyMemcachedController测试控制类:
package com.hulei.memcached.spymemcached; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class SpyMemcachedController { @Resource private SpyMemcachedConfig spyMemcachedConfig; @RequestMapping("/spyMemcachedIndex") public String spyMemcachedIndex() throws InterruptedException { /*这个过期时间单位是秒,最大值是60*60*24*30*/ spyMemcachedConfig.getClient().set("spyMemcachedKey",1,"张三"); System.out.println("基于spyMemcached实现,现在的值为 "+spyMemcachedConfig.getClient().get("spyMemcachedKey")); Thread.sleep(2000); System.out.println("1秒后缓存内容清除,现在的值为: "+spyMemcachedConfig.getClient().get("spyMemcachedKey")); return "SpyMemcached"; } }
浏览器输入地址:http://localhost:8080/spyMemcachedIndex,观察IDEA控制台输出:
pom中再引入依赖:
<!--XMemcached -->
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.8</version>
</dependency>
XMemcachedConfig配置类:
package com.hulei.memcached.xmemcached; import lombok.extern.slf4j.Slf4j; import net.rubyeye.xmemcached.MemcachedClient; import net.rubyeye.xmemcached.MemcachedClientBuilder; import net.rubyeye.xmemcached.XMemcachedClientBuilder; import net.rubyeye.xmemcached.command.BinaryCommandFactory; import net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @Slf4j public class XMemcachedConfig { @Value("${XMemcached.memcached.server}") private String server; @Value("${XMemcached.memcached.opTimeout}") private Integer opTimeout; @Value("${XMemcached.memcached.poolSize}") private Integer poolSize; @Value("${XMemcached.memcached.failureMode}") private boolean failureMode; @Value("${XMemcached.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容器中 */ @Bean(name = "memcachedClient") public MemcachedClient getClient(@Qualifier("memcachedClientBuilder") MemcachedClientBuilder memcachedClientBuilder) { MemcachedClient client; try { client = memcachedClientBuilder.build(); } catch(Exception e) { log.error("创建MemcachedClient对象失败", e); throw new RuntimeException("创建MemcachedClient对象失败", e); } return client; } }
yml配置:
XMemcachedController测试服务类:
package com.hulei.memcached.xmemcached; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import net.rubyeye.xmemcached.MemcachedClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @Slf4j public class XMemcachedController { @Resource private MemcachedClient memcachedClient; @RequestMapping("/XMemcachedIndex") public String XMemcachedIndex() { try { //新增操作 memcachedClient.set("XMemcachedKeyOne",0,"张三"); System.out.println((String)memcachedClient.get("XMemcachedKeyOne")); //删除操作 memcachedClient.delete("XMemcachedKeyOne"); System.out.println((String)memcachedClient.get("XMemcachedKeyOne")); //设置存活时间 memcachedClient.set("XMemcachedKeyTwo",1,"李四"); Thread.sleep(2000); System.out.println((String)memcachedClient.get("XMemcachedKeyTwo")); //更新操作 memcachedClient.set("XMemcachedKeyThree",0,"王五"); System.out.println((String)memcachedClient.get("XMemcachedKeyThree")); memcachedClient.set("XMemcachedKeyThree",0,"王五他儿子"); System.out.println((String)memcachedClient.get("XMemcachedKeyThree")); } catch (Exception e) { log.error("XMemcachedIndex error",e); } return "xMemcachedIndex"; } }
浏览器输入地址:http://localhost:8080/XMemcachedIndex,观察IDEA控制台输出:
通过以上三个java客户端的案例,演示了Memcached的实际用法,整体较为简单,笔者把三个客户端的代码用一个项目演示,需要源码的请从文章顶部获取。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。