杜娟's profile娟娟的糖果屋PhotosBlogListsMore Tools Help

Blog


    October 23

    用JFreeChart画统计分析柱状图

    我们介绍使用 JFreeChart 生成柱状图,首先从一个最简单的例子开始。
    一 最简单的例子 为了降低门槛,让大家心理有个底,先介绍一个简单的不能再简单的例子,图片中的各类属性都采用默认值。 <%@ page contentType="text/html;charset=GBK"%>
    <%@ page import="org.jfree.chart.ChartFactory,
    org.jfree.chart.JFreeChart,
    org.jfree.chart.plot.PlotOrientation,
    org.jfree.chart.servlet.ServletUtilities,
    org.jfree.data.DefaultCategoryDataset"%>
    <%

    DefaultCategoryDataset dataset = new DefaultCategoryDataset();
    dataset.addValue(300, "广州", "苹果");
    dataset.addValue(200, "广州", "梨子");
    dataset.addValue(500, "广州", "葡萄");
    dataset.addValue(340, "广州", "芒果");
    dataset.addValue(280, "广州", "荔枝");
    JFreeChart chart = ChartFactory.createBarChart3D("水果销量统计图","水果","销量",dataset,PlotOrientation.VERTICAL,false,false,false);
    String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300, null, session);
    String graphURL = request.getContextPath() + "/servlet/DisplayChart?filename=" + filename;
    %>
    <IMG height=300 src="<%= graphURL %>" width=500 useMap="#<%= filename %>" border=0>
    这个 JSP 程序运行的结果如下图

    PieChart1
    图 一

    二 柱状图高级特性
            上面的程序简单,但生成的柱状图也很简单。更多的时候,我们可能需要不同的效果。org.jfree.chart.ChartFactory 这个工厂类有createBarChart,createStackedBarChart,createBarChart3D,createStackedBarChart3D这几个工厂方法创建不同类型的柱状图。关于这四个方法的 JFreeChart 的 Java Doc API 文档有详细说明,比较重要的是 PlotOrientation.VERTICAL 让平行柱垂直显示,而 PlotOrientation.HORIZONTAL 则让平行柱水平显示。
    几个对柱状图影响较大的几个类,它们分别是:
    org.jfree.chart.axis.CategoryAxis
    org.jfree.chart.axis.ValueAxis
    org.jfree.chart.renderer.BarRenderer
    org.jfree.chart.renderer.BarRenderer3D 我们还是以实例来说明这几个类,先来假设一个需要统计的数据表:

    北京 上海 广州 成都 深圳
    苹果 672 766 223 540 126
    梨子 325 521 210 340 106
    葡萄 332 256 523 240 526
    根据上表数据,首先构造 CategoryDataset, 这里不再使用上面简单例子里面的 DefaultCategoryDataset 类,而是 DatasetUtilities 更有效的构造 CategoryDataset,如下列代码:

    double[][] data = new double[][] {{672, 766, 223, 540, 126}, {325, 521, 210, 340, 106}, {332, 256, 523, 240, 526} };
    String[] rowKeys = {"苹果","梨子","葡萄"};
    String[] columnKeys = {"北京","上海","广州","成都","深圳"};
    CategoryDataset dataset = DatasetUtilities.createCategoryDataset(rowKeys, columnKeys, data);

    用上面的 dataset 生成的 3D 柱状图

    PieChart2
    图 二
    org.jfree.chart.axis.CategoryAxis
    CategoryAxis domainAxis = plot.getDomainAxis();
    //设置 columnKey 是否垂直显示
    domainAxis.setVerticalCategoryLabels(true);
    //设置距离图片左端距离
    domainAxis.setLowerMargin(0.1);
    //设置距离图片右端距离
    domainAxis.setUpperMargin(0.1);
    //设置 columnKey 是否间隔显示
    domainAxis.setSkipCategoryLabelsToFit(true);
    plot.setDomainAxis(domainAxis);
    上面代码产生的效果如下图,注意与图二的区别。

    PieChart4
    图 三
    org.jfree.chart.axis.ValueAxis

    ValueAxis rangeAxis = plot.getRangeAxis();
    //设置最高的一个柱与图片顶端的距离
    rangeAxis.setUpperMargin(0.15);
    //设置最低的一个柱与图片底端的距离
    //rangeAxis.setLowerMargin(0.15);
    plot.setRangeAxis(rangeAxis);


    上面代码产生的效果如下图,注意与图二的区别。

    PieChart5
    图 四
    org.jfree.chart.renderer.BarRenderer3D


    BarRenderer3D renderer = new BarRenderer3D();
    renderer.setBaseOutlinePaint(Color.BLACK);
    //设置 Wall 的颜色
    renderer.setWallPaint(Color.gray);
    //设置每种水果代表的柱的颜色
    renderer.setSeriesPaint(0, new Color(0, 0, 255));
    renderer.setSeriesPaint(1, new Color(0, 100, 255));
    renderer.setSeriesPaint(2, Color.GREEN);
    //设置每种水果代表的柱的 Outline 颜色
    renderer.setSeriesOutlinePaint(0, Color.BLACK);
    renderer.setSeriesOutlinePaint(1, Color.BLACK);
    renderer.setSeriesOutlinePaint(2, Color.BLACK);
    //设置每个地区所包含的平行柱的之间距离
    renderer.setItemMargin(0.1);
    //显示每个柱的数值,并修改该数值的字体属性
    renderer.setItemLabelGenerator(new StandardCategoryItemLabelGenerator());
    renderer.setItemLabelFont(new Font("黑体",Font.PLAIN,12));
    renderer.setItemLabelsVisible(true);

    上面代码产生的效果如下图,注意与图二的区别。

    PieChart6
    图 五
    补充两个有用的方法
    补充 org.jfree.chart.plot.CategoryPlot 的两个方法,这两个方法对所有类型的图表都有作用,因为在前面没有介绍,这里补充一下。


    //设置地区、销量的显示位置
    plot.setDomainAxisLocation(AxisLocation.TOP_OR_RIGHT);
    plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT);

    上面代码产生的效果如下图,注意与图二的区别。

    PieChart7
    图 六三 完整范例
    前面都是一些代码片段,现在把这些片段组合成一个完整范例。

    <DIV class=code>
    <%@ page contentType="text/html;charset=GBK"%>
    <%@ page import="java.awt.Color,
    java.awt.Font,
    org.jfree.chart.ChartFactory,
    org.jfree.chart.JFreeChart,
    org.jfree.chart.plot.PlotOrientation,
    org.jfree.chart.servlet.ServletUtilities,
    org.jfree.data.CategoryDataset,
    org.jfree.data.DatasetUtilities,
    org.jfree.chart.plot.CategoryPlot,
    org.jfree.chart.axis.CategoryAxis,
    org.jfree.chart.axis.ValueAxis,
    org.jfree.chart.renderer.BarRenderer3D,
    org.jfree.chart.labels.StandardCategoryItemLabelGenerator,
    org.jfree.chart.axis.AxisLocation"%>
    <%
    double[][] data = new double[][] {
    {672, 766, 223, 540, 126},
    {325, 521, 210, 340, 106},
    {332, 256, 523, 240, 526}
    };
    String[] rowKeys = {"苹果","梨子","葡萄"};
    String[] columnKeys = {"北京","上海","广州","成都","深圳"};
    CategoryDataset dataset = DatasetUtilities.createCategoryDataset(rowKeys, columnKeys, data);
    JFreeChart chart = ChartFactory.createBarChart3D("水果销量图统计",null,null,dataset,
    PlotOrientation.VERTICAL,true,false,false);
    chart.setBackgroundPaint(Color.WHITE);
    CategoryPlot plot = chart.getCategoryPlot();
    CategoryAxis domainAxis = plot.getDomainAxis();
    domainAxis.setVerticalCategoryLabels(false);
    plot.setDomainAxis(domainAxis);
    ValueAxis rangeAxis = plot.getRangeAxis();
    //设置最高的一个 Item 与图片顶端的距离
    rangeAxis.setUpperMargin(0.15);
    //设置最低的一个 Item 与图片底端的距离
    rangeAxis.setLowerMargin(0.15);
    <BR>plot.setRangeAxis(rangeAxis);
    BarRenderer3D renderer = new BarRenderer3D();
    renderer.setBaseOutlinePaint(Color.BLACK);
    //设置 Wall 的颜色<BR>
    renderer.setWallPaint(Color.gray);
    //设置每种水果代表的柱的颜色
    renderer.setSeriesPaint(0, new Color(0, 0, 255));
    renderer.setSeriesPaint(1, new Color(0, 100, 255));
    renderer.setSeriesPaint(2, Color.GREEN);
    //设置每个地区所包含的平行柱的之间距离
    renderer.setItemMargin(0.1);
    //显示每个柱的数值,并修改该数值的字体属性<BR>
    renderer.setItemLabelGenerator(new StandardCategoryItemLabelGenerator());
    renderer.setItemLabelsVisible(true);
    plot.setRenderer(renderer);
    //设置柱的透明度<BR>
    plot.setForegroundAlpha(0.6f);
    //设置地区、销量的显示位置<BR>
    plot.setDomainAxisLocation(AxisLocation.TOP_OR_RIGHT);
    plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT);
    String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300, null, session);
    String graphURL = request.getContextPath() + "/servlet/DisplayChart?filename=" + filename;
    %s>
    <img src="<%= graphURL %>" width=500 height=300 border=0 usemap="#<%= filename %>"&gt;
    </DIV>
    看看程序运行的结果吧:

    PieChart8

    图 七

    三 总结


    我只介绍了少量的方法,更多的请参考 JFreeChart 的 Java Doc API 文档和 Sample Code。

    初识 JMS [转]

        自己搞了J2EE这么久,EJB/JNDI/RMI/JTA这些都了解的七七八八。但是,对于JMS这个东东,只听说在EJB的消息bean中有用到过,一直以来俺都敬而远之。最近公司要做一个国外的外包项目,里头有涉及到JMS的技术,现在只能临时抱佛脚。互联网上关于JMS的文章少之又少,没办法,只有自力更生,硬是查看了SUN的官方技术API文档,也算是初步揭开JMS这个神秘的面纱吧。

    JMS的基本概念

        JMS(Java Message Service) 的主要目的是为了在我们的Java程序之间用分布式的方式实现对消息的创建、发送、接收和读取。一个典型的JMS应用,主要包含以下几个部分:
    ●JMS Provider -- JMS的核心模块,用来控制和管理JMS应用。如果说JMS整个是一个十字路口,那么这个Provider应该就充当交通信号灯和
    交警的角色了。
    ●JMS Clients -- 接收和发送message的Java程序终端,简单的说应该就是交互的源/目标对象,可以有一个或多个Clients存在。
    ●Messages -- 在JMS Clients中间传递交互的消息对象,这是JMS最基本的媒体单元。
    ●Administered Objects --  由JMS Provider创建和定义,然后提供给JMS Clients使用。 JMS定义了两种 Administered Objects:
      1) ConnectionFactory:提供给Clients使用,用来建立和JMS Provider之间的连接。
      2) Destination:在Clients之间交互时,携载了消息对象的发送源与接收目标。
    ●JMS Clients是通过JNDI的方式来查找这些Administered Objects的。
    ●Non-JMS Clients -- 使用本地消息的API来实现的Clients。

        此外,JMS可以在不同的域对象之间实现以下两种方式的消息传输机制:PTP(Point-to-Porint)和Pub/Sub(Publish-and-Subscribe),这两种方式可以并存于同一个应用之中。PTP是点对点传输消息,建立在消息队列的基础上,每个客户端对应一个消息队列,客户端发送消息到对方的消息队列中,从自己的消息队列读取消息。 Pub/Sub是将消息定位到某个层次结构栏目的节点上,Pub/Sub通常是匿名的并能够动态发布消息,Pub/Sub必须保证某个节点的所有发布者(Publisher)发布的信息准区无误地发送到这个节点的所有消息订阅者(Subscriber)。

        我们不需要仔细去了解这两种方式的意义,JMS在此提供了一个更加统一的实现方式,该方式主要有以下几个接口:ConnectionFactory、Connection、Destination、Session(消息交互线程的上下文环境)、MessageProducer(消息的发送者)、MessageConsumer(消息的接收者)。

    使用JMS API不仅使通信变得松散耦合,而且它还使通信变得:
    ●异步:JMS提供者将到来的消息发送给客户,客户不用发送请求接收消息。
    ●可靠:JMS API确保消息传送一次而且只传送一次。可靠性差的应用程序可能会丢失消息或者重复接收消息

    这里,我不想详细去写一个JMS应用的Demo。简单的就其API文档所提供的资料,来总结一下吧。

    JMS应用中的消息发送

    1、获得一个上下文环境:
    Context ctx = new InitialContext();
    2、通过JNDI查找来建立一个ConnectionFactory对象:
    ConnectionFactory cf = (ConnectionFactory) ctx.lookup("java:JmsXA");
    3、通过JNDI查找来建立一个或多个Destination对象:
    Destination dest = (Queue) ctx.lookup("queue/A");  
    4、通过ConnectionFactory创建一个JMS Connection连接:
    Connection conn = cf.createConnection();
    5、创建一个或多个JMS Session对象:
    Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
    6、使用一个Session对象和Destination对象,合作创建一个MessageProducer(消息发送端):
    MessageProducer msgp = session.createProducer(dest);   //此处的dest已经被强制为P2P Queue对象
    QueueSender sender = (QueueSender) msgp; 
    //此处发送端指明采用的是 PTP的方式来实现JMS。当然你也可以用Pub/Sub的方式来实现
    Destination dest = (Topic) ctx.lookup("queue/A");  
    ...
    TopicPublisher sender = (TopicPublisher) msgp;
    7、通过某个Session建立一个或多个JMS Message对象:
    //此处仅以文字消息发送为例,您也可以建立和发送MapMessage, BytesMessage, ObjectMessage, StreamMessage
    TextMessage msg = session.createTextMessage();
    msg.setText("消息详细内容");
    8、在已有的连接上,开始发布我们已经建立好的消息对象:
    sender.send(msg);
    9、关闭已经建立的连接:
    conn.close();

    JMS应用中的消息接收

    1-5:同发送过程的初始化步骤1-5,因为是消息的接收方,JNDI的寻址方式应该同于发送方,这里的JNDI相当于是双方接头的暗号。
    6、使用一个Session对象和Destination对象,合作创建一个MessageConsumer(消息接收端):
    MessageConsumer msgconsumer = session.createConsumer(dest);
    7、启动这个已经建立的连接,准备接收来自发送方的消息:
    conn.start();
    8、截获来自发送端的消息:
    TextMessage msg = (TextMessage) msgconsumer.receive();
    String message = msg.getText();
    9、关闭已经建立的连接:
    conn.close();

    以上的消息接收端成功的接收了一条消息,如果我们要监控JMS消息的接收事件,需要在第7步之前,声明一个实现了MessageListener接口的对象:
    MessageListener ml = new JmsListenner();  
    msgConsumer.setMessageListener(ml); 
    这里的JmsListenner是我们自定实现的一个MessageListener,其中关键的事件是:
    public void onMessage(Message message);
    一个Session对象可以同时控制旗下的多个Consumer对象和MessageListener对象,但是这个Session是一个单线程模式的传递机制,即:必须在当前Message对象被onMessage处理之后,才能继续接收下一条异步传送过来的Message对象。

    总结

    以上是我对JMS的一个初步的了解,也希望在以后的工作中对此有更进一步的认识,很乐意和大家探讨 :)

    离线状态下,解析XML出错 [转]

    在最近的一个项目中,我用到Dom4j去解析现有的Spring2.0中的bean节点,从中获取一些公用的属性信息。

    java 代码
    1. // Get params of hsql setting from web context   
    2. SAXReader xmlReader = new SAXReader();   
    3. Document doc = xmlReader.read(getSpringContextFilePath(sce));   
    4. Element beanNode = (Element) doc.selectSingleNode("/beans/bean[@id='DatabaseParam']");  


    这段代码在电脑连接到互联网上的时候工作都正常,但是如果电脑在端线状态的时候就会报错如下:

    org.dom4j.DocumentException: http://www.springframework.org/ Nested exception: http://www.springframework.org/
    at org.dom4j.io.SAXReader.read(SAXReader.java:484)
    at org.dom4j.io.SAXReader.read(SAXReader.java:321)
    at com.adeptra.demo.SystemListener.contextInitialized(SystemListener.java:86)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3764)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4216)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:760)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:740)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:544)
    at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:920)
    at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java:883)
    at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:492)
    at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1138)
    at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:120)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1022)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:736)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1014)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
    at org.apache.catalina.core.StandardService.start(StandardService.java:448)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:700)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:552)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:295)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:433)
    Nested exception:
    java.net.UnknownHostException: http://www.springframework.org/
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:177)
    at java.net.Socket.connect(Socket.java:507)
    at java.net.Socket.connect(Socket.java:457)
    at sun.net.NetworkClient.doConnect(NetworkClient.java:157)
    ....

    看错误的提示,应该是说现在电脑无法找到一个http://www.springframework.org/ 的网络主机。

    仔细察看一下Spring的配置文件头,里面有如下的一个头文件注释:

    xml 代码
    1. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">   

    问题就出现在这里!xmlReader.read 读取Spring配置文件的时候报错。

    所以,我们需要替换掉这里的http://www.springframework.org/ 为一个本地的dtd文件。如何替换呢?

    可以考虑将这里的http:///协议的路径改为一个file:/// 方式的本地文件,但是这样路径就指定死了,不便于程序移植。

    另外一种方案就是,为这里的xmlReader添加一个EntiyResolver:

    java 代码
    1. // Set a resolver for reader to locate dtd file for Spring   
    2. xmlReader.setEntityResolver(new EntityResolver()   
    3. {   
    4.     public InputSource resolveEntity(String publicId,   
    5.             String systemId)   
    6.     {   
    7.         if (publicId.equals("-//SPRING//DTD BEAN//EN"))   
    8.         {   
    9.             InputStream in = getClass().getResourceAsStream(   
    10.                     "/spring-beans-2.0.dtd");   
    11.             return new InputSource(in);   
    12.         }   
    13.         return null;   
    14.     }   
    15. });  

    这样,每一次的xmlReader调用时,dom4j首先就会去从本地的classpath下面去寻找一个相应的dtd去处理,问题也就迎刃而解了。

    可能有人有另外的疑问了,为什么Spring本身解析xml没有出现过这个错误呢? 其实Spring会首先在本地的固定路径中寻找spring-beans-2.0.dtd文件(具体在spring-beans.jar包中的com.springframework.beans.factory.xml路径下),如果找不到,然后才会参照DOCTYPE中的地址去寻找。

    October 18

    再述oracle数据库体系结构 之二:逻辑结构 [转]

    第六篇 再述oracle数据库体系结构 之二:逻辑结构
        oracle的逻辑结构包括表空间(tablespace),段(segment),数据块(data block)以及模式对象(schema)。
        oracle数据库在逻辑上是由多个表空间组成的,表空间在物理上包含一个或多个数据文件。而数据文件大小是块大小的整数倍;表空间中存储的对象叫段,比如数据段,索引段,和回退段。段由区组成,区是磁盘分配的最小单位。段的增大是通过增加区的个数来实现的。每个区的大小是数据块大小的整数倍,区的大小可以不相同;数据块是数据库中最小的I/O单位,同时也是内存数据缓冲区的单位,及数据文件存储空间单位。块的大小由参数DB_BLOCK_SIZE设置,其值应设置为操作系统块大小的整数倍。

    一、表空间
    1.什么是表空间?

        表空间是数据库中最大的逻辑单位,每一个表空间由一个或多个数据文件组成,一个数据文件只能与一个表空间相联系。每一个数据库都有一个SYSTEM表空间,该表空间是在数据库创建或数据库安装时自动创建的,用于存储系统的数据字典表,程序系统单元,过程函数,包和触发器等,也可用于存储用户数据表,索引对象。
        表空间具有在线(online)和离线(offline)属性,可以将除SYSTEM和RBS以外的其他作何表空间置为离线。
        查询表空间的文件信息:SELECT * FROM DBA_DATA_FILES。查询表空间的空闲空间信息:SELECT * FROM DBA_FREE_SPACE。若表空间的剩余空间不够,可以重新设置数据文件的大小:ALTER DATABASE DATAFILE 'filenamespec' RESIZE 500m。查询数据库中的表空间信息:SELECT * FROM DBA_TABLESPACE。

    2.表空间的特性
    a.限制用户可以使用的磁盘空间大小
    SQL>ALTER USER user1 QUOTA 100M ON tablespace1
    b.设置为离线、在线、只读、读写
    SQL>ALTER TABLESPACE tbs1 ONLINE;
    SQL>ALTER TABLESPACE tbs1 OFFLIKE;
    SQL>ALTER TABLESPACE tbs1 READ ONLY;
    SQL>ALTER TABLESPACE tbs1 READ WRITE;
      注意,在表空间被设置为READ ONLY或OFFLINE之后,该表空间数据文件的同步信息将不能改变,从而会造成不同步。在表空间置为READ WRITE或ONLINE之后,使用命令ALTER SYSTEM SWITCH LOGFILE强制执行多次日志切换后,系统自动保持所有数据文件的同步。
      不能将SYSTEM表空间置为READ ONLY或OFFLINE。如果undo空间中已经创建了回退段,且该回退段的状态为ONLINE,则undo表空间不能设置为READ ONLY或OFFLINE。
    c.备份与恢复表空间
      数据库备份时,可以只备份一个独立的表空间。表空间备份可以在线执备份也可以离线冷备份。
    d.优化I/O性能,应将表空间中的各数据文件放在不同的磁盘以平衡I/O。

    3.临时表空间
      临时表空间用于为用户排序的ORDER BY语句使用。创建数据库时可以指定数据库的默认临时表空间。在创建用户的时候可以指定用户的默认临时表空间。
    SQL>CREATE USER fey IDENTIFIED BY 12345 TEMPORARY TABLESPACE temp;
      在oracle8i中,如果创建用户时未指出用户的临时表空间,系统使用SYSTEM表空间作为该用户的临时表空间。而在oracle10g中,如果创建用户时未指出用户的临时表空间,系统使用TEMP表空间作为该用户的临时表空间。

    二、段
      数据库的段可以分为四类:数据段、索引段、回退段、临时段。
    1.数据段
      数据段是用于存储数据的段,他存储在数据表空间中。每一个表都有一个数据段(聚簇除外),表的数据存储在数据段中,默认情况下段名与表名相同。以下语句查询段的信息:
    SQL>SELECT SEGMENTNAME,TABLESPACE,NAME,BYTES,BLOCKS FROM USER_EXTENTS;

    2.索引段
      索引段用于存储索引,以下语句查询索引信息:
    SQL>SELECT * FROM ALL_INDEXS
    SQL>SELECT * FROM USER_INDEXS

    3.回退段
      回退段用于存储用户数据修改之前的值,因而可以在特定条件下回退数据。回退段与事务是一对多的关系,一个事务只能使用一个回退段,而一个回退段可存放一个或多个事务的回退数据。
      在oracle8i及之前的版本中,管理回退段是项很复杂的工作,管理员需要根据每个事务的大小和事务数量来确定、计算回退段的数量及存储参数,在oracle9i之后,oracle增加了undo表空间,由系统自动管理它。从此不再需要考虑对回退段的管理。参数undo_management决定数据库使用的是回退段还是undo表空间,其值为auto表示使用undo表空间自动管理,manual表示手动管理回退段。

    4.临时段
      临时段用于order by语句的排序以及一些汇总。

    三、区
      区是磁盘空间分配的最小单位。磁盘按区划分,每次至少分配一个区。区存储于段中,它由连续的数据块组成。
      一个段中,最小的区的个数是由参数MINEXTENTS来确定。参数MAXEXTENTS决定一个数据段中的区的最大个数。这些参数是可以在线动态修改的。
      区的分配过程中,每次至分配5个区,如果所剩的空闲空间不够5个区,就会出现错误:ORA-01653。
      可以通过字典dba_tablespace查询表空间中区的信息。可以通过字典user_table查询段中区的信息。可以通过字典user_extents查询区的分配状况。

    四、数据块
      数据块是数据中中最小的数据组织单位与管理单位,是数据文件磁盘存储空间单位,也是数据库I/O 的最小单位,数据块大小由DB_BLOCK_SIZE参数决定,不同的oracle版本DB_BLOCK_SIZE的默认值是不同的。
      9i以前的版本只能设定一种块大小,在9i及10g中,可以设定多种块大小,不同表空间可以指定不同的块大小。

    五、模式对象
      模式对象是一种应用,包括:表、视图、聚簇、索引序列生成器、同义词、哈希、程序单元、数据库链等。模式对象以后在oracle schema专栏中详细说明。
        

    再述oracle数据库体系结构 之一:物理结构 [转]

    在一个服务器中,每一个运行的数据库都有一个数据库实例(instancename) 相联系。数据库启动时,系统首先在服务器内存中分配系统全局区(SGA),即构成了oracle的内存结构。然后启动若干个常驻内存的操作系统进程,即组成了oracle的进程结构。除此之外,还有数据库的物理与逻辑结构。数据库体系统结构图可以参考第一篇。在接下来的篇幅中将分五篇来详细说明oracle数据库的体系结构。
    • oracle物理结构
    • oracle逻辑结构
    • oracle内存结构
    • oracle进程结构
    • oracle连接配置结构

      因为我的学习路线与学任何东西一样,分四个阶段,先需了解总体结构和原理,然后再进一步型清楚他们,第三再分块理解吸收,最后再综合提升。当然动手做试验须贯穿始终。

      本篇说明物理结构。

      物理结构,即oracle数据库使用的操作系统文件结构。对于数据库物理结构文件,不同的oracle版本,不同的操作系统平台上有不同的存储目录结构。

    • winnt  | d:\oracle\product\10.1.0\oradata\DB_NAME\*.*(oracle 10g);d:\orant\database\*.*(oracle7,oracle8)
    • Unix | /home/app/oracle/product/10.1.0/oradata/DB_NAME/*.*(10g);/home/app/oradata/db_name/*.*(8i,9i)

      数据库的物理结构文件按其作用可以分为三类:

    • 数据文件
    • 日志文件
    • 控制文件

    一、数据文件

      数据文件用来存储数据库的数据,如表、索引等。读取数据时,系统首先从数据库文件中读取数据,并存储到SGA的数据缓冲区中。这是为了减少I/O,如果读取数据时,缓冲区中已经有要读取的数据,就不需要再从磁盘中读取了。存储数据时也是一样,事务提交时改变的数据先存储到内存缓冲区中,再由oracle后台进程DBWR决定如何将其写入到数据文件中。

    1.查询数据文件的信息

    sql>select * from dba_data_files或

    sql>select * from v$datafile(此数据字典包含文件的动态信息)

    一个数据文件只与一个数据库相联系。数据文件的大小是可以改变的。可以通过以下语句查询表空间的空间空闲量

    sql>select * from dba_free_space

    2.修改数据文件的大小

    sql>alter database datafile "d:\...\df1.dbf" resize 800m

    3.数据库文件的自动扩展特性。请看下面的例子:

    sql>alter tablespace tbs1 add datafile "d:\...\df2.dbf" size 500m autoextend on next 50m maxsize 1000m

    sql>alter database mydb1 datafile "d:\...\df2.dbf","d:\...\df3.dbf" autoexetend off

    sql>alter database mydb1 datafile "d:\...\df2.dbf","d:\...\df3.dbf" autoexetend on next 30m maxsize unlimited

    二、重做日志文件

      重做日志文件记录对数据库的所有修改信息。它是三类文件中最复杂的一类文件,也是保证数据库安全与数据库备份与恢复有直接关系的文件。

     1.日志文件组与日志成员

      在每一个oracle数据库中,至少有两个重做日志文件组。每组有一个个或多个重做日志文件,即日志成员。同一组中的成员是镜像关系,它们存储的内容是一模一样的。oracle在写日志时,以一个日志组为逻辑单位写入,只在将日志都写入日志组中的每个成员文件中后,写日志才完成。

    2.日志工作原理

      oracle有多个日志文件组,当一个日志文件组中所有的成员所有的成员同时被写满数据时,系统自动转换到下一个日志文件组,这个转换过程称为日志切换。

      当日志切换后,会给前一个日志组编一个号,用于归档日志的编号,这个编号称为日志序列号。此编号由1开始,每切换一次,序列号自动加1,最大值受参数MAXLOGHISTORY限制,该参数的最大值为65534。

      当oracle把最后一个日志组写满了以后,自动转向第一个日志组,这时,再向第一个日志组写日志的时候,如果数据库运行在非归档模式下,这个日志组中的原有日志信息就会被覆盖。

      使用以下语句查询日志文件信息:

    sql>select * from v$log

    相关字段说明如下:

    GROUP#:日志文件组号

    THREAD#:日志文件线程号,一般为1,双机容时为2

    SEQUENCE#:日志序列号

    BYTES:日志文件大小

    MEMBERS:该组的日志成员个数

    ARC:该组日志信息是否已经完成归档

    STATUS:该组状态(CURRENT:表示当前正在使用的组;NACTIVE:表示非活动组;ACTIVE:表示归档未完成)

    FIRST_CHANGE#:系统改变号SCN,也叫检查点号

    FIRST_TIME:系统改变时间

    DBA可以使用下列命令进行强制日志切换

    sql>alter system switch logfile

     3.NOARCHIVELOG/ARCHIVELOG

      NOARCHIVELOG是非归档模式,如果数据库运行在这种模式下,当日志切换时,新切换到的日志组中的日志信息会被覆盖。ARCHIVELOG:归档模式,如果数据库运行在这种模式下,日志会被归档存储,产生归档日志,且在未归档之前,日志不允许被覆盖写入。

      要确认数据库的归档方式,可以查询数据字典v$database:

    sql>select log_mode from v$database

      要了解归档日志的信息,可以查询数据字典v$archived_log。

      要将数据库改为归档模式:

    a.alter database archivelog

    b.设置初始化参数LOG_ARCHIVE_START=TRUE

    c.设置归档文件目标存储路径 LOG_ARCHIVE_DEST=C:\ORA\ARCHIVE

    d.设置归档文件命名格式参数 LOG_ARCHIVE_FORMAT="ORCK%T%S.ARC"。这个格式中的%S表示日志序列号,自动左边补零;%s表示日志序列号,自动左边不补零;%T表示日志线程号,左边补零;%t表示日志线程号不补零。

    e.重新启动数据库

    4.CKPT进程(检查点进程)

      CKPT进程保证有修改过的数据库缓冲区中的数据都被写入到数据文件,日志文件、数据文件、数据库头和控制文件中都有写入检查点标记。数据库在恢复时,只需提供自上一个检查以来所做的修改。检查点完成时系统将更新数据库数据库头和控制文件。

      参数LOG_CHECKPOINT_TIMEOUT决定一个检查点发生的时间间隔。LOG_CHECKPOINT_INTERVAL决定一个检查需要填充的日志文件块的数量。检查点号,也称系统改变号(SCN),它标识一个检查点。可以通过v$log查询日志文件的检查点信息,通过v$datafile查询数据文件的检查点信息,通过v$database查询数据库头的检查点信息。三个地方的检查点号相同,如果不同,说明发明数据库不同步,此时数据库肯定无法正常启动。

    5.增加与删除日志文件组、日志成员(详细语法请参考oracle文档)

    alter database [database] add logfile [group integer] filespec[,[group...

    alter database [database] add logfile (...)

    alter database [database] drop logfile [grout integer]

    alter database  [database] add logfile member "filespec" [reuse] to group integer

    alter database [database] drop logfile member "filename","filename"...

    alter database [database] rename file "filename" to "filename"

    6.清除日志文件数据

    alter database [database] clear [unarchived] logfile group integer|filespec

     

    三、控制文件

      控制文件是一个二进制文件,用来描述数据库的物理结构,一个数据库只需要一个控制文件,控制文件的内容包括:

    • 数据库名及数据库唯一标识
    • 数据文件和日志文件标识
    • 数据库恢复所需的同步信息,即检查点号

      控制文件由参数control_files指定,格式如下:

    control_files=("home/app/.../control01.ctl","home/app/.../control02.ctl")

    参数中各个文件是镜像关系,也就是说,几个文件中只要有一个文件完好,数据库就可以正常运行。

      以下语句查询控制文件的信息:

    sql>select * from v$controlfile

      如果控制文件损坏或丢失,数据库将终止并且无法启动,所以,要对控制文件进行镜象,手工镜像步骤如下:

    a.关闭数据库

    b.复制控制文件

    c.修改参数文件,加入新增的控制文件位置描述

    d.重新启动数据库

      另外注意,控制文件中还包含几个服务器参数的设置,如果修改这些参数的值,刚需要重新创建控制文件,这些参数是:

    • MAXLOGFILES:最大日志文件个数
    • MAXLOGMEMBERS:最大日志成员个数
    • MAXLOGHISTORY:最大历史日志个数
    • MAXDATAFILES:最大数据文件个数
    • MAXINSTANCES:最大实例文件个数

      所有修改数据库结构的命令都会引起控制文件的改变。同时出会记录在oracle跟踪文件中,跟踪文件的名称为alter_SID.log,路径如下:

    d:\oracle\product\10.1.0\admin\DB_NAME\bdump\SIDALRT.log(unix是alter_SID.ora)

    也可以在参数文件中指定跟踪文件的存储路径,后台进程跟踪文件目录由参数background_dump_dest指定,用户跟踪文件位置由参数user_bdump_dest指定,如:

    background_bdump_dest=/u01/app/oracle/oralog/bdump

    user_bdump_dest=/u01/app//oralog/udump