志当存高远,功到自然成!

少年强则中国强,少年进步则中国进步!

BlogJava 首页 新随笔 联系 聚合 管理
  53 Posts :: 2 Stories :: 2 Comments :: 0 Trackbacks

2010年8月11日 #

mysql 5.1支持触发器以及自定义函数接口(UDF)的特性,如果配合libmemcache以及Memcached Functions for MySQL,就能够实现memcache的自动更新。简单记录一下安装测试步骤。

  mysql 5.1支持触发器以及自定义函数接口(UDF)的特性,如果配合libmemcache以及Memcached Functions for MySQL,就能够实现memcache的自动更新。简单记录一下安装测试步骤。

  安装步骤

  安装memcached,这个步骤很简单,随处可见。安装mysql server 5.1RC,安装办法也很大众,不废话 了。

  编译libmemcached,解压后安装即可./configure; make; make install

  编译Memcached Functions for MySQL,在http://download.tangent.org/找一个最新的版本下载就 是,./configure --with-mysql=/usr/local/mysql/bin/mysql_config -- libdir=/usr/local/mysql/lib/mysql/

  make

  make install

  接下来有两个办法让Memcached Functions for MySQL在mysql中生效。

  在mysql的shell中执行memcached_functions_mysql源码目录下的sql/install_functions.sql,这会 把memcache function作为UDF加入mysql。

  运行memcached_functions_mysql源码目录下的utils/install.pl,这是一个perl脚本,作用同上一 条。

  测试memcache function。

  以下测试脚本摘自memcached_functions_mysql的源码目录,有兴趣可以试试。


PLAIN TEXTCODE: drop table if exists urls;
create table urls (
id int(3) not null,
url varchar(64) not null default '',
primary key (id)
);
select memc_servers_set('localhost:11211');
select memc_set('urls:sequence', 0);
DELIMITER |
DROP TRIGGER IF EXISTS url_mem_insert;
CREATE TRIGGER url_mem_insert
BEFORE INSERT ON urls
FOR EACH ROW BEGIN
SET NEW.id= memc_increment('urls:sequence');
SET @mm= memc_set(concat('urls:',NEW.id), NEW.url);
END |
DELIMITER ;
insert into urls (url) values ('http://google.com');
insert into urls (url) values ('http://www.ooso.net/index.php');
insert into urls (url) values ('http://www.ooso.net/');
insert into urls (url) values ('http://slashdot.org');
insert into urls (url) values ('http://mysql.com');
select * from urls;
select memc_get('urls:1');
select memc_get('urls:2');
select memc_get('urls:3');
select memc_get('urls:4');
select memc_get('urls:5');
posted @ 2010-08-12 15:22 存鹰之心于高远,取鹰之志而凌云,习鹰之性以涉险,融鹰之神在山巅. 阅读(454) | 评论 (0)编辑 收藏

假设我们有3台memcached 服务器,server1 和server2 有3GB 的内存空间,server3 有2GB 的内存空间.
下面程序说明怎么来创建客户端.
import com.danga.MemCached.*;

public class MyClass {

// 创建一个 memcached 客户端对象

protected static MemCachedClient mcc = new MemCachedClient();

// 创建  memcached连接池

static

{  // 指定memcached服务地址 String[] servers =
{ "server1.mydomain.com:1121","server2.mydomain.com:1121",
"server3.mydomain.com:1121" };
// 指定memcached服务器负载量
Integer[]  weights    ={ 3, 3, 2 };
// 从连接池获取一个连接实例

SockIOPool pool = SockIOPool.getInstance();

// 设置服务器和服务器负载量

pool.setServers( servers );

pool.setWeights( weights );

// 设置一些基本的参数

//设置初始连接数5   最小连接数 5   最大连接数 250

//设置一个连接最大空闲时间6小时

pool.setInitConn( 5 );

pool.setMinConn( 5 );

pool.setMaxConn( 250 );

pool.setMaxIdle( 1000 * 60 * 60 * 6 );

// 设置主线程睡眠时间

// 每隔30秒醒来  然后

// 开始维护 连接数大小

pool.setMaintSleep( 30 );

// 设置tcp 相关的树形

// 关闭nagle算法

// 设置 读取 超时3秒钟  set the read timeout to 3 secs

//  不设置连接超时

pool.setNagle( false );

pool.setSocketTO( 3000 );

pool.setSocketConnectTO( 0 );

// 开始初始化 连接池

pool.initialize();

// 设置压缩模式

//如果超过64k压缩数据

mcc.setCompressEnable( true );

mcc.setCompressThreshold( 64 * 1024 );

}

public static void examples() {

mcc.set( "foo", "This is a test String" );

String bar = mcc.get( "foo" );

}

}
MemCachedClient 类 常用的方法说明

创建 client对象 设置参数:

MemCachedClient mc = new MemCachedClient();
//压缩模式
mc.setCompressEnable(true);
// 如果 cache数据 大于4 KB  就启用压缩
mc.setCompressThreshold(4096);
// 基本类型tostring方法
// 通常不需要设置
mc.setPrimitiveAsString(true);
存储一个对象:

MemCachedClient mc = new MemCachedClient();

String key   = "cacheKey1";

Object value = SomeClass.getObject();

mc.set(key, value);
用客户端hashcode 存储一个对象:


MemCachedClient mc = new MemCachedClient();

String key   = "cacheKey1";

Object value = SomeClass.getObject();

Integer hash = new Integer(45);

mc.set(key, value, hash);

set方法:在cache中存储一个指定对象

   add 和replace 方法功能差不多

  add -- 如果不存在 这个key的对象,将会存储一个对象到cache中
  replace --只有当存在指定key对象的时候 会覆盖已有对象
删除一个对象:

MemCachedClient mc = new MemCachedClient();

String key   = "cacheKey1";

mc.delete(key);
结合hashcode 删除一个对象:

MemCachedClient mc = new MemCachedClient();

String key   = "cacheKey1";

Integer hash = new Integer(45);

mc.delete(key, hashCode);
怎么cache计数,增 减计数:

MemCachedClient mc = new MemCachedClient();

String key   = "counterKey";

mc.storeCounter(key, new Integer(100));

System.out.println("counter after adding      1: " mc.incr(key));

System.out.println("counter after adding      5: " mc.incr(key, 5));

System.out.println("counter after subtracting 4: " mc.decr(key, 4));

System.out.println("counter after subtracting 1: " mc.decr(key));
利用客户端的hashcode存储计数 增减 计数:

MemCachedClient mc = new MemCachedClient();

String key   = "counterKey";

Integer hash = new Integer(45);

mc.storeCounter(key, new Integer(100), hash);

System.out.println("counter after adding      1: " mc.incr(key, 1, hash));

System.out.println("counter after adding      5: " mc.incr(key, 5, hash));

System.out.println("counter after subtracting 4: " mc.decr(key, 4, hash));

System.out.println("counter after subtracting 1: " mc.decr(key, 1, hash));
获取一个对象:

MemCachedClient mc = new MemCachedClient();

String key   = "key";

Object value = mc.get(key);
用客户端hashcode获取一个对象:

MemCachedClient mc = new MemCachedClient();

String key   = "key";

Integer hash = new Integer(45);

Object value = mc.get(key, hash);
MemCachedClient mc = new MemCachedClient();

String key   = "key";

Integer hash = new Integer(45);

Object value = mc.get(key, hash);
从cache 中获取多个对象

MemCachedClient mc = new MemCachedClient();

String[] keys      ={ "key", "key1", "key2" };Mapvalues = mc.getMulti(keys);
用客户端hashcode 从cache中获取多个对象
MemCachedClient mc = new MemCachedClient();
String[] keys      = { "key", "key1", "key2" };

Integer[] hashes   =
{ new Integer(45), new Integer(32), new Integer(44) };
Mapvalues = mc.getMulti(keys, hashes);
清空所有的对象
MemCachedClient mc = new MemCachedClient();

mc.flushAll();
得到服务器memcached的状态信息
MemCachedClient mc = new MemCachedClient();

Map stats = mc.stats();
注意点
1:Failover/Failback
当一个memcached服务器失效的时候客户端默认会failover另一个服务去.

如果失效的服务器 恢复运行,客户端会返回到原来连接的服务器.
如果你不想用这个功能 设置下面的参数
pool.setFailover( false );
pool.setFailback( false );

2:序列化

Boolean
Byte
String
Character
StringBuffer
StringBuilder
Short
Long
Double
Float
Date
java默认的类型没有实现序列化 可以设置
mcc.setPrimitiveAsString( true )替代.
Meetup.com实践过程中得出的一个经验 ,项目中model 对象implement Externalizable 实现序列化,
可以节省cache 对象的大小。从而节省网络带宽和内存空间。
posted @ 2010-08-11 23:21 存鹰之心于高远,取鹰之志而凌云,习鹰之性以涉险,融鹰之神在山巅. 阅读(10756) | 评论 (1)编辑 收藏

一:硬架构

1 :机房的选择:

在 选择机房的时候,根据网站用户的地域分布,可以选择网通或电信机房,但更多时候,可能双线机房才是合适的。越大的城市,机房价格越贵,从成本的角度看可以 在一些中小城市托管服务器,比如说广州的公司可以考虑把服务器托管在东莞,佛山等地,不是特别远,但是价格会便宜很多。

2 :带宽的大小:

通常老板花钱请我们架构网站的时候,会给我们提出一些目标,诸如网站每天要能承受100 万PV 的访问量等等。这时我们要预算一下大概需要多大的带宽,计算带宽大小主要涉及两个指标(峰值流量和页面大小),我们不妨在计算前先做出必要的假设:

第一:假设峰值流量是平均流量的5 倍。
第二:假设每次访问平均的页面大小是100K 字节左右。

如果100 万PV 的访问量在一天内平均分布的话,折合到每秒大约12 次访问,如果按平均每次访问页面的大小是100K 字节左右计算的话,这12 次访 问总计大约就是1200K 字节,字节的单位是Byte ,而带宽的单位是bit ,它们之间的关系是1Byte = 8bit ,所以1200K Byte 大致就相当于9600K bit ,也就是9Mbps 的样子,实际情况中,我们的网站必须能在峰值流量时保持正常访问,所以按照假设的峰值流量算,真实带宽的需求应该在45Mbps  左右。

当然,这个结论是建立在前面提到的两点假设的基础上,如果你的实际情况和这两点假设有出入,那么结果也会有差别。

3 :服务器的划分:

先看我们都需要哪些服务器:图片服务器,页面服务器,数据库服务器,应用服务器,日志服务器等等。

对于访问量大点的网站而言,分离单独的图片服务器和页面服务器相当必要,我们可以用lighttpd 来跑图片服务器,用apache 来跑页面服务 器,当然也可以选择别的,甚至,我们可以扩展成很多台图片服务器和很多台页面服务器,并设置相关域名,如img.domain.com 和 www.domain.com ,页面里的图片路径都使用绝对路径,如<img src="http://img.domain.com/abc.gif" /> ,然后设置DNS 轮循,达到最初级的负载均衡。当然,服务器多了就不可避免的涉及一个同步的问题,这个可以使用rsync 软件来搞定。

数据库服务器是重中之重,因为网站的瓶颈问题十有八九是出在数据库身上。现在一般的中小网站多使用MySQL 数据库,不过它的集群功能似乎还没有达 到stable 的阶段,所以这里不做评价。一般而言,使用MySQL 数据库的时候,我们应该搞一个主从(一主多从)结构,主数据库服务器使用innodb  表结构,从数据服务器使用myisam 表结构,充分发挥它们各自的优势,而且这样的主从结构分离了读写操作,降低了读操作的压力,甚至我们还可以设定一个专门的从服务器做备份服务器,方便备份。不然如果你只有一台主服务器,在大数据量的情况下,mysqldump 基本就没戏了,直接拷贝数据文件的话,还得先停止数据库服务再拷贝,否则备份文件会出错。但对于很多网站而言,即使数据库服务仅停止了一秒也是不可接受的。如果你有了一台从数据库服务器,在备份数 据的时候,可以先停止服务(slave stop )再备份,再启动服务(slave start )后从服务器会自动从主服务器同步数据,一切都没有影响。但是主从结构也是有致命缺点的,那就是主从结构只是降低了读操作的压力,却不能降低写操作的压力。为了适应更大的规模,可能只剩下最后这招了:横向/ 纵向分割数据库。所谓横向分割数据库,就是把不同的表保存到不同的数据库服务器上,比如说用户表保存在A 数据库服务器上,文章表保存在B 数据库服务器上,当然这样的分割是有代价的,最基本的就是你没法进行LEFT JOIN 之类的操作了。所谓纵向分割数据库,一般是指按照用户标识(user_id )等来划分数据存储的服务器,比如说:我们有5 台数据库服务器,那么 “user_id % 5 + 1” 等于1 的就保存到1 号服务器,等于2 的就保存到2 好服务器,以此类推,纵向分隔的原则有很多种,可以视情况选择。不过和横向分割数据库一样,纵向分割 数据库也是有代价的,最基本的就是我们在进行如COUNT, SUM 等汇总操作的时候会麻烦很多。综上所述,数据库服务器的解决方案一般视情况往往是一个混合的方案,以其发挥各种方案的优势,有时候还需要借助 memcached 之类的第三方软件,以便适应更大访问量的要求。

如果有专门的应用服务器来跑PHP 脚本是最合适不过的了,那样我们的页面服务器只保存静态页面就可以了,可以给应用服务器设置一些诸如 app.domain.com 之类的域名来和页面服务器加以区别。对于应用服务器,我还是更倾向于使用prefork 模式的apache ,配上必要的 xcache 之类的PHP 缓存软件,加载模块要越少越好,除了mod_rewrite 等必要的模块,不必要的东西统统舍弃,尽量减少httpd 进程的内存消耗,而那些图片服务器,页面服务器等静态内容就可以使用lighttpd 或者tux 来搞,充分发挥各种服务器的特点。

如果条件允许,独立的日志服务器也是必要的,一般小网站的做法都是把页面服务器和日志服务器合二为一了,在凌晨访问量不大的时候cron 运行前一天 的日志计算,不过如果你使用awstats 之类的日志分析软件,对于百万级访问量而言,即使按天归档,也会消耗很多时间和服务器资源去计算,所以分离单独的日志服务器还是有好处的,这样不会影响正式服务器的工作状态。

二:软架构

1 :框架的选择:

现在的PHP 框架有很多选择,比如:CakePHP ,Symfony ,Zend Framework 等等,至于应该使用哪一个并没有唯一的答案,要根据Team 里团队成员对各个框架的了解程度而定。很多时候,即使没有使用框架,一样能写出好的程序来,比如Flickr 据说就是用Pear+Smarty 这样的类库写出来的,所以是否用框架,用什么框架,一般不是最重要,重要的是我们的编程思想里要有框架的意识。

现在的.NET 框架有很多选择,比如:cnForums ,.text ,cs, Castle, 等等

2 :逻辑的分层:

网站规模到了一定的程度之后,代码里各种逻辑纠缠在一起,会给维护和扩展带来巨大的障碍,这时我们的解决方式其实很简单,那就是重构,将逻辑进行分层。通常,自上而下可以分为表现层,应用层,领域层,持久层。

所 谓表现层,并不仅仅就指模板,它的范围要更广一些,所有和表现相关的逻辑都应该被纳入表现层的范畴。比如说某处的字体要显示为红色,某处的开头要空两格, 这些都属于表现层。很多时候,我们容易犯的错误就是把本属于表现层的逻辑放到了其他层面去完成,这里说一个很常见的例子:我们在列表页显示文章标题的时 候,都会设定一个最大字数,一旦标题长度超过了这个限制,就截断,并在后面显示“..” ,这就是最典型的表现层逻辑,但是实际情况,有很多程序员都是在非表现层代码里完成数据的获取和截断,然后赋值给表现层模板,这样的代码最直接的缺点就是同样一段数据,在这个页面我可能想显示前10 个字,再另一个 页面我可能想显示前15 个字,而一旦我们在程序里固化了这个字数,也就丧失了可移植性。正确的做法是应该做一个视图助手之类的程序来专门处理此类逻辑,比如说:Smarty 里的truncate 就属于这样的视图助手(不过它那个实现不适合中文)。

所谓应用层,它的主要作用是定义用户可以做什么,并把操作结果反馈给表现层。至于如何做,通常不是它的职责范围(而是领域层的职责范围),它会通过委派把如何做的工作交给领域层去处理。在使用MVC 架构的网站中,我们可以看到类似下面这样的URL :domain.com/articles/view/123 ,其内部编码实现,一般就是一个Articles 控制器类,里面有一个view 方法,这就是一 个典型的应用层操作,因为它定义了用户可以做一个查看的动作。在MVC 架构中,有一个准则是这么说的:Rich Model Is Good 。言外之意,就是Controller 要保持“ 瘦” 一些比较好,进而说明应用层要尽量简单,不要包括涉及领域内容的逻辑。

所谓领域层,最直接的解释就是包含领域逻辑的层。它是一个软件的灵魂所在。先来看看什么叫领域逻辑,简单的说,具有明确的领域概念的逻辑就是领域逻辑,比如我们在ATM 机上取钱,过程大致是这样的:插入银联卡,输入密码,输入取款金额,确定,拿钱,然后ATM 吐出一个交易凭条。在这个过程中,银联卡 在ATM 机器里完成钱从帐户上划拨的过程就是一个领域逻辑,因为取钱在银行中是一个明确的领域概念,而ATM 机吐出一个交易凭条则不是领域逻辑,而仅是一 个应用逻辑,因为吐出交易凭条并不是银行中一个明确的领域概念,只是一种技术手段,对应的,我们取钱后不吐交易凭条,而发送一条提醒短信也是可能的,但并 不是一定如此,如果在实际情况中,我们要求取款后必须吐出交易凭条,也就是说吐出交易凭条已经和取款紧密结合,那么你也可以把吐出交易凭条看作是领域逻辑 的一部分,一切都以问题的具体情况而定。在Eric 那本经典的领域驱动设计中,把领域层分为了五种基本元素:实体,值对象,服务,工厂,仓储。具体可以参 阅书中的介绍。领域层最常犯的错误就是把本应属于领域层的逻辑泄露到了其他层次,比如说在一个CMS 系统,对热门文章的定义是这样的:每天被浏览的次数多 于1000 次,被评论的次数多于100 次,这样的文章就是热门文章。对于一个CMS 来说,热门文章这个词无疑是一个重要的领域概念,那么我们如何实现这个逻辑的设计的?你可能会给出类似下面的代码:“SELECT ... FROM ... WHERE  浏览 > 1000 AND  评论 > 100” ,没错,这是最简单的实现方式,但是这里需要注意的是“ 每天被浏览的次数多于1000 次,被评论的次数多于100 次” 这个重要的领域逻辑被隐藏到了SQL 语句中,SQL 语句显然不属于领域层的范畴,也就是说,我们的领域逻辑泄露了。

所谓持久层,就是指把我们的领域模型保存到数据库中。因为我们的程序代码是面向对象风格的,而数据库一般是关系型的数据库,所以我们需要把领域模型 碾平,才能保存到数据库中,但是在 PHP 里,直到目前还没有非常好的 ORM 出现,所以这方面的解决方案不是特别多,参考 Martin 的企业应用架构模式一   书,大致可以使用的方法有行数据入口( Row Data Gateway )或者表数据入口( Table Data Gateway),或者把领域层和持久层合二为一变成活动记录(Active Record)的方式。

posted @ 2010-08-11 14:32 存鹰之心于高远,取鹰之志而凌云,习鹰之性以涉险,融鹰之神在山巅. 阅读(401) | 评论 (0)编辑 收藏

TOMCAT崩溃事件

http://www.blogjava.net/tedeyang/archive/2008/06/04/205740.html

今天一大早产品一部项目经理就来找我,他们的一台服务器昨天晚上tomcat服务崩溃,还不能重启服务,最后将服务器重启才OK。
我将事件过程和分析过程记录如下:

服务器:win 2000 sp4,apache 2 + tomcat 5.0 采用mod_jk级联。内存2G,硬盘剩余空间充足,CPU基本空闲。
主要应用:J2EE 1.4,JDBC(连接另一台mysql服务器)
崩溃时间: 2008-6-3 18:37:50

一.各种日志综合如下:

   1.37分45秒,操作系统事件中诺顿杀毒软件报内存过低警报
   2.37分45秒,web应用抛出JDBC连接异常:

2008-06-03 18:37:45 cn.*.db.DBManager.getConnection(DBManager.java:157) ERROR swim.db.DBManager   com.mysql.jdbc.CommunicationsException: Communications link failure due to underlying exception:
** BEGIN NESTED EXCEPTION **
java.net.SocketException
MESSAGE: java.net.SocketException: No buffer 
space available (maximum connections reached?): JVM_Bind


   3.37分50秒,tomcat抛出session无法save异常:

2008-06-03 18:37:50 ERROR- IOException while saving persisted sessions: java.io.FileNotFoundException: "izzs"SESSIONS.ser (系统资源不足,无法完成请求的服务。)
java.io.FileNotFoundException: "izzs"SESSIONS.ser (系统资源不足,无法完成请求的服务。)
    at java.io.FileOutputStream.open(Native Method)
    at java.io.FileOutputStream.
<init>(FileOutputStream.java:179)
    at java.io.FileOutputStream.
<init>(FileOutputStream.java:70)
    at org.apache.catalina.session.StandardManager.doUnload(StandardManager.java:
511)
    at org.apache.catalina.session.StandardManager.unload(StandardManager.java:
485)
    at org.apache.catalina.session.StandardManager.stop(StandardManager.java:
687)
    at org.apache.catalina.core.StandardContext.stop(StandardContext.java:
4496)
    at org.apache.catalina.core.StandardContext.reload(StandardContext.java:
3037)
    at org.apache.catalina.core.StandardContext.backgroundProcess(StandardContext.java:
4658)
    at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:
1619)
    at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:
1628)
    at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:
1628)
    at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:
1608)
    at java.lang.Thread.run(Thread.java:
534)


 

二.简单分析

崩溃原因:内存不足导致资源不足,引起Tomcat的session崩溃。
  这台服务器上运行着很多应用,是什么原因引起内存不足还无法确定。
初步判断罪魁祸首可能是apache,该进程平常占用500MB内存,经常会飚到1G以上。

Apache2的配置文件中:KeepAlive=On,MaxKeepAliveRequests=100,KeepAliveTimeout=15,分析aceess.log文件可以发现每个页面触发的request数量在10个以下,点击率较低,可能使连接过多。
我建议将keepAlive设为off,增加CPU负载,降低内存消耗。

三.效果

 有待观察......

参考资料:
http://www.withend.com/post/78.html

四.结局 
时隔一天,晚上九点再次崩溃,黑暗事件重演。
这一次,我才得知原来该apache还配置有其他域名,于是调出该域名下的access.log。项目经理去了机房,在轰轰地风扇声中打电话给我,让我分析分析。
仔细看访问日志,发现原来有N多Connect 443连接,443是什么?是SSL端口!HTTPS!,Connect命令则显然是代理功能!
而且这些connect的IP来自全球各地,加拿大、美国、澳洲、新西兰、北京、上海、英国、哪都有。
看来这台服务器是被人当代理服务器用了。
怪不得半夜会死机,人家西半球那时正大白天撒欢儿呢。

问题就出在apache的配置上,由于应用众多,并且这台服务器还是其他几台web服务器的对外出口,因此apache中配置了反向代理,不过不小心把正向代理(mod_proxy模块的ProxyRequests指令)也打开了。
看看apache2.0的官方文档中mod_proxy部分,里面明明白白写着:

警告
在您没有对服务器采取安全措施之前,请不要用ProxyRequests启用您的代理。一个开放的代理服务器不仅对您的网络有威胁,对整个因特网来说也同样如此。
真的是很有威胁!大量代理请求急剧消耗内存,最终造成死机!

解决办法就是把正向代理关掉:ProxyRequests Off
posted @ 2010-08-11 14:25 存鹰之心于高远,取鹰之志而凌云,习鹰之性以涉险,融鹰之神在山巅. 阅读(2011) | 评论 (0)编辑 收藏

分布式缓存系统Memcached简介与实践

http://blog.csdn.net/hitman9099/archive/2008/09/04/2878417.aspx

 

缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载。缓存是解决这个问题的好办法。但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够灵活。此时Memcached或许是你想要的。

Memcached是什么?
Memcached是由Danga Interactive开发的,高性能的,分布式的内存对象缓存系统,用于在动态应用中减少数据库负载,提升访问速度。

Memcached能缓存什么?
通过在内存里维护一个统一的巨大的hash表,Memcached能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。

Memcached快么?

非 常快。Memcached使用了libevent(如果可以的话,在linux下使用epoll)来均衡任何数量的打开链接,使用非阻塞的网络I/O,对 内部对象实现引用计数(因此,针对多样的客户端,对象可以处在多样的状态), 使用自己的页块分配器和哈希表, 因此虚拟内存不会产生碎片并且虚拟内存分配的时间复杂度可以保证为O(1).。

Danga Interactive为提升Danga Interactive的速度研发了Memcached。目前,LiveJournal.com每天已经在向一百万用户提供多达两千万次的页面访问。而这 些,是由一个由web服务器和数据库服务器组成的集群完成的。Memcached几乎完全放弃了任何数据都从数据库读取的方式,同时,它还缩短了用户查看 页面的速度、更好的资源分配方式,以及Memcache失效时对数据库的访问速度。

Memcached的特点
Memcached的缓存是一种分布式的,可以让不同主机上的多个用户同时访问, 因此解决了共享内存只能单机应用的局限,更不会出现使用数据库做类似事情的时候,磁盘开销和阻塞的发生。

Memcached的使用 
 
Memcached服务器端的安装 (此处将其作为系统服务安装)
下载文件:memcached 1.2.1 for Win32 binaries (Dec 23, 2006)
    1 解压缩文件到c:\memcached
   2 命令行输入 'c:\memcached\memcached.exe -d install' 
    3 命令行输入 'c:\memcached\memcached.exe -d start' ,该命令启动 Memcached ,默认监听端口为 11211
通过 memcached.exe -h 可以查看其帮助

 

一个简单的测试例子

  1. package com.mapbar.util.cache;

  2. import java.util.Date;
  3. import java.util.ResourceBundle;

  4. import com.danga.MemCached.MemCachedClient;
  5. import com.danga.MemCached.SockIOPool;
  6. /**
  7.  * 使用配置文件方式的memcache组件
  8.  *
  9.  */
  10. public class Cache {

  11.  protected static ResourceBundle rb=ResourceBundle.getBundle("memcached");;
  12.  public static MemCachedClient mcc = new MemCachedClient(); 
  13.  static {
  14.   init();
  15.  }     
  16.  public static void init(){
  17.   synchronized (mcc) {  
  18.    System.out.println("init call");
  19.    String[] servers =rb.getString("cache.servers").split(",");       
  20.          
  21.          String[] strWeights =rb.getString("cache.weights").split(",");  
  22.          Integer[] weights = new Integer[strWeights.length];
  23.          for(int i=0;i<strWeights.length;i++){
  24.           weights[i]=Integer.parseInt(strWeights[i]);
  25.          }
  26.         
  27.          //创建一个实例对象SockIOPool     
  28.          SockIOPool pool = SockIOPool.getInstance();       
  29.         
  30.          // set the servers and the weights    
  31.          //设置Memcached Server    
  32.          pool.setServers( servers );       
  33.          pool.setWeights( weights );       
  34.          pool.setInitConn(Integer.parseInt(rb.getString("cache.initConn")));       
  35.          pool.setMinConn(Integer.parseInt(rb.getString("cache.minConn")));       
  36.          pool.setMaxConn(Integer.parseInt(rb.getString("cache.maxConn")));       
  37.          pool.setMaxIdle(Long.parseLong(rb.getString("cache.maxIdle")));       
  38.         
  39.          // set the sleep for the maint thread       
  40.          // it will wake up every x seconds and       
  41.          // maintain the pool size       
  42.          pool.setMaintSleep( 30 );       
  43.         
  44. //         Tcp的规则就是在发送一个包之前,本地机器会等待远程主机    
  45. //         对上一次发送的包的确认信息到来;这个方法就可以关闭套接字的缓存,    
  46. //         以至这个包准备好了就发;    
  47.          pool.setNagle( false );       
  48.          //连接建立后对超时的控制    
  49.          pool.setSocketTO( 3000 );    
  50.          //连接建立时对超时的控制    
  51.          pool.setSocketConnectTO( 0 );       
  52.         
  53.          // initialize the connection pool       
  54.          //初始化一些值并与MemcachedServer段建立连接    
  55.          pool.initialize();    

  56.          // lets set some compression on for the client       
  57.          // compress anything larger than 64k       
  58.          mcc.setCompressEnable( true );       
  59.          mcc.setCompressThreshold( 64 * 1024 );  
  60.   }
  61.  }
  62.  protected static void bulidCache(){  
  63.   
  64.         //set(key,value,Date) ,Date是一个过期时间,如果想让这个过期时间生效的话,这里传递的new Date(long date) 中参数date,需要是个大于或等于1000的值。    
  65.         //因为java client的实现源码里是这样实现的 expiry.getTime() / 1000 ,也就是说,如果 小于1000的值,除以1000以后都是0,即永不过期    
  66.         mcc.set( "test""This is a test String" ,new Date(100000));   //十秒后过期    
  67.               
  68.     }       
  69.       
  70.  protected static void output() {       
  71.         //从cache里取值    
  72.         String value = (String) mcc.get( "test" );       
  73.         System.out.println(value);        
  74.     }       
  75.            
  76.  public static void main(String[] args){       
  77.         bulidCache();      
  78.         output();           
  79.     }        

  80. }


其中在classespath下有个memcached.properties文件

内容如下:

##muti servers spilt by ,

#这里是你的memcached启动的地址
cache.servers=192.168.0.116:11211  
cache.weights=1
#memcached be used
cache.cluster=true

#set some basic pool settings       
#5 initial, 5 min, and 250 max conns       
#and set the max idle time for a conn       
#to 6 hours  6*60*60*1000
cache.initConn=5
cache.minConn=5
cache.maxConn=250
cache.maxIdle=21600000

 

 

 

 

在Windows下安装Memcached

http://www.oschina.net/docs/article/4

很多phper不知道如何在Windows下搭建Memcache的开发调试环境,最近个人也在研究Memcache,记录下自己安装搭建的过程。
其实我开始研究Memcache的时候并不知道居然还有memcached for Win32这个鸟东西,害得我在CnetOS下折腾1天才搞定,今天突然发现Windows下的Memcache进行开发调试完全没有问题,所以写篇 Memcache的文档分享给大家。

Windows下的Memcache安装
1. 下载memcache的 windows稳定版,解压放某个盘下面,比如在c:\memcached
2. 在终端(也即cmd命令界面)下输入 ‘c:\memcached\memcached.exe -d install’ 安装
3. 再输入: ‘c:\memcached\memcached.exe -d start’ 启动。NOTE: 以后memcached将作为windows的一个服务每次开机时自动启动。这样服务器端已经安装完毕了。
4.下载php_memcache.dll, 请自己查找对应的php版本的文件
5. 在C:\winnt\php.ini 加入一行 ‘extension=php_memcache.dll’
6.重新启动Apache,然后查看一下phpinfo,如果有memcache,那么就说明安装成功!

memcached的基本设置

-p 监听的端口
-l 连接的IP地址, 默认是本机
-d start 启动memcached服务
-d restart 重起memcached服务
-d stop|shutdown 关闭正在运行的memcached服务
-d install 安装memcached服务
-d uninstall 卸载memcached服务
-u 以的身份运行 (仅在以root运行的时候有效)
-m 最大内存使用,单位MB。默认64MB
-M 内存耗尽时返回错误,而不是删除项
-c 最大同时连接数,默认是1024
-f 块大小增长因子,默认是1.25
-n 最小分配空间,key+value+flags默认是48
-h 显示帮助

Memcache环境测试
运行下面的php文件,如果有输出This is a test!,就表示环境搭建成功。开始领略Memcache的魅力把!
< ?php
$mem = new Memcache;
$mem->connect(”127.0.0.1″, 11211);
$mem->set(’key’, ‘This is a test!’, 0, 60);
$val = $mem->get(’key’);
echo $val;
?>

posted @ 2010-08-11 14:22 存鹰之心于高远,取鹰之志而凌云,习鹰之性以涉险,融鹰之神在山巅. 阅读(451) | 评论 (0)编辑 收藏

日志原文:http://j2ee.blog.sohu.com/70343632.html   
memcache for win 软件主页:http://jehiah.cz/projects/memcached-win32/  
http://labs.northscale.com/memcached-packages/
meecache for liunx 软件主页:http://memcached.org/  
                                     
最近一直在做一个项目的前期设计工作,考虑到后期系统的扩展和性能问题也找了很多解决方法,有一个就是用到了数据库的缓存工具memcached(当然该工具并不仅仅局限于数据库的缓存)。先简单的介绍下什么是memcached。

    Memcached是高性能的,分布式的内存对象缓存系统,用于在动态应用中减少数据库负 载,提升访问速度。Memcached由Danga Interactive开发,用于提升LiveJournal.com访问速度的。LJ每秒动态页面访问量几千次,用户700万。Memcached将数 据库负载大幅度降低,更好的分配资源,更快速访问。

    上网baidu了很多东西,几乎都差不多,而且基于java的说的很少,所有只有在研究了各个其他语言类的应用后再来尝试在java上进行简单的操作应 用。先从memcached上进行说明,memcached的最新版是采用c语言进行开发和设计的,据说旧版的是采用perl语言开发的,而且它是一个应 用软件来的,是作为缓存服务器的服务器端运行在服务器上的,需要使用特定的语言编写客户端与其进行通信来进行数据的缓存和获取。通常我们是把 memcached安装运行在web服务器上,然后通过对需要的数据进行缓存,据我目前所知,所有数据的缓存设置和存取操作,以及数据的更新后替换操作全 部需要程序来进行,而不是自动进行的(自动不知道能不能成功,呵呵)。下面从一个实际的例子来应用memcached。

    首先到http://danga.com/memcached/下 载memcached的windows版本和java客户端jar包,目前最新版本是memcached-1.2.1-win32.zip和 java_memcached-release_1.6.zip,分别解压后即可!首先是安装运行memcached服务器,我们将memcached- 1.2.1-win32.zip解压后,进入其目录,然后运行如下命令:

c:>memcached.exe -d install
c:>memcached.exe -l 127.0.0.1 -m 32 -d start

    第一行是安装memcached成为服务,这样才能正常运行,否则运行失败!第一行是启动memcached的,作为测试我们就简单的只分配32M内存 了,然后监听本机端口和以守护进行运行。执行完毕后,我们就可以在任务管理器中见到memcached.exe这个进程了。好了,我们的服务器已经正常运 行了, 下面我们就来写java的客户端连接程序。

    我们将java_memcached-release_1.6.zip解压后的目录中的java_memcached-release_1.6.jar文件复制到java项目的lib目录下,然后我们来编写代码,比如我提供的一个应用类如下:

package utils.cache;

import java.util.Date;

import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;


/**
* 使用memcached的缓存实用类.
*
*
@author 铁木箱子
*
*/
public class MemCached
{
   
// 创建全局的唯一实例
    protected static MemCachedClient mcc = new MemCachedClient();
   
   
protected static MemCached memCached = new MemCached();
   
   
// 设置与缓存服务器的连接池
    static {
       
// 服务器列表和其权重
        String[] servers = {"127.0.0.1:11211"};
        Integer[] weights
= {3};

       
// 获取socke连接池的实例对象
        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 MemCached()
    {
       
    }
   
   
/**
     * 获取唯一实例.
     *
@return
    
*/
   
public static MemCached getInstance()
    {
       
return memCached;
    }
   
   
/**
     * 添加一个指定的值到缓存中.
     *
@param key
     *
@param value
     *
@return
    
*/
   
public boolean add(String key, Object value)
    {
       
return mcc.add(key, value);
    }
   
   
public boolean add(String key, Object value, Date expiry)
    {
       
return mcc.add(key, value, expiry);
    }
   
   
public boolean replace(String key, Object value)
    {
       
return mcc.replace(key, value);
    }
   
   
public boolean replace(String key, Object value, Date expiry)
    {
       
return mcc.replace(key, value, expiry);
    }
   
   
/**
     * 根据指定的关键字获取对象.
     *
@param key
     *
@return
    
*/
   
public Object get(String key)
    {
       
return mcc.get(key);
    }
   
   
public static void main(String[] args)
    {
        MemCached cache
= MemCached.getInstance();
        cache.add(
"hello", 234);
        System.out.print(
"get value : " + cache.get("hello"));
    }
}

    那么我们就可以通过简单的像main方法中操作的一样存入一个变量,然后再取出进行查看,我们可以看到先调用了add,然后再进行get,我们运行一次 后,234这个值已经被我们存入了memcached的缓存中的了,我们将main方法中红色的那一行注释掉后,我们再运行还是可以看到get到的 value也是234,即缓存中我们已经存在了数据了。

    对基本的数据我们可以操作,对于普通的POJO而言,如果要进行存储的话,那么比如让其实现java.io.Serializable接口,因为 memcached是一个分布式的缓存服务器,多台服务器间进行数据共享需要将对象序列化的,所以必须实现该接口,否则会报错的。比如我们写一个简单的测 试Bean如下:

class TBean implements java.io.Serializable
{
   
private static final long serialVersionUID = 1945562032261336919L;
   
   
private String name;

   
public String getName()
    {
       
return name;
    }

   
public void setName(String name)
    {
       
this.name = name;
    }
}

    然后我们在main方法中加入如下几行代码:

TBean tb = new TBean();
tb.setName(
"铁木箱子");
cache.add(
"bean", tb);
TBean tb1
= (TBean)cache.get("bean");
System.out.print(
"name=" + tb1.getName());
tb1.setName(
"铁木箱子_修改的");
tb1
= (TBean)cache.get("bean");
System.out.print(
"name=" + tb1.getName());

    我们首先把TBean的一个实例放入缓存中,然后再取出来,并进行名称的修改,然后我们再取这个对象,我们再看其名称,发现修改的对象并不是缓存中的对 象,而是通过序列化过来的一个实例对象,这样我们就无须担心对原生类的无意修改导致缓存数据失效了,呵呵~~看来我也是多此一想啊。所以这表明从缓存中获 取的对象是存入对象的一个副本,对获取对象的修改并不能真正的修改缓存中的数据,而应该使用其提供的replace等方法来进行修改。

    以上是我在windows下对memcached的一点小学习和实践,在以后的项目开发过程中将会更深入的学习和应用这一缓存工具,也希望和有兴趣的同行一起讨论学习该工具的使用~~ 微笑


posted @ 2010-08-11 13:51 存鹰之心于高远,取鹰之志而凌云,习鹰之性以涉险,融鹰之神在山巅. 阅读(745) | 评论 (0)编辑 收藏