| 网站首页 | 测试咨讯 | 新手入门 | 专注性能 | 测试技术 | 测试管理 | 测试工具 | 开发社区 | 工具下载 | 资料下载 | 测试论坛 | 

[CNTester联盟群]交流群:34446273/21968356/64461572 白盒群:18400216 自动化群:2706508 性能群:4498858 外包群:59649884 管理群(需有管理经验):64442523

新手入门
 入门指南
 经验之谈
测试咨讯
 行业新闻
 网站动态
 人才就业
 CNTester基金
 标准规范
专注性能
 性能测试知识
 性能测试工具
 程序设计性能
 数据库性能
 Web应用服务器性能
 操作系统性能
 服务器与网络性能
测试技术
 用例设计
 性能测试
 自动化测试
 Web测试
 面向对象测试
 综合技术
 阶段性测试
 行业类测试
测试管理
 项目管理
 项目案例
 质量管理
 软件过程
测试工具
 Mercury系列
 Rational系列
 测试管理工具
 性能测试工具
 功能测试工具
 单元测试工具
 其它测试工具
开发社区
 Java技术
 DotNet技术
 C技术
 数据库技术
 操作系统
 服务器与中间件
工具下载
 测试工具
 开发工具
 企业工程
 网络通信
 媒体桌面
 游戏娱乐
 其他工具
资料下载
 测试资料
 程序设计
 数据库
 操作系统
 应用服务器
 电子期刊
 其他资料
 
 
您现在的位置: 中国测试员网站 >> 专注性能 >> 程序设计 >> Java性能 >> 文章正文
  [组图]如何提高系统性能指标       ★★★ 【字体:
如何提高系统性能指标
MMF在Java中的应用
作者:王和全    文章来源:IBM developerWorks    点击数:    更新时间:2007-8-30    
我在这里不想也不可能针对提高系统性能的方方面面一一讲解,只是想结合具体的个案来谈谈我们在进行系统移植过程中对提高J2EE系统性能指标的一些思考和设计方案,主要是关于数据缓存技术的应用,但愿不会给大家带来太大的误会。

前言

当我写下这个标题后,我被自己吓啦一大跳,怎么取个这么大的题目。要知道这可是无数人一生的舞台!我在这里不想也不可能针对提高系统性能的方方面面一一讲解,只是想结合具体的个案来谈谈我们在进行系统移植过程中对提高J2EE系统性能指标的一些思考和设计方案,主要是关于数据缓存技术的应用,但愿不会给大家带来太大的误会。

说明:

本文所有的讨论都是基于Windows平台,至于其他主流平台比如Unix、Linux等也有类似于MMF的实现机制,在此不再赘述。


 

概念--什么是MMF?(1)

从现在开始,MMF一词将在本文中大量出现。所以,我在此先对MMF做一个简单的描述。MMF,全称Memory Mapped Files,从宏观上看,它是一种数据内存映射的技术或者说管理动态内存的一种方法,Randy Kath这样定义到MMF:Memory-mapped files(MMFs) offer a unique memory management feature that allows applications to access files on disk in the same way they access dynamic memory-through pointers。从微观的角度,它主要具有以下几个特性:

  • 概念:MMF是一个Windows对象,你可以通过Windows API创建和访问它。
  • 本质:你可以把MMF当成一个普通的文件,只不过它贮存在系统内存中。
    图一:MMF在各个进程间实现共享(来自MSDN Online)
    图一:MMF在各个进程间实现共享(来自MSDN Online)
  • 特性:MMF可以被任何进程、线程所访问,这说明MMF具有可在进程间共享的特性,这也正是它的最大"魅力"所在。当然,因为所有的存取操作都在内存中进行,它也同时具备快速的特点。
  • 实现原理:MMF是基于现代操作系统都普遍采用的虚拟内存(virtual memory)技术,而虚拟内存是基于一种被称作Paging的机制之上的(2)。所以可以这样认为,只要某个操作系统采用了基于Page的虚拟内存管理系统,它就可以实现MMF这种功能特性。
  • 生存周期:MMF一直存在直到对它的最后一个引用被断开。

MMF其实是Windows平台下的一个基本特性,所有关于它的操作都可以通过Windows API获得,它使得DNA架构下COM跨进程访问数据成为可能。利用它,可以将数据库端的业务数据缓存到应用服务器端或者客户端的MMFs中,省去频繁访问数据库的开销,极大地提高系统访问性能。对于Java,我们也在Jdk1.4的NIO规范下找到了利用MMF的类集合,虽然在Jdk1.4的API文档中并没有明确地提出这样一个概念,但是我们在FileChannel和ByteBuffer类的文档中了解到FileChannel对象具有映射文件至内存的功能,从上面的介绍中我们可以看出这实际上就是创建了MMF。


 

背景--我们遇到什么困难?

我们希望对某个购买系统进行升级开发,自然就会涉及到平台的选型。原来的系统是基于微软的DNA架构,我们现在倾向于将之移植到J2EE平台。在此之前自然要进行必要的可行性分析,除去其他方面的考虑之外,我们最关心的自然落到关键技术的可行性上面,因为我们希望最大限度地利用原有系统的架构设计。

由于该系统基于微软的DNA架构,采用DCOM远程访问组件的方式,系统性能自然成为一个非常重要的考虑。所以,在原有系统中最大的亮丽之处在于花费大量工作来提高整个系统的性能指标,使得整个系统无论在系统响应速度,还是大数据量并发操作方面都有很杰出的表现。在这其中尤以数据缓存技术MMF的应用最为关键,通过服务器端和客户端的数据缓存,有效地提升了整个系统的性能。


图二:应用MMF后的系统图
图二:应用MMF后的系统图

图示说明:

  1. 图中的"Server Cache(Business Rules)"部分即为利用MMF进行的数据缓存;
  2. 另外,在客户端也大量利用到MMF,在图中并未标出。

整个系统沿着这样一个思路来利用MMF:每次系统启动的时候,程序访问数据库,获取表中数据,通过一系列步骤将之缓存至应用服务器端MMFs,见下图中黑线所示。以后客户端每次请求数据,将直接访问应用服务器端MMFs,见图中红线所示,并且同时将数据缓存到客户端。此后,如果有任何配置数据的改变,可以重新装载数据到MMFs。当然,与之配套的还有一套比较合理的定时数据比较机制。


图三、系统与MMF的交互图
图三、系统与MMF的交互图

以上这些就是我们所要实现的MMF缓存机制,简单地说,我们就是要在Java中找出与之对应的缓存机制解决方案。





回页首


解决方案--我们想到了什么?

明确了目标之后,我们就开始了在Java中寻找的征程。可以说几乎涵盖了现有的所有可行的方案,下面就是我们探索和思考的点点滴滴。

一、 利用JNI

我们首先想到的就是JNI(Java Native Interface,Java本地接口),毕竟这是最直观和最省事的解决方案。在Java中利用JNI直接调用已有的VC或者VB代码,不需要重新编写这些代码,节省了时间,而且程序执行效率也相当不错。但是,利用JNI也存在着诸多的问题:不同程序代码之间的兼容性和可协调性,不易维护性。总之,对于这种夹生饭可以作为一时的权宜之计,在项目时间紧迫的情况下可以考虑使用,但是从长远考虑还是不宜采用。(3)

二、 利用XML

这其中我们也想到了利用XML,作为时下非常流行和实用的一门技术,Jdk1.4中提供了一整套比较完整的XML API,使得产生以及解析XML文件变得非常的容易。但是,个人觉得XML最大的优势在于为不同系统间的数据交换提供一种通用的格式,在于数据存储、解析和转换方面,作为数据缓存的候选虽然也未尝不可,但是从最优系统性能和充分继承原有系统架构考虑,还不是最优解决方案。

三、 利用MMF

因为原有系统是使用的MMF,所以我们也自然而然想到了JAVA中是否也存在MMF。经过对Jdk1.4的仔细研究,我们也如愿找到了我们希望的功能。经过各方面的讨论,我们决定在新系统中采用该技术。


 

解决方案--我们做了些什么?

在做出决定之后,我们就需要对Java中的MMF做一个详细的研究。在Jdk1.4中,关于MMF的API主要位于java.nio和java.nio.channels包下。在新的JAVA NIO中着重提到两个概念Buffer和Channel,MMF其实是作为它们的一个附属品被提出来的。其中的FileChannel类的map方法能够完成这样一种功能"Maps a region of this channel's file directly into memory",返回一个MappedByteBuffer对象。由此我们可见在Jdk1.4中,MMF的表现形式为MappedByteBuffer类及其父类ByteBuffer,你可以通过这些类提供的一些方法来操纵MMF对象,而创建MMF的功能主要由FileChannel类来完成。(4)

在使用类MappedByteBuffer之前,你必须弄清楚这样几个概念:capacity, limit, position,这在所有Buffer类中都是非常关键的。这里我直接引用Jdk1.4文档中的解释:

A buffer's capacity is the number of elements it contains. The capacity of a buffer is never negative and never changes.

A buffer's limit is the index of the first element that should not be read or written. A buffer's limit is never negative and is never greater than its capacity.

A buffer's position is the index of the next element to be read or written. A buffer's position is never negative and is never greater than its limit.

也许这样一个数学公式更加直观:0 <= position <= limit <= capacity。

在进行大规模的系统应用之前,我们建立个简单的应用模型。今天,我们介绍一下这其中关于MMF最简单的一些操作。

1、 创建MMF

上面我们已经提到,调用FileChannel类的map()方法可以创建MMF,详细的方法说明如下:

abstract MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)

通过设置不同的MapMode类型,可以分别得到只读的、可读写的和私有的MMF,因此可以视情况而定创建不同的MMF。同时通过设置参数position和size可以指定文件的某一部分映射至内存,该特点对于大文件是非常有用的。

//清单一:创建不同类型的MMF
try 
{
        File file = new File("filename");
    
        // 创建一个只读的memory-mapped file
        FileChannel roChannel = new RandomAccessFile(file, "r").getChannel();
        ByteBuffer roBuf = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, (int)roChannel.size());
    
        // 创建一个可读写的 memory-mapped file
        FileChannel rwChannel = new RandomAccessFile(file, "rw").getChannel();
        ByteBuffer wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, (int)rwChannel.size());
    
        // 创建一个私有的 (copy-on-write) memory-mapped file.
        // Any write to this channel results in a private copy of the data.
        FileChannel pvChannel = new RandomAccessFile(file, "rw").getChannel();
        ByteBuffer pvBuf = roChannel.map(FileChannel.MapMode.PRIVATE, 0, (int)rwChannel.size());
}
catch (IOException e) 
{}

2、 向MMF中插入数据

你可以利用类MappedByteBuffer的capacity来得到它里面包含的字节数,这是个常量。你可以利用方法put()来向MMF中插入数据,它有两种不同的版本:绝对位置插入put(int index, byte b),为此你必须指定index(0<=index<=capacity-1);相对位置插入put(byte b),它是利用了position和limit属性。利用相对位置插入数值后,position也相应地加1,直至达到limit的限制。而且,针对不同的数据类型,有各自相对应的put方法,比如putChar, putDouble之类。

    // Create an empty MappedByteBuffer with a 10 byte capacity
    ByteBuffer bbuf = MappedByteBuffer.allocate(10);
    
    // Get the buffer's capacity
    int capacity = bbuf.capacity(); // 10
    
    // Use the absolute put().
    // This method does not affect the position.
    bbuf.put(1,(byte)0xFF); // position=0
    
    // Set the position
    bbuf.position(5);
    
    // Use the relative put()
    bbuf.put((byte)0xFF);
    
    // Get the new position
    int pos = bbuf.position(); // 6
    
    // Get remaining byte count
    int rem = bbuf.remaining(); // 4
    
    // Set the limit
    bbuf.limit(7); // remaining=1
    
    // This convenience method sets the position to 0
    bbuf.rewind(); // remaining=7
	

3、 从MMF中获得数据

与上述的过程相反,你可以通过不同的get方法来从MMF中获得数据。

 
    // Create an empty MappedByteBuffer with a 10 byte capacity
    ByteBuffer bbuf = MappedByteBuffer.allocate(10);
    
    // Get the MappedByteBuffer's capacity
    int capacity = bbuf.capacity(); // 10
    
    // Use the absolute get().
    // This method does not affect the position.
    byte b = bbuf.get(5); // position=0
    
    // Set the position
    bbuf.position(5);
    
    // Use the relative get()
    b = bbuf.get();
    
    // Get the new position
    int pos = bbuf.position(); // 6
    
    // Get remaining byte count
    int rem = bbuf.remaining(); // 4
    
    // Set the limit
    bbuf.limit(7); // remaining=1
    
    // This convenience method sets the position to 0
    bbuf.rewind(); // remaining=7

 

解决方案--需要注意的地方

上面我们给出的只是一个非常简单的读写MMF的例子,在实际的使用过程中会复杂得多,下面几个因素可能是你要好好考虑的:

1、 数据与MMF的对应关系

既然是要将数据缓存到MMF中,那我们就必须确立数据库表与MMF的对应关系。我们推荐使用的方式是每一张表对应一个MMF文件。

2、 MMF文件长度的设计

确立了对应关系之后,我们需要分析一下如何设定MMF文件的初始长度。文件长度不能太小,否则就不能容纳所有的数据,同时文件也不能太长,那样一来浪费系统内存,二来也会使创建MMF的开销急剧增大。那刚好能容纳所有的记录呢?听起来是个不错的主意,但是如果这个时候需要添加一条记录呢?麻烦就来啦,由于原有长度不够。系统需要重新re-map MMF文件,造成系统内频繁地创建MMF,反而使性能下降。经过我们研究后得出,这个比例在1.1-1.3之间比较合适,也就是MMF文件略大于表中现有记录的总和。

3、 针对不同性质的数据进行不同的处理

明确以上两点,我们还需要对数据本身做一番研究。有些数据趋于固化,一般不会有什么改变,比如国家、省份等,而有些数据则会经常变化,比如产品等,对于这两种不同类型的数据,你可以采取不同的处理方式,以达到最优的系统性能。


 

可能存在的问题--我们需要预防些什么?

1、 MMF不是万能灵药

千万不要以为有了MMF,你就可以高枕无忧,可以轻轻松松搞定系统的缓存机制。事实远非如此,MMF只不过是一把利刃,更重要的是你自己要仔细认真地设计好系统的缓存机制。要知道,解决交通堵塞问题的关键不是把路修得多么宽,而是要合理地规划整个交通路线。要知道在某些操作系统中,使用MMF的代价是非常昂贵的,失去好的规划,你可能会适得其反,系统反而会更加的拥挤不堪。况且,使用MMF还会带来很多的副作用。

2、 性能与数据差错容忍度之间的平衡

我们知道,随着数据缓存的大量使用,不可避免地会产生某种程度上的数据不一致,也就可能会产生某些数据差错。所以说,数据缓存使用的力度决定于系统客户对这些错误的容忍程度有多大。在某些非常关键的业务数据应用数据缓存技术时,必须格外地小心。

3、 需要额外的MMF支持代码

如上所述,为了最大限度地减少数据的不统一,我们必须提供一套非常合理和有效的数据同步机制,某种程度上甚至可以认为数据同步机制的好坏决定了数据缓存技术的成败。而这些是我们在使用MMF的过程中需要额外提供的代码。

4、 MMF与平台的相关性

现在大部分编程语言中使用MMF的方法都是,提供相应的接口创建和操作MMF或者系统API,而底层的具体MMF细节则由相应的操作系统去决定。这样每种操作系统中MMF不同的实现细节也在某种程度上影响着我们对MMF的使用。

5、 使用MMF必须十分的小心

既然MMF是贮存在系统内存中,所以对于某些错误必须时刻警惕,比如"Array Out of Bound"等。要是您的系统没有很好地捕获这些错误,您的系统可能会彻底崩溃。每当你编写这些MMF代码的时候,你必须时刻牢记在心:我是在与系统内存打交道,这家伙可是娇贵的很。

6、 由于Jdkl1.4的推出时间不长,基于MMF的现有应用几乎没有,所以没有真正能够在现实环境中检验MMF的使用情况,可能会存在一些不可预知的风险。


 

总结

通过以上的介绍,相信大家对MMF在Java中的应用都有了一个初步的印象。实际上,提高应用系统的性能一直是所有应用系统开发人员追求的目标。除去本文谈到的缓存技术之外,在J2EE中,你还可以通过各种池技术的应用,EJB组件的优化来提高系统性能(5)。但愿,本文能够给你带来这方面的一些启示。



参考资料

  1. 关于MMF,微软MSDN站点Randy Kath的文章"Managing Memory-Mapped Files in Win32"不可不读。另外关于MMF的应用实例,可以参考Zhefu Zhang的文章"High Performance Solution Ini File Class with MMF"。
  2. 关于这一点,可以参考文章"What are Memory Mapped Files?"。
  3. 利用JNI来访问MMF的实例可以参考Stanley Wang的文章"Using Memory Mapped Files and JNI to communicate between Java and C++ programs"。
  4. 关于Java NIO新功能的详细讲解,请参考Jdk1.4的API文档,而Richard G. Baldwin的在线教学文档也不可不读。
  5. 关于这些技术的应用,可以参考developerWorks Java 技术专区相关的文章。


关于作者

王和全,毕业于南昌大学。现主要在J2EE平台上从事广电行业运营系统的开发工作,擅长组件技术,多层架构下的编程。喜欢钻研新的技术,最近又迷上了Linux。除了写程序,平生最大的爱好就是旅游,梦想有一天能开着自己的宝马去郊游。您可以通过 wanghequan@shdv.com与我联系,我期待着朋友们的来信。

文章录入:root    责任编辑:root 
  • 上一篇文章:

  • 下一篇文章: 没有了
  • 发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
     
    最新热点 最新推荐 相关文章
    · mysql优化基础
    · MySQL服务器安装完之后如何调节性能
    · Mysql数据库管理系统优化方案
    · MySQL Performance Tuning Primer Script
    · MySQL 性能跟踪语句
    · Linux 和对称多处理
    · 应用与数据库性能测试解决方案,QUEST SO
    · 负载、性能测试和容量测试的关系和区别
    · LoadRunner监控Windows和Linux常见问题
    · 31个用来测试网站各项性能的免费工具
    · 应用与数据库性能测试解决方案,QUEST SO
    · 研究项目: JBoss架构分析
    · 如何通过 20% 的工作获得 80% 的性能改善
    · Linux 调度器内幕
    · 在 Linux 上利用数据分区功能提高可伸缩性
    · 降低 Linux 内存开销
    · Linux 和对称多处理 在 SMP 系统上发挥 L
    · 如何提高系统性能指标
    · Java 优化技术 充分挖掘 Java 应用程序性
    · 高速缓存和连接池对访问数据库性能的影响
    31个用来测试网站各项性能的
    研究项目: JBoss架构分析
    Rational LoadTest 性能测试
    loadrunner 负载测试计划
    IBM Rational助您轻松完成基
    如何通过 20% 的工作获得 80
    基于 WebSphere Application
    Linux 调度器内幕
    在 Linux 上利用数据分区功能
    使用异步 I/O 大大提高应用程
     
     
     
    ======> [CNTester联盟群]交流群:34446273/21968356/64461572 白盒群:18400216 自动化群:2706508 性能群:4498858 外包群:59649884 管理群(需有管理经验):64442523
    | 设为首页 | 加入收藏 | 联系站长 | 友情链接 | 版权申明 | 网站公告 | 管理登录 | 

    Copyright@2007 by CNTester.com 中国测试员网站 桂ICP备07005590

    本站为开源免费网站,非商业赢利性组织。本站文章部分从网络搜索获取,如果您认为某些侵犯了您的权益,麻烦您联系本站,我们会尽快删除相关内容,同时也希望您的谅解,我们的初衷是为了让更多人去学习这方面的知识,让行业有更好的发展。

    联系电话: 15021358905