技术标签: memcached java 数据库、缓存、nosql 服务器
一、概念
Memcached是danga.com开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升性能。
二、原理
Memcached有两个核心组件组成:服务端(ms)和客户端(mc)。首先mc拿到ms列表,并对key做hash转化,根据hash值确定kv对所存的ms位置。然后在一个memcached的查询中,mc先通过计算key的hash值来确定kv对所处在的ms位置。当ms确定后,客户端就会发送一个查询请求给对应的ms,让它来查找确切的数据。因为ms之间并没有护卫备份,也就不需要互相通信,所以效率较高。
四、客户端版本
Memcached Client目前有一下四种:
Memcached Client for Java,比 SpyMemcached更稳定、更早、更广泛;
SpyMemcached,比 Memcached Client for Java更高效;
XMemcached,比 SpyMemcache并发效果更好。
alisoft-xplatform-asf-cache阿里软件的架构师岑文初进行封装的。里面的注释都是中文的,比较好
五、服务器端
安装
这里介绍windows环境的安装。
1.下载memcache的windows稳定版,解压放某个盘下面,比如在c:\memcached
2.在cmd下输入 'c:\memcached\memcached.exe -d install' 安装
3.再输入: 'c:\memcached\memcached.exe -d start' 启动。
以后memcached将作为windows的一个服务每次开机时自动启动。这样服务器端已经安装完毕了。
内存分配
默认情况下,ms是用一个内置的叫“块分配器”的组件来分配内存的。舍弃c++标准的malloc/free的内存分配,而采用块分配器的主要目的 是为了避免内存碎片,否则操作系统要花费更多时间来查找这些逻辑上连续的内存块(实际上是断开的)。用了块分配器,ms会轮流的对内存进行大块的分配,并 不断重用。当然由于块的大小各不相同,当数据大小和块大小不太相符的情况下,还是有可能导致内存的浪费。
同时,ms对key和data都有相应的限制,key的长度不能超过250字节,data也不能超过块大小的限制 --- 1MB。
因为 mc所使用的hash算法,并不会考虑到每个ms的内存大小。理论上mc会分配概率上等量的kv对给每个ms,这样如果每个ms的内存都不太一样,那可能 会导致内存使用率的降低。所以一种替代的解决方案是,根据每个ms的内存大小,找出他们的最大公约数,然后在每个ms上开n个容量=最大公约数的 instance,这样就等于拥有了多个容量大小一样的子ms,从而提供整体的内存使用率。
缓存策略
当ms的hash表满了之后,新的插入数据会替代老的数据,更新的策略是LRU(最近最少使用),以及每个kv对的有效时限。Kv对存储有效时限是在mc端由app设置并作为参数传给ms的。
同时ms采用是偷懒替代法,ms不会开额外的进程来实时监测过时的kv对并删除,而是当且仅当,新来一个插入的数据,而此时又没有多余的空间放了,才会进行清除动作。
六、范例
1.加载commons-pool-1.5.6.jar、java_memcached-release_2.6.6.jar、slf4j-api-1.6.1.jar、slf4j-simple-1.6.1.jar
2.创建memcached工具类:
public class MemcachedUtil {
/** * memcached客户端单例 */ private static MemCachedClient cachedClient = new MemCachedClient(); /** * 初始化连接池 */ static { //获取连接池的实例 SockIOPool pool = SockIOPool.getInstance(); //服务器列表及其权重 String[] servers = {"127.0.0.1:11211"}; Integer[] weights = {3}; //设置服务器信息 pool.setServers(servers); pool.setWeights(weights); //设置初始连接数、最小连接数、最大连接数、最大处理时间 pool.setInitConn(10); pool.setMinConn(10); pool.setMaxConn(1000); pool.setMaxIdle(1000*60*60); //设置连接池守护线程的睡眠时间 pool.setMaintSleep(60); //设置TCP参数,连接超时 pool.setNagle(false); pool.setSocketTO(60); pool.setSocketConnectTO(0); //初始化并启动连接池 pool.initialize(); //压缩设置,超过指定大小的都压缩
// cachedClient.setCompressEnable(true);
// cachedClient.setCompressThreshold(1024*1024);
}
private MemcachedUtil(){
}
public static boolean add(String key, Object value) {
return cachedClient.add(key, value);
}
public static boolean add(String key, Object value, Integer expire) {
return cachedClient.add(key, value, expire);
}
public static boolean put(String key, Object value) {
return cachedClient.set(key, value);
}
public static boolean put(String key, Object value, Integer expire) {
return cachedClient.set(key, value, expire);
}
public static boolean replace(String key, Object value) {
return cachedClient.replace(key, value);
}
public static boolean replace(String key, Object value, Integer expire) {
return cachedClient.replace(key, value, expire);
}
public static Object get(String key) {
return cachedClient.get(key);
}
}3. 创建需要缓存的对象:
public class UserBean implements Serializable {
private static final long serialVersionUID = 9174194101246733501L; private String username; private String password; public UserBean(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((password == null) ? 0 : password.hashCode()); result = prime * result + ((username == null) ? 0 : username.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; UserBean other = (UserBean) obj; if (password == null) { if (other.password != null) return false; } else if (!password.equals(other.password)) return false; if (username == null) { if (other.username != null) return false; } else if (!username.equals(other.username)) return false; return true; } @Override public String toString() { return "username:" + username + ",password:" + password; }
}4.创建测试用例:
public class MemcachedUtilTest {
@Test public void testMemcached() { MemcachedUtil.put("hello", "world", 60); String hello = (String) MemcachedUtil.get("hello"); Assert.assertEquals("world", hello); for(int i = 0; i < 10000000; ++i) { UserBean userBean = new UserBean("Jason" + i, "123456-" + i); MemcachedUtil.put("user" + i, userBean, 60); Object obj = MemcachedUtil.get("user" + i); Assert.assertEquals(userBean, obj); } }
}
5.通过spring注入memcached:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="memcachedPool" class="com.danga.MemCached.SockIOPool" factory-method="getInstance" init-method="initialize"> <constructor-arg> <value>neeaMemcachedPool</value> </constructor-arg> <property name="servers"> <list> <value>127.0.0.1:11211</value> </list> </property> <property name="initConn"> <value>20</value> </property> <property name="minConn"> <value>10</value> </property> <property name="maxConn"> <value>50</value> </property> <property name="nagle"> <value>false</value> </property> <property name="socketTO"> <value>3000</value> </property> </bean> <bean id="memcachedClient" class="com.danga.MemCached.MemCachedClient"> <constructor-arg> <value>neeaMemcachedPool</value> </constructor-arg> </bean>
</beans>
6.创建测试用例:
public class MemcachedSpringTest {
private MemCachedClient cachedClient; @Before public void init() { ApplicationContext context = new ClassPathXmlApplicationContext("com/luo/config/beans.xml"); cachedClient = (MemCachedClient)context.getBean("memcachedClient"); } @Test public void testMemcachedSpring() { UserBean user = new UserBean("luo", "hi"); cachedClient.set("user", user); UserBean cachedBean = (UserBean)user; Assert.assertEquals(user, cachedBean); }
}
七、注意点
第一、memcached是在服务器端的内存中缓存对象的,不是缓存或硬盘;
第二、memcached的pool可以关联多个server,
String[] servers = {"10.20.185.12:11001","10.20.185.25:11001"};
Integer[] weights = {3,7};
该配置表示30%的缓存在放在第一台服务器,70%的将放在第二台服务器,这样便可以充分利用不同服务器的内存了;
第三、我最困惑的是client是如何得到相应的pool的,后然看了点源码才知道是这样的。client是通过pool的name关联到某个pool的,上面的例子中在SockIOPool pool = SockIOPool.getInstance(); 和MemCachedClient client=new MemCachedClient();虽然都没写poolName,但就是新建了一个”default“的pool,然后client关联到了这个”default“的pool。当然我们在新建这两个对象时可以给定具体的poolName。
下一篇:memcached真实项目中的应用http://blog.csdn.net/sup_heaven/article/details/32728477
参考文章:
http://snowolf.iteye.com/blog/1471805
http://my249645546.iteye.com/blog/1420061
http://blog.csdn.net/loujinhe/article/details/8491673?reload
http://hzp.iteye.com/blog/1872664
文章浏览阅读6.8k次,点赞2次,收藏8次。第一种Transactional注解标注方法修饰符为非public时,@Transactional注解将会不起作用。第二种在类内部调用调用类内部@Transactional标注的方法。这种情况下也会导致事务不开启。既然事务管理是基于动态代理对象的代理逻辑实现的,那么如果在类内部调用类内部的事务方法,这 个调用事务方法的过程并不是通过代理对象来调用的,而是直接通过this对象来调用方法,绕过的代理对 象,肯定就是没有代理逻辑了。解决方法1.可以在service层 注入自己 用自己调用方_transactional注解失效
文章浏览阅读559次。接口耦合优化与灾难控制
文章浏览阅读1.9k次,点赞4次,收藏2次。laravel7 版本移除了 auth,大家都知道以前版本是直接使用php artisan make:auth就可以使用,但是这版本不行了,那么要怎么弄呢?今天和大家说一下具体步骤。Laravel7 的 laravel/ui 包提供了一种快速方法,可以使用一些简单的命令来支持你进行身份验证所需的所有路由和视图:安装依赖包laravel/ui直接使用命令进行安装composer require laravel/ui创建auth脚手架直接使用命令进行创建#注意这里的v._laravel7.x 如何使用自定义的auth进行身份验证
文章浏览阅读9.9k次,点赞4次,收藏12次。1. pprof生成CPU和memory profile前段时间项目中遇到golang程序的性能上不去,想要找到程序的性能瓶颈所在,使用golang自带的pprof输出cpu和mem的profile文件进行分析。具体如何生成profile文件参考如下:https://golang.org/pkg/runtime/pprof/https://blog.golang.org/profiling..._gogc参数
文章浏览阅读2.1w次,点赞3次,收藏12次。学Spring Security来实现重新生成Session_shiro 登录后会修改会话id吗
文章浏览阅读1.1k次。转:http://blog.chinaaet.com/justlxy/p/5100053262 前面的文章中介绍过,每一个PCIe设备可以只有一个功能(Function),即Fun0。也可以拥有最多8个功能,即多功能设备(Multi-Fun)。不管这个PCIe设备拥有多少个功能,其每一个功能都有一个唯一独立的配置空间(Configuration Space)与之对应。 和PCI总线一样,PCIe总线中的每一个功能_pcie bdf分配
文章浏览阅读146次。1.安装OpenNMS Helm(Grafana组件)1)加载资源位置yum install https://yum.opennms.org/repofiles/opennms-repo-stable-rhel7.noarch.rpmrpm --import https://yum.opennms.org/OPENNMS-GPG-KEY2)安装yum install opennms-he..._opennms 告警
文章浏览阅读1.1k次,点赞2次,收藏4次。【第六章】组合数据类型一、组合数据类型的基本概念Python语言中最常用的组合数据类型有3大类,分别是集合类型、序列类型和映射类型。集合类型是一个具体的数据类型名称,而序列类型和映射类型是一类数据类型的总称。集合类型是一个元素集合,元素之间无序,相同元素在集合中唯一存在。序列类型是一个元素向量,元素之间存在先后关系,通过序号访问,元素之间不排他。序列类型的典型代表是字符串类型和列表类型..._ls=[1010,1010]
文章浏览阅读4.9k次。近日,第三届世界人工智能大会(WAIC)在上海召开,本次大会以“智联世界,共同家园”为主题,线下云端同步进行。此次开幕式由演讲、对话和发布活动组成。多位商界领袖、行业专家对于人工智能发表了自己的观点与见解。华为陶景文:人工智能以及数字技术将催生12万亿美元市场“随着5G、AI、产业互联网等技术的不断成熟,以及数据中心、云等新一代基础设施的加速建设和推进,第四次工业革命已经到来。”7月9日,华为公司副董事长兼CIO陶景文在2020世界人工智能大会上表示,技术正在加速第四代工业互联网在万物互联、万物智能以_人工智能 noip
文章浏览阅读4.1k次。anaconda环境下:错误:python 命令行运行出错:module ‘scipy.misc‘ has no attribute ‘toimage‘解决:打开Anaconda prompt,输入:conda install pillow 即可解决_module ‘scipy.misc‘ has no attribute ‘toimage‘
文章浏览阅读705次。简介:MMM也就是Master-Master replication Manager for MySQL,MySQL主主复制管理器。关于MySQL主主复制配置的监控,故障转移和管理的一套可伸缩的脚本套件,可以用这个套件在一组居于复制的服务器启动虚拟IP,除此以外,还有对从服务器的延迟监控,主从数据备份,节点之间重新同步功能。通过MMM方案可以实现MySQL服务器的故障转移,从而实现MySQL的..._mmm集群,三主三从
文章浏览阅读779次。1.HALL开关原理及手机应用 手机中HALL传感器由一个开关型HALL元件和两个电源开关控制管组成。其导通与否完全受到手机CPU输 出的HALL高电平信号控制,电源则来自于电池。当翻盖合上时装在翻盖中的磁铁的磁场作用于HALL传感器 (一般翻盖/折叠手机都把磁铁安装在翻盖上),HALL电路中的三极管导通,从传感器的引脚输出低电平,如果是在通话后则作为“_android hall