近年来,内存容量不断提高,价格不断下跌,操作系统已经可以支持更大的地址空间,充分利用技术提升系统性能成为一个热点。
可从数据库方面来考虑,此种方案可使用“内存数据库”,另一种方案是使用“内存对象缓存系统”,将某些量小、使用次数多的数据以key/value的方式保存在内存对象缓存系统中,减少数据库查询访问带来的性能下降。
在本文中,主要讲述后者,但在本章也对“内存数据库”的概念进行说明。
1.1 内存数据库
内存数据库,就是将数据放在内存中直接操作的数据库。相对于磁盘,内存的数据读写速度要高出几个数量级,将数据保存在内存中相比从磁盘上访问能够极大地提高应用的性能。
在数据库技术中,目前主要使用两种方法来使用大量的内存:
1) 在传统的数据库中,增大缓冲池,将一个事务所涉及的数据都放在缓冲池中,组织成相应的数据结构来进行查询和更新处理,也就是常说的共享内存技术,这种方法优化的主要目标是最小化磁盘访问。
2) 使用内存数据库技术(也叫主存数据库)技术,也就是干脆重新设计一种数据库管理系统,对查询处理、并发控制和恢复的算法和数据结构进行重新设计,以更有效的使用CPU周期和内存,这种技术近乎把整个数据库放进内存中,因而产生一种根本性的变化。
1.2 内存对象缓存系统
内存对象缓存系统用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态、数据库驱动网站的速度。
常用的是Memcached 是一个高性能的分布式内存对象缓存系统,它基于一个存储键/值对的hashmap。其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。但是它并不提供冗余(例如,复制其hashmap条目);当某个服务器S停止运行或崩溃了,所有存放在S上的键/值对都将丢失。
Memcached由Danga Interactive开发,用于提升LiveJournal.com访问速度的。LJ每秒动态页面访问量几千次,用户700万。Memcached将数据库负载大幅度降低,更好的分配资源,更快速访问。
2、应用场景Memcached一般与数据库系统配合使用,它用于存储一些经常需要读取的数据,从来达到提高性能的目的。
下面讲几个在呼叫中心系统中的应用实例。
2.1 任务可执行性检查
例如,外呼任务需要在指定“任务开始时间”和“任务结束时间”中运行,并且在“启用”状态才能运行,而且单个外呼任务的进程需要通过查询数据库定时检查(例如:1分钟)该任务是继续运行,还是终止运行。当多个外呼任务并行运行时,这种查询会给系统增加负担。
若使用内存对象缓存系统,将每个任务的状态信息以key/value对的方式存储在Memcached的“任务信息表”中(key=任务id,value:是否可运行标志),在插入任务时,在Memcached的“任务状态表”中插入一条数据;当“任务开始时间”和“任务结束时间”或“状态”发生改变时,根据key对应改变Memcached的“任务信息表”的value值。在外呼任务进行中,查询任务是否可运行时不再需要查询数据库,一般情况下只需要查询Memcached即可,从而减少了对数据库的访问。
2.2 日程检查
在呼叫中心路由时,有时候需要提供根据日程进行路由的功能,例如满足某个日程(例如:每年的“10月1日00:00:00”到“10月7日23:59:59”的日程)时才路由到A中继。一个日程可以对应多个条目,若在路由前需要要验证当前时间是否满足日程,那对应需要查询两表的信息,还需要是否满足日程条件。当并发很多个用户进行呼叫时,这种检查会给系统增加很大的负荷,在此处也可以使用内存对象缓存系统来解决问题。
开发定时触发(1分钟执行一次)程序去日程表和日程条目表中查询多条信息,将当前时间与某个日程比较,将其存入Memcached中的“日程表”中,key为日程键,value为当前时间是否在当前日程内的标志。
路由程序根据日程键检查Memcached中的“日程表”,若满足某个日程,则路由到A中继,基本不再需要针对日程对数据库进行查询。
3、应用禁忌3.1 不要将Memcached当数据库用
Memcached 的首要目的就是加快数据的响应时间,否则数据从其他数据源构建或恢复需要很长时间。一个典型的例子就是从一个数据库中恢复信息,特别是在信息显示给用户前 需要对信息进行格式化或处理的时候。Memcached 被设计用来将信息存储在内存中以避免每次在数据需要恢复时重复执行相同的任务。
切不可将 Memcached 用作运行应用程序所需信息的惟一信息源;数据应总是可以从其他信息源获取。此外,要记住 Memcached 只是一个键/值的存储。不能在数据上执行查询,或者对内容进行迭代来提取信息。
3.2 不要使用Memcached存储大批量数据
首先Memcached空间大小不算大,而且主要用于缓存,所以不应该在Memcached中存储大批量数据。若在Memcached中按照一定格式存储数据表的行数据,而且表的数据量又很大,首先空间不允许,而且查询非常麻烦,此类数据适合保存在数据库中。
3.2 Memcached并不安全
为了确保最佳性能,Memcached 并未提供任何形式的安全性,没有身份验证,也没有加密。这意味着对 memcached 服务器的访问应该这么处理:一是通过将它们放到应用程序部署环境相同的私有侧,二是如果安全性是必须的,那么就使用 UNIX® socket 并只允许当前主机上的应用程序访问此 Memcached 服务器。
这多少牺牲了一些灵活性和弹性,以及跨网络上的多台机器共享 RAM 缓存的能力,但这是在目前的情况下确保 memcached 数据安全性的惟一一种解决方案。
1、Memcached介绍Memcached是高性能的,分布式的内存对象缓存系统,用于在动态应用中减少数据库负载,提升访问速度。Memcached由Danga Interactive开发,用于提升LiveJournal.com访问速度的。LJ每秒动态页面访问量几千次,用户700万。Memcached将数据库负载大幅度降低,更好的分配资源,更快速访问。
Memcached的最新版是采用c语言进行开发和设计的,它是一个应用软件,是作为缓存服务器的服务器端运行在服务器上的,需要使用特定的语言编写客户端与其进行通信来进行数据的缓存和获取。
在系统中,通常将Memcached安装运行在服务器上,然后通过对需要的数据进行缓存,所有数据的缓存设置和存取操作,以及数据的更新后替换操作全部需要程序来进行。
2、Memcached的安装2.1 主程序的安装
一般的服务器都是采用Linux,笔者只是讲述在Windows上如何安装Memcached,在Linux上的安装请参考网上其它资料。
Windows版本的下载地址为:http://code.jellycan.com/memcached/
当前win32的最新版本是1.2.6,下载页面参考如下:
在上图中点击“memcached-1.2.6-win32-bin.zip”进入下载页面,下载后,将其解压到D盘下,解压后的D:"memcached-1.2.6-win32-bin目录下有一个memcached.exe。
在Windows的命令行(cmd命令进入命令行)窗口进入该目录,首先运行:
上面这行表示安装Memcached为服务,这样才能正常运行。接着运行如下这样来启动Memcached,还可指定-l参数,表示启动的IP,-m表示缓存大小:
若指定了-m,则表示缓存大小为-m后的数字,单位是M,例如:
运行参考如下图所示:
2.2 Java客户端的安装
下载地址为:https://github.com/gwhalin/Memcached-Java-Client
下载页面参考如下:
在上图中点击右侧区域的“Downloads”,弹出的下载小窗口如下图:
当前最新的版本是2.5.2,点击“java_memcached-release_2.5.2.zip”下载。下载后解压,目录结构如下图所示:
在应用中,需要将“java_memcached-release_2.5.2.jar”包拷贝到Java项目中。
3、Memcached的使用3.1 创建项目
在MyEclipse中创建一个名为memcacheddemo的测试项目,src放源代码,bin放classes文件,lib放jar包,并将java_memcached-release_2.5.2.jar拷贝到lib目录中,目录结构如下:
3.2 SockIOPool类及其常用方法
SockIOPool是socket连接池类,常用方法如下:
l setServers(String[] servers):设置服务器信息数组;
l setWeights(String[] weights):设置服务器权重数组;
l setInitConn(int count):设置初始连接数;
l setMinConn(int minConn):设置最小连接数;
l setMaxConn(int maxConn):设置最大连接数;
l setMaxIdle(long arg0):设置最大处理时间;
l setMaintSleep(long arg0):主线程的睡眠时间;
l initialize():初始化连接池。
3.3 MemCachedClient类及其常用方法
MemCachedClient类用于对Memcached内存对象缓存系统进行操作,常用方法如下:
l add(String key, Object value):添加一个键值对到缓存中;
l add(String key, Object value,Date expires):添加一个键值对到缓存中,并设置其超时时间;
l set(String key, Object value):在缓存中设置一个键的值;
l set(String key, Object value, Date expires):在缓存中设置一个键的值,并设置其超时时间;
l get(String key):获得某个键的值。
l incr(String key):为某个键上的值执行+1操作;
l decr(String key):为某个键上的值执行-1操作;
l replace(String key, String value):将某个键的值替换成新的值;
l replace(String key, String value, Date expires):将某个键的值替换成新的值,并设置其超时时间。
3.4 使用实例
在memcacheddemo工程的源码目录创建测试的Java类MemcachedTest,该类的代码参考如下:
import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;
/**
* 使用memcached的缓存测试类.
* @author 阿蜜果
*/
public class MemcachedTest {
// 创建全局的唯一实例
protected static MemCachedClient mcc = new MemCachedClient();
protected static MemcachedTest memCached = new MemcachedTest();
// 设置与缓存服务器的连接池
static {
// 服务器列表和其权重
String[] servers = {"127.0.0.1:11211"};
Integer[] weights = {3};
// 获取socket连接池的实例对象
SockIOPool pool = SockIOPool.getInstance();
// 设置服务器信息
pool.setServers(servers);
pool.setWeights(weights);
// 设置初始连接数、最小和最大连接数以及最大处理时间
pool.setInitConn(5);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaxIdle(1000 * 60 * 60 * 6);
// 设置主线程的睡眠时间
pool.setMaintSleep(30);
// 设置TCP的参数,连接超时等
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setSocketConnectTO(0);
// 初始化连接池
pool.initialize();
// 压缩设置,超过指定大小(单位为K)的数据都会被压缩
mcc.setCompressEnable(true);
mcc.setCompressThreshold(64 * 1024);
}
/**
* 保护型构造方法,不允许实例化
*/
protected MemcachedTest() {
}
/**
* 获取唯一实例.
* @return
*/
public static MemcachedTest getInstance() {
return memCached;
}
/**
* 添加一个指定的值到缓存中.
* @param key 键
* @param value 值
* @return 在缓存中若该key不存在,并成功添加返回true,否则将返回false
*/
public boolean add(String key, Object value) {
return mcc.add(key, value);
}
/**
* 添加一个键值对到缓存中.
* @param key 键
* @param value 值
* @param expires 超时时间
* @return 在缓存中若该key不存在,并成功添加返回true,否则将返回false
*/
public boolean add(String key, Object value, Date expires) {
return mcc.add(key, value, expires);
}
/**
* 将某个键的值改变成新值,首先需要保证该键存在.
* @param key 键
* @param value 值
* @return 成功返回true,失败返回false
*/
public boolean replace(String key, Object value) {
return mcc.replace(key, value);
}
/**
* 将某个键的值改变成新值,首先需要保证该键存在.
* @param key 键
* @param value 值
* @param expires 超时时间
* @return 成功返回true,失败返回false
*/
public boolean replace(String key, Object value, Date expires) {
return mcc.replace(key, value, expires);
}
/**
* 添加一个指定的值到缓存中.
* @param key
* @param value
* @return 成功返回true,否则返回false
*/
public boolean set(String key, Object value) {
return mcc.set(key, value);
}
/**
* 添加一个指定的值到缓存中,并设置其超时时间.
* @param key 键
* @param value 值
* @param expires 超时时间
* @return 成功返回true,否则返回false
*/
public boolean set(String key, Object value, int expires) {
return mcc.set(key, value, expires);
}
/**
* 根据指定的关键字获取对象.
* @param key
* @return 返回value
*/
public Object get(String key) {
return mcc.get(key);
}
/**
* 将指定key的value值+1,将返回最后的value值
* @param key
* @return 返回最后的value值
*/
public long incr(String key) {
return mcc.incr(key);
}
/**
* 将指定key的value值-1,将返回最后的value值
* @param key
* @return 返回最后的value值
*/
public long decr(String key) {
return mcc.decr(key);
}
/**
* 测试方法
* @param args
*/
public static void main(String[] args) {
MemcachedTest cache = MemcachedTest.getInstance();
cache.set("count", 123);
System.out.println("count=" + cache.get("count"));
boolean flag = cache.add("schedule_2", "0");
System.out.println("flag=" + flag);
System.out.println("schedule_2=" + cache.get("schedule_2"));
}
}
运行结果为:
flag=true
schedule_2=0
《Memcached Java客户端编程》: