2012年7月5日
#
1使用不带参数的存储过程
1
{call procedure-name}
作为实例,在 SQL Server 2005 AdventureWorks 示例数据库中创建以下存储过程:
CREATE PROCEDURE GetContactFormalNames
AS BEGIN SELECT TOP 10 Title + ' ' + FirstName + ' ' + LastName AS FormalName
FROM Person.Contact END
此存储过程返回单个结果集,其中包含一列数据(由 Person.Contact 表中前十个联系人的称呼、名称和姓氏组成)。
在下面的实例中,将向此函数传递 AdventureWorks 示例tb数据库的打开连接,然后使用 executeQuery 方法调用 GetContactFormalNames 存储过程。
public static void executeSprocNoParams(Connection con)
…{
try …{
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("{call dbo.GetContactFormalNames}");
while (rs.next())
…{
System.out.println(rs.getString("FormalName"));
}
rs.close();
stmt.close();
}
catch (Exception e)
…{
e.printStackTrace();
}
}
2使用带有输入参数的存储过程
使用 JDBC 驱动程序调用带参数的存储过程时,必须结合 SQLServerConnection 类的 prepareCall 方法使用 call SQL 转义序列。带有 IN 参数的 call 转义序列的语法如下所示:
{call procedure-name[([parameter][,[parameter]]…)]}http://jie.baijiale.94ibc.com
构造 call 转义序列时,请使用 ?(问号)字符来指定 IN 参数。此字符充当要传递给该存储过程的参数值的占位符。可以使用 SQLServerPreparedStatement 类的 setter 方法之一为参数指定值。可使用的 setter 方法由 IN 参数的数据类型决定。
向 setter 方法传递值时,不仅需要指定要在参数中使用的实际值,还必须指定参数在存储过程中的序数位置。例如,如果存储过程包含单个 IN 参数,则其序数值为 1.如果存储过程包含两个参数,则第一个序数值为 1,第二个序数值为 2.
作为如何调用包含 IN 参数的存储过程的实例,使用 SQL Server 2005 AdventureWorks 示例数据库中的 uspGetEmployeeManagers 存储过程。此存储过程接受名为 EmployeeID 的单个输入参数
1. 记住 - "越少越好"并非总是如此(Keep in Mind – "Less is more" is not always better)。 – 高效率的代码是件好事,但很多情况下,并非代码行数越少效率就越高
2. 不要把简单事情复杂化(Do not complicate things)。 – 我曾经这么做过,我相信你也一样。开发者都倾向于采用复杂方式解决简单问题。我们在一个只有5个用户的系统中引入EJB,为一个并不需要框架的应用实现一套框架,采用属性文件、采用面向tb对象解决方案、使用线程,而这些根本用不着。为什么会这么做?一些人可能不知道有更好的解决方案,但另一些人可能故意这样做来学习新知识,或仅仅是因为有趣。对那些不知道更好解决方案的人,要多听有经验程序员的建议。对于那些纯粹出于个人目的而将设计复杂化的人,我建议你要更加专业一点。
3. 不要"硬编码"(No hard coding please)。 – 由于时间紧迫,开发者总是会忘记或故意忽略这一条。然而另一种可能是,遵循这条戒律,我们就不会陷入"时间紧迫"的困境。定义一个static final 变量,增加一行代码,又能花多长时间呢?
4. 为代码添加注释(Add comments to your code)。 – 每个人都知道这一点,但不是每个人都会这么做。你有多少次"忘记"添加注释了?确实,注释不会为你的程序增加任何函数功能。但是,有多少次,看到2周前写的代码,你都记不起它是干什么的?你很幸运,那些未注释的代码是你自己写的,你脑海中还会有残存的印象。非常不幸,大多时候,代码是别人写的,并且那个人很可能已经离开公司了。有句谚语说的好:"有来有往,互惠互利",因此程序员应该体谅彼此(还有你自己),给你的代码加上注释。
时在java开发中会操作excel表格,其实操作起来也特别简单。这里把前期操作步骤说一下,本文会简单的介绍一个开放源码项目:Java Excel Api,使用它大家就可以方便的操作Excel文件了。
首先下载好:Java Excel Api,这个文件我已经和 JAVA+Excel+API详细教程。pdf一并压缩上传了,感兴趣的朋友可以下载!
我这里用的开发平台是Eclipse,这里我把操作简单说一下:
1, 建,立java项目,在这个项目在建立一个新的文件夹lib;
2, 将jxl.jar,即Java Excel Ap,复制到lib
3,然后右键点击这个java项目,tb选择Propertieshttp://jie.baijiale.ibc198.com
4,在左侧列表里选中Java Build Path ,右侧选中Libraries
5,点击Add JARs
6, 然后去选择这个项目中lib文件夹中的jxl.jar,点击确定
成功后,项目中会多一个文件夹为:Referenced Libraries
准备工作完成后,就可以去操作excel了,
Quartz框架是一个全功能、开源的任务调度服务,可以集成几乎任何的java应用程序—从小的单片机系统到大型的电子商务系统。Quartz可以执行上千上万的任务调度。
核心概念
Quartz核心的概念:scheduler任务调度、Job任务、Trigger触发器、JobDetail任务细节
Job任务:其实Job是接口,其中只有一个execute方法:
package org.quartz;
public abstract interface Job
{
public abstract void execute(JobExecutionContext paramJobExecutionContext)
throws JobExecutionException;
}
我们开发者只要实现此接口,实现execute方法即可。把我们想做的事情,在execute中执行即可。
JobDetail:任务细节,Quartz执行Job时,需要新建个Job实例,但是不能直接操作Job类,所以通过JobDetail来获取Job的名称、描述信息。
Trigger触发器:执行任务的规则;比如每天,每小时等。
一般情况使用SimpleTrigger,和CronTrigger,这个触发器实现了Trigger接口。
对于复杂的时间表达式来说,比如每个月15日上午几点几分,使用CronTrigger
对于简单的时间来说,比如每天执行几次,使用SimpleTrigger
scheduler任务调度:是最核心的概念,需要把JobDetail和Trigger注册到scheduler中,才可以执行。
注意:
不同的版本的jar包,具体的操作不太相同,但是tbw思路是相同的;比如1.8.6jar包中,JobDetail是个类,直接通过构造方法与Job类关联。SimpleTrigger和CornTrigger是类;在2.0.2jar包中,JobDetail是个接口,SimpleTrigger和CornTrigger是接口
不同版本测试:
1.8.6jar包:
[html]
package com.test;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* 需要执行的任务
* @author lhy
*
*/
public class MyJob implements Job {
@Override
//把要执行的操作,写在execute方法中
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("测试Quartz"+new Date());
}
}
package com.test;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* 需要执行的任务
* @author lhy
*
*/
public class MyJob implements Job {
@Override
//把要执行的操作,写在execute方法中
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("测试Quartz"+new Date());
}
}
使用SimpleTrigger触发器
[html]
package com.test;
import java.util.Date;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
/**
* 调用任务的类
* @author lhy
*
*/
public class SchedulerTest {
public static void main(String[] args) {
//通过schedulerFactory获取一个调度器
SchedulerFactory schedulerfactory=new StdSchedulerFactory();
Scheduler scheduler=null;
try{
// 通过schedulerFactory获取一个调度器
scheduler=schedulerfactory.getScheduler();
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
JobDetail jobDetail=new JobDetail("job1", "jgroup1", MyJob.class);
// 定义调度触发规则,比如每1秒运行一次,共运行8次
SimpleTrigger simpleTrigger=new SimpleTrigger("simpleTrigger","triggerGroup");
// 马上启动
simpleTrigger.setStartTime(new Date());
// 间隔时间
simpleTrigger.setRepeatInterval(1000);
// 运行次数
simpleTrigger.setRepeatCount(8);
// 把作业和触发器注册到任务调度中
scheduler.scheduleJob(jobDetail, simpleTrigger);
// 启动调度
scheduler.start();
}catch(SchedulerException e){
e.printStackTrace();
}
}
}
package com.test;
import java.util.Date;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
/**
* 调用任务的类
* @author lhy
*
*/
public class SchedulerTest {
public static void main(String[] args) {
//通过schedulerFactory获取一个调度器
SchedulerFactory schedulerfactory=new StdSchedulerFactory();
Scheduler scheduler=null;
try{
// 通过schedulerFactory获取一个调度器
scheduler=schedulerfactory.getScheduler();
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
JobDetail jobDetail=new JobDetail("job1", "jgroup1", MyJob.class);
// 定义调度触发规则,比如每1秒运行一次,共运行8次
SimpleTrigger simpleTrigger=new SimpleTrigger("simpleTrigger","triggerGroup");
// 马上启动
simpleTrigger.setStartTime(new Date());
// 间隔时间
simpleTrigger.setRepeatInterval(1000);
// 运行次数
simpleTrigger.setRepeatCount(8);
// 把作业和触发器注册到任务调度中
scheduler.scheduleJob(jobDetail, simpleTrigger);
// 启动调度
scheduler.start();
}catch(SchedulerException e){
e.printStackTrace();
}
}
} 若使用CornTrigger触发器:
[html]
package com.test;
import java.util.Date;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
/**
* 调用任务的类
* @author lhy
*
*/
public class CronTriggerTest {
public static void main(String[] args) {
//通过schedulerFactory获取一个调度器
SchedulerFactory schedulerfactory=new StdSchedulerFactory();
Scheduler scheduler=null;
try{
// 通过schedulerFactory获取一个调度器
scheduler=schedulerfactory.getScheduler();
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
JobDetail jobDetail=new JobDetail("job1", "jgroup1", MyJob.class);
// 定义调度触发规则,每天上午10:15执行
CronTrigger cornTrigger=new CronTrigger("cronTrigger","triggerGroup");
// 执行规则表达式
cornTrigger.setCronExpression("0 15 10 * * ? *");
// 把作业和触发器注册到任务调度中
scheduler.scheduleJob(jobDetail, cornTrigger);
// 启动调度
scheduler.start();
}catch(Exception e){
e.printStackTrace();
}
}
}
package com.test;
import java.util.Date;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
/**
* 调用任务的类
* @author lhy
*
*/
public class CronTriggerTest {
public static void main(String[] args) {
//通过schedulerFactory获取一个调度器
SchedulerFactory schedulerfactory=new StdSchedulerFactory();
Scheduler scheduler=null;
try{
// 通过schedulerFactory获取一个调度器
scheduler=schedulerfactory.getScheduler();
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
JobDetail jobDetail=new JobDetail("job1", "jgroup1", MyJob.class);
// 定义调度触发规则,每天上午10:15执行
CronTrigger cornTrigger=new CronTrigger("cronTrigger","triggerGroup");
// 执行规则表达式
cornTrigger.setCronExpression("0 15 10 * * ? *");
// 把作业和触发器注册到任务调度中
scheduler.scheduleJob(jobDetail, cornTrigger);
// 启动调度
scheduler.start();
}catch(Exception e){
e.printStackTrace();
}
}
}
对于2.0.2jar包如下:
其中的job类不变,主要是调度类如下:
[html]
package com.test;
import java.util.Date;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
/**
* 调用任务的类
* @author lhy
*
*/
public class SchedulerTest {
public static void main(String[] args) {
//通过schedulerFactory获取一个调度器
SchedulerFactory schedulerfactory=new StdSchedulerFactory();
Scheduler scheduler=null;
try{
// 通过schedulerFactory获取一个调度器
scheduler=schedulerfactory.getScheduler();
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
JobDetail job=JobBuilder.newJob(MyJob.class).withIdentity("job1", "jgroup1").build();
// 定义调度触发规则
// 使用simpleTrigger规则
// Trigger trigger=TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup")
// .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withRepeatCount(8))
// .startNow().build();
// 使用cornTrigger规则 每天10点42分
Trigger trigger=TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup")
.withSchedule(CronScheduleBuilder.cronSchedule("0 42 10 * * ? *"))
.startNow().build();
// 把作业和触发器注册到任务调度中
scheduler.scheduleJob(job, trigger);
// 启动调度
scheduler.start();
}catch(Exception e){
e.printStackTrace();
}
}
}
package com.test;
import java.util.Date;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
/**
* 调用任务的类
* @author lhy
*
*/
public class SchedulerTest {
public static void main(String[] args) {
//通过schedulerFactory获取一个调度器
SchedulerFactory schedulerfactory=new StdSchedulerFactory();
Scheduler scheduler=null;
try{
// 通过schedulerFactory获取一个调度器
scheduler=schedulerfactory.getScheduler();
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
JobDetail job=JobBuilder.newJob(MyJob.class).withIdentity("job1", "jgroup1").build();
// 定义调度触发规则
// 使用simpleTrigger规则
// Trigger trigger=TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup")
// .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withRepeatCount(8))
// .startNow().build();
// 使用cornTrigger规则 每天10点42分
Trigger trigger=TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup")
.withSchedule(CronScheduleBuilder.cronSchedule("0 42 10 * * ? *"))
.startNow().build();
// 把作业和触发器注册到任务调度中
scheduler.scheduleJob(job, trigger);
// 启动调度
scheduler.start();
}catch(Exception e){
e.printStackTrace();
}
}
}
上述demo下载:1.8版本demo下载
2.0版本demo下载
对于CornExpress讲解如下:
字段 允许值 允许的特殊字符
秒 0-59 , - * /
分 0-59 , - * /
小时 0-23 , - * /
日期 1-31 , - * ? / L W C
月份 1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - * ? / L C #
年(可选) 留空, 1970-2099 , - * /
表达式 意义
"0 0 12 * * ?" 每天中午12点触发
"0 15 10 ? * *" 每天上午10:15触发
"0 15 10 * * ?" 每天上午10:15触发
"0 15 10 * * ? *" 每天上午10:15触发
"0 15 10 * * ? 2005" 2005年的每天上午10:15触发
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
"0 15 10 15 * ?" 每月15日上午10:15触发
"0 15 10 L * ?" 每月最后一日的上午10:15触发
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
特殊字符 意义
* 表示所有值;
? 表示未说明的值,即不关心它为何值;
- 表示一个指定的范围;
, 表示附加一个可能值;
/ 符号前表示开始时间,符号后表示每次递增的值;
L("last") ("last") "L" 用在day-of-month字段意思是 "这个月最后一天";用在 day-of-week字段, 它简单意思是 "7" or "SAT"。 如果在day-of-week字段里和数字联合使用,它的意思就是 "这个月的最后一个星期几" – 例如: "6L" means "这个月的最后一个星期五". 当我们用“L”时,不指明一个列表值或者范围是很重要的,不然的话,我们会得到一些意想不到的结果。
W("weekday") 只能用在day-of-month字段。用来描叙最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近这个月第15天的工作日”,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周日,那么触发器将会在这个月第16天即周一触发;如果这个月第15天是周二,那么就在tbw触发器这天触发。注意一点:这个用法只会在当前月计算值,不会越过当前月。“W”字符仅能在day-of-month指明一天,不能是一个范围或列表。也可以用“LW”来指定这个月的最后一个工作日。
# 只能用在day-of-week字段。用来指定这个月的第几个周几。例:在day-of-week字段用"6#3"指这个月第3个周五(6指周五,3指第3个)。如果指定的日期不存在,触发器就不会触发。
C 指和calendar联系后计算过的值。例:在day-of-month 字段用“5C”指在这个月第5天或之后包括calendar的第一天;在day-of-week字段用“1C”指在这周日或之后包括calendar的第一天
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现Serializable接口,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
写对象和读对象的时候一定要使用序列化:
import java.io.*;
class Product implements Serializable {
private static final long serialVersionUID = 1L;
private float price;
private float tax;
public Product(float price) {
this.price = price;
tax = (float)(price*0.20);
}
public String toString() {
return "price:"+price+",tax:"+tax;
}
}
public class CmdDemo {
public static void main(String[] strtb) throws Exception {
Product p1 = new Product(100);
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream
("d:\product.txt"));
os.writeObject(p1);
os.close();
ObjectInputStream is = new ObjectInputStream(new FileInputStream
("d:\product.txt"));
Product p2 = (Product) is.readObject();
System.out.println(p2.toString());
}
}
本文分享了关于Java数组最顶级的11大方法,帮助你解决工作流程问题,无论是运用在团队环境或是在私人项目中,你都可以直接拿来用!
0. 声明一个数组(Declare an array)
String[] aArray = new String[5];
String[] bArray = {"a","b","c", "d", "e"};
String[] cArray = new String[]{"a","b","c","d","e"};
1. 在Java中输出一个数组(Print an array in Java)
int[] intArray = { 1, 2, 3, 4, 5 };
String intArrayString = Arrays.toString(intArray);
// print directly will print reference value
System.out.println(intArray);
// [I@7150bd4d
System.out.println(intArrayString);
// [1, 2, 3, 4, 5]
2. 从数组中创建数组列表(Create an ArrayList from an array)
String[] stringArray = { "a", "b", "c", "d", "e" };
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(stringArray));
System.out.println(arrayList);
// [a, b, c, d, e]
3. 检查爱淘宝数组中是否包含特定值(Check if an array contains a certain value)
String[] stringArray = { "a", "b", "c", "d", "e" };
boolean b = Arrays.asList(stringArray).contains("a");
System.out.println(b);
// true
4. 连接两个数组( Concatenate two arrays)
int[] intArray = { 1, 2, 3, 4, 5 };
int[] intArray2 = { 6, 7, 8, 9, 10 };
// Apache Commons Lang library
int[] combinedIntArray = ArrayUtils.addAll(intArray, intArray2);
5. 声明一个数组内链(Declare an array inline )
method(new String[]{"a", "b", "c", "d", "e"});
6. 将数组元素加入到一个独立的字符串中(Joins the elements of the provided array into a single String)
// containing the provided list of elements
// Apache common lang
String j = StringUtils.join(new String[] { "a", "b", "c" }, ", ");
System.out.println(j);
// a, b, c
7. 将数组列表转换成一个数组 (Covnert an ArrayList to an array)
String[] stringArray = { "a", "b", "c", "d", "e" };
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(stringArray));
String[] stringArr = new String[arrayList.size()];
arrayList.toArray(stringArr);
for (String s : stringArr)
System.out.println(s);
8. 将数组转换成一个集合(Convert an array to a set)
Set<String> set = new HashSet<String>(Arrays.asList(stringArray));
System.out.println(set);
//[d, e, b, c, a]
9. 反向数组(Reverse an array)
int[] intArray = { 1, 2, 3, 4, 5 };
ArrayUtils.reverse(intArray);
System.out.println(Arrays.toString(intArray));
//[5, 4, 3, 2, 1]
10. 删除数组元素(Remove element of an array)
int[] intArray = { 1, 2, 3, 4, 5 };
int[] removed = ArrayUtils.removeElement(intArray, 3);
//create a new array
System.out.println(Arrays.toString(removed));
One more – convert int to byte array
byte[] bytes = ByteBuffer.allocate(4).putInt(8).array();
for (byte t : bytes) {
System.out.format("0x%x ", t);
}
所谓动态代理类是在运行时生成的class,在生成它时,你必须提供一组interface给它,则动态代理类就宣称它实现了这些interface。当然,动态代理类就充当一个代理,你不要企图它会帮你干实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
下面通过实例来说明:
Subject.java 抽象借口:声明代理对象和真实对象的共同接口
[java]
public interface Subject {
public void doSomething();
}
public interface Subject {
public void doSomething();
}
RealSubject.java 真实被tb代理对象
[java]
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("RealSubject.doSomething");
}
}
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("RealSubject.doSomething");
}
}
DynamicProxy.java 代理对象
[java]
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
System.out.println("Before Invoke ! method : " + method);
//我们可以再代理方法调用前后添加功能
Object result = method.invoke(object, args);
System.out.println("object : " + object + " result : " + result + " args : " + args);
System.out.println("After Invoke !");
return result;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
System.out.println("Before Invoke ! method : " + method);
//我们可以再代理方法调用前后添加功能
Object result = method.invoke(object, args);
System.out.println("object : " + object + " result : " + result + " args : " + args);
System.out.println("After Invoke !");
return result;
}
}
Client.java 测试
[java]
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) throws Exception {
//创建目标对象,也就是被代理对象
RealSubject realSubject = new RealSubject();
//将目标对象交给代理
InvocationHandler handler = new DynamicProxy(realSubject);
// Class proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader()
// , new Class[]{Subject.class});
// Subject subject = (Subject)proxyClass.getConstructor(new Class[]{InvocationHandler.class})
// .newInstance(new Object[]{handler});
//返回代理对象,相当于上面两句
Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler);
//叫代理对象去doSomething(),其实在代理对象中的doSomething()中还是会
//用handler来调用invoke(proxy, method, args) 参数proxy为调用者subject(this),
//method为doSomething(),tb参数为方法要传入的参数,这里没有
subject.doSomething();
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) throws Exception {
//创建目标对象,也就是被代理对象
RealSubject realSubject = new RealSubject();
//将目标对象交给代理
InvocationHandler handler = new DynamicProxy(realSubject);
// Class proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader()
// , new Class[]{Subject.class});
// Subject subject = (Subject)proxyClass.getConstructor(new Class[]{InvocationHandler.class})
// .newInstance(new Object[]{handler});
//返回代理对象,相当于上面两句
Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler);
//叫代理对象去doSomething(),其实在代理对象中的doSomething()中还是会
//用handler来调用invoke(proxy, method, args) 参数proxy为调用者subject(this),
//method为doSomething(),参数为方法要传入的参数,这里没有
subject.doSomething();
}
}
打印结果:
Before Invoke ! method : public abstract void Subject.doSomething()
RealSubject.doSomething
object : RealSubject@ec6b00 result : null args : null
After Invoke !
注意:
Java动态代理涉及到的两个类:
InvocationHandler:该接口中仅定义了一个Object : invoke(Object proxy, Method method, Object[] args);参数proxy指代理类,method表示被代理的方法,args为method中的参数数组,返回值Object为代理实例的方法调用返回的值。这个抽象方法在代理类中动态实现。
Proxy:所有动态代理类的父类,提供用于创建动态代理类和实例的静态方法。
(1) 类名首字母应该大写。字段、方法以及对象(句柄)的首字母应小写。对于所有标识符,其中包含的所有单词都应紧靠在一起,而且大写中间单词的首字 母。例如:
ThisIsAClassName
thisIsMethodOrFieldName
若在定义中出现了常数初始化字符,则大写static final基本类型标识符中的所有字母。这样便可标志出它们属于编译期的常数。
Java包(Package)属于一种特殊情况:它们全都是小写字母,即便中间的单词亦是如此。对于域名扩展名称,如com,org,net或者edu 等,全部都应小写(这也是Java 1.1和Java 1.2的区别之一)。
(2) 为了常规用途而创建一个类时,请采取“tb经典形式”,并包含对下述元素的定义:
equals()
hashCode()
toString()
clone()(implement Cloneable)
implement Serializable
(3) 对于自己创建的每一个类,都考虑置入一个main(),其中包含了用于测试那个类的代码。为使用一个项目中的类,我们没必要删除测试代码。若 进行了任何形式的改动,可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。
(4) 应将方法设计成简要的、功能性单元,用它描述和实现一个不连续的类接口部分。理想情况下,方法应简明扼要。若长度很大,可考虑通过某种方式将其分割成较短的几个方法。这样做也便于类内代码的重复使用(有些时候,方法必须非常大,但它们仍应只做同样的一件事情)。 (5) 设计一个类时,请设身处地为客户程序员考虑一下(类的使用方法应该是非常明确的)。然后,再设身处地为管理代码的人考虑一下(预计有可能进行哪些形式的修改,想想用什么方法可把它们变得更简单)。
(6) 使类尽可能短小精悍,而且只解决一个特定的问题。下面是对类设计的一些建议:
■一个复杂的开关语句:考虑采用“多形”机制
■数量众多的方法涉及到类型差别极大的操作:考虑用几个类来分别实现
■许多成员变量在特征上有很大的差别:考虑使用几个类 。
(7) 让一切东西都尽可能地“私有”——private。可使库的某一部分“公共化”(一个方法、类或者一个字段等等),就永远不能把它拿出。若强行拿出,就可能破坏其他人现有的代码,使他们不得不重新编写和设计。若只公布自己必须公布的,就可放心大胆地改变其他任何东西。在多线程环境中,隐私是特别重要的一个因素——只有private字段才能在非同步使用的情况下受到保护。
(8) 谨惕“巨大对象综合症”。对一些习惯于顺序编程思维、且初涉OOP领域的新手,往往喜欢先写一个顺序执行的程序,再把它嵌入一个或两个巨大的 对象里。根据编程原理,对象表达的应该是应用程序的概念,而非应用程序本身。
(9) 若不得已进行一些不太雅观的编程,至少应该把那些代码置于一个类的内部。
(10) 任何时候只要发现类与类之间结合得非常紧密,就需要考虑是否采用内部类,从而改善编码及维护工作(参见第14章14.1.2小节的“用内部 类改进代码”)。
(11) 尽可能细致地加上释,并用javadoc注释文档语法生成自己的程序文档。
(12) 避免使用“魔术数字”,这些数字很难与代码很好地配合。如以后需要修改它,无疑会成为一场噩梦,因为根本不知道“100”到底是指“数组大小”还是“其他全然不同的东西”。所以,我们应创建一个常数,并为其使用具有说服力的描述性名称,并在整个程序中都采用常数标识符。这样可使程序更易理解以及更易维护。
(13) 涉及构建器和异常的时候,通常希望重新丢弃在构建器中捕获的任何异常——如果它造成了那个对象的创建失败。这样一来,调用者就不会以为那个 对象已正确地创建,从而盲目地继续。
(14) 当客户程序员用完对象以后,若你的类要求进行任何清除工作,可考虑将清除代码置于一个良好定义的方法里,采用类似于cleanup()这样的名字,明确表明自己的用途。除此以外,可在类内放置一个boolean(布尔)标记,指出对象是否已被清除。在类的finalize()方法里,请确定对象已被清除,并已丢弃了从RuntimeException继承的一个类(如果还没有的话),从而指出一个编程错误。在采取象这样的方案之前,请确定 finalize()能够在自己的系统中工作(可能需要调用System.runFinalizersonExit(true),从而确保 这一行为)。
(15) 在一个特定的作用域内,若一个对象必须清除(非由tb垃圾收集机制处理),请采用下述方法:初始化对象;若成功,则立即进入一个含有 finally从句的try块,开始清除工作。
(16) 若在初始化过程中需要覆盖(取消)finalize(),请记住调用super.finalize()(若Object属于我们的直接超类,则无此必要)。在对finalize()进行覆盖的过程中,对super.finalize()的调用应属于最后一个行动,而不应是第一个行动,这样可确保在需要基础类组件的时候它们依然有效。
(17) 创建大小固定的对象集合时,请将它们传输至一个数组(若准备从一个方法里返回这个集合,更应如此操作)。这样一来,我们就可享受到数组在编 译期进行类型检查的好处。此外,为使用它们,数组的接收者也许并不需要将对象“造型”到数组里。
(18) 尽量使用interfaces,不要使用abstract类。若已知某样东西准备成为一个基础类,那么第一个选择应是将其变成一个 interface(接口)。只有在不得不使用方法定义或者成员变量的时候,才需要将其变成一个abstract(抽象)类。接口主要描述了客户希望做什么事情,而一个类则致力于(或允许)具体的实施细节。
(19) 在构建器内部,只进行那些将对象设为正确状态所需的工作。尽可能地避免调用其他方法,因为那些方法可能被其他人覆盖或取消,从而在构建过程 中产生不可预知的结果(参见第7章的详细说明)。
(20) 对象不应只是简单地容纳一些数据;它们的行为也应得到良好的定义。
(21) 在现成类的基础上创建新类时,请首先选择“新建”或“创作”。只有自己的设计要求必须继承时,才应考虑这方面的问题。若在本来允许新建的场 合使用了继承,则整个设计会变得没有必要地复杂。
(22) 用继承及方法覆盖来表示行为间的差异,而用字段表示状态间的区别。一个非常极端的例子是通过对不同类的继承来表示颜色,这是绝对应该避免 的:应直接使用一个“颜色”字段。
(23) 为避免编程时遇到麻烦,请保证在自己类路径指到的任何地方,每个名字都仅对应一个类。否则,编译器可能先找到同名的另一个类,并报告出错消 息。若怀疑自己碰到了类路径问题,请试试在类路径的每一个起点,搜索一下同名的.class文件。
(24) 在Java 1.1 AWT 中使用事件“适配器”时,特别容易碰到一个陷阱。若覆盖了某个适配器方法,同时拼写方法没有特别讲究,最后的结果就是新添加一个方法,而不是覆盖现成方法。然而,由于这样做是完全合法的,所以不会从编译器或运行期系统获得任何出错提示——只不过代码的工作就变得不正常了。
(25) 用合理的设计方案消除“伪功能”。也就是说,假若只需要创建类的一个对象,就不要提前限制自己使用应用程序,并加上一条“只生成其中一个 ” 注释。请考虑将其封装成一个“独生子”的形式。若在主程序里有大量散乱的代码,用于创建自己的对象,请考虑采纳一种创造性的方案,将些代码封装起来。
(26) 警惕“分析瘫痪”。请记住,无论如何都要提前了解整个项目的状况,再去考察其中的细节。由于把握了全局,可快速认识自己未知的一些因素,防 止在考察细节的时候陷入“死逻辑”中。
(27) 警惕“过早优化”。首先让它运行起来,再考虑变得更快——但只有在自己必须这样做、而且经证实在某部分代码中的确存在一个性能瓶颈的时候, 才应进行优化。除非用专门的工具分析瓶颈,否则很有可能是在浪费自己的时间。性能提升的隐含代价是自己的代码变得难于理解,而且难于维护。
(28) 请记住,阅读代码的时间比写代码的时间多得多。思路清晰的设计可获得易于理解的程序,但注释、细致的解释以及一些示例往往具有不可估量的价 值。无论对你自己,还是对后来的人,它们都是相当重要的。如对此仍有怀疑,那么请试想自己试图从联机Java文档里找出有用信息时碰到的挫折,这样或许能 将你说服。
(29) 如认为自己已进行了良好的分析、设计或者实施,那么请稍微更换一下思维角度。试试邀请一些外来人士——并不一定是专家,但可以是来自本公司其他部门的人。请他们用完全新鲜的眼光考察你的工作,看看是否能找出你一度熟视无睹的问题。采取这种方式,往往能在最适合修改的阶段找出一些关键性的问题,避免产品发行后再解决问题而造成的金钱及精力方面的损失。
(30) 良好的设计能带来最大的回报。简言之,对于一个特定的问题,通常会花较长的时间才能找到一种最恰当的解决方案。但一旦找到了正确的方法,以后的工作就轻松多了,再也不用经历数小时、数天或者数月的痛苦挣扎。我们的努力工作会带来最大的回报(甚至无可估量)。而且由于自己倾注了大量心血,最终获得一个出色的设计方案,成功的快感也是令人心动的。坚持抵制草草完工的诱惑——那样做往往得不偿失
1进程和线程的概念
1.1什么是进程
一个进程就是在一个运行的程序,它有自己独立的内存空间,一组系统资源,每个进程的内部数据和状态都是独立的,例如在window是同时打开多个记事本,虽然它们所运行的程序代码都是一样的,但是所使用的内存空间是独立的,互不干扰.
1.2什么是线程
线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制;但与进程不同的是,同类的多个线程共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈
1.3进程与线程的区别
1. 进程:每个进程都有独立的代码和数据空间(进程上下文) ,tb进程切换的开销大.
2. 线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小.
3. 多进程:在操作系统中,能同时运行多个任务程序.
4. 多线程:在同一应用程序中,有多个顺序流同时执行.
1.4线程创建的两种方式
采用继承Thread类创建线程
该方法比较简单,主要是通过继承java.lang.Thread类,并覆盖Thread类的run()方法来完成线成的创建.Thread 类是一个具体的类,即不是抽象类,该类封装了线程的行为.要创建一个线程,程序员必须创建一个从 Thread 类导出的新类.Thread类中有两个最重要的函数run()和start().
通过实现Runnable接口创建线程
该方法通过生成实现java.lang.Runnable接口的类.该接口只定义了一个方法run(),所以必须在新类中实现它.但是 Runnable 接口并没有任何对线程的支持,我们还必须创建 Thread 类的实例,这一点通过 Thread 类的构造函数
public Thread(Runnable target);来实现.
2 单线程和多线程性能比较
以使用蒙特卡罗概率算法求π为例,进行单线程和多线程时间比较
2.1什么是蒙特卡罗概率算法
蒙特卡罗法(Monte Carlo method)是以概率和统计的理论、方法为基础的一种计算方法,将所求解的问题同一定的概率模型相联系,用电子计算机实现统计模拟或抽样,以获得问题的近似解,故又称统计模拟法或统计试验法. --百度百科
蒙特卡罗求算法求π
第一步
画正方形和内切圆
第二步
变换表达式
正方形面积As=(2R)^2
圆的面积Ac=πR^2
Ac/As=(2R)^2/πR^2
π=4As/Ac
令P=As/Sc,则π=4P
第三步
重复N次实验求平均值
在正方形区域内随机生成一个点A,若A落在圆区域内,M++
P=M/N
π=4P,N的取值越大,π的值越精确
2.2 java代码实现算法
N取值为10000万,多线程的数为100,每个线程执行100万次模拟实验
线程实现
import java.util.concurrent.CountDownLatch;
public class ProModel implements Runnable {
public int N;//随机实验的总次数
public static int M;//随机点落在圆中的次数
private int id;
private final CountDownLatch doneSignal;
OBJ semaphore;
public ProModel(int id,CountDownLatch doneSignal,int N,OBJ semaphore2){
this.id=id;
this.doneSignal=doneSignal;
this.N=N;
this.semaphore=semaphore2;
M=0;
}
public void run(){
int tempM=0;
for(int i=0;i
if(isInCircle()){
tempM++;
}
}
synchronized (semaphore) {
add(tempM);
}
doneSignal.countDown();//使end状态减1
}
public void add(int tempM){
System.out.println(Thread.currentThread().getName());
M=M+tempM;
System.out.println(M);
}
//随机产生一个在正方形区域的点,判断它是否在圆中
public boolean isInCircle(){
double x=Math.random();
double y=Math.random();
if((x-0.5)*(x-0.5)+(y-0.5)*(y-0.5)<0.25)
return true;
else
return false;
}
public static int getTotal(){
return M;
}
}
多线程Main实现
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MutliThread {
public static void main(String[] args) throws InterruptedException {
long begin=System.currentTimeMillis();
int threadSize=100;
int N=1000000;
OBJ semaphore = new OBJ();
CountDownLatch doneSignal = new CountDownLatch(threadSize);
ProModel[] pros=new ProModel[threadSize];
//设置特定的线程池,大小为threadSizde
System.out.println(“begins!”);
ExecutorService exe = Executors.newFixedThreadPool(threadSize);
for(int i=0;i
exe.execute(new ProModel(i+1,doneSignal,N,semaphore));
try{
doneSignal.await(); //等待end状态变为0, }catch (InterruptedException e) {
// TODO: handle exception35
e.printStackTrace();
}finally{
System.out.println(“ends!”);
System.out.println(4*(float)ProModel.getTotal()/(float)(threadSize*N));
}
exe.shutdown();
long end=System.currentTimeMillis();
System.out.println(“used time(ms):”+(end-begin));
}
}
class OBJ{}
单线程Main实现
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThread {
public static void main(String[] args) {
long begin=System.currentTimeMillis();
int threadSize=1;
int N=100000000;
OBJ semaphore = new OBJ();
CountDownLatch doneSignal = new CountDownLatch(threadSize);
ProModel[] pros=new ProModel[threadSize];
//设置特定的线程池,大小为5
System.out.println(“begins!”);
ExecutorService exe = Executors.newFixedThreadPool(threadSize);
for(int i=0;i
exe.execute(new ProModel(i+1,doneSignal,N,semaphore));
try{
doneSignal.await(); //等待end状态变为0, }catch (InterruptedException e) {
// TODO: handle exception35
e.printStackTrace();
}finally{
System.out.println(“ends!”);
System.out.println(4*(float)ProModel.getTotal()/(float)(threadSize*N));
}
exe.shutdown();
long end=System.currentTimeMillis();
System.out.println(“used time(ms):”+(end-begin));
}
}
Arrays工具类提供了一些比较实用的方法,比如sort, binarySearch, fill等。其中还有一个asList方法,此方法能够将一个变长参数或者数组转换成List。
但是,这个生成的List,它是固定长度的,如果对其进行add或者remove的操作,会抛出UnsupportedOperationException,为什么会这样呢?
带着疑问,查看一下Arrays的源码,可以得到问题的结果。
Java代码
/**
* Returns a fixed-size list backed by the specified array. (Changes to
* the returned list "write through" to the array.) This method acts
* as bridge between array-based and collection-based APIs, in
* combination with Collection.toArray. The returned list is
* serializable and implements {@link RandomAccess}.
*
*
This method also provides a convenient way to create a fixed-size
* list initialized to contain several elements:
*
* List stooges = Arrays.asList("Larry", "Moe", "Curly");
*
*
* @param a the array by which the list will be backed.
* @return a list view of the specified array.
* @see Collection#toArray()
*/
public static List asList(T... a) {
return new ArrayList(a);
方法asList返回的是new ArrayList(a)。但是,这个ArrayList并不是java.util.ArrayList,它是一个Arrays类中的重新定义的内部类。
具体的实现如下:
Java代码
/**
* @serial include
*/
private static class ArrayList extends AbstractList
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private Object[] a;
ArrayList(E[] array) {
if (array==null)
throw new NullPointerException();
a = array;
}
public int size() {
return a.length;
}
public Object[] toArray() {
return (Object[])a.clone();
}
public E get(int index) {
return (E)a[index];
}
public E set(int index, E element) {
Object oldValue = a[index];
a[index] = element;
return (E)oldValue;
}
public int indexOf(Object o) {
if (o==null) {
for (int i=0; i
if (a[i]==null)
return i;
} else {
for (int i=0; i
if (o.equals(a[i]))
return i;
}
return -1;
}
public boolean contains(Object o) {
return indexOf(o) != -1;
}
}
从这个内部类ArrayList的实现可以看出,它继承了类AbstractList,但是没有重写add和remove方法,没有给出具体的实现。查看一下AbstractList类中对add和remove方法的定义,如果一个list不支持add和remove就会抛出UnsupportedOperationException。
Java代码
public abstract class AbstractList extends AbstractCollection implements List {
/**
* Sole constructor. (For invocation by subclass constructors, typically
* implicit.)
*/
protected AbstractList() {
}
/**
* Appends the specified element to the end of this List (optional
* operation).
*
* This implementation calls add(size(), o).
*
* Note that this implementation throws an
* UnsupportedOperationException unless add(int, Object)
* is overridden.
*
* @param o element to be appended to this list.
*
* @return true (as per the general contract of
* Collection.add).
*
* @throws UnsupportedOperationException if the add method is not
* supported by this Set.
*
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this set.
*
* @throws IllegalArgumentException some aspect of this element prevents
* it from being added to this collection.
*/
public boolean add(E o) {
add(size(), o);
return true;
}
/**
* Inserts the specified element at the specified position in this list
* (optional operation). Shifts the element currently at that position
* (if any) and any subsequent elements to the right (adds one to their
* indices).
*
* This implementation always throws an UnsupportedOperationException.
*
* @param index index at which the specified element is to be inserted.
* @param element element to be inserted.
*
* @throws UnsupportedOperationException if the add method is not
* supported by this list.
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this list.
* @throws IllegalArgumentException if some aspect of the specified
* element prevents it from being added to this list.
* @throws IndexOutOfBoundsException index is out of range (index <
* 0 || index > size()).
*/
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
/**
* Removes the element at the specified position in this list (optional
* operation). Shifts any subsequent elements to the left (subtracts one
* from their indices). Returns the element that was removed from the
* list.
*
* This implementation always throws an
* UnsupportedOperationException.
*
* @param index the index of the element to remove.
* @return the element previously at the specified position.
*
* @throws UnsupportedOperationException if the remove method is
* not supported by this list.
* @throws IndexOutOfBoundsException if the specified index is out of
* range (index < 0 || index >= size()).
*/
public E remove(int index) {
throw new UnsupportedOperationException();
}
}
至此,为什么Arrays.asList产生的List是不可添加或者删除,否则会产生UnsupportedOperationException,就可以得到解释了。
如果我们想把一个变长或者数据转变成List, 而且tb期望这个List能够进行add或者remove操作,那该怎么做呢?
我们可以写一个类似的方法,里面直接采用java.util.ArrayList即可。
比如:
Java代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyArrays {
public static List asList(T... a) {
List list = new ArrayList();
Collections.addAll(list, a);
return list;
}
}
测试代码如下:
Java代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Test {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
List stooges = Arrays.asList("Larry", "Moe", "Curly");
print(stooges);
List> seasonsList = Arrays.asList(retrieveSeasonsList());
print(seasonsList);
/*
* 自己实现一个asList方法,能够添加和删除。
*/
List list = MyArrays.asList("Larry", "Moe", "Curly");
list.add("Hello");
print(list);
}
private static void print(List list) {
System.out.println(list);
}
private static List retrieveSeasonsList() {
List seasonsList = new ArrayList();
seasonsList.add("Spring");
seasonsList.add("Summer");
seasonsList.add("Autumn");
seasonsList.add("Winter");
return seasonsList;
}
}
输出结果:
[Larry, Moe, Curly]
[[Spring, Summer, Autumn, Winter]]
[Larry, Moe, Curly, Hello]
集合可以理解为在内存中存放一组对象的容器,对象是数据的封装,而对象又构成了集合。在java中的集合框架是一种线性的数据结构,但是它分为两种,一种是物理结构,一种是逻辑结构。物理结构是一种连续存储的结构,比如说数组;而逻辑结构可以理解为在内存块中是不连续的,比如说链表,一个链表是分为两中内容的,一个是该链表所存储的数据,还有一个呢,那就是指向下一个链表的指针,通过指针而把表给连起来,称之为链表。数组其实也是可以完成像集合一样的存储的,但是数组存在一定的弊端,数组一旦创建,其大小,类型是固定的,在特定的情况下不能更方便的使用,因此,为了与数组互补,集合也就应运而生了。
在java.util包中存在三种最重要的集合,其分别是list,set,map,它们各自有各自的特点,这三种都是接口。其中list:1> list的实现类主要有ArrayList,Linkedlist及其Vector,list中存储的数据是有序的,而且也是可以重复的,也就是说list按照添加的顺序,依次的tb存储在list下标从小到大的位置。做一个简单的代码测试
public class Test {
public static void main(String[] args) {
Test test = new Test();
test.testList();
public void testList(){//类
//创建一个List对象
List list = new ArrayList();
list.add(1);
list.add(1);
list.add(2);
for(Object a:list){
System.out.print(a+" ");
}
}
此段代码的运行结果为:{1 1 2}
反应了其有序可重复的特点。
2>set同样是一个接口,它常用的实现类有Hashset,Treeset。set集合的存储特点可以说是和list完全相反的,它是一种无序而且不能重复的存储特点。同样的用代码做一个测试 public class Test {
public static void main(String[] args) {
Test test = new Test();
test.testSet();
public void testSet(){
//创建Set对象
Set set = new HashSet();
set.add(1);
set.add(2);
set.add(5);
set.add(3);
set.add(4);
set.add(1);
set.add(null);
System.out.println(set);
Iterator it = set.iterator();
while(it.hasNext()){//判断有元素可迭代
int i = it.next();
System.out.print(i+" ");
}
}
}
}
此段代码的运行结果是:{1 2 3 4 5}
可以说明其无序不可重复的特点。
3>最后则是map集合,map的实现类常用的有Hashmap,Hashtable和Treemap。
map与以上两者稍微的有点不同,它是一种映射关系,在map中,存储两种数据,tb表达为map,而以上两者都只有一个数据,而且都是通过下标来访问的,map中k是不可以重复的,而v是可以重复的,进行一段代码测试
public class Test{
public static void main(String[] args){
testmap test=new testmap();
public void testMap(){
Map map = new HashMap();
map.put(1, "aaa");
map.put(2, "bbb");
map.put(3,"ccc");
map.put(2, "ddd");
System.out.println(map);
//获取key集合(Set)
Set set = map.keySet();
Iterator it = set.iterator();
while(it.hasNext()){
int key = it.next();
//通过key获取对应的value值
String value = map.get(key);
System.out.println("key="+key+" value="+value);
}
}
}
}
此段代码的运行结果是:key=1 value=aaa;key=2 value=ddd;key=3 value=ccc。
这三种集合各有其所试用的地方,对于像我这种初学者可以让整个代码简化,思路更清晰。
二:获得各种数据存储方式长度的方法。
数组:定义一个数组Array[];那么获得该数组长度的方法是使用其length方法。
字符串: String st = "aaa";
st.length();
这里的length是字符串的一种属性,而数组的length是一种方法。
List:迭代或者直接通过下标输出,list.size()
set:迭代while(it.hasNext()){//判断有元素可迭代
int i = it.next();
System.out.print(i+" ");
}
map:只能用迭代的方法,
//获取key集合(Set)
Set set = map.keySet();
Iterator it = set.iterator();
while(it.hasNext()){
int key = it.next();
//通过key获取对应的value值
String value = map.get(key);
System.out.println("key="+key+" value="+value);
}
map先是通过迭代器先得到key值,因为是一种一一对应的关系,所以用key值就可以得到value值了。
先举个hibernate执行SQL的例子:
[java] view plaincopy
public boolean addUser(UserDO userDO) {
boolean b = false;
if (userDO != null) {
try {
getHibernateTemplate().save(userDO);
b = true;
} catch (Exception e) {
b = false;
}
} else {
b = false;
}
return b;
}
一、hibernate操作数据库的执行步骤,其实很简单,可以自己实现一个小的hibernate框架:
1、运用java反射机制,获得user对象的类型user.class
2、参考对象-关系映射元数据(可能是DO的spring注解,可能是hibernate配置文档),找到和user类对应的表为user表,并建立对象域跟表属性的对应
3、根据以上映射信息,生成SQL语句
4、通过JDBC API来执行SQL语句
二、hibernate的核心接口:
1、Configuration接口:配置hibernate,根启动hibernate,创建sessionfactory对象。hibernate通过configuration实例来获得对象-关系映射文件中的元数据,以及动态的配置hibernate属性,然后创建sessionfactory实例。具体执行过程如下:
[java] view plaincopy
Configuration config=new Configuration();
创建configuration实例,configuration类的构造方法中把applicationContext.xml文档加载到内存,tb读取文档中sessionFactory Bean的配置。然后运用java反射机制,获得userDO对象的类型UserDO。configuration类的addClass方法
[java] view plaincopy
config.addClass(userDO.class);
该方法将hbm.xml文档或者通过hibernate注解形式的“对象-关系映射元数据”读入内存,找到和UserDO类对应的表为USER表,并建立对象域跟表属性的对应。
2、sessionfactory接口:初始化hibernate,充当数据存储源的代理,创建session对象。一个sessionfactory实例对应着一个数据存储源。重量级,一个数据库只创建一个sessionfactory实例,它需要一个很大的缓存,用来存放预定义的SQL语句及映射的元数据等。
3、session接口:负责保存、更新、删除、加载和查询对象。session对象不是线程安全的,因此应该避免过多个线程共享一个session实例。当session执行sql时候,session会针对上面生成的对象和关系映射,动态组成sql语句。
4、transaction:事务管理接口。它对底层的事务接口做了封装。
5、query:执行数据库查询。query实例封装了一个HQL语句,HQL语句是面向对象的,它引用类名和类的属性。
三、hibernate的使用步骤:
1、创建hibernate配置文件
2、创建持久化类
3、创建对象-关系映射文件
4、通过hibernate API编写数据库访问代码
1. 垃圾回收
JVM运行环境中垃圾对象的定义:
一个对象创建后被放置在JVM的堆内存(heap)中,当永远不再引用这个对象时,它将被JVM在堆内存(heap)中回收。被创建的对象不能再生,同时也没法通过程序语句释放它们。
不可到达的对象被JVM视为垃圾对象,JVM将给这些对象打上标记,然后清扫回收它们,并将散碎的内存单元收集整合。
JVM管理的两种类型的内存:
堆内存(heap),主要存储程序在运行时创建或实例化的对象与变量。
栈内存(stack),主要存储程序代码中声明为静态(static)(或非静态)的方法。
堆内存(heap)通常情况下被分为两个区域:新对象(new object)区域与老对象(old object)区域。
新对象区域:
又可细分为Eden区域、From区域与To区域。
Eden区域保存新创建的对象。当该区域中的对象满了后,JVM系统将做可达性测试,主要任务是检测有哪些对象由根集合出发是不可到达的,这些对象就可被JVM回收,且将所有的活动对象从Eden区域拷到To区域,此时有一些对象将发生状态交换,有的对象就从To区域被转移到From区域,此时From区域就有了对象。
该过程执行期间,JVM的性能非常低下,会严重影响到正在运行的应用的性能。
老对象区域:
在老对象区域中的对象仍有一个较长的生命周期。经过一段时间后,被转入
tb老对象区域的对象就变成了垃圾对象,此时它们被打上相应的标记,JVM将自动回收它们。
建议不要频繁强制系统做垃圾回收,因为JVM会利用有限的系统资源,优先完成垃圾回收工作,致使应用无法快速响应来自用户端的请求,这样会影响系统的整体性能。
2. JVM中对象的生命周期
对象的整个生命周期大致分为7个阶段:创建(creation)、应用(using)、不可视(invisible)、不可到达(unreachable)、可收集(collected)、终结(finalized)、释放(free)。
1) 创建阶段
系统通过下面步骤,完成对象的创建:
a) 为对象分配存储空间
b) 开始构造对象
c) 递归调用其超类的构造方法
d) 进行对象实例初始化与变量初始化
e) 执行构造方法体
在创建对象时的几个关键应用规则:
避免在循环体中创建对象,即使该对象占用内存空间不大
尽量及时使对象符合垃圾回收标准
不要采用过深的继承层次
访问本地变量优于访问类中的变量
该说的都在注释中说完了。直接给程序吧。
[java] view plaincopyprint?
package test.javaPuzzler.p5;
import java.io.*;
import java.io.ObjectInputStream.GetField;
import java.io.ObjectOutputStream.PutField;
// 转载请注明来自http://blog.csdn.net/sunxing007
// 一个类实现Serializable来表明自己可以被序列化;
// 有一点需要特别注意的是:
// 如果子类实现了Serializable,而父类没有,则父类不会被序列化;
public class SerializableObject implements Serializable {
// 生成的序列化版本号会因为编译环境,声明的类名,成员名称和数量的变化而不同;
// 也就是说这个版本号一定程度上记录着类的定义性的信息,如果类的定义变化了,最好重新生成版本号;
// 如果新的代码使用了旧的版本号,则在反序列化的时候,可以兼容读取旧类的字节码而不会报错;
private static final long serialVersionUID = 9038542591452547920L;
public String name;
public String password;
// 如果你不希望某个非静态成员被序列化,可以用transient来修饰它;
public transient int age;
// 静态成员不会被序列化,因为序列化保存的是实例的状态信息,而静态成员是类的状态信息;
public static int version = 1;
public SerializableObject(String name, String password) {
this.name = name;
this.password = password;
}
// 每个类可以写一个writeObject方法,这个方法将会负责该类自身的序列化过程;
// 比如对于敏感信息如password,可以加密之后再序列化;
// 这个过程需要用到PutField,它可以指定哪些域会被序列化,怎么序列化(比如加密);
// 如果没有定义这个方法,将会调用ObjectOutputStream 的 defaultWriteObject;
// 你可以注释掉readObject方法,然后运行测试用例来测试密码是否被加密;
private void writeObject(ObjectOutputStream out) throws IOException {
PutField putFields = out.putFields();
putFields.put("name", name);
// 模拟加密密码
putFields.put("password", "thePassword:" + password);
out.writeFields();
}
// 每个类可以写一个readObjectb方法,该方法负责该类自身的反序列化过程;
// 比如对序列化时加密后的密码解密;
// 这个过程需要用到GetField,他可以具体地读取每个域;或执行解密动作等等;
// 如果没有定义这个方法,将会调用ObjectInputStream 的 defaultReadObject;
private void readObject(ObjectInputStream in)
throws ClassNotFoundException, IOException {
GetField readFields = in.readFields();
// 读取到成员的值之后,直接赋给该域,即完成该域的反序列化;
name = (String) readFields.get("name", "defaultName");
// 模拟解密密码
String encPassword = (String) readFields.get("password",
"thePassword:defaultValue");
password = encPassword.split(":")[1];
}
// 序列化
// 主要用到ObjectOutputStream;
public void save() throws IOException {
FileOutputStream fout = new FileOutputStream("e:obj");
ObjectOutputStream oout = new ObjectOutputStream(fout);
oout.writeObject(this);
oout.close();
fout.close();
}
// 反序列化
// 主要用到ObjectInputStream
public static SerializableObject load() throws IOException,
ClassNotFoundException {
FileInputStream fin = new FileInputStream("e:obj");
ObjectInputStream oin = new ObjectInputStream(fin);
Object o = oin.readObject();
return (SerializableObject) o;
}
@Override
public String toString() {
return "name: " + name + ", password: " + password;
}
// tb测试用例
public static void main(String[] args) throws IOException,
ClassNotFoundException {
SerializableObject so = new SerializableObject(
"http://blog.csdn.net/sunxing007", "123456");
so.save();
System.out.println(so);
System.out.println(SerializableObject.load());
}
}
1.绝对值:abs()
select abs(-2) value from dual;
2.取整函数(大):ceil()
select ceil(-2.001) value from dual;(-2)
3.取整函数(小):floor()
select floor(-2.001) value from dual;(-3)
4.取整函数(截取):trunc()
select trunc(-2.001) value from dual; (-2)
5.四舍五入:round()
select round(1.234564) value from dual;(1.2346)
6.取平方:Power(m,n)
select power(4,2) value from dual;(16)
7.取平方根:SQRT()
select sqrt(16) value from dual;(4)
8.取随机数:dbms_random(minvalue,maxvalue)
select sys.dbms.random.value(0,1) value from dual;
9.取符号:Sign()
select sign(-3) value from dual;(-)
10,取集合的最大值:greatest(value)
select greatest(-1,3,5,7,9) value from dual;(9)
11.取集合的最小值:least(value)
select least(-1,3,5,7,9) value from dual;(-1)
12.处理Null值:nvl(空值,代替值)
select nvl(null,10) value from dual;(10)
13.求字符序号:ascii()
select ascii(a) value from dual;
14.求序号字符:chr()
select chr(97) value from dual;
15.链接:concat()
select concat("11","22") value from dual;(1122)
16.获取系统时间:sysdate()
select sysdate value from dual;
17.求日期
select trunc(sysdate) from dual;
18.求时间
select to_char(sysdate,"hh24:mm:ss") from dual;
19.首字母大写:InitCAP()
select INITCAP(abc def ghi) value from dual;tbw(Abc Def Ghi)
Sapan Diwakar在过去几年间一直从事Android开发工作,同时他也积累了一些非常实用的Android应用开发资源,希望对你有所帮助。
1. Android AnnotationsAndroid Annotations是一个能够加速Android开发的开源框架,它可以帮助开发者处理一些前后台任务、rest服务、应用类、代码片段等,让开发者专注于真正重要的东西。
2. ActionBarSherlokActionBarSherlock是一个扩展的Android支持库,旨在允许开发者通过一个单一的API,在所有的Android版本中都能够非常方便地使用活动栏设计模式。
3. Spring AndroidJava开发者应该比较熟悉,这是一个针对Android开发的Spring框架。我使用最多的是RestTemplate功能,此外,AndroidAnnotations已经支持Spring Rest Template,使得编写REST客户端更加容易。
4. URLImageViewHelper如果你想在应用程序中通过URL来加载远程图像,这是最好的选择。URLImageViewHelper提供了辅助
tb类,可以很容易地加载远程图像,同时还提供了图像缓存功能。
5. SubtlePatternsSubtlePatterns提供了一些高质量的纹理图案资源,图案都非常适合作为应用背景,并且使用也很简单。你可以点击这里来看我如何在应用中使用这些图案。
6. Sliding Menu(滑动菜单)顾名思义,SlidingMenu是一个在Android应用中添加滑动菜单效果的库,尽管网上还有一些其他类似的库,但我发现从性能和易于集成方面,SlidingMenu是最好的。
7. Android ViewPagerIndicator一个非常棒的库,用于在一个多页面切换视图中指示当前的页面。
8. Vogella Android教程vogella团队几乎已经将Android开发中的所有事情做成了教程,对于开发入门非常有帮助。
9. Android-PullToRefresh针对Android的下拉刷新库。
10. 谷歌的Android教程开始Android开发必备的指南、培训资源。
11. Commonsware Github库几款Android应用开发库,可以使你的开发工作更加轻松。
你有什么好的资源,欢迎补充。
package com.hospital.dao.tools;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import org.apache.log4j.Logger;
/**
* 数据库操作管理类
*
* @author Harlyhood
*
*/
public class DBManager {
// --------------------------------------------------------- Instance
private static Logger logger = Logger.getLogger(DBManager.class);
// --------------------------------------------------------- Methods
// 数据库连接对象
private Connection con;
// SQL语句对象
private Statement stmt;
// 带参数的Sql语句对象
private PreparedStatement pstmt;
// 记录集对象
private ResultSet rs;
// 数据连接管理(连接池对象)
private DBConnectionManager dcm = null;
/** ***********************手动设置的连接参数********************************* */
@SuppressWarnings("unused")
private static String _DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
@SuppressWarnings("unused")
private static String _URL = "jdbc:sqlserver://localhost:1433;database=Hospital_AI_DB;characterEncoding=gb2312";
@SuppressWarnings("unused")
private static String _USER_NA = "sa";
@SuppressWarnings("unused")
private static String _PASSWORD = "";
/** ********************************************************************** */
// 默认构造
public DBManager() {
}
/** ****************************************************************************************** */
/**
* **************************************** 数据库连接初始化
* ***********************************
*/
/** ****************************************************************************************** */
/**
* 得到一个默认的数据库连接[从 com.hospital.dao.tools.db.properties文件初始化]
*
* @throws Exception
*/
private void getConnection() {
logger.info("###############open:::::从默认的配置文件得到一个数据库连接");
// 获取一个连接tb池管理类的实例
dcm = DBConnectionManager.getInstance();
// 得到一个数据库连接
con = dcm.getConnection("mysql");
try {
con.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 从指定参数得到一个连接对象
*
* @param driver
* @param url
* @param user_na
* @param password
* @throws Exception
*/
public void getConnection(String driver, String url, String user_na,
String password) throws Exception {
try {
logger.info("###############open:::::从指定配置中得到一个数据库连接");
Class.forName(driver);
con = DriverManager.getConnection(url, user_na, password);
} catch (ClassNotFoundException ex) {
logger
.info("###############Error[com.hospital.dao.tools.DBManager^^^Method:getConnection^^^Line:81]找不到类驱动类: "
+ driver);
throw ex;
} catch (SQLException ex) {
logger
.info("###############Error[com.hospital.dao.tools.DBManager^^^Method:getConnection^^^Line:81]加载类: "
+ driver + " 时出现 SQLException 异常");
throw ex;
}
}
/** ****************************************************************************************** */
/**
* **************************************** 数据库操作方法
* ***********************************
*/
/** ****************************************************************************************** */
/**
* 执行SQL语句操作(更新数据 无参数)
*
* @param strSql
* SQL语句
* @throws Exception
*/
public boolean executeUpdate(String strSql) throws SQLException {
getConnection();
// getConnection(_DRIVER,_URL,_USER_NA,_PASSWORD);
boolean flag = false;
stmt = con.createStatement();
logger.info("###############::执行SQL语句操作(更新数据 无参数):" + strSql);
try {
if (0 < stmt.executeUpdate(strSql)) {
close_DB_Object();
flag = true;
con.commit();
}
} catch (SQLException ex) {
logger
.info("###############Error DBManager Line126::执行SQL语句操作(更新数据 无参数):"
+ strSql + "失败!");
flag = false;
con.rollback();
throw ex;
}
return flag;
}
/**
* 执行SQL语句操作(更新数据 有参数)
*
* @param strSql
* sql指令
* @param prams
* 参数列表
* @return
* @throws SQLException
*/
public boolean executeUpdate(String strSql, HashMap<Integer, Object> prams)
throws SQLException, ClassNotFoundException {
getConnection();
// getConnection(_DRIVER,_URL,_USER_NA,_PASSWORD);
boolean flag = false;
try {
pstmt = con.prepareStatement(strSql);
setParamet(pstmt, prams);
logger.info("###############::执行SQL语句操作(更新数据 有参数):" + strSql);
if (0 < pstmt.executeUpdate()) {
close_DB_Object();
flag = true;
con.commit();
}
} catch (SQLException ex) {
logger
.info("###############Error DBManager Line121::执行SQL语句操作(更新数据 无参数):"
+ strSql + "失败!");
flag = false;
con.rollback();
throw ex;
} catch (ClassNotFoundException ex) {
logger
.info("###############Error DBManager Line152::执行SQL语句操作(更新数据 无参数):"
+ strSql + "失败! 参数设置类型错误!");
con.rollback();
throw ex;
}
return flag;
}
/**
* 执行SQL语句操作(查询数据 无参数)
*
* @param strSql
* SQL语句
* @return 数组对象列表
* @throws Exception
*/
public ArrayList<HashMap<Object, Object>> executeSql(String strSql)
throws Exception {
getConnection();
// getConnection(_DRIVER,_URL,_USER_NA,_PASSWORD);
stmt = con.createStatement();
logger.info("###############::执行SQL语句操作(查询数据):" + strSql);
rs = stmt.executeQuery(strSql);
con.commit();
if (null != rs) {
return convertResultSetToArrayList(rs);
}
close_DB_Object();
return null;
}
/**
* 执行SQL语句操作(查询数据 有参数)
*
* @param strSql
* SQL语句
* @param prams
* 参数列表
* @return 数组对象列表
* @throws Exception
*/
public ArrayList<HashMap<Object, Object>> executeSql(String strSql,
HashMap<Integer, Object> prams) throws Exception {
getConnection();
// getConnection(_DRIVER,_URL,_USER_NA,_PASSWORD);
pstmt = con.prepareStatement(strSql);
setParamet(pstmt, prams);
logger.info("###############::执行SQL语句操作(查询数据):" + strSql);
rs = pstmt.executeQuery();
con.commit();
if (null != rs) {
return convertResultSetToArrayList(rs);
}
return null;
}
/**
* 执行存储过程(查询数据 无参数)
*
* @param procName
* 存储过程名称
* @return 数组列表对象
* @throws Exception
*/
public ArrayList<HashMap<Object, Object>> executeProcedureQuery(
String procName) throws Exception {
getConnection();// 获取连接
String callStr = "{call " + procName + "}";// 构造执行存储过程的sql指令
CallableStatement cs = con.prepareCall(callStr);
logger.info("###############::执行存储过程(查询数据):" + procName);
rs = cs.executeQuery();
con.commit();
cs.close();
close_DB_Object();
return convertResultSetToArrayList(rs);
}
/**
* 执行存储过程(查询数据,带参数)返回结果集合
*
* @param procName
* 存储过程名称
* @param parameters
* 参数对象数组
* @param al
* 数组列表对象
* @return 数组列表对象
* @throws Exception
*/
public ArrayList<HashMap<Object, Object>> executeProcedureQuery(
String procName, Object[] parameters) throws Exception {
int parameterPoint = 0;
// 获取存储过程信息列表集合
ArrayList<HashMap<Object, Object>> procedureInfo = getProcedureInfo(procName);
// 获取存储过程的完全名称
String procedureCallName = getProcedureCallName(procName,parameters.length);
// 获取连接对象
getConnection();
// 初始化 存储过程 执行对象
CallableStatement cs = con.prepareCall(procedureCallName);
// 参数下标变量
int index = 0;
// 获取 存储过程信息列表集合的 迭代器 对象
Iterator<HashMap<Object, Object>> iter = procedureInfo.iterator();
// 遍历存储过程信息列表集合
while (iter.hasNext()) {
HashMap<Object, Object> hm = iter.next();
parameterPoint++;
// 如果参数是输入参数 way = 0
if (hm.get("WAY").equals("0")) {
// 设置参数到cs
cs.setObject(parameterPoint, parameters[index]);
// 参数下标+1
index++;
}
}
// 释放这个对象,做为第二次使用
procedureInfo = null;
logger.info("###############::执行存储过程(查询数据):::::" + procedureCallName);
rs = cs.executeQuery();
con.commit();
procedureInfo = convertResultSetToArrayList(rs);
cs.close();
close_DB_Object();
return procedureInfo;
}
/**
* 执行存储过程(更新,查询数据[简单查询、非纪录集],返回输出参数[非纪录集])
*
* @param procName
* 存储过程名称
* @param parameters
* 参数对象数组
* @param os
* 输出参数对象数组
* @return 输出参数对象数组
* @throws Exception
*/
public Object[] executeProcedureUpdate(String procName, Object[] parameters)
throws Exception {
logger.info("------------------------------------------------------------------------------------------------------");
logger.info(" Run --> executeProcedureUpdate ############## 正在执行 存储过程: " + procName +" ##############");
CallableStatement cs = null;
Object []returnVal = null;
try {
// 获取 存储过程 调用全名
String fullPCallName = getProcedureCallName(procName,parameters.length);
logger.info(" Run --> executeProcedureUpdate # 存储过程命令: " + fullPCallName +" #");
//获取存储过程参数信息
ArrayList<HashMap<Object, Object>> p_Call_Info_List = getProcedureInfo(procName);
//获取连接
getConnection();
//创建 存储过程 执行对象
cs = con.prepareCall(fullPCallName);
//数组下标
int index = 1;
//输出参数下标 纪录
ArrayList<Integer> outPutIndexList = new ArrayList<Integer>();
logger.info(" Run --> executeProcedureUpdate # 参数个数是: " + parameters.length +" #");
for(HashMap<Object,Object> tempHash:p_Call_Info_List)
{
if("0".equals(tempHash.get("WAY")))
{
//设置输入参数
cs.setObject(index, parameters[index-1]);
logger.info(" Run --> executeProcedureUpdate # 输入 Input: 编号:" + index +" 值: "+parameters[index-1]+" 类型: "+parameters[index-1].getClass()+" #");
}
else
{
//注册输出参数
cs.registerOutParameter(index, getDataType(tempHash.get("TYPENAME").toString()));
//纪录输出参数的下标
outPutIndexList.add(index);
logger.info(" Run --> executeProcedureUpdate # 输出 OutPut: 编号:" + index +" 值: "+parameters[index-1]+" 类型: "+parameters[index-1].getClass()+" #");
}
index++;
}
logger.info(" Run --> executeProcedureUpdate # 参数设置完毕,正在执行中 : #");
//-------------------- 执行 -----------------
if(!cs.execute())
{
returnVal = new Object[outPutIndexList.size()];
logger.info(" Run --> executeProcedureUpdate # 执行成功! : #");
//取输 出参数的 返回值
for(int i = 0 ;i<outPutIndexList.size();i++)
{
returnVal[i] = cs.getObject(outPutIndexList.get(i));
logger.info(" Run --> executeProcedureUpdate # 返回值 "+(i+1)+" "+returnVal[i]+" #");
}
con.commit();//提交
}
} catch (Exception e) {
logger.info(" Run --> executeProcedureUpdate # 执行失败!事务回滚中 : #");
con.rollback();
throw e;
}
logger.info("------------------------------------------------------------------------------------------------------");
return returnVal;
}
/** ****************************************************************************************** */
/**
* ********************************* 小工具
* ************************************************
*/
/** ****************************************************************************************** */
/**
* 关闭数据对象
*/
public void close_DB_Object() {
logger.info("###############close:::::关闭连接对象,语句对象,记录集对象");
if (null != rs) {
try {
rs.close();
} catch (SQLException ex) {
rs = null;
}
}
if (null != stmt) {
try {
stmt.close();
} catch (SQLException ex) {
stmt = null;
}
}
if (null != pstmt) {
try {
pstmt.close();
} catch (SQLException ex) {
pstmt = null;
}
}
if (con != null) {
dcm.freeConnection("mysql", con);
}
}
/**
* 设置Sql 指令参数
*
* @param p_stmt
* PreparedStatement
* @param pramets
* HashMap
*/
private PreparedStatement setParamet(PreparedStatement p_stmt,
HashMap<Integer, Object> pramets) throws ClassNotFoundException,
SQLException {
// 如果参数为空
if (null != pramets) {
// 如果参数个数为0
if (0 <= pramets.size()) {
for (int i = 1; i <= pramets.size(); i++) {
try {
// 字符类型 String
if (pramets.get(i).getClass() == Class
.forName("java.lang.String")) {
p_stmt.setString(i, pramets.get(i).toString());
}
// 日期类型 Date
if (pramets.get(i).getClass() == Class
.forName("java.sql.Date")) {
p_stmt.setDate(i, java.sql.Date.valueOf(pramets
.get(i).toString()));
}
// 布尔类型 Boolean
if (pramets.get(i).getClass() == Class
.forName("java.lang.Boolean")) {
p_stmt.setBoolean(i, (Boolean) (pramets.get(i)));
}
// 整型 int
if (pramets.get(i).getClass() == Class
.forName("java.lang.Integer")) {
p_stmt.setInt(i, (Integer) pramets.get(i));
}
// 浮点 float
if (pramets.get(i).getClass() == Class
.forName("java.lang.Float")) {
p_stmt.setFloat(i, (Float) pramets.get(i));
}
// 双精度型 double
if (pramets.get(i).getClass() == Class
.forName("java.lang.Double")) {
p_stmt.setDouble(i, (Double) pramets.get(i));
}
} catch (ClassNotFoundException ex) {
throw ex;
} catch (SQLException ex) {
throw ex;
}
}
}
}
return p_stmt;
}
/**
* 转换记录集对象为数组列表对象
*
* @param rs
* 纪录集合对象
* @return 数组列表对象
* @throws Exception
*/
private ArrayList<HashMap<Object, Object>> convertResultSetToArrayList(
ResultSet rs) throws Exception {
logger.info("###############::转换记录集对象为数组列表对象");
// 获取rs 集合信息对象
ResultSetMetaData rsmd = rs.getMetaData();
// 创建数组列表集合对象
ArrayList<HashMap<Object, Object>> tempList = new ArrayList<HashMap<Object, Object>>();
HashMap<Object, Object> tempHash = null;
// 填充数组列表集合
while (rs.next()) {
// 创建键值对集合对象
tempHash = new HashMap<Object, Object>();
for (int i = 0; i < rsmd.getColumnCount(); i++) {
// 遍历每列数据,以键值形式存在对象tempHash中
tempHash.put(rsmd.getColumnName(i + 1).toUpperCase(), rs
.getString(rsmd.getColumnName(i + 1)));
}
// 第一个键值对,存储在tempList列表集合对象中
tempList.add(tempHash);
}
close_DB_Object();// 关闭相关链接
return tempList;// 返回填充完毕的数组列表集合对象
}
/**
* 从数据库得到tb存储过程信息
*
* @param procName
* 存储过程名称
* @return 数组列表对象
* @throws Exception
*/
private ArrayList<HashMap<Object, Object>> getProcedureInfo(String procName)
throws Exception {
return this.executeSql("select Syscolumns.isoutparam as Way,systypes.name as TypeName from sysobjects,syscolumns,systypes where systypes.xtype=syscolumns.xtype and syscolumns.id=sysobjects.id and sysobjects.name='"
+ procName + "' order by Syscolumns.isoutparam");
}
/**
* 从数据库得到存储过程参数个数
*
* @param procName
* 存储过程名称
* @return 数组列表对象
* @throws Exception
*/
@SuppressWarnings("unused")
private int getParametersCount(String procName) throws Exception {
int returnVal = 0;
for (HashMap<Object, Object> tempHas : this
.executeSql("select count(*) as RowsCount from sysobjects,syscolumns,systypes where systypes.xtype=syscolumns.xtype and syscolumns.id=sysobjects.id and sysobjects.name='"
+ procName + "'")) {
returnVal = Integer.parseInt(tempHas.get("ROWSCOUNT").toString());
}
return returnVal;
}
/**
* 得到调用存储过程的全名
*
* @param procName
* 存储过程名称
* @return 调用存储过程的全名
* @throws Exception
*/
private String getProcedureCallName(String procName, int prametCount)
throws Exception {
String procedureCallName = "{call " + procName;
for (int i = 0; i < prametCount; i++) {
if (0 == i) {
procedureCallName = procedureCallName + "(?";
}
if (0 != i) {
procedureCallName = procedureCallName + ",?";
}
}
procedureCallName = procedureCallName + ")}";
return procedureCallName;
}
/**
* 得到数据类型的整型值
*
* @param typeName
* 类型名称
* @return 数据类型的整型值
*/
private int getDataType(String typeName) {
if (typeName.equals("varchar"))
return Types.VARCHAR;
if (typeName.equals("int"))
return Types.INTEGER;
if (typeName.equals("bit"))
return Types.BIT;
if (typeName.equals("float"))
return Types.FLOAT;
return 0;
}
// 设置驱动路径
@SuppressWarnings("static-access")
public void set_DRIVER(String _DRIVER) {
this._DRIVER = _DRIVER;
}
// 设置数据库密码
@SuppressWarnings("static-access")
public void set_PASSWORD(String _PASSWORD) {
this._PASSWORD = _PASSWORD;
}
// 设置数据库连接字符串
@SuppressWarnings("static-access")
public void set_URL(String _URL) {
this._URL = _URL;
}
// 设置数据库用户名
@SuppressWarnings("static-access")
public void set_USER_NA(String _USER_NA) {
this._USER_NA = _USER_NA;
}
}
function coun
tbCharacters(str)
{
var totalCount = 0;
for (var i=0; i<str.length; i++)
{
var c = str.charCodeAt(i);
if ((c >= 0x0001 && c <= 0x007e) || (0xff60<=c && c<=0xff9f))
{
totalCount++;
}
else
{
totalCount+=2;
}
}
return totalCount;
}
<script type="text/javascript">
//一个汉字相当于2个字符
function get_length(s){
var char_length = 0;
for (var i = 0; i < s.length; i++){
var son_char = s.charAt(i);
encodeURI(son_char).length > 2 ? char_length += 1 : char_length += 0.5;
}
return char_length;
}
function cut_str(stbr, len){
var char_length = 0;
for (var i = 0; i < str.length; i++){
var son_str = str.charAt(i);
encodeURI(son_str).length > 2 ? char_length += 1 : char_length += 0.5;
if (char_length >= len){
var sub_len = char_length == len ? i+1 : i;
return str.substr(0, sub_len);
break;
}
}
}
// 截取15个字(30个字符)
// cut_str('aa啊啊啊啊啊啊啊啊啊啊啊啊啊k的啊是', 15);
</script>
-
- *
- * 处理过长的字符串,截取并添加省略号
- * 注:半角长度为1,全角长度为2
- *
- * pStr:字符串
- * pLen:截取长度
- *
- * return: 截取后的字符串
- *
- function autoAddEllipsis(pStr, pLen) {
-
- var _ret = cutString(pStr, pLen);
- var _cutFlag = _ret.cutflag;
- var _cutStringn = _ret.cutstring;
-
- if ("1" == _cutFlag) {
- return _cutStringn + "...";
- } else {
- return _cutStringn;
- }
- }
-
- *
- * 取得指定长度的字符串
- * 注:半角长度为1,全角长度为2
- *
- * pStr:字符串
- * pLen:截取长度
- *
- * return: 截取后的字符串
- *
- function cutString(pStr, pLen) {
-
- // 原字符串长度
- var _strLen = pStr.length;
-
- var _tmpCode;
-
- var _cutString;
-
- // 默认情况下,返回的字符串是原字符串的一部分
- var _cutFlag = "1";
-
- var _lenCount = 0;
-
- var _ret = false;
-
- if (_strLen <= pLen/2) {
- _cutString = pStr;
- _ret = true;
- }
-
- if (!_ret) {
- for (var i = 0; i < _strLen ; i++ ) {
- if (isFull(pStr.charAt(i))) {
- _lenCount += 2;
- } else {
- _lenCount += 1;
- }
-
- if (_lenCount > pLen) {
- _cutString = pStr.substring(0, i);
- _ret = true;
- break;
- } else if (_lenCount == pLen) {
- _cutString = pStr.substring(0, i + 1);
- _ret = true;
- break;
- }
- }
- }
-
- if (!_ret) {
- _cutString = pStr;
- _ret = true;
- }
-
- if (_cutString.length == _strLen) {
- _cutFlag = "0";
- }
-
- return {"cutstring":_cutString, "cutflag":_cutFlag};
- }
-
- *
- * 判断是否为全角
- *
- * pChar:长度为1的字符串
- * return: tbtrue:全角
- * false:半角
- *
-
- function isFull (pChar) {
- for (var i = 0; i < pChar.strLen ; i++ ) {
- if ((pChar.charCodeAt(i) > 128)) {
- return true;
- } else {
- return false;
- }
- }
- }
- 用例:
- testStr = "测试1字符串";
autoAddEllipsis(testStr, 1); // "测..."
autoAddEllipsis(testStr, 2); // "测..."
autoAddEllipsis(testStr, 3); // "测..."
autoAddEllipsis(testStr, 4); // "测试..."
autoAddEllipsis(testStr, 5); // "测试1..."
autoAddEllipsis(testStr, 6); // "测试1..."
autoAddEllipsis(testStr, 7); // "测试1字..."
客户在比较繁忙的时刻执行了索引的REBUILD操作,导致大量会话被锁,最终对ONLINE REBUILD操作执行了取消操作,引发了一系列更严重的ORA-600错误。
登录数据库后,发现两个节点上ORACLE_BASE所在目录都已经100%占用。数据库无法正常通过/ AS SYSDBA方式登录。
查询告警日志发现大量的ORA-600和ORA-7445错误:
Tue May 08 21:20:26 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_1555.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:27 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_3891.trc:
ORA-00600: internal error code, arguments: [kghstack_underflow_internal_3], [0x60000000003002F0], [keybuf], [], [], [], [], []
Tue May 08 21:20:27 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_26190.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:27 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_2873.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:27 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_4518.trc:
ORA-00600: internal error code, arguments: [kghstack_underflow_internal_3], [0x60000000003002F0], [keybuf], [], [], [], [], []
Tue May 08 21:20:27 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_22469.trc:
ORA-00600: internal error code, arguments: [kghstack_underflow_internal_3], [0x60000000003002F0], [keybuf], [], [], [], [], []
Tue May 08 21:20:28 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_26440.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:29 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_762.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:29 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_26106.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:30 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_1597.trc:
ORA-07445:出现异常错误:核心转储[0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:30 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_856.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
.
.
.
检查对应的TRACE文件,发现导致错误语句执行的是TABLE_A表的插入:
ksedmp: internal or fatal error tb
ORA-00600:内部错误代码,参数: [kghstack_underflow_internal_3], [0x60000000003002F0], [keybuf], [], [], [], [], []
Current SQL statement for this session:
INSERT INTO TABLE_A (O_ID, P_ID, S_ID, F_ITEM, F_NAME, T_MON, D_MON, I_MON, P_STATE, P_TIME, R1, R2) VALUES (:B10 , SUBSTR(:B10 , LENGTH(:B10 ) - 1, 2), :B9 , :B8 , :B7 , :B6 , :B5 , :B4 , :B3 , NULL, :B2 , :B1 )
----- PL/SQL Call Stack -----
object line object
handle number name
c00000203c2cc550 119 package body U1.P_O_I
c00000203b788200 288 procedure U1.U_B_O_I
c00000203719b8d0 1 anonymous block
----- Call Stack Trace -----
calling call entry argument values in hex
location type point (? means dubious value)
-------------------- -------- -------------------- ----------------------------
ksedst()+64 call ksedst1() 000000000 ? 000000001 ?
ksedmp()+2176 call ksedst() 000000000 ?
C000000000000D20 ?
4000000004037940 ?
000000000 ? 000000000 ?
000000000 ?
ksfdmp()+112 call ksedmp() 000000003 ?
9FFFFFFFFFFF1230 ?
60000000000BA290 ?
9FFFFFFFFFFF1800 ?
C000000000000999 ?
400000000407F9B0 ?
kgerinv()+304 call ksfdmp() 9FFFFFFFFFFF1D90 ?
000000003 ?
9FFFFFFFFFFF1810 ?
60000000000BA290 ?
C000000000000612 ?
40000000098C38B0 ?
kgeasnmierr()+144 call kgerinv() 60000000000318D0 ?
4000000001AD98A0 ?
6000000000032988 ?
4000000001AD98A0 ?
9FFFFFFFFFFF1DD0 ?
$cold_kghstack_unde call kgeasnmierr() 60000000000318D0 ?
rflow_internal()+36 9FFFFFFFBF3B1168 ?
8 9FFFFFFFBF3B1178 ?
6000000000032D00 ?
000000002 ?
60000000003002F0 ?
000000001 ? 000000006 ?
kghstack_free()+208 call $cold_kghstack_unde 60000000000318D0 ?
rflow_internal() 60000000003002F0 ?
60000000000BA290 ?
C000000000000B1D ?
4000000002F7A510 ?
00002C87B ?
6000000000031A10 ?
ksmfrs()+48 call kghstack_free() 60000000000318D0 ?
60000000003002F0 ?
rpiswu2()+1312 call ksmfrs() 60000000003002F0 ?
C000000000001026 ?
4000000002F78960 ?
000000000 ? 000000000 ?
000000000 ?
rpidrv()+2352 call rpiswu2() 9FFFFFFFFFFF2AF0 ?
4000000002F7AE60 ?
00002F833 ?
60000000000A7D20 ?
9FFFFFFFFFFF1E20 ?
C000000000001ABD ?
4000000000F14558 ?
60000000000C6CF0 ?
psddr0()+864 call rpidrv() 000000018 ? 000000066 ?
9FFFFFFFFFFF3700 ?
000000038 ?
9FFFFFFFFFFF2B20 ?
60000000000BA290 ?
psdnal()+736 call psddr0() 000000018 ? 000000066 ?
9FFFFFFFFFFF3700 ?
000000030 ?
pevm_EXECC()+832 call psdnal() 9FFFFFFFFFFF54D0 ?
C00000203489A9F8 ?
C0000000000011AA ?
9FFFFFFFBE832840 ?
C00000203C2CC550 ?
400000000313C770 ?
000026035 ?
pfrinstr_EXECC()+16 call pevm_EXECC() 9FFFFFFFBE83D1D0 ?
0 9FFFFFFFBE8328B0 ?
000000020 ?
pfrrun_no_tool()+19 call pfrinstr_EXECC() 9FFFFFFFBE83D1D0 ?
2 C000001DA198D61C ?
9FFFFFFFBE83D238 ?
pfrrun()+1376 call pfrrun_no_tool() 9FFFFFFFBE83D1D0 ?
000002001 ?
9FFFFFFFBE83D238 ?
60000000000BA290 ?
C000000000000A1C ?
4000000003163040 ?
9FFFFFFFBE83D620 ?
9FFFFFFFBE83D298 ?
plsql_run()+1328 call pfrrun() 9FFFFFFFFFFF3788 ?
9FFFFFFFFFFF3770 ?
60000000000BA290 ?
9FFFFFFFFFFF4370 ?
9FFFFFFFFFFF4370 ?
C000000000000E23 ?
4000000002C34D70 ?
peicnt()+560 call plsql_run() 9FFFFFFFFFFF4380 ?
000000001 ? 000000000 ?
9FFFFFFFFFFF4380 ?
60000000000BA290 ?
9FFFFFFFFFFF4E90 ?
kkxexe()+1008 call peicnt() 9FFFFFFFFFFF54D0 ?
9FFFFFFFBE83D1D0 ?
9FFFFFFFFFFF4EA0 ?
60000000000BA290 ?
9FFFFFFFFFFF5420 ?
C000000000000A1C ?
000000020 ?
9FFFFFFFFFFF4F00 ?
opiexe()+8016 call kkxexe() 0000051F0 ?
.
.
.
这个SQL语句是正常的业务操作,而这种简单的INSERT都会导致ORA-600和ORA-7445错误,说明插入的表或索引本身存在问题。
检查表和索引的状态,发现索引部分分区状态异常:
CREATE INDEX "U1"."IDX_F_2"
ON "U1"."TABLE_A" ("S_ID")
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(
BUFFER_POOL DEFAULT) LOCAL
(PARTITION "P1"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS7" ,
PARTITION "P2"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS8" ,
PARTITION "P3"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS9" ,
PARTITION "P4"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS10" ,
PARTITION "P5"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS1" ,
PARTITION "P6"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS2" ,
PARTITION "P7"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS3" ,
PARTITION "P8"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS4" ,
PARTITION "P9"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS5" ,
PARTITION "P10"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS6" )
ALTER INDEX "U1"."IDX_F_2" MODIFY PARTITION "P6" UNUSABLE
ALTER INDEX "U1"."IDX_F_2" MODIFY PARTITION "P7" UNUSABLE
和客户沟通后,确认今天问题发生之前有人对索引进行维护操作,操作语句包括:
alter index u1.IDX_F_2 rebuild partition p6 parallel
alter index U1.IDX_T_2 rebuild partition P9 nologging parallel online
alter index U1.IDX_T_1 rebuild partition P3 nologging parallel online
alter index U1.IDX_F_2 rebuild partition P1 nologging parallel online
alter index U1.IDX_T_4 rebuild partition P1 nologging parallel online
alter index U1.IDX_F_4 rebuild partition P5 nologging parallel online
在对索引进行维护之后,数据库中出现了大量的enq: TM – contention等待,TM锁出现的原和是索引IDX_F_2的PARTITION P6重建语句没有添加ONLINE参数有直接关系。随后大量会话被阻塞,而整个索引重建操作被人为中止,其中包括正在运行的ONLINE REBUILD操作,而ONLINE REBUILD操作被中止是十分危险的,很容易导致bug的产生,而当前就导致了ORA-600和ORA-7445的产生。
由于客户着急解决问题,因此对数据库进行了重启。重启后,ORA-600错误信息不再出现。但是前台应用报错,索引失效。
SELECT 'ALTER INDEX U1.' || INDEX_NAME || ' REBUILD PARTITION ' || PARTITION_NAME || ';'
FROM DBA_IND_PARTITIONS
WHERE INDEX_OWNER = 'U1'
AND STATUS != 'USABLE';
获取所有失效的索引重建语句,将索引重新编译后,数据库恢复正常。
在Oracle的MOS上没有找到与之最相关的bug信息,反而是找到了一个相关性很高的补丁信息,而对应的bug信息并没有公开:补丁程序13737888: ONLINE DDL:ORA-600[KGHSTACK_UNDERFLOW_INTERNAL_3], [0X2B7F4E1E7678], [KEYBUF]。
前两天看到一篇Redhat官方的Oracle安装文档,对于Linux内核参数的修改描述的非常清晰。
安装Oracle之前,除了检查操作系统的硬件和软件是否满足安装需要之外,一个重点就是修改内核参数,其中最主要的是和内存相关的参数设置。
SHMMAX参数:Linux进程可以分配的单独共享内存段的最大值。一般设置为内存总大小的一半。这个值的设置应该大于SGA_MAX_TARGET或MEMORY_MAX_TARGET的值,因此对于安装Oracle数据库的系统,shmmax的值应该比内存的二分之一大一些。
# grep MemTotal /proc/meminfo
# cat /proc/sys/kernel/shmmax
上面的命令是检查系统内存的大小,以及当前shmmax的设置。
# echo 21474836480 > /proc/sys/kernetbl/shmmax
# sysctl -w kernel.shmmax=21474836480
# echo "kernel.shmmax=21474836480" >> /etc/sysctl.conf
这是设置shmmax参数的几种方法,这三种方式都可以将shmmax设置为20G。这个参数的修改可以不重启数据库。个人推荐使用第二种sysctl命令的方式。采用第三种方式需要执行sysctl –t操作或重启,但是为了确保下次重启后设置值仍然生效,第三种方式是必不可少的。前两种方式类似alter system set scope = memory,而第三种方式则类似alter system set scope = spfile。
SHMMNI参数:设置系统级最大共享内存段数量。Oracle10g推荐最小值为4096,可以适当比4096增加一些。
# cat /proc/sys/kernel/shmmni
# echo 4096 > /proc/sys/kernel/shmmni
# sysctl -w kernel.shmmni=4096
# echo "kernel.shmmni=4096" >> /etc/sysctl.conf
检查和设置方法如上,这和shmmax的修改方式没有区别,不在赘述。
SHMALL参数:设置共享内存总页数。这个值太小有可能导致数据库启动报错。很多人调整系统内核参数的时候只关注SHMMAX参数,而忽略了SHMALL参数的设置。这个值推荐设置为物理内存大小除以分页大小。
# getconf PAGE_SIZE
通过getconf获取分页的大小,用来计算SHMALL的合理设置值:
SQL> select 32*1024*1024*1024/4096 from dual;
32*1024*1024*1024/4096
----------------------
8388608
对于32G的内存,4K分页大小的系统而言,SHMALL的值应该设置为8388608。
# cat /proc/sys/kernel/shmall
# echo 8388608 > /proc/sys/kernel/shmall
# sysctl -w kernel.shmall=8388608
# echo " kernel.shmall=8388608" >> /etc/sysctl.conf
查询和设置方法如上。
信号灯semaphores是进程或线程间访问共享内存时提供同步的计数器。
SEMMSL参数:设置每个信号灯组中信号灯最大数量,推荐的最小值是250。对于系统中存在大量并发连接的系统,推荐将这个值设置为PROCESSES初始化参数加10。
SEMMNI参数:设置系统中信号灯组的最大数量。Oracle10g和11g的推荐值为142。
SEMMNS参数:设置系统中信号灯的最大数量。操作系统在分配信号灯时不会超过LEAST(SEMMNS,SEMMSL*SEMMNI)。事实上,如果SEMMNS的值超过了SEMMSL*SEMMNI是非法的,因此推荐SEMMNS的值就设置为SEMMSL*SEMMNI。Oracle推荐SEMMNS的设置不小于32000,假如数据库的PROCESSES参数设置为600,则SEMMNS的设置应为:
SQL> select (600+10)*142 from dual;
(600+10)*142
------------
86620
SEMOPM参数:设置每次系统调用可以同时执行的最大信号灯操作的数量。由于一个信号灯组最多拥有SEMMSL个信号灯,因此有推荐将SEMOPM设置为SEMMSL的值。Oracle验证的10.2和11.1的SEMOPM的配置为100。
通过下面的命令可以检查信号灯相关配置:
# cat /proc/sys/kernel/sem
250 32000 100 128
对应的4个值从左到右分别为SEMMSL、SEMMNS、SEMOPM和SEMMNI。修改方法为:
# echo 610 86620 100 142 > /proc/sys/kernel/sem
# sysctl -w kernel.sem="610 86620 100 142"
# echo "kernel.sem=610 86620 100 142" >> /etc/sysctl.conf
在9i以前,很多功能都是不区分表和物化视图的区别的,到了10g以后。很多功能会将表和物化视图区分对待。
原本通用的COMMENT ON TABLE语句,对物化视图不再有效,必须要使用COMMENT ON MATERIALIZED VIEW语句代替。
SQL> SELECT * FROM V$VERSION;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE 10.2.0.4.0 Production
TNS for Linux: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production
SQL> CREATE TABLE T_BASE (tbID NUMBER, TYPE VARCHAR2(30), NUM NUMBER);
Table created.
SQL> CREATE MATERIALIZED VIEW LOG ON T_BASE
2 WITH ROWID, SEQUENCE (TYPE, NUM)
3 INCLUDING NEW VALUES;
Materialized view log created.
SQL> CREATE MATERIALIZED VIEW MV_BASE
2 REFRESH FAST ENABLE QUERY REWRITE AS
3 SELECT TYPE, SUM(NUM) SUM_NUM, COUNT(NUM) CNT_NUM, COUNT(*) CNT
4 FROM T_BASE
5 GROUP BY TYPE;
Materialized view created.
SQL> COMMENT ON TABLE MV_BASE IS 'COMMENT ON A MATERIALIZED VIEW ';
COMMENT ON TABLE MV_BASE IS 'COMMENT ON A MATERIALIZED VIEW '
*
ERROR at line 1:
ORA-12098: cannot comment on the materialized view
SQL> COMMENT ON MATERIALIZED VIEW MV_BASE IS 'COMMENT ON A MATERIALIZED VIEW ';
Comment created.
SQL> COL COMMENTS FOR A60
SQL> SELECT * FROM USER_MVIEW_COMMENTS;
MVIEW_NAME COMMENTS
------------------------------ ------------------------------------------------------------
MV_BASE COMMENT ON A MATERIALIZED VIEW
其实不只是COMMENT发生了变化,关于物化视图的执行计划Oracle也对其进行细化,将物化视图的扫描和全表扫描区分开:
SQL> SET AUTOT ON EXP
SQL> SELECT COUNT(*) FROM MV_BASE;
COUNT(*)
----------
0
Execution Plan
----------------------------------------------------------
Plan hash value: 3034976462
-------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | MAT_VIEW ACCESS FULL| MV_BASE | 1 | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement
SQL> SELECT /*+ REWRITE */ TYPE, COUNT(*) FROM T_BASE GROUP BY TYPE;
no rows selected
Execution Plan
----------------------------------------------------------
Plan hash value: 1008429399
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 2 (0)| 00:00:01 |
| 1 | MAT_VIEW REWRITE ACCESS FULL| MV_BASE | 1 | 30 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement
在9i以前,很难从执行计划中区分扫描的是表还是物化视图,但是现在一目了然了。
总的来说,这种改进还是很有意义的,用户可以更清楚的了解处理的对象到底是表还是物化视图。
客户RAC环境在一个节点重启后,另一个节点出现IPC send timeout信息。
详细错误信息为:
Wed May 2 22:07:00 2012
IPC Send timeout detected.Sender: ospid 20808
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:02 2012
IPC Send timeout detected.Sender: ospid 6677
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:09 2012
IPC Send timeout detected.Sender: ospid 16758
Receiver: inst 1 binc 1718096035 ospid 16261
Wed May 2 22:07:13 2012
IPC Send timeout detected.Sender: ospid 8947
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:13 2012
IPC Send timeout detected.Sender: ospid 6583
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:31 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 132
Wed May 2 22:07:31 2012
IPC Send timeout detected.Sender: ospid 17068
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:34 2012
Communications reconfiguration: tbinstance_number 1
Wed May 2 22:07:34 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 154
Wed May 2 22:07:45 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 64
Wed May 2 22:07:45 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 95
Wed May 2 22:07:54 2012
IPC Send timeout detected.Sender: ospid 21078
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:59 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 24
Wed May 2 22:08:04 2012
Trace dumping is performing id=[cdmp_20120502220729]
Wed May 2 22:08:24 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 146
Wed May 2 22:08:36 2012
Trace dumping is performing id=[cdmp_20120502220805]
Wed May 2 22:08:38 2012
Trace dumping is performing id=[cdmp_20120502220805]
Wed May 2 22:10:55 2012
Evicting instance 1 from cluster
Wed May 2 22:11:32 2012
Waiting for instances to leave:
1
这个信息并不正常,查询MOS后发现,这是一个bug,问题描述可以参考:'IPC Send Timeout Detected' errors between QMON Processes after RAC reconfiguration [ID 458912.1]。
对于当前的10.2.0.4环境,需要针对Bug 6200820进行PATCH修正,而对于10.2.0.3版本则需要应用Patch 6326889。
在MOS中查到不少类似IPC Timeout的问题,多数都会影响10.2.0.4版本,且大部分都在10.2.0.5中被fixed,因此如果这个问题出现频繁,升级到10.2.0.5也是一个不错的选择。
Oracle 10g监听listener不能启动的原因有很多种,本文我们对这些造成listener不能启用的原因进行了总结,接下来就让我们一起来了解一下这部分内容。
1.当遇到Oracle出现下面提示时:ora-01034:oracle not available,ora-27101:shared mermory realm does not exist.
解决方式:
方法1:
1.输入:connect/as sysdba;
2.重启计算机就OK了;
方法2:
在命令行中输入命令如下:
1. C:>svrmgrl
2. Oracle Server Manager Release 3.1.7.0.0 - Production
3. Copyright (c) 2000, Oracle Corporation.
tbAll Rights Reserved.
4. Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
5. With the Partitioning option
6. JServer Release 8.1.7.0.0 - Production
7.
8. SVRMGR> connect internal/oracle
9. 连接成功。
10.
11. SVRMGR> startup startup
12.
后再连接数据库应该没有问题了。
2. Oracle的Listener突然不能启动,错误码是1067.
解决方法:
进入dos
输入lsnrctl start
等待显示结果,我的结果如下
启动tnslsnr:请稍候…
TNSLSNR for 32-bit Windows: Version 9.2.0.1.0 - Production
系统参数文件为E:oracleora92networkadminlistener.ora
写入E:oracleora92networkloglistener.log的日志信息
监听该对象时出错: (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=123.118.1.39)(PORT=1521)))。
TNS-12545:因目标主机或对象不存在,连接失败。
TNS-12560: TNS:协议适配器错误。
TNS-00515:因目标主机或对象不存在,连接失败。
32-bit Windows Error: 49: Unknown error.
监听程序未能启动。请参阅上面的错误消息…
仔细核对,原来是自己修改了Ip地址。
方法1:可修改ip地址。
方法2:可修改E:oracleora92networkadminlistener.ora文件的配置。
3. AGENT服务无法启动
解决方法︰
可以删除networkagent目录下的。q,.ver等文件,然后重新agentctl start即可。
若提示sqlnet.ora错误,可以查看一下sqlnet.ora内容。
1. # SQLNET.ORA Network Configuration file: D:oracleora92networkadminsqlnet.ora
2. # Generated by Oracle configuration tools.
3. SQLNET.AUTHENTICATION_SERVICES= (NTS)
4. NAMES.DIRECTORY_PATH= (TNSNAMES, ONAMES, HOSTNAME)
三层结构的sqlnet.ora的name
1. # NAMES.PREFERRED_SERVERS =
2. # (ADDRESS_LIST =
3. # (ADDRESS = (PROTOCOL = TCP)(HOST = MICROSOF-3D9384)(PORT = 1575))
4. # )
4. Oracle http server启动不了
把IIS里面用80端口的站点停掉就行了,或者把Oracle Http Server端口改掉。
关于Oracle 10g监听listener不能启动的错误解决方案的总结就介绍到这里了,希望本次的介绍能够对您有所收获!
#g_kclist{font-size:12px;width:570px;float:none; margin-top:5px; clear:right}
#g_kclist a{color:#000; text-decoration:none}
#g_kclist h2{margin:0px;padding:0px;font-size:14px; text-align:center;background:url(http://www.thea.cn/zt/zt_img/zczhongduan.gif) no-repeat;line-height:31px;color:#fff}
#g_kclist table{line-height:25px;background:#B0DA90;margin-top:8px}
#g_kclist table td{ text-align:center;background:#fff}
#g_kclist table td.td1 a{color:#f00}
#g_kclist table th{background:#F2F7ED;color:#525F46}
Oracle中可以按照如下方式对用户登陆失败进行审计:
1、确认sys.aud$ 是否存在?
desc sys.aud$
2、观察user$表中lcount为非0的用户,如果包含被锁账户,则可以判定很有可能是该用户登陆尝试失败过多
造成了账户被锁:
select name,lcount from sys.user$;
3、修改audit参数: audit_trail=none
alter system set audit_trail=db scope=spfile;
重启数据库。参数生效。
4、开启tb登陆失败审计:
AUDIT SESSION WHENEVER NOT SUCCESSFUL;
5、登陆失败尝试。
sqlplus w/错误密码
6、检查审计记录
select * from sys.aud$;
里面有会话基本信息和机器名,用户名等。
解锁用户
alter user atest account unlock;
解除由于密码连续错误而锁定用户
alter profile default limit failed_login_attempts unlimited;
#g_kclist{font-size:12px;width:570px;float:none; margin-top:5px; clear:right}
#g_kclist a{color:#000; text-decoration:none}
#g_kclist h2{margin:0px;padding:0px;font-size:14px; text-align:center;background:url(http://www.thea.cn/zt/zt_img/zczhongduan.gif) no-repeat;line-height:31px;color:#fff}
#g_kclist table{line-height:25px;background:#B0DA90;margin-top:8px}
#g_kclist table td{ text-align:center;background:#fff}
#g_kclist table td.td1 a{color:#f00}
#g_kclist table th{background:#F2F7ED;color:#525F46}
由于历史的原因,在多数企业都同时存在多个数据库平台,在每个数据库平台上都运行着相关的一套或多套应用。随着单位业务不断扩大,如何在不影响现有应用运行的前提下,快速有效地整合这些分布在单位内部不同数据库平台上的数据,是一个困扰CIO们的问题。面对这一问题,现有解决方案大致可分为以下两种:
1.在应用程序上建立连接不同数据源的数据连接,这样做要求程序员分清哪个连接是对应哪个数据库的,而且如果设计时涉及到存储过程还要按照不同数据库的要求分别编写,加重了程序员的要求。
2.在数据库中设立快照,定时把其他数据源的数据复制到本地数据库,这样虽然解决了前一种方法中不同数据源的问题,但是由于是定时复制,数据不能实时同步,在实时性要求高的应用中这种方法便不能使用。
由于上述两种方法都存在一定的缺点,这里介绍一种Oracle提供的解决oracle数据库与异种数据源的连接问题的解决方案—Oracle的异构服务(Heterogeneous Services)。
程序运行效果截图
异构服务
“异构服务”是集成在Oracle 8i数据库软件中的功能,它提供了从Oracle数据库访问其他非Oracle数据库的通用技术。熟悉Oracle的读者都很清楚,Oracle提供通过建立DB Link的方法访问非本地数据库,而“异构服务”提供通过建立DB Link使你能够执行Oracle SQL查询,透明地访问其他非Oracle数据库里的数据,就像访问Oracle远程数据库一样。“异构服务”分为两种:
1. 事务处理服务(Transation Service):通过事务处理服务,使用户在访问非Oracle数据库中支持事务处理功能。
2. SQL服务: 通过SQL服务,使用户直接在Oracle数据库中执行对非Oracle数据库的各种SQL语句。
根据异构服务代理程序的不同,“异构服务”连接方式可以分为透明网关和通用连接两种。
透明网关(Transparent Gateways) 透明网关使用Oracle提供的特定网关程序来设置代理,tb例如连接SQL Server则必须要有SQL Transparent Gateway for SQL Server。
通用连接(Generic Connectivity) 通用连接又分为ODBC连接和OLE DB连接两种,其连接方法和透明网关没有本质区别,只不过通用连接是和数据库一起提供的功能,你不需要向Oracle购买相关的透明网关程序。
连接实例
这个实例的应用环境是Oracle 8.1.7,操作系统Windows 2000 Server英文版,采用通用连接的ODBC for SQL Server连接SQL Server 2000中文版。安装步骤如下:
1. 安装HS部件。
缺省情况下,HS服务是和Oracle 8.1.7一起安装的,你可以查询SYS用户下是否存在HS_BASE_CAPS视图,以确认HS部件是否安装,如果没有可以用相关的安装盘进行安装。
2. 配置ODBC系统连接字。
在控制面板选择“Data Sources (ODBC)”,在“系统DNS”内配置ODBC for SQL Server连接字(dnsora2sql)。
3.配置tnsnames.ora,它位于ORACLE_HOME\NETWORK\ADMIN。
在这个文件中增加如下代码:
Lnk2sql =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = HOSTNAME)(PORT = 1521)))
(CONNECT_DATA =
(SID = hs4sql) |< 服务的SID名称,要和Listener里配置的sid相同)
(HS=OK) |< 打开HS服务选项
)
4.配置listener.ora,它位于ORACLE_HOME\NETWORK\ADMIN。
在这个文件中增加如下代码:
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = hs4sql) |< 服务的SID名称,与tnsname名称相对应
(ORACLE_HOME = C:\oracle\ora9201)
(PROGRAM = hsodbc) |< 要使用的HS服务程序,如果使用OLE DB,程序名为hsole )
)
5. 重新启动Oracle listener。
6. 编辑位于ORACLE_HOME\HS\ADMIN内init.ora,这里是iniths4sql。
修改如下两行代码:
HS_FDS_CONNECT_INFO = dnsora2sql |< ODBC系统名
HS_FDS_TRACE_LEVEL = 0
7. 创建DATABASE LINK。
create database link ‘ora2sql’ connect
to sql1 identified by sql1 using ‘lnk2sql’;
8. 测试连接。如:
SQL> select * from 到此我们已经完成了使用ODBC连接SQL SERVER的配置工作。
总体上说,异构服务扩展了Oracle数据库连接异种数据源的能力,加强了企业数据的整合,是一个快速有效经济地整合企业内部异构数据的解决方案。
ADO是Active Data Object的缩写,称为ActiveX数据对象。利用ADO对象,通过ODBC驱动程序或OLE DB连接字符串,可实现对任意数据库的存取和访问。
OLE DB是微软用来替代ODBC的一种数据库访问技术。是一种对关系型数据库和非关系型数据库均有效的一种数据库访问技术。
ADO提供了7个独立的对象,利用这些对象,可实现对数据库的存取和访问:
·Connection 连接对象。
·Command 命令对象,利用命令对象可执行一个SQL存储过程或有参数的查询。
·Parameter 参数对象。
·Recordset 记录集对象,代表从数据表中通过查询所获得的一组记录。通过该对象的方法和属性,可实现对记录的存取和操作。
·Field 字段对象,代表记录集中的一个字段。Fields为字段集合,tb代表一条记录的全部字段。
·Property 属性对象。
·Error 错误对象。
Connection,Command和Recordset是整个ADO的核心,通过Connection对象与一个数据库建立连接,然后利用Command对象执行查询,从而返回查询结果,并将结果(记录集)存入Recordset对象中,利用服务器端脚本,通过访问Recordset对象,便可获得查询到的记录内容。
另外,利用Connection对象的Execute方法和Recordset对象的Open方法,也可执行一个查询,返回一个记录集。
ODBC数据源的用户数据源,系统数据源和文件数据源。用户和系统DSN存储在WindowsNT注册表中,系统DSN可被登录的所有用户访问和使用,用户DSN只能提供特定的用户访问和使用。 文件DSN是存储在一个扩展名为.dsn的文本文件中,可供多个用户访问和使用,并可实现复制,通用性强,一般采用此方式。
ADO连接对象
连接对象在使用前必须先创建该对象的实例:
Set 实例名=Server.CreateObject("ADODB.Connection")
连接对象的方法:
·Open方法
连接对象.Open 数据源名 | 连接字符串
带参数调用Open方法时,其参数实质是传递给连接对象的ConnectionString属性的。因此,可事先设置ConnectionString属性的值,然后再调用不带参数的Open方法。
·Close方法
连接对象.Close 释放:Set conn=Nothing
·Execute方法
该方法用于执行SQL语句。根据SQL语句执行后是否返回记录集,该方法的使用格式分为以下两种:
1.执行SQL查询语句时,将返回查询得到的记录集。
用法为:
Set 对象变量名=连接对象.Execute("SQL 查询语言")
Execute方法调用后,会自动创建记录集对象,并将查询结果存储在该记录对象中,通过Set方法,将记录集赋给指定的对象保存,以后对象变量就代表了该记录集对象。
2.执行SQL的操作性语言时,没有记录集的返回。
此时用法为:
连接对象.Execute "SQL 操作性语句" [, RecordAffected][, Option]
·RecordAffected 为可选项,此出可放置一个变量,SQL语句执行后,所生效的记录数会自动保存到该变量中。通过访问该变量,就可知道SQL语句队多少条记录进行了操作。
·Option 可选项,该参数的取值通常为adCMDText,它用于告诉ADO,应该将Execute方法之后的第一个字符解释为命令文本。通过指定该参数,可使执行更高效。
·BeginTrans、RollbackTrans、CommitTrans方法
这三个方法是连接对象提供的用于事务处理的方法。BeginTrans用于开始一个事物;RollbackTrans用于回滚事务;CommitTrans用于提交所有的事务处理结果,即确认事务的处理。
事务处理可以将一组操作视为一个整体,只有全部语句都成功执行后,事务处理才算成功;若其中有一个语句执行失败,则整个处理就算失败,并恢复到处里前的状态。
BeginTrans和CommitTrans用于标记事务的开始和结束,在这两个之间的语句,就是作为事务处理的语句。判断事务处理是否成功,可通过连接对象的Error集合来实现,若Error集合的成员个数不为0,则说明有错误发生,事务处理失败。Error集合中的每一个Error对象,代表一个错误信息。
另外,利用SQL本身所提供的事务处理语句,通过编写存储过程,然后利用ADO命令对象的相关方法,通过调用执行存储过程,也可实现事务。
DECODE()函数用法
作用:将输入数值与函数中的参数列表相比较,根据输入值返回一个对应值。函数的参数列表是由若干数值及其对应结果值组成的若干序偶形式。当然,假如未能与任何一个实参序偶匹配成功,则函数也有默认的返回值。 区别于SQL的其它函数,DECODE函数还能识别和操作空值。
语法:DECODE(control_value,value1,result1[,value2,result2…][,default_result]);
control _value试图处理的数值。DECODE函数将该数值与后面的一系列的偶序相比较,以决定返回值。 value1是一组成序偶的数值。假如输入数值与之匹配成功,则相应的结果将被返回。对应一个空的返回值,可以使用要害字NULL于之对应 result1 是一组成序偶的结果值。 default_result 未能与任何一个值匹配时,函数返回的默认值。
例如: selectdecode( x , 1 , 'x is 1 ', 2 , 'x is 2 ', 'others') from dual 当x等于1时,则返回'x is 1'. 当x等于2时,则返回'x is 2'. 否则,返回others'. 需要,比较2个值的时候,可以配合SIGN()函数一起使用。 SELECT DECODE( SIGN(5 -6), 1 'Is Positive', -1, 'Is Nagative', 'Is Zero') 同样,也可以用CASE实现: SELECT CASE SIGN(5 - 6) WHEN 1 THEN 'Is Positive'WHEN -1 THEN 'Is Nagative'ELSE 'Is Zero' ENDFROM DUAL此外,还可以在Order by中使用Decode. 例如:表table_subject,有subject_name列。要求按照:语、数、外的顺序进行排序。
tb这时,就可以非常轻松的使用Decode完成要求了。 select * from table_subject order by decode(subject_name, '语文', 1, '数学', 2, , '外语',3)
将所有的结果全部写出
select * from classes t
数据为
1 1 一班 NUM_1
2 2 二班 NUM_2
3 3 三班 NUM_3
4 4 四班 NUM_4
select t.* from classes t order by decode(t.classnum,'NUM_1',4,'NUM_2',3,'NUM_3',1);
1 3 三班 NUM_3
2 2 二班 NUM_2
3 1 一班 NUM_1
4 4 四班 NUM_4
必须将所有的结果写出
本周Oracle宣布下一步将全力实施它的安全策略,并透露计划去整合安全产品到应用产品线同时扩展企业安全搜索。为确保自己成为一家名副其实的安全产品厂商,Oracle花费了大量资金,先后收购了许多访问管理、身份管理和其他安全性软件制造商。
在周三的安全性策略简报中,Oracle公司承诺将会整合些安全性产品到自己的Fusion应用系统中。该系统将在2008年发布。公司还计划下个月扩展新的企业安全搜索的能力,允许搜索引擎不仅可以搜索Oracle应用系统和数据库而且还可直接访问电子邮件和文件系统。
当Oracle做到产品安全时,它的范围将是非常广阔的。在数据库方面,公司提出高级安全性选项用于数据加密,数据库保护用于防止内部用户滥用他们的数据访问权限,安全标签用于指定不同安全级别的数据,安全备份用于加密备份的磁带。Oracle还提供了多样的身份管理能力,包括目录服务、身份验证、web服务访问控制。Oracle还将审计和一致性应用作为其安全措施的一部分。
如何辨别安全领域的产品主要看产品是否具备自动安全处理能力如身份管理和数据保护。Oracle负责服务器技术的高级副总裁Thomas Kurian说:“通过Fusion中我们将整合那些全部的安全特性到我们的企业应用中。”但这并意味着Oracle的单机安全应用会在近期发布,目前它还在研发过程中。所以客户需要继续去管理他们的非Oracle的应用系统。
Oracle的企业安全搜索可以让企业员工在整个公司数据库和应用系统中搜寻信息。Oracle总裁Charles Phillips说:“客户说希望让他们的用户能够象用Google一样的搜索,但不能搜寻到他们不该看到的信息。”企业安全搜索设计与Oracle Portal开发的企业门户配合使用,如同在Oracle数据库开发数据库应用。公司计划去新增个适配器实现企业对微软Exchange和SharePoint的搜索。
企业策略组织的高级分析师Jon Oltsik说,Oracle认定产品的安全特性有广阔市场机会,同样市场目标IBM收购了Internet Security Systems,EMC收购了RSA Security。但是客户关心的是增强他们IT基础设置的安全性,而不是去防止他们咨询奇特的问题。虽然历史不是总能暗示出未来,但IT安全支持者不可能忘记在Oracle危急补丁升级期间的那些坏经历。当Oracle转向安全领域时,它被人们认为是卖万金油的,主要就是他们产品质量的因素。
投资者金融服务公司Investors Bank & Trust早已经使用了Oracle的电子商务应用和数据库,但是系统使用的效果没有之前预期的好。该金融服务公司在过去两年一直是采用的IBM的访问和身份管理技术。Investors Bank & Trust公司企业应用主管说:"如果Oracle真的能够将安全特性整合到他们的产品中,能够他们承诺的那样去做的话,他们的软件产品将变的更加引人注目"。
从目前来看,Oracle Fusion计划的实现还有很长的路要走。在这期间Oracle需要继续面对那些长期的竞争对手,tb如CA,IBM和sun等。现在的疑问不是Oracle能不能够将安全性产品推销给自己的客户群而是他能不能就产品销售给竞争对手的客户。
#g_kclist{font-size:12px;width:570px;float:none; margin-top:5px; clear:right}
#g_kclist a{color:#000; text-decoration:none}
#g_kclist h2{margin:0px;padding:0px;font-size:14px; text-align:center;background:url(http://www.thea.cn/zt/zt_img/zczhongduan.gif) no-repeat;line-height:31px;color:#fff}
#g_kclist table{line-height:25px;background:#B0DA90;margin-top:8px}
#g_kclist table td{ text-align:center;background:#fff}
#g_kclist table td.td1 a{color:#f00}
#g_kclist table th{background:#F2F7ED;color:#525F46}
是谁"偷偷的"用了那么多空间呢(本来有几十个G的Free磁盘空间的)?
检查数据库表空间占用空间情况:
SQL> select tablespace_name,sum(bytes)/1024/1024/1024 GB
2 from dba_data_files group by tablespace_name
3 union all
4 select tablespace_name,sum(bytes)/1024/1024/1024 GB
5 from dba_temp_files group by tablespace_name order by GB;
TABLESPACE_NAME GB
------------------------------ ----------
USERS .004882813
UNDOTBS2 .09765625
SYSTEM .478515625
SYSAUX .634765625
WAPCM_TS_VISIT_DETAIL .9765625
HY_DS_DEFAULT 1
MINT_TS_DEFAULT 1
MMS_TS_DATA2 1.375
MMS_IDX_SJH 2
MMS_TS_DEFAULT 2
IVRCN_TS_DATA 2
TABLESPACE_NAME GB
------------------------------ ----------
MMS_TS_DATA1 2
CM_TS_DEFAULT 5
TEMP 20.5498047
UNDOTBS1 27.1582031
15 rows selected.
不幸的发现,UNDO表空间已经扩展至27G,而TEMP表空间也扩展至20G,这2个表空间加起来占用了47G的磁盘空间,导致了空间不足。
显然曾经有大事务占用了大量的UNDO表空间和Temp表空间,Oracle的AUM(Auto Undo Management)从出生以来就经常出现只扩展,不收缩(shrink)的情况(通常我们可以设置足够的UNDO表空间大小,然后取消其自动扩展属性).
现在我们可以采用如下步骤回收UNDO空间:
1.确认文件
SQL> select file_name,bytes/1024/1024 from dba_data_files
2 where tablespace_name like 'UNDOTBS1';
FILE_NAME
--------------------------------------------------------------------------------
BYTES/1024/1024
---------------
+ORADG/danaly/datafile/undotbs1.265.600173875
27810
2.检查UNDO Segment状态
SQL> select usn,xacts,rssize/1024/1024/1024,hwmsize/1024/1024/1024,shrinks
2 from v$rollstat order by rssize;
USN XACTS RSSIZE/1024/1024/1024 HWMSIZE/1024/1024/1024 SHRINKS
---------- ---------- --------------------- ---------------------- ----------
0 0 .000358582 .000358582 0
2 0 .071517944 .071517944 0
3 0 .13722229 .13722229 0
9 0 .236984253 .236984253 0
10 0 .625144958 .625144958 0
5 1 1.22946167 1.22946167 0
8 0 1.27175903 1.27175903 0
4 1 1.27895355 1.27895355 0
7 0 1.56770325 1.56770325 0
1 0 2.02474976 2.02474976 0
6 0 2.9671936 2.9671936 0
11 rows selected.
3.创建新的UNDO表空间
SQL> create undo tablespace undotbs2;
Tablespace created.
4.切换UNDO表空间为新的UNDO表空间
SQL> alter system set undo_tablespace=undotbs2 scope=both;
System altered.
此处使用spfile需要注意,以前曾经记录过这样一个案例:Oracle诊断案例-Spfiletb案例一则
5.等待原UNDO表空间所有UNDO SEGMENT OFFLINE
SQL> select usn,xacts,status,rssize/1024/1024/1024,hwmsize/1024/1024/1024,shrinks
2 from v$rollstat order by rssize;
USN XACTS STATUS RSSIZE/1024/1024/1024 HWMSIZE/1024/1024/1024 SHRINKS
---------- ---------- --------------- --------------------- ---------------------- ----------
14 0 ONLINE .000114441 .000114441 0
19 0 ONLINE .000114441 .000114441 0
11 0 ONLINE .000114441 .000114441 0
12 0 ONLINE .000114441 .000114441 0
13 0 ONLINE .000114441 .000114441 0
20 0 ONLINE .000114441 .000114441 0
15 1 ONLINE .000114441 .000114441 0
16 0 ONLINE .000114441 .000114441 0
17 0 ONLINE .000114441 .000114441 0
18 0 ONLINE .000114441 .000114441 0
0 0 ONLINE .000358582 .000358582 0
USN XACTS STATUS RSSIZE/1024/1024/1024 HWMSIZE/1024/1024/1024 SHRINKS
---------- ---------- --------------- --------------------- ---------------------- ----------
6 0 PENDING OFFLINE 2.9671936 2.9671936 0
12 rows selected.
再看:
11:32:11 SQL> /
USN XACTS STATUS RSSIZE/1024/1024/1024 HWMSIZE/1024/1024/1024 SHRINKS
---------- ---------- --------------- --------------------- ---------------------- ----------
15 1 ONLINE .000114441 .000114441 0
11 0 ONLINE .000114441 .000114441 0
12 0 ONLINE .000114441 .000114441 0
13 0 ONLINE .000114441 .000114441 0
14 0 ONLINE .000114441 .000114441 0
20 0 ONLINE .000114441 .000114441 0
16 0 ONLINE .000114441 .000114441 0
17 0 ONLINE .000114441 .000114441 0
18 0 ONLINE .000114441 .000114441 0
19 0 ONLINE .000114441 .000114441 0
0 0 ONLINE .000358582 .000358582 0
11 rows selected.
Elapsed: 00:00:00.00
6.删除原UNDO表空间
11:34:00 SQL> drop tablespace undotbs1 including contents;
Tablespace dropped.
Elapsed: 00:00:03.13
7.检查空间情况
由于我使用的ASM管理,可以使用10gR2提供的信工具asmcmd来察看tb空间占用情况.
[oracle@danaly ~]$ export ORACLE_SID=+ASM
[oracle@danaly ~]$ asmcmd
ASMCMD> du
Used_MB Mirror_used_MB
21625 21625
ASMCMD> exit
空间已经释放。
.item-area{width:578px;margin:15px auto;border-top:1px solid #ddd;color:#666}
.item-area a,.item-area a:link,.item-area a:visited{color:#666;text-decoration:none}
.item-area a:hover{color:#3a7ad9;text-decoration:underline;}
a img{border:none;vertical-align:middle}
.item-area h2,.item-area h3{float:none;font-size:100%;font-weight:normal;}
.item-area .h2{height:25px;margin:10px 0;padding-left:35px;*float:left;font:bold 14px/25px "宋体";background:url(http://sns.thea.cn/module/images/icos.png) no-repeat 0 0}
.item-area span.more{float:right;font:normal 12px/25px "宋体"}
.item-area a.more{float:right;font:normal 12px/25px "宋体"}
.item-a{margin-bottom:15px}
.item-a .h-ksrm{background-position:0 0}
.item-a li{*display:inline;overflow:hidden;zoom:1;line-height:2em;padding-left:35px;font-size:14px;background: url(http://sns.thea.cn/module/images/btns.png) no-repeat -1px -28px;}
.item-a li a{float:left;}
.item-a .testBtn{float:right;width:58px;height:21px;line-height:21px;font-size:12px;margin-top:5px;margin-top:3px;text-align:center;background:url(http://sns.thea.cn/module/images/btns.png) no-repeat -1px -1px; color:#FFFFFF;}
.item-a a.freeBtn{width:20px;margin:0 0 0 6px;line-height:28px;color:#fff;font-size:12px;text-indent:-9999px;background: url(http://sns.thea.cn/module/images/icos.png) no-repeat 0 -131px;}
.item-a li.hots a.freeBtn{background-position:0 -105px}
.item-a a.examnum em{font-style:normal;color:red;font-weight:bold;}
.item-b {padding:5px 0 20px;border-top:1px dashed #ddd;border-bottom:1px dashed #ddd}
.xsjl-list-col3s li{display:table-cell;*display:inline;zoom:1;vertical-align:top;width:182px;padding-right:10px;line-height:150%;font-size:12px;}
.item-b .h-xsjl{background-position:0 -26px}
.item-b .pic{float:left;margin:3px 10px 0 0;}
.item-b em{font-style:normal;color:#dc2c2c}
.item-b a.join{display:inline-block;padding-left:20px;background:url(http://sns.thea.cn/module/images/icos.png) no-repeat 0 -160px}
.item-b .xsjl-list-col3s h3 a{display:inline-block;width:120px;overflow:hidden;white-space:nowrap;color:#3a7ad9}
.item-b .xsjl-list-col3s h3{text-align:left;line-height:150%;font-family:"宋体","微软雅黑"}
客户数据库使用sqlplus直连方式连接数据库报错,而如果使用tnsnames方式则可以正常连接。
详细错误信息为:
Thu Apr 26 10:17:56 2012
Errors in file /oracle/admin/trs/udump/trs2_ora_2619.trc:
ORA-00603: ORACLE server session terminated by tb fatal error
ORA-27504: IPC error creating OSD context
ORA-27300: OS system dependent operation:IPC init failed with status: 65
ORA-27301: OS failure message: Package not installed
ORA-27302: failure occurred at: skgxpcini
ORA-27303: additional information: libskgxpd.so called
libskgxp10.so should reference real implementation.
根据MOS文档,这个问题的原因是由于环境变量中指定了CRS的目录,导致部分LIB没有找到:sqlplus Local connection to Instance is not possible , remote Using tns is fine . [ID 859778.1]。
解决问题的方法是在环境变量SHLIB_PATH和LIBPATH中,去掉CRS的HOME信息,使得Oracle正确找到ORACLE_HOME下的LIB目录。
数据库的Oracle 10204 RAC for Windows出现频繁节点重启的问题。
从告警日志看,当前节点的重启一般发生在节点刚启动或关闭时:
Thu May 03 17:22:45 2012
cluster interconnect IPC tb version:Oracle 9i Winsock2 TCP/IP IPC
IPC Vendor 0 proto 0
Version 0.0
PMON started with pid=2, OS id=1616
DIAG started with pid=3, OS id=120
PSP0 started with pid=4, OS id=6104
LMON started with pid=5, OS id=3844
LMD0 started with pid=6, OS id=6120
LMS0 started with pid=7, OS id=3548
LMS1 started with pid=8, OS id=5688
LMS2 started with pid=9, OS id=3636
LMS3 started with pid=10, OS id=3588
MMAN started with pid=11, OS id=3168
DBW0 started with pid=12, OS id=3208
DBW1 started with pid=13, OS id=5784
LGWR started with pid=14, OS id=6208
CKPT started with pid=15, OS id=3100
SMON started with pid=16, OS id=5948
RECO started with pid=17, OS id=3748
CJQ0 started with pid=18, OS id=7152
MMON started with pid=19, OS id=4552
MMNL started with pid=20, OS id=6940
Thu May 03 17:22:46 2012
lmon registered with NM - instance id 1 (internal mem no 0)
Thu May 03 17:22:46 2012
Reconfiguration started (old inc 0, new inc 8)
List of nodes:
0 1
Global Resource Directory frozen
* allocate domain 0, invalid = TRUE
Communication channels reestablished
Error: KGXGN aborts the instance (6)
Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_lmon_3844.trc:
ORA-29702: ???????????
LMON: terminating instance due to error 29702
Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_pmon_1616.trc:
ORA-29702: ???????????
Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_psp0_6104.trc:
ORA-29702: ???????????
Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_dbw0_3208.trc:
ORA-29702: ???????????
Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_mman_3168.trc:
ORA-29702: ???????????
Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_dbw1_5784.trc:
ORA-29702: ???????????
Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_ckpt_3100.trc:
ORA-29702: ???????????
Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_lgwr_6208.trc:
ORA-29702: ???????????
Thu May 03 17:22:52 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_reco_3748.trc:
ORA-29702: ???????????
Thu May 03 17:22:52 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_smon_5948.trc:
ORA-29702: ???????????
Thu May 03 17:22:52 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_lms1_5688.trc:
ORA-29702: ???????????
Thu May 03 17:22:52 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_lms0_3548.trc:
ORA-29702: ???????????
Instance terminated by LMON, pid = 3844
而从CSSD日志文件中可以发现下面的信息:
[ CSSD]2012-04-29 16:26:07.953 [7112] >TRACE: clssgmReconfigThread: completed for reconfig(13), with status(1)
2012-04-30 09:07:04.718: [ OCROSD]utgdv:11:could not read reg value ocrmirrorconfig_loc os error=操作系统找不到已输入的环境选项。
2012-04-30 09:07:04.718: [ OCROSD]utgdv:11:could not read reg value ocrmirrorconfig_loc os error=操作系统找不到已输入的环境选项。
[ CSSD]2012-04-30 09:07:04.765 >USER: Copyright 2012, Oracle version 10.2.0.4.0
[ CSSD]2012-04-30 09:07:04.765 >USER: CSS daemon log for node crct-oadb, number 1, in cluster crs
[ CSSD]2012-04-30 09:07:04.765 [3780] >TRACE: clssscmain: local-only set to false
[ clsdmt]Listening to (ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=61180))
[ CSSD]2012-04-30 09:07:04.781 [3780] >TRACE: clssnmReadNodeInfo: added node 1 (crct-oadb) to cluster
[ CSSD]2012-04-30 09:07:04.781 [3780] >TRACE: clssnmReadNodeInfo: added node 2 (crct-oapt) to cluster
[ CSSD]2012-04-30 09:07:04.828 [3724] >TRACE: clssnm_skgxninit: Compatible vendor clusterware not in use
[ CSSD]2012-04-30 09:07:04.828 [3724] >TRACE: clssnm_skgxnmon: skgxn init failed
[ CSSD]2012-04-30 09:07:04.843 [3780] >TRACE: clssnmNMInitialize: misscount set to (60)
[ CSSD]2012-04-30 09:07:04.843 [3780] >TRACE: clssnmNMInitialize: Network heartbeat thresholds are: impending reconfig 30000 ms, reconfig start (misscount) 60000 ms
[ CSSD]2012-04-30 09:07:04.843 [3780] >TRACE: clssnmDiskStateChange: state from 1 to 2 disk (0/\\.\votedsk1)
[ CSSD]2012-04-30 09:07:04.843 [3112] >TRACE: clssnmvDPT: spawned for disk 0 (\\.\votedsk1)
[ CSSD]2012-04-30 09:07:06.843 [3112] >TRACE: clssnmDiskStateChange: state from 2 to 4 disk (0/\\.\votedsk1)
[ CSSD]2012-04-30 09:07:06.843 [4492] >TRACE: clssnmvKillBlockThread: spawned for disk 0 (\\.\votedsk1) initial sleep interval (1000)ms
根据这些信息查询,发现属于10.2.0.4上的bug:10gR2/11gR1: Instances Abort With ORA-29702 When The Server is rebooted or shut down [ID 752399.1]。这个bug影响10.2.0.1到10.2.0.4以及11.1.0.6和11.1.0.7版本。
Oracle给出的解决方案是修改操作系统启动时调用的K96 link替换为K19 link。不过当前版本是Windows环境,显然这种解决方法并不适用。恐怕除了升级版本外,没有什么太好的其他解决方法。
将产品环境部署在Windows环境下的系统确实少见,而在Windows上部署RAC的就更是凤毛麟角了,而大多数这样部署的不只是对于Oracle不了解,连Windows和Linux的稳定性的差别都不是很清楚,出现各种问题的几率自然要大得多了。
ORA-1错误代表唯一冲突,而空间不足时出现这个错误还是第一次碰到。
错误信息如下:
Tue May 01 05:00:11 2012
Non critical error ORA-00001 caught while writing to trace file "/home/oracle/base/diag/rdbms/orcl/orcl/trace/orcl_ora_31131.trc"
Error message: Linux-x86_64 Error: 28: No space left on device
Additional information: 1
Writing to the above trace file is disabled for now on...
这个错误是11.2.0.2引入的,Oracle本应该返回操作系统上的错误,结果返回了ORA-00001错误信息,这是一个未发布的BUG 8367518导致的,可以参考文档Non Critical Error ORA-00001 Caught While Writing To Trace File tb[ID 1354863.1]。
Oracle给出的解决方法就是应用BUG 8367518的补丁,当然这个问题并不严重,完全可以将其忽略。
客户一个并不繁忙的数据库出现长时间Streams miscellaneous event等待。
数据库版本是RAC 11.2.0.2 for Linux X64,其中一个节点的TOP 5等待信息为:
Event |
Waits |
Time(s) |
Avg wait (ms) |
% DB time |
Wait Class |
Streams miscellaneous event |
62,377 |
31,274 |
501 |
93.26 |
Other |
DB CPU |
|
1,887 |
|
5.63 |
|
log file sequential read |
648,206 |
276 |
0 |
0.82 |
System I/O |
control file sequential read |
519,487 |
122 |
0 |
0.36 |
System I/O |
Disk file operations I/O |
483,960 |
99 |
0 |
0.30 |
User I/O |
显然,这个数据库的主要负载发生在这个等待事件上,而这个系统中部署了Goldengate。查询MOS,很容易找到文档Why do I see Streams Miscellaneous Event in AWR as a Top Event When GoldenGate Extractb is Running [ID 1317122.1]。
文档对这个问题进行了简单的描述,这个等待事件是Goldengate在等待日志中的额外工作的,在11.2.0.2.X版本以后,这个等待事件改名为Waiting for additional work from the logfile,而且被记入到空闲等待中。
对于这个问题,可以安全的将其忽略掉。
关于RTX二次开发
请问各位高手,怎么能够实现RTX与OA的反向登录啊?由RTX登录OA(有具体代码最好了,RTXClientSDKHandbook.CHM文档 我有)
------解决方案--------------------------------------------------------自己先顶!!!
------解决方案--------------------------------------------------------由OA登录RTX很简单,但是反过来,比较难!
------解决方案--------------------------------------------------------没有搞过rtx啊。原理应该差不多吧,传用户名和密码到OA的登陆页,然后提交就可以了。
------解决方案--------------------------------------------------------来顶贴!
------解决方案--------------------------------------------------------就是SSO啊,
TB模拟登录
为什么条码打印出来,扫描枪不能扫描?
条码字体我是从http://font.chinaz.com/TiaoXingMaZiTi.html上下载下来的,字体名字叫做IntHrP72DlTt.TTF,我把字体的fontsize设成了24,但是为什么我打印出来的条码,扫描枪却无法识别呢,扫描枪时二维扫描枪ms1690,测试过无问题
------解决方案--------------------------------------------------------to use the code providey by the printer provider
------解决方案--------------------------------------------------------条码打印有没有超出条码纸的边界,如果没有超过,
tb把字体调大一下,如果超过把字体调小一些。
求:根据这几条反编译后的语句,如何找到数据库的连接信息?我反编译了一个PBD文件,可以看到以下代码片断:******************************* <0><7> create ()*******************************appname = "dossier"message = create messagesqlca = create transactionsqlda = create dynamicdescriptionareasqlsa = create dynamicstagingareaerror = create error...uf_window_center(this)ls_dsn = profilestring("Dagl.ini","tbdatabase","DSN","Error")..dagl.ini文件的内容如下:[system]AppName="*****"[database]DSN=DaglLogId=admin[content]PaperSize=256Left=2350Right=1340Top=950Bottom=850[Post]PaperSize=256Left=2500Right=1940Top=1530Bottom=1260[gz]PaperSize=256Left=2000Right=910Top=1400Bottom=1080我在哪里能找到连接该数据库的连接代码吗?profilestring------解决方案--------------------------------------------------------ODBC吧,看一下ODBC里有没有Dagl。要么注册表里找一下。 ------解决方案--------------------------------------------------------要是看连接该数据库的连接代码就要找 connect using sqlca要是看数据库连接参数就要通过ODBC,注意ODBC里的用户DSN和系统DSN都要看一下。顺便问一句用的是什么数据库?
如何让multilineedit滚动到最前面
mle_1.scroll(1)可以实现逐行往下滚动,那请问如何让multilineedit滚动到最前面?
------解决方案--------------------------------------------------------editname.Scroll(1)就是将multilineedit滚动第一行的,中间的参数是滚到的行数
一个客户碰到的问题,由于分区维护操作,导致个别分区对应的索引处于UNUSABLE状态,最终导致基于绑定变量的查询无法利用索引。
通过一个具体的例子来说明这个问题:
SQL> create table t_part
2 (id number,
3 name varchar2(30))
4 partition by range (id)
5 (partition p1 values less than (10),
6 partition p2 values less than (20),
7 partition pmax values less than (maxvalue));
Table created.
SQL> create index ind_t_part_id on t_part(id) local;
Index created.
SQL> insert into t_part
2 select rownum, object_name
3 from user_objects;
94 rows created.
SQL> commit;
Commit complete.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART', cascade => true)
PL/SQL procedure successfully completed.
SQL> select index_name, partition_name, status
2 from user_ind_partitions
3 where index_name = 'IND_T_PART_ID';
INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
IND_T_PART_ID P1 USABLE
IND_T_PART_ID P2 USABLE
IND_T_PART_ID PMAX USABLE
创建分区表后,分别采用硬编码和绑定变量的方式进行查询:
SQL> var v_id number
SQL> exec :v_id := 5
PL/SQL procedure successfully tb completed.
SQL> set autot on exp
SQL> select * from t_part where id = 5;
ID NAME
---------- ------------------------------
5 WRH$_ACTIVE_SESSION_HISTORY
Execution Plan
----------------------------------------------------------
Plan hash value: 4087175928
--------------------------------------------------------------------------------------------
|Id|Operation |Name |Rows|Bytes|Cost|Time |Pstart|Pstop|
--------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT | | 1| 31| 2|00:00:01| | |
| 1| PARTITION RANGE SINGLE | | 1| 31| 2|00:00:01| 1 | 1|
| 2| TABLE ACCESS BY LOCAL INDEX ROWID|T_PART | 1| 31| 2|00:00:01| 1 | 1|
|*3| INDEX RANGE SCAN |IND_T_PART_ID| 1| | 1|00:00:01| 1 | 1|
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("ID"=5)
SQL> select * from t_part where id = :v_id;
ID NAME
---------- ------------------------------
5 WRH$_ACTIVE_SESSION_HISTORY
Execution Plan
----------------------------------------------------------
Plan hash value: 2089936139
--------------------------------------------------------------------------------------------
|Id|Operation |Name |Rows|Bytes|Cost|Time |Pstart|Pstop|
--------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT | | 1| 17| 2|00:00:01| | |
| 1| PARTITION RANGE SINGLE | | 1| 17| 2|00:00:01| KEY | KEY|
| 2| TABLE ACCESS BY LOCAL INDEX ROWID|T_PART | 1| 17| 2|00:00:01| KEY | KEY|
|*3| INDEX RANGE SCAN |IND_T_PART_ID| 1| | 1|00:00:01| KEY | KEY|
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("ID"=TO_NUMBER(:V_ID))
无论采用那种方式,Oracle都会选择分区索引扫描的执行计划。
下面MOVE一个查询并不会访问的分区,使其索引状态变为UNUSABLE:
SQL> alter table t_part move partition p2;
Table altered.
SQL> set autot off
SQL> select index_name, partition_name, status
2 from user_ind_partitions
3 where index_name = 'IND_T_PART_ID';
INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
IND_T_PART_ID P1 USABLE
IND_T_PART_ID P2 UNUSABLE
IND_T_PART_ID PMAX USABLE
SQL> set autot on exp
SQL> select * from t_part where id = 5;
ID NAME
---------- ------------------------------
5 WRH$_ACTIVE_SESSION_HISTORY
Execution Plan
----------------------------------------------------------
Plan hash value: 4087175928
--------------------------------------------------------------------------------------------
|Id|Operation |Name |Rows|Bytes|Cost|Time |Pstart|Pstop|
--------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT | | 1| 31| 2|00:00:01| | |
| 1| PARTITION RANGE SINGLE | | 1| 31| 2|00:00:01| 1 | 1|
| 2| TABLE ACCESS BY LOCAL INDEX ROWID|T_PART | 1| 31| 2|00:00:01| 1 | 1|
|*3| INDEX RANGE SCAN |IND_T_PART_ID| 1| | 1|00:00:01| 1 | 1|
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("ID"=5)
SQL> select * from t_part where id = :v_id;
ID NAME
---------- ------------------------------
5 WRH$_ACTIVE_SESSION_HISTORY
Execution Plan
----------------------------------------------------------
Plan hash value: 1818654859
--------------------------------------------------------------------------------------------
| Id| Operation | Name | Rows | Bytes |Cost(%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | 1 | 17 | 2 (0)| 00:00:01 | | |
| 1| PARTITION RANGE SINGLE| | 1 | 17 | 2 (0)| 00:00:01 | KEY | KEY |
|* 2| TABLE ACCESS FULL | T_PART | 1 | 17 | 2 (0)| 00:00:01 | KEY | KEY |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("ID"=TO_NUMBER(:V_ID))
可以看到,对应非绑定变量方式,Oracle是可以明确定位到要访问的分区,因此SQL执行计划不受影响,仍然是索引扫描。而对于绑定变量的方式则不同,由于这个执行计划对于任何一个输入值都要采用相同的计划,因此Oracle无法判断一个查询是否会访问分区索引UNUSABLE的分区,所以Oracle对于绑定变量的查询采用了单分区的全表扫描执行计划。
为了解决这个问题,除了REBUILD失效的分区外,还可以采用HINT的方式,强制Oracle选择索引扫描的执行计划:
SQL> select /*+ index(t_part ind_t_part_id) */ * from t_part where id = :v_id;
ID NAME
---------- ------------------------------
5 WRH$_ACTIVE_SESSION_HISTORY
Execution Plan
----------------------------------------------------------
Plan hash value: 2089936139
--------------------------------------------------------------------------------------------
|Id|Operation |Name |Rows|Bytes|Cost|Time |Pstart|Pstop|
--------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT | | 1| 17| 2|00:00:01| | |
| 1| PARTITION RANGE SINGLE | | 1| 17| 2|00:00:01| KEY | KEY|
| 2| TABLE ACCESS BY LOCAL INDEX ROWID|T_PART | 1| 17| 2|00:00:01| KEY | KEY|
|*3| INDEX RANGE SCAN |IND_T_PART_ID| 1| | 1|00:00:01| KEY | KEY|
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("ID"=TO_NUMBER(:V_ID))
SQL> exec :v_id := 15
PL/SQL procedure successfully completed.
SQL> select /*+ index(t_part ind_t_part_id) */ * from t_part where id = :v_id;
select /*+ index(t_part ind_t_part_id) */ * from t_part where id = :v_id
*
ERROR at line 1:
ORA-01502: index 'TEST.IND_T_PART_ID' or partition of such index is in unusable state
SQL> select * from t_part where id = :v_id;
ID NAME
---------- ------------------------------
15 WRH$_ACTIVE_SESSION_HISTORY_PK
Execution Plan
----------------------------------------------------------
Plan hash value: 1818654859
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |Cost(%CPU)| Time |Pstart| Pstop |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 17 | 2 (0)| 00:00:01 | | |
| 1 | PARTITION RANGE SINGLE| | 1 | 17 | 2 (0)| 00:00:01 | KEY | KEY |
|* 2 | TABLE ACCESS FULL | T_PART | 1 | 17 | 2 (0)| 00:00:01 | KEY | KEY |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("ID"=TO_NUMBER(:V_ID))
虽然使用HINT可以让Oracle强制索引扫描,但是如果绑定变量的值指向失效的索引分区,则会导致执行报错。而默认的不使用HINT的语句则不会报错。
测试控制全局死锁的隐含参数_lm_dd_interval时,突然想到这个问题。
RAC全局死锁检测时间:http://yangtingkun.net/?p=955
Oracle的死锁判断是没有优先级的,也就是说,当两个或多个会话发生死锁的时候,无法指定牺牲哪个会话,而是由Oracle随机决定。
不过对于RAC环境而言,死锁的检查不在是内部的随机实现,Oracle通过隐含参数_lm_dd_interval来控制死锁的检测时间。更重要的是,对于RAC环境而言,Oracle允许不同实例设置不同的值。而不同实例的检测死锁间隔不同,就意味着优先级的出现。
如果实例1上设置该值为默认值60秒,而实例2设置为30秒,那么当发生死锁后,永远是实例2上先检测到死锁,也就是说,实例2上会话会被牺牲掉。
这是两个实例上设置该参数相同的情况,两个会话分别连接到两个实例,产生死锁。实例1上的会话1:
SQL> select name from v$database;
NAME
---------
ORCL
SQL> select instance_number, instance_name from v$instance;
INSTANCE_NUMBER INSTANCE_NAME
--------------- ----------------
1 orcl1
SQL> set sqlp 'I1S1> '
I1S1> show parameter _lm
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
_lm_dd_interval integer 30
I1S1> set timing on
I1S1> update t_deadlock set name = 'a1' where id = 1;
1 row updated.
Elapsed: 00:00:00.07
在实例2上连接会话2:
SQL> select name from v$database;
NAME
---------
ORCL
SQL> select instance_number, instance_name from v$instance;
INSTANCE_NUMBER INSTANCE_NAME
--------------- ----------------
2 orcl2
SQL> set sqlp 'I2S2> '
I2S2> show parameter _lm
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
_lm_dd_interval integer 30
I2S2> set timing on
I2S2> update t_deadlock set name = 'b2' where id = 2;
1 row updated.
Elapsed: 00:00:00.04
I2S2> update t_deadlock set name = 'a2' where id = 1;
会话1上锁定记录2,产生死锁:
I1S1> update t_deadlock set name = 'b1' where id = 2;
第一次是实例2上的会话2被牺牲报错:
update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
Elapsed: 00:00:32.15
I2S2> update t_deadlock set name = 'a2' where id = 1;
可以看到,会话2等待30秒后报错,此时会话2执行同样的语句再次引发死锁:
update t_deadlock set name = 'b1' where id = 2
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
Elapsed: 00:01:00.39
I1S1> update t_deadlock set name = 'b1' where id = 2;
这次变成实例1上的会话1被牺牲报错,可以看到tb,会话1经历了两次死锁检测,因此执行时间为1分钟。会话1再次引入死锁:
update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
Elapsed: 00:01:01.69
I2S2>
被牺牲的又变成了会话2。
上面这个测试是在两个实例的_lm_dd_interval参数设置相同的情况下,下面修改实例2上的参数设置为5秒:
I2S2> alter system set "_lm_dd_interval" = 5 scope = spfile sid = 'orcl2';
System altered.
Elapsed: 00:00:00.09
I2S2> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
I2S2> startup
ORACLE instance started.
Total System Global Area 281018368 bytes
Fixed Size 2095672 bytes
Variable Size 121636296 bytes
Database Buffers 150994944 bytes
Redo Buffers 6291456 bytes
Database mounted.
Database opened.
I2S2> show parameter _lm
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
_lm_dd_interval integer 5
I2S2> update t_deadlock set name = 'b2' where id = 2;
1 row updated.
Elapsed: 00:00:00.06
实例2参数生效后连接会话更新该表,实例1上的会话1取消之前的修改,重新进行更新:
1 row updated.
Elapsed: 00:10:08.98
I1S1> rollback;
Rollback complete.
Elapsed: 00:00:00.00
I1S1> update t_deadlock set name = 'a1' where id = 1;
1 row updated.
Elapsed: 00:00:00.01
I1S1> update t_deadlock set name = 'b1' where id = 2;
下面在实例2上的会话2,引入死锁:
I2S2> update t_deadlock set name = 'a2' where id = 1;
update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
Elapsed: 00:00:06.07
I2S2> update t_deadlock set name = 'a2' where id = 1;
update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
Elapsed: 00:00:05.95
I2S2> update t_deadlock set name = 'a2' where id = 1;
update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
Elapsed: 00:00:06.63
I2S2> update t_deadlock set name = 'a2' where id = 1;
update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
Elapsed: 00:00:05.89
显然由于不同实例的_lm_dd_interval参数的值设置不同,现在每次死锁都会在设置值更小的实例2上被检测,实例2上的会话每次都会被死锁牺牲掉。尝试设置不同的参数值在不同实例上设置死锁检测优先级获得成功。
从这篇文档中学到了不少语法和功能。
虽然对于TYPE以及COLLECTION类型经常使用,但是涉及到把这些类型作为表的存储结构接触的就少多了,而如果说在加上继承等告警特性,平常使用的就更少了。
在Oracle使用对象一般而言是为了简化某些工作,而把所有面向对象的语法和功能都搬到数据库中不太现实,而且一旦TYPE被应用到表结构中,就很难再进行改变,因此数据库中使用TYPE有一定的局限性。
不过对于一些语法和功能还是有必要知道的,TB或者至少在碰到问题时,应该知道查看那篇文档。
一直认为Oracle对于所有分区的操作都是一样的,只有数据的改变才会导致分区状态的失效,没想到HASH分区的实现方式并不相同。
HASH分区表增加新的分区的一点研究:
看一个范围分区SPLIT的例子:
SQL> CREATE TABLE T_PART
2 (ID NUMBER, NAME VARCHAR2(30))
3 PARTITION BY RANGE (ID)
4 (PARTITION P1 VALUES LESS THAN (10),
5 PARTITION PMAX VALUES LESS THAN (MAXVALUE));
Table created.
SQL> INSERT INTO T_PART
2 SELECT ROWNUM, TNAME
3 FROM TAB;
12 rows created.
SQL> CREATE INDEX IND_T_PART_ID ON T_PART(ID) LOCAL;
Index created.tb
SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
2 FROM USER_IND_PARTITIONS
3 WHERE INDEX_NAME = 'IND_T_PART_ID';
INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
IND_T_PART_ID P1 USABLE
IND_T_PART_ID PMAX USABLE
SQL> SELECT COUNT(*) FROM T_PART PARTITION (PMAX);
COUNT(*)
----------
3
SQL> ALTER TABLE T_PART SPLIT PARTITION PMAX AT (20)
2 INTO (PARTITION P2, PARTITION P3);
Table altered.
SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
2 FROM USER_IND_PARTITIONS
3 WHERE INDEX_NAME = 'IND_T_PART_ID';
INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
IND_T_PART_ID P2 USABLE
IND_T_PART_ID P3 USABLE
IND_T_PART_ID P1 USABLE
可以看到,对于范围分区而言,即使是SPLIT包含数据的分区,只要没有真正导致数据发生变化,就不会导致索引的失效。这里将PMAX分区SPLIT成P2和P3两个分区,其中PMAX中的所有数据都进入P2分区,而P3分区为空,这种情况下没有数据的改变,因此所有分区索引的状态都不会变为UNUSABLE。
但是HASH分区的ADD PARTITION并没有遵守这个规则,事实上对于每次ADD分区,都会导致一个分区的数据发生分裂,而分裂的结果不管原分区的数据是否发生变化,都会导致原分区索引状态变为UNUSABLE,至于新增分区的索引状态,则取决于是否有数据的改变。
SQL> CREATE TABLE T_HASH
2 (ID NUMBER)
3 PARTITION BY HASH (ID)
4 (PARTITION P1,
5 PARTITION P2,
6 PARTITION P3,
7 PARTITION P4);
Table created.
SQL> CREATE INDEX IND_T_HASH_ID ON T_HASH(ID) LOCAL;
Index created.
SQL> INSERT INTO T_HASH SELECT ROWNUM FROM TAB;
12 rows created.
SQL> COMMIT;
Commit complete.
SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
2 FROM USER_IND_PARTITIONS
3 WHERE INDEX_NAME = 'IND_T_HASH_ID';
INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
IND_T_HASH_ID P1 USABLE
IND_T_HASH_ID P2 USABLE
IND_T_HASH_ID P3 USABLE
IND_T_HASH_ID P4 USABLE
SQL> SELECT * FROM T_HASH PARTITION (P1);
ID
----------
6
11
SQL> SELECT * FROM T_HASH PARTITION (P2);
ID
----------
9
10
12
SQL> SELECT * FROM T_HASH PARTITION (P3);
ID
----------
2
5
8
SQL> SELECT * FROM T_HASH PARTITION (P4);
ID
----------
1
3
4
7
下面新增一个PARTITION P5:
SQL> ALTER TABLE T_HASH ADD PARTITION P5;
Table altered.
SQL> SELECT * FROM T_HASH PARTITION (P5);
no rows selected
SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
2 FROM USER_IND_PARTITIONS
3 WHERE INDEX_NAME = 'IND_T_HASH_ID';
INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
IND_T_HASH_ID P5 USABLE
IND_T_HASH_ID P1 UNUSABLE
IND_T_HASH_ID P2 USABLE
IND_T_HASH_ID P3 USABLE
IND_T_HASH_ID P4 USABLE
新增的PARTITION P5中并没有任何的数据,也就是说没有任何的数据从P1迁移到P5中,但是查询分区索引的状态发现,P1对应的分区索引状态已经变为UNUSABLE。这和范围分区的处理方式完全不同。而P5分区由于没有任何数据,因此分区状态是USABLE。
SQL> ALTER TABLE T_HASH ADD PARTITION P6;
Table altered.
SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
2 FROM USER_IND_PARTITIONS
3 WHERE INDEX_NAME = 'IND_T_HASH_ID';
INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
IND_T_HASH_ID P5 USABLE
IND_T_HASH_ID P6 UNUSABLE
IND_T_HASH_ID P1 UNUSABLE
IND_T_HASH_ID P2 UNUSABLE
IND_T_HASH_ID P3 USABLE
IND_T_HASH_ID P4 USABLE
6 rows selected.
SQL> DELETE T_HASH WHERE ID = 5;
1 row deleted.
SQL> COMMIT;
Commit complete.
SQL> ALTER TABLE T_HASH ADD PARTITION P7;
Table altered.
SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
2 FROM USER_IND_PARTITIONS
3 WHERE INDEX_NAME = 'IND_T_HASH_ID';
INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
IND_T_HASH_ID P5 USABLE
IND_T_HASH_ID P6 UNUSABLE
IND_T_HASH_ID P7 UNUSABLE
IND_T_HASH_ID P1 UNUSABLE
IND_T_HASH_ID P2 UNUSABLE
IND_T_HASH_ID P3 UNUSABLE
IND_T_HASH_ID P4 USABLE
7 rows selected.
SQL> SELECT * FROM T_HASH PARTITION (P3);
no rows selected
SQL> SELECT * FROM T_HASH PARTITION (P7);
ID
----------
2
8
为了更好的说明这个问题,在增加PARTITION P7之前,删除了ID为5的记录,这是增加分区后可以发现,原有的P3已经不包含任何的数据,全部的记录都进入到新增的P7分区,但是无论是P3还是P7,状态都是UNUSABLE。这证明了前面提到的,只要是新增HASH分区,就会导致源分区索引状态变为UNUSABLE,除非是一种情况:源分区本身就没有数据:
SQL> ALTER TABLE T_HASH ADD PARTITION P8;
Table altered.
SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
2 FROM USER_IND_PARTITIONS
3 WHERE INDEX_NAME = 'IND_T_HASH_ID';
INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
IND_T_HASH_ID P5 USABLE
IND_T_HASH_ID P6 UNUSABLE
IND_T_HASH_ID P7 UNUSABLE
IND_T_HASH_ID P1 UNUSABLE
IND_T_HASH_ID P2 UNUSABLE
IND_T_HASH_ID P3 UNUSABLE
IND_T_HASH_ID P4 USABLE
IND_T_HASH_ID P8 USABLE
8 rows selected.
事实上,对于HASH分区的ADD PARTITION操作,Oracle基本上还是秉承了没有数据变化就不会导致索引失效的思路。唯一的差别在于,对于源分区包含记录的情况,Oracle并没有最后去验证,是否真的发生了数据的迁移。
今天是ACOUG活动的日子,也是上半年的最后一天。
参加ACOUG的人很多,现场估计超过了100人,而且今天到会的ACE也很多,除了tbEygle、Kamus、崔华和刚晋升为ACE的侯圣文之外,还有从成都赶过来的老熊。老熊虽然是第一次见面,此前打交道的次数也不多,但是有种一见如故的感觉,可能和老熊直爽的性格有关。
今天的第一个主题是杨海朝带来的《MySql的那点事儿》。杨海朝是新浪的首席DBA负责新浪微博的后台数据库,他带来的MySql方面的演讲介绍了如何利用mysql处理海量增长的大数据,以及如何满足前端应用的高可用需求。
第二个是Kamus带来的11g新特性。在10g已经推出了正常服务期,12c马上就要推出的今天,仍然有很多DBA对于11g的新特性不是很了解。Kamus的演讲应该算是一个新特性的普及,这次的内容是关于分区和高可用特性方面的新特性。
最后是崔华带来的SQL优化方面的主题,这次的内容是统计信息。崔华的演讲贯彻了他一向的风格,包含了大量的深入信息,并给出了很多Oracle内部算法和公式,而这些公式大部分来自他深入的研究,在其他的地方是无法看到的。
对于单实例数据库而言,死锁的检测在秒级完成,而RAC环境则死锁的检测时间默认达到了1分钟。
对于单实例环境如果出现了死锁,那么马上其中一个进程就被中止,用户可以快速的得到错误返回。而对于RAC而言,死锁的检测并不是实时完成,而是需要60秒左右的时间。
会话1执行:
SQL> create table t_deadlock (id number primary key, name varchar2(30));
Table created.
Elapsed: 00:00:00.12
SQL> insert into t_deadlock values (1, 'a');
1 row created.
Elapsed: 00:00:00.00
SQL> insert into t_deadlock values (2, 'b');
1 row created.
Elapsed: 00:00:00.00
SQL> commit;
Commit complete.
Elapsed: 00:00:00.00
SQL> update t_deadlock set name = 'a1' where id = 1;
1 row updated.
Elapsed: 00:00:00.00
会话2执行:
SQL> set timing on
SQL> update t_deadlock set name = 'b2' where id = 2;
1 row updated.
Elapsed: 00:00:00.00
SQL> update t_deadlock set name = 'a2' where id = 1;
此时,会话2等待会话1的最终操作,下面会话1更新被会话2锁定的行,引发死锁:
SQL> update t_deadlock set name = 'b1' where id = 2;
update t_deadlock set name = 'b1' where id = 2
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
Elapsed: 00:01:00.12
可以看到,死锁的超时检测为1分钟。
而这个死锁的检测时间是可以调整的,Oracle通过隐含参数_lm_dd_interval控制:
SQL> conn / as sysdba
Connected.
SQL> alter system set "_lm_dd_interval" = 30 scope = spfile;
System altered.
SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.
Total System Global Area 281018368 bytes
Fixed Size 2095672 bytes
Variable Size 104859080 bytes
Database Buffers 167772160 tb bytes
Redo Buffers 6291456 bytes
Database mounted.
Database opened.
再次测试死锁的检测时间,会话1:
SQL> update t_deadlock set name = 'a1' where id = 1;
1 row updated.
SQL> set timing on
会话2执行更新:
SQL> set timing on
SQL> update t_deadlock set name = 'b2' where id = 2;
1 row updated.
Elapsed: 00:00:00.02
SQL> update t_deadlock set name = 'a2' where id = 1;
会话1执行更新引发死锁:
SQL> update t_deadlock set name = 'b1' where id = 2;
大约30秒后,会话2报错ORA-60:
update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
Elapsed: 00:00:30.27
在10.2.0.2版本上,Oracle存在一个bug,允许这个参数设置为0,在10.2.0.3以后,这个bug被修正,如果设置为0后,则数据库无法正常启动:
[oracle@node1 ~]$ sqlplus / as sysdba
SQL*Plus: Release 10.2.0.5.0 - Production on Mon Jun 4 07:54:09 2012
Copyright (c) 1982, 2010, Oracle. All Rights Reserved.
Connected to an idle instance.
SQL> startup
ORA-00067: invalid value 0 for parameter _lm_dd_interval; must be at least 1
最后修改隐含参数是Oracle不推荐的,而且修改这个参数势必会影响RAC的正常工作方式导致LDM进程的繁忙度增加,而且可能影响RAC环境的稳定性和可用性。
如果确实对于前台的死锁检查时间要求较高,建议在测试环境中详细测试后再部署到产品环境中。
11g增强了数据泵的NETWORK_LINK功能,使得远端的导入或导出也支持分区粒度。
在10g的时候,数据泵的NETWORK_LINK只支持表粒度:
[orat3@hpserver2 ~]$ expdp test/test network_link=ora10204 dumpfile=tpart.dp logfile=test.log directory=dd tables=t_part:p1
Export: Release 10.2.0.4.0 - 64bit Production on Monday, 4 June, 2012 0:38:31
Copyright (c) 2003, 2007, Oracle. All rights reserved.
Connected to: Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
ORA-39001: invalid argument value
ORA-39203: Partition selection is not supported over a network link.
很明显10g并不支持通过网络方式的分区选择功能。而且对于分区表而言,无论是导出还是导入,都是已一个整体完成的。
而在11g中,无论是导入还是导出,NETWORK_LINK方式都支持分区粒度:
solaris*orcl-/home/oracle$ expdp test/test network_link=t111g dumpfile=t_part_hash.dp directory=d_output nologfile=y tables=t_part_hash:sys_p61
Export: Release 11.2.0.3.0 - Production on Mon Jun 4 08:24:54 2012
Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved.
Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 tb- 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
Starting "TEST"."SYS_EXPORT_TABLE_01": test/******** network_link=t111g dumpfile=t_part_hash.dp directory=d_output nologfile=y tables=t_part_hash:sys_p61
Estimate in progress using BLOCKS method...
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
Total estimation using BLOCKS method: 8 MB
Processing object type TABLE_EXPORT/TABLE/TABLE
. . exported "TEST"."T_PART_HASH":"SYS_P61" 5.476 KB 13 rows
Master table "TEST"."SYS_EXPORT_TABLE_01" successfully loaded/unloaded
******************************************************************************
Dump file set for TEST.SYS_EXPORT_TABLE_01 is:
/home/oracle/t_part_hash.dp
Job "TEST"."SYS_EXPORT_TABLE_01" successfully completed at 08:25:57
这个功能没有在新特性文档中体现,但是却是一个不错的功能提升。
如果需要了解或架构高可用环境,建议看一下这篇文档。
这篇文档介绍了Oracle高可用相关的知识点,包括ASM、CLUSTER、RAC、DATA GUARD、GOLDENGATE、BACKUP、RESTORE等等,此外还介绍了高可用环境的监控,以及如何利用这些高可用功能来减少维护的停机时间。
从文档的名称高可用最佳实践和文档的页数高,这篇文档中不会包括这些知识点的具体概念,而应该是这些功能在哪些场合使用最合适,以及相互之间如何配合。
照例给出网址:http://www.oracle.com/pls/db112/to_toc?pathname=server.112%2Fe10803%2Ftoc.htm&remark=portal+%28Books%29
客户数据库在SHUTDOWN的过程中执行了取消操作,导致了ORA-600错误。
数据库版本为10.2.0.4 RAC for HP-UX,详细错误信息为:
Thu Oct 22 15:05:14 2009
Shutting down instance: further logons disabled
Thu Oct 22 15:05:15 2009
Stopping background process QMNC
Thu Oct 22 15:05:15 2009
Stopping background process CJQ0
Thu Oct 22 15:05:17 2009
Stopping background process MMNL
Thu Oct 22 15:05:18 2009
Stopping background process MMON
Thu Oct 22 15:05:19 2009
Shutting down instance (immediate)
License high water mark = 8
Thu Oct 22 15:05:19 2009
Stopping Job queue slave processes, flags = 7
Thu Oct 22 15:05:19 2009
Job queue slave processes stopped
Thu Oct 22 15:05:20 2009
ALTER DATABASE CLOSE NORMAL
Thu Oct 22 15:05:20 2009
SMON: disabling tx recovery
SMON: disabling cache recovery tb
Thu Oct 22 15:05:21 2009
LGWR: Waiting for ORLs to be archived...
Thu Oct 22 15:05:36 2009
Reconfiguration started (old inc 2, new inc 4)
List of nodes:
0 1
Global Resource Directory frozen
Communication channels reestablished
* domain 0 valid = 1 according to instance 0
Thu Oct 22 15:05:36 2009
Master broadcasted resource hash value bitmaps
Non-local Process blocks cleaned out
Thu Oct 22 15:05:36 2009
LMS 0: 0 GCS shadows cancelled, 0 closed
Thu Oct 22 15:05:36 2009
LMS 1: 0 GCS shadows cancelled, 0 closed
Set master node info
Submitted all remote-enqueue requests
Dwn-cvts replayed, VALBLKs dubious
All grantable enqueues granted
Thu Oct 22 15:05:36 2009
LMS 1: 2008 GCS shadows traversed, 1001 replayed
Thu Oct 22 15:05:36 2009
LMS 0: 1988 GCS shadows traversed, 1034 replayed
Thu Oct 22 15:05:36 2009
Submitted all GCS remote-cache requests
Fix write in gcs resources
Reconfiguration complete
Thu Oct 22 15:05:51 2009
CLOSE: Error 1013 during database close
Thu Oct 22 15:05:51 2009
SMON: enabling cache recovery
SMON: enabling tx recovery
Thu Oct 22 15:05:51 2009
ORA-1013 signalled during: ALTER DATABASE CLOSE NORMAL...
Thu Oct 22 15:05:51 2009
Errors in file /u01/app/oracle/admin/orcl3/bdump/orcl32_lns1_4678.trc:
ORA-00600: internal error code, arguments: [2658], [4], [2], [], [], [], [], []
Thu Oct 22 15:05:52 2009
Trace dumping is performing id=[cdmp_20091022150552]
Thu Oct 22 15:05:52 2009
Errors in file /u01/app/oracle/admin/orcl3/bdump/orcl32_lns1_4678.trc:
ORA-00600: internal error code, arguments: [2658], [4], [2], [], [], [], [], []
Thu Oct 22 15:05:55 2009
LGWR: ORLs successfully archived
Shutting down archive processes
Archiving is disabled
Thu Oct 22 15:06:00 2009
ARCH shutting down
ARC3: Archival stopped
Thu Oct 22 15:06:05 2009
ARCH shutting down
ARC2: Archival stopped
Thu Oct 22 15:06:10 2009
ARCH shutting down
ARC1: Archival stopped
Thu Oct 22 15:06:15 2009
ARCH shutting down
ARC0: Archival stopped
Thu Oct 22 15:06:16 2009
Thread 2 closed at log sequence 23
Successful close of redo thread 2
可以看到,用户在执行了SHUTDOWN IMMEDIATE操作后,执行了CTRL+C取消操作,导致了ORA-1031错误,之后同一时间引发了ORA-600[2658]错误。显然这个错误的产生和用户发出的取消命令直接相关,而且最终这个命令并未生效,数据库成功的被关闭。
在MOS上查询了一下ORA-600[2658]的相关错误,发现多部分错误都出现在7.3和8.1版本上,在9i以后这个错误已经很少见了,从这一点也可以看出,导致这个错误出现的是偶然的因素,那么取消操作显然是导致这个错误的最大可能性。
数据库本身处于关闭过程中,因此这个错误没有影响,可以直接忽略掉。
两个TIMESTAMP之差得到的是INTERVAL类型,而有时我们只需要得到两个时间相差的秒数,如果变成INTERVAL之后,想要获取这个值会非常麻烦。
比较常见的方法是使用EXTRACT来抽取获得的INTERVAL类型的日、时、分和秒来分别计算并求和:
SQL> create table t_timestamp (id number, t1 timestamp, t2 timestamp);
Table created.
SQL> insert into t_timestamp
2 values (1, to_timestamp('20120603222324', 'yyyymmddhh24miss'), to_timestamp('20120526152354', 'yyyymmddhh24miss'));
1 row created.
SQL> commit;
Commit complete.
SQL> select t1 - t2 from t_timestamp where id = 1;
T1-T2
---------------------------------------------------------------------------
+000000008 06:59:30.000000
SQL> with t as (select t1 - t2 interval from t_timestamp where id = 1)
2 select extract(day from interval) * 86400
3 + extract(hour from interval) * 3600
4 + extract(minute from interval) * 60
5 + extract(second from interval) interval
6 from t;
INTERVAL
----------
716370
对于不需要考虑毫秒的情况而言,这种计算过于麻烦了,而对于DATE类型而言,tb计算差值非常方便,直接就可以返回两个日期相差的天数,在乘以86400就可以得到结果。
可惜的是,无论是ROUND还是TRUNC参数,都不支持TIMESTAMP类型:
SQL> select trunc(t1, 'ss') from t_timestamp where id = 1;
select trunc(t1, 'ss') from t_timestamp where id = 1
*
ERROR at line 1:
ORA-01899: bad precision specifier
SQL> select round(t1, 'ss') from t_timestamp where id = 1;
select round(t1, 'ss') from t_timestamp where id = 1
*
ERROR at line 1:
ORA-01899: bad precision specifier
其实对于这个问题,最简单的方法是利用隐式转换,变成DATE类型的操作:
SQL> select (t1 - 0 - (t2 - 0)) * 86400 from t_timestamp;
(T1-0-(T2-0))*86400
-------------------
716370
当然最标准的方法还是显示的转换:
SQL> select (cast(t1 as date) - cast(t2 as date)) * 86400 from t_timestamp;
(CAST(T1ASDATE)-CAST(T2ASDATE))*86400
-------------------------------------
716370
显然这种方便比利用EXTRACT要简单得多。
一直认为这篇文档作为工具书更合适,不过细读的过程中却改变了我的看法。
之所以这篇文档的优先级放得比较低,和这篇文档的厚度也有关系。不过这1200多页的内容却包含了很多Oracle中最关键的信息。
第一部分就是初始化参数的详细描述,即使这篇文档只包含了这部分内容,也值得一看了。
第二部分是静态数据字典视图,第三部分是动态数据字典视图。这两部分的内容除了作为工具书备查之外,也是补充自己知识全面性的重要文档,Oracle的提供的数据字典覆盖了非常全面的信息,而我们平时只利用了很少的一部分,如果能充分利用这些视图,那么解决问题会更加的轻松。
最后在附录部分还有重要的内容,tb比如所有等待事件的描述。
在VM Manager中搜索VM Server时出现这个错误。
按照VM Server以及VM Manager后,通过指定IP地址,让VM Manager自动寻找VM Server,结果JOB运行报错,详细的错误信息为:
Job Construction Phase
----------------------
begin()
Appended operation 'Discover Manager Server Discover'tb to object 'OVM Foundry : Discover Manager'.
commit()
Completed Step: COMMIT
Objects and Operations
----------------------
Object (IN_USE): [Server] 35:38:33:39:31:34:43:4e:47:31:33:30:53:37:33:42 (server2.zihexin.com)
Object (IN_USE): [DiscoverManager] OVM Foundry : Discover Manager
Operation: Discover Manager Server Discover
Job Running Phase at 18:05 on Fri, Nov 25, 2011
----------------------------------------------
Job Participants: []
Actioner
--------
Starting operation 'Discover Manager Server Discover' on object 'OVM Foundry : Discover Manager'
Setting Context to model only in job with id=1322215534120
Job Internal Error (Operation)com.oracle.ovm.mgr.api.exception.FailedOperationException: OVMAPI_4010E Attempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:474)
at com.oracle.ovm.mgr.action.ActionEngine.sendDiscoverCommand(ActionEngine.java:283)
at com.oracle.ovm.mgr.action.ServerAction.getServerInfo(ServerAction.java:95)
at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:131)
at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:61)
at com.oracle.ovm.mgr.discover.ovm.DiscoverHandler.execute(DiscoverHandler.java:50)
at com.oracle.ovm.mgr.discover.DiscoverEngine.handleDiscover(DiscoverEngine.java:435)
at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverNewServer(DiscoverEngine.java:345)
at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverServer(DiscoverEngine.java:265)
at com.oracle.ovm.mgr.op.manager.DiscoverManagerServerDiscover.action(DiscoverManagerServerDiscover.java:48)
at com.oracle.ovm.mgr.api.job.JobEngine.operationActioner(JobEngine.java:191)
at com.oracle.ovm.mgr.api.job.JobEngine.objectActioner(JobEngine.java:257)
at com.oracle.ovm.mgr.api.job.InternalJobDbImpl.objectCommitter(InternalJobDbImpl.java:1019)
at com.oracle.odof.core.AbstractVessel.invokeMethod(AbstractVessel.java:223)
at com.oracle.odof.core.BasicWork.invokeMethod(BasicWork.java:136)
at com.oracle.odof.command.InvokeMethodCommand.process(InvokeMethodCommand.java:100)
at com.oracle.odof.core.BasicWork.processCommand(BasicWork.java:81)
at com.oracle.odof.core.TransactionManager.processCommand(TransactionManager.java:751)
at com.oracle.odof.core.WorkflowManager.processCommand(WorkflowManager.java:395)
at com.oracle.odof.core.WorkflowManager.processWork(WorkflowManager.java:453)
at com.oracle.odof.io.AbstractClient.run(AbstractClient.java:42)
at java.lang.Thread.run(Thread.java:662)
Caused by: com.oracle.ovm.mgr.api.exception.IllegalOperationException: OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
at com.oracle.ovm.mgr.action.ActionEngine.sendAction(ActionEngine.java:752)
at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:470)
... 24 more
FailedOperationCleanup
----------
Starting failed operation 'Discover Manager Server Discover' cleanup on object 'OVM Foundry : Discover Manager'
Complete rollback operation 'Discover Manager Server Discover' completed with direction=OVM Foundry : Discover Manager
Rollbacker
----------
Objects To Be Rolled Back
-------------------------
Object (IN_USE): [Server] 35:38:33:39:31:34:43:4e:47:31:33:30:53:37:33:42 (server2.zihexin.com)
Object (IN_USE): [DiscoverManager] OVM Foundry : Discover Manager
Completed Step: ROLLBACK
Job failed commit (internal) due to OVMAPI_4010E Attempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
com.oracle.ovm.mgr.api.exception.FailedOperationException: OVMAPI_4010E Attempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:474)
at com.oracle.ovm.mgr.action.ActionEngine.sendDiscoverCommand(ActionEngine.java:283)
at com.oracle.ovm.mgr.action.ServerAction.getServerInfo(ServerAction.java:95)
at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:131)
at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:61)
at com.oracle.ovm.mgr.discover.ovm.DiscoverHandler.execute(DiscoverHandler.java:50)
at com.oracle.ovm.mgr.discover.DiscoverEngine.handleDiscover(DiscoverEngine.java:435)
at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverNewServer(DiscoverEngine.java:345)
at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverServer(DiscoverEngine.java:265)
at com.oracle.ovm.mgr.op.manager.DiscoverManagerServerDiscover.action(DiscoverManagerServerDiscover.java:48)
at com.oracle.ovm.mgr.api.job.JobEngine.operationActioner(JobEngine.java:191)
at com.oracle.ovm.mgr.api.job.JobEngine.objectActioner(JobEngine.java:257)
at com.oracle.ovm.mgr.api.job.InternalJobDbImpl.objectCommitter(InternalJobDbImpl.java:1019)
at sun.reflect.GeneratedMethodAccessor1001.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.oracle.odof.core.AbstractVessel.invokeMethod(AbstractVessel.java:223)
at com.oracle.odof.core.BasicWork.invokeMethod(BasicWork.java:136)
at com.oracle.odof.command.InvokeMethodCommand.process(InvokeMethodCommand.java:100)
at com.oracle.odof.core.BasicWork.processCommand(BasicWork.java:81)
at com.oracle.odof.core.TransactionManager.processCommand(TransactionManager.java:751)
at com.oracle.odof.core.WorkflowManager.processCommand(WorkflowManager.java:395)
at com.oracle.odof.core.WorkflowManager.processWork(WorkflowManager.java:453)
at com.oracle.odof.io.AbstractClient.run(AbstractClient.java:42)
at java.lang.Thread.run(Thread.java:662)
Caused by: com.oracle.ovm.mgr.api.exception.IllegalOperationException: OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
at com.oracle.ovm.mgr.action.ActionEngine.sendAction(ActionEngine.java:752)
at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:470)
... 24 more
----------
End of Job
----------
由于关键性信息确实,所以无法判断导致错误的原因。即使是在metalink或GOOGLE中查询,也得不到任何有价值的信息。
虽然在VM Manager中得不到有意义的信息,但是在VM Server上,却可以得到更详细的信息,通过检查var/log/ovs-agent.log文件,获取到下面的信息:
[2011-04-16 13:21:46 25970] ERROR (OVSAgentServer:108) Unauthorized access attempt from ('10.0.10.173', 59424)!
Traceback (most recent call last):
File "/opt/ovs-agent-3.0/OVSAgentServer.py", line 103, in do_POST
auth(username, password)
File "/opt/ovs-agent-3.0/OVSAgentServer.py", line 42, in auth
raise Exception('Authorization failed: user does not exist or password error.')
Exception: Authorization failed: user does not exist or password error.
[2011-04-16 13:21:46 25970] INFO (OVSAgentServer:169) code 403, message Unauthorized access attempt from ('10.0.10.173', 59424)!
这次信息就明确多了,显然是由于VM Manager中配置的密码不正确所致,在VM Server上修改oracle用户密码:
[root@server2 ~]# ovs-agent-passwd oracle
Password:
Again:
在搜索VM Server时使用这里修改的密码,VM Manager成功的发现了VM Server信息。
简单描述一下Oracle VM Server的安装过程。
需要注意,VM 3.0以上版本才支持升级操作,因此在VM 2.2没有办法升级到当前版本,安装VM 3.0将会删除服务器上所有的数据。
将VM Server的光盘放入,并从光盘启动服务器。
在启动界面直输入Enter开始安装过程:
Oracle会提示是否监测截至,这里可以直接SKIP跳过;
键盘选择:选择us;
然后是版权声明,选择Accept后,开始正式的安装步骤;
如果服务器上没有系统,那么会直接进入后面的分区阶段,否则会提示重装系统还是在原有系统上升级;
选择ReInstall后,会显示当前系统磁盘分区信息,首先选择准备进行系统安装的分区,然后选择Remove tb all partitions and create a new default partition layout,Oracle在格式化分区之前会要求再次确认,并询问是否预览分区空间详细配置,可以完全按照默认推荐值安装,因此这里可以跳过,也可以进入到分区空间修改页面进行自定义的修改;
随后选择Boot Loader配置,选择Master Boot Record;
然后选择一个管理网络接口,手工输入IP和掩码,在下一个页面输入网关、DNS信息,接着是主机名信息;
配置服务器所在时区,配置中找不到北京,可以设置Asia/Shanghai代替;
分别输入Agent密码和root密码后,安装操作完成,这是会提示整个安装的日志文件的位置。
在重启界面选择REBOOT,完成整个安装过程。
启动后,进入Oracle VM Server 3.0控制台界面,可以通过Alt + F2进入linux的登录界面。至此VM Server安装完成。
客户的10.2.0.4 RAC for Hp-un环境碰到了这个错误。
错误信息为:
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_11261.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM TB
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_32036.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_5935.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_5026.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_7620.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:08 2012
Trace dumping is performing id=[cdmp_20120229194207]
Wed Feb 29 19:42:17 2012
Trace dumping is performing id=[cdmp_20120229194217]
这个ORA-600[qersqCloseRem-2]错误非常罕见, TB MOS上居然没有任何记载。不过从错误信息进行进一步的分析,这个错误发生在远端数据库的访问异常。
检查进一步的详细信息:
*** 2012-02-29 19:42:05.564
ksedmp: internal or fatal error
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Current SQL statement for this session:
SELECT ACCESS_LOG_SEQUENCE.NEXTVAL@WEBDB.COM FROM DUAL
----- PL/SQL Call Stack -----
object line object
handle number name
0x39b5c3720 5 ECOMMERCE.P_USER_AT
----- Call Stack Trace -----
calling call entry argument values in hex
location type point (? means dubious value)
-------------------- -------- -------------------- ----------------------------
ksedst()+31 call ksedst1() 000000000 ? 000000001 ?
7FBFFF4370 ? 7FBFFF43D0 ?
7FBFFF4310 ? 000000000 ?
ksedmp()+610 call ksedst() 000000000 ? 000000001 ?
7FBFFF4370 ? 7FBFFF43D0 ?
7FBFFF4310 ? 000000000 ?
ksfdmp()+21 call ksedmp() 000000003 ? 000000001 ?
7FBFFF4370 ? 7FBFFF43D0 ?
7FBFFF4310 ? 000000000 ?
.
.
.
0059DF200 ? 683F6E400000001 ?
main()+116 call opimai_real() 000000002 ? 7FBFFFF4E0 ?
000000004 ? 7FBFFFF478 ?
0059DF200 ? 683F6E400000001 ?
__libc_start_main() call main() 000000002 ? 7FBFFFF4E0 ?
+219 000000004 ? 7FBFFFF478 ?
0059DF200 ? 683F6E400000001 ?
_start()+42 call __libc_start_main() 0007139F8 ? 000000002 ?
7FBFFFF628 ? 0052B4BD0 ?
000000000 ? 000000002 ?
--------------------- Binary Stack Dump ---------------------
从详细TRACE分析,在问题发生时刻,正在通过数据库链读取远端序列的值。而此时出现的ORA-3113通信错误,多半与远端数据库状态异常有关。
检查远端数据库的告警日志,果然发现在问题出现时刻,数据库状态异常并最终导致了实例重启:
Wed Feb 29 19:39:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:39:30 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
.
.
.
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:30 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:45:26 2012
PMON failed to acquire latch, see PMON dump
Wed Feb 29 19:46:32 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:33 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:34 2012
PMON failed to acquire latch, see PMON dump
Wed Feb 29 19:46:40 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:43 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:44 2012
Errors in file /opt/app/oracle/admin/orcl/bdump/orcl1_asmb_14614.trc:
ORA-15064: communication failure with ASM instance
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:46:44 2012
ASMB: terminating instance due to error 15064
Wed Feb 29 19:46:44 2012
System state dump is made for local instance
System State dumped to trace file /opt/app/oracle/admin/orcl/bdump/orcl1_diag_14555.trc
Wed Feb 29 19:46:47 2012
Shutting down instance (abort)
License high water mark = 1623
Wed Feb 29 19:46:49 2012
Instance terminated by ASMB, pid = 14614
Wed Feb 29 19:46:52 2012
Instance terminated by USER, pid = 3684
显然远端数据库状态异常是这个ORA-600错误的直接原因。
基于版本的重定义
Oracle 11g R2增加了一个强大的新工具,它可以检出应用程序数据库对象的任一版本,不是所有数据库对象都支持版本化管理,但私有的同义词,视图和几乎所有的PL/SQL对象,包括存储过程、函数、类型、类型主体、包、包主体和触发器,版本化管理的真正好处是简化了部署一个修改版本的应用程序代码到生产数据库,如果部署时遇到一系列的错误,可以很容易地将所有影响的对象回滚到上一个版本。
消除了闪回数据归档上的DDL限制
在前一篇文章中,我深入研究了Oracle 11g R1的新特性“闪回数据归档(Flashback Data Archive,FBDA)”,它也被称为“全部召回(Total Recall)”,它只捕获变化的数据,将这些数据放在一套特殊的对象中,它们构成了FBDA,当用户通过闪回版本查询(Flashback Versions query)查询表的历史记录时,Oracle将会直接从数据库的UNDO表空间返回最近变化的数据,从FBDA返回更旧的数据。
虽然这个特性很好,但在早期版本中也有很多限制,包括增加、修改、重命名、删除表的列、truncate表、修改表的约束、以及修改分区表的分区规范,在Oracle 11g R2中,这些限制全部没有了,对于更复杂的DDL操作,如使用DBMS_REDEFINITION包重定义已经存储到FBDA的基础表,Oracle 11g R2提供了新的DBMS_FLASHBACK_ARCHIVE包,存储过程DISASSOCIATE_FBA将会把基础表从FBDA中分离出来,一旦请求的改变完成,存储过程REASSOCIATE_FBA会被用来重新关联修改的表和基础表。
按需创建分段
在之前的版本中,使用CREATE TABLE语句创建表时,会同时自动创建表的初始段,从Oracle 11gR2开始,这个默认的行为有所变化,创建表时不会创建初始段,直到有数据插入到这个表。此外,任何依赖于该表的索引或LOB段也不会创建,直到有数据插入才会创建,表的SEGMENT CREATION DEFERRED存储属性指定了这个默认行为,但可以使用SEGMENT CREATION IMMEDIATE属性覆盖它。
不可用索引大小归零
在重新载入大表时,比如一个有上百万行的数据仓库事实表,要提高这种表的加载速度,最简单的办法是将该表上的所有索引置为不可用,在数据加载完毕后,在重建这些索引,Oracle 11g R2认可了这一做法,并采取了实质性的措施,tb当索引被标记为不可用时,它会自动删除所有索引段。
小结
Oracle 11g R2延续了自Oracle 10g以来令人称道的自我管理,自我调整,自我治愈的特性,这个新版本提供了太多的新特性,有些是迟来的功能,有些是革新,Oracle DBA可以借助这些新特性提高工作效率,成为一名真正的“信息工程师”。
2009年9月Oracle公司发布了期待已久的Oracle 11g R2,本系列文章将给读者一一揭开新版本中的新特性,并会介绍企业如何利用这些新特性将现有的Oracle 9i,10g,11g R1升级到Oracle 11g R2.
经历了难以忍受的长时间等待,Oracle公司突然在9月1发布了Oracle 11g R2,我不得不承认Oracle的保密工作做得多么好,我相信Oracle公司选择这个时候发布时为了刺激参加Oracle OpenWorld 2009大会的人兴奋和期待。
经过四处搜索Oracle 11g R2新特性文档,并体验了新的OUI(Oracle通用安装程序),创建了我的第一个单实例RAC后,我总结一下Oracle 11g R2中我最喜欢的五大新特性。在后面的文章中我将陆续介绍其它新特性,请锁定本系列文章。
NO.1 随处可见的集群
在以前的版本中,Oracle Clusterware必须要独立地安装在它自己的ORACLE HOME中,并且也只能在RAC环境下使用,这一切在Oracle 11g R2得到彻底颠覆,因为在这个版本中支持安装Oracle网格基础架构,而且只需要一个独立的ORACLE HOME,它包括了Oracle Clusterware和Oracle自动存储管理(ASM)。通过升级后的Oracle通用安装程序安装了网格基础架构后,你将会看到一个全新的功能和服务矩阵,我简单地列举几个吧:
单实例RAC(Oracle重启):听起来似乎自相矛盾,但Oracle 11g R2扩展了Oracle Clusterware的功能,为任何单实例提供了高可用特性,本质上是将数据库变成了单实例RAC数据库。Oracle 11g R2中的Oracle重启特性帮助Oracle网格基础架构的高可用服务控制服务器重启时哪一个监听器,ASM实例和数据库应该启动,它完全取代了过去DBA们经常用到的DBSTART脚本。同样,当单个数据库实例崩溃或其它异常终止时,Oracle重启功能会自动监测失效的实例,并自动重启它,就好像是在一个真实的RAC环境中一样。
SRVCTL升级:如果你管理过旧版本的RAC环境,你可能已经熟悉了RAC环境中的维护工具SRVCTL,在11g R2中,该工具被扩展了,现在可以管理单实例RAC,以及监听器和ASM实例。
集群时间同步服务:Oracle 11g R2现在需要在所有RAC节点上配置时间同步,如果你曾经经历过某个节点被驱逐出RAC集群配置,你一定知道其难度有多大,特别是两个服务器的时间不同步和日志文件的时间戳不同步时,Oracle之前的版本借助系统提供的网络时间协议(NTP)同步所有节点时间,但这需要连接到互联网上的标准时间服务器,作为NTP的替代者,Oracle 11g R2提供了一个新的集群时间同步服务,确保集群中的所有节点的时间都保持一致。
网格即插即用:在以前的版本中,配置RAC最复杂的部分是确定和设置所有节点都需要用到的公共ip地址,私有ip地址和虚拟ip地址。为了简化RAC的安装,Oracle 11g R2提供了一个全新的网格名称服务(GNS),它和域名服务器协作,处理每个网格组件的ip地址分配,当集群环境跨越多个数据库时这个新特性极其有用。
干净地卸载RAC组件:如果你曾经尝试过删除多个节点上的所有RAC痕迹,那一定会钟情于这项新特性,在Oracle 11g R2中,所有安装和配置助手,特别是Oracle通用安装程序(OUI),数据库配置助手(DBCA)和网络配置助手(NETCA),都得到了增强,当需要卸载RAC组件时,可以保证卸得干干净净。
NO.2 ASM加入了集群
Oracle 11g R2 ASM充满了许多吸引人的新特性,对于初学者来说,ASM和Oracle 11g R2 Clusterware安装在同一个Oracle Home下,因此消除了之前推荐的冗余Oracle Home安装方法,并且ASM也从DBCA脱离出来了,有了专门的自动存储管理配置助手(ASMCA)。下面是我认为最有趣的ASM新特性:
智能化数据布局:在之前的版本中,要配置ASM磁盘可能需要存储管理员的参与,需要配置磁盘I/O子系统,Oracle 11g R2提供了ASM分配单元,可以直接受益于磁盘外缘柱面,获得更快的速度,可以将数据文件,重做日志文件和控制放在磁盘外缘获得更好的性能。
EM支持工作台扩展:在这个版本中对Oracle 11g R1引入到企业管理控制台中的自动诊断仓库(ADR)进行了扩展,包括支持ASM诊断,将所有诊断信息打包直接发送给Oracle技术支持,以便获得更快速的ASM性能问题解决方案。
ASMCMD增强:自动存储管理命令行实用工具(ASMCMD)也获得了不少增强,包括:
1)启动和停止ASM实例;
2)备份,恢复和维护ASM实例的服务器参数文件(spfile);
3)实用iostat监控ASM磁盘组的性能;
4)维护新的ASM集群文件系统(ACFS)中的磁盘卷,目录和文件存储,我的下一个话题就是它。
NO.3 ACFS – 一个强健的集群文件系统
Oracle之前也发布过集群文件系统(OCFS),之后又发布了增强版OCFS2,它让Oracle RAC实例可以通过共享存储读写数据库文件,重做日志文件和控制文件。
此外,OCFS也允许RAC数据库的Oracle集群注册文件(OCR)和表决磁盘存储在集群文件系统中,在Oracle 10g R2中,这个需求被取消了,OCR文件和表决磁盘可以存储在裸设备或裸块设备中,如果你曾经在原始设备上丢失过这些文件的所有副本,你一定了解要恢复它们是多么繁琐,因此,在Oracle 11g R2中,将不再支持将这些文件存储在裸设备上。
为了提高这些关键文件的存活能力,Oracle 11g R2正式引入了一种新的集群文件系统,称之为ASM集群文件系统(ACFS),在RAC环境中,ACFS可以为OCR文件和表决磁盘提供更好的保护,它允许创建五份OCR文件副本,tb之前的集群文件系统仅允许保存两份OCR文件,一个主OCR,一个镜像OCR,但ACFS不适合单独的RAC环境,除此之外,几乎所有与操作系统和数据相关的文件都可以从ACFS的安全性和文件共享特性受益。
动态卷管理器:Oracle 11g R2提供了一个新的ASM动态卷管理器(ADVM)来配置和维护存储在ACFS文件系统中的文件,使用ADVM可以在ASM磁盘组内构建一个ADVM卷设备,管理存储在ADVM卷设备中的文件,以及按需调整ADVM卷设备空间大小,最重要的是,因为ADVM是构建在ASM文件系统架构之上的,可以保证存储在这些卷中的文件受到良好的保护,不会出现意外丢失,因为ASM提供了类似RAID的磁盘阵列的功能。
文件访问控制:使用传统的Windows风格访问控制列表(ACL)或Unix/Linux下的用户/组/其它访问权限风格为ACFS目录和文件授予读,写和执行权限,可以通过图形化的企业管理控制台或命令行程序ASMCMD管理ACFS目录和文件安全。
文件系统快照(FSS):Oracle 11g R2通过它的文件系统快照(FSS)功能可以对ACFS文件系统执行快照,一个快照是所选ACFS文件系统的一个只读副本,对相同的ACFS,它会自动保留63个独立的ACFS快照,当某个ACFS文件被不经意地更新,删除或其它危险操作时,这个特性非常有用,利用11g R2企业管理控制台或ACFS acfsutil命令行工具可以找出该文件合适的版本并执行恢复。
NO.4 改善的软件安装和打补丁过程
我发现作为一名DBA压力最大的活就是给Oracle数据库打补丁了,如果补丁可能会引入对数据库有害的行为,我不得不花费大量的时间和精力来确定和审核,因此我对Oracle 11g R2中提供的新功能感到很欢喜。
集群验证实用程序集成:从Oracle 10g开始引入了集群验证实用程序(CVU),现在已经完全集成到Oracle通用安装程序(OUI)和其它配置助手(如DBCA,DBUA)中了。
零停机修补的集群:当为Oracle集群打补丁时,Oracle 11g R2在一个不合适的位置升级方式应用补丁,这意味着会有两个Oracle Home,其中一个专门用来存放补丁的,但一次只能激活一个Oracle Home,在Oracle 11g R2中不用再为升级全部关闭Oracle集群了,实现真正的零停机打补丁。
NO.5 DBMS_SCHEDULER升级
古老的DBMS_SCHEDULER包得到了彻底的更新,DBA经常使用这个包来调度作业。
文件监视器:以前的版本无法在批处理过程中检测大多数触发事件,如检测一个文件抵达某个目录,在Oracle 11g R2中,使用新的文件监视器可以缓解这个问题,一旦预期的文件抵达目录,DBMS_SCHEDULER现在就可以检测到了,并在新的对象类型SCHEDULER_FILEWATCHER_RESULT中注册它的到来,它通过新的CREATE_FILE_WATCHER存储过程向DBMS_SCHEDULER发送一个信号触发作业。
内置的email通知:无论何时,DBMS_SCHEDULER调度任务启动、失败或完成时,任务的状态可以立即通过email发送出去,虽然在以前的版本中也能实现这个功能,但要么调用DBMS_MAIL存储过程,要么调用DBMS_SMTP存储过程,现在这个功能合并到DBMS_SCHEDULER中了。
远程作业:DBMS_SCHEDULER现在允许DBA在远程数据库上创建和调度作业,现在我终于可以在生产库PROD03上通过DBMS_SCHEDULER调用生产库DBMS_SCHEDULER上的存储过程执行任务了,这意味着我现在可以在一台数据库上集中tb创建和维护调度任务了。
多作业目标:最后,现在可以在多个数据库实例上同时调度DBMS_SCHEDULER任务了,在RAC环境中,这个特性非常有用,因为我可以利用多个数据库实例将长时间运行任务分成几部分,分别在不同的数据库实例上执行更小的任务。
使用IBM DB2 for z/OS和DB2 for Linux,UNIX和Windows (LUW),那就没有问题,下面让我们一起回顾一下什么时候使用XML存储,以及如何自定义XML存储的一些最佳实践吧!
为了形象地说明,我将使用一个XML文档,内容如下:
- <order OrderID="9001" OrderDate="2009-10-18">>
- <customerID>26914</customerID>
- <item id="LK-486">
- <name>Magictb Potion</name>
- <size>300ml</size>
- <price>19.99</price>
- </item>
- <item id="VF-145">
- <name>Crystal Ball, Deluxe</name>
- <color>crystal clear</color>
- <price>295.00</price>
- </item>
- </order>
它展示了一个包括订单ID,日期,客户ID和其它条目的订单XML文档,注意有些条目的描述方式有所不同,如size和color。我们假设需要在DB2中管理许多与此类似的XML文档。
如何拆分和重组XML
我在另一篇文章“15个DB2 pureXML性能最佳实践”中谈到了你应该明智地选择文档的粒度,实际上就是要将存储在DB2中的XML文档与应用程序的业务逻辑对象和主要的访问粒度匹配。
在我们的例子中,假设订单变化非常频繁,订单内的条目读取,添加或删除是最关键的操作,需要最佳的性能,在这种情况下,你可以考虑将订单文档拆分,将每一个条目作为一个独立的文档存储到DB2表的每一行中,这个存储方法(与原来的完整存储订单文档的方法相比)的好处是它使得操作所存储的数据更容易,更快速:
可以使用单行读取检索一个条目,不用从一个完整的订单文档中抽取条目了;
可以通过删除表中的行简单地从订单中删除一个条目,不再需要操作完整的订单文档;
可以迅速插入一个新条目到订单中,这时也不需要操作完整的订单文档。
这种轻松添加和移除订单条目的功能在DB2 9 for z/OS中尤其有价值,因为这个版本不支持在现有XML文档中添加或删除元素。
下面的代码显示了一个表的定义,以及拆分一个订单文档的INSERT语句,相关的列分别存储订单ID,客户ID,订单日期和一个条目流水号。
- CREATE TABLE items(ordID INTEGER, custID INTEGER,
- odate DATE, seqNo INTEGER, item XML);
- INSERT INTO items(ordID, custID, odate, seqno, item)
- SELECT T.ordID, T.custID, T.odate, T.seqno, XMLDOCUMENT( T.item)
- FROM
- XMLTABLE('$d/order/item' PASSING cast(? AS XML) "d"
- COLUMNS
- ordID INTEGER PATH '../@OrderID',
- custID INTEGER PATH '../customerID'
- odate DATE PATH '../@OrderDate',
- seqNo FOR ORDINALITY,
- item XML PATH '.') AS T;
条目信息是以XML格式存储的,因为条目可能有不同的元素和属性,如:
- ORDID CUSTID ODATE SEQNO ITEM
-
- 9001 26914 10/18/2009 1 <item id="LK-486">
- <name>Magic Potion</name>
- <size>300ml</size>
- <price>19.99</price>
- </item>
- 9001 26914 10/18/2009 2 <item id="VF-145">
- <name>Crystal Ball, Deluxe</name>
- <color>crystaltb clear</color>
- <price>295.00</price>
- </item>
- 2 record(s) selected.
INSERT语句包括一个XMLTABLE函数,这个函数从输入XML文档抽取插入items表中的值,它将会拆分输入XML文档,生成独立条目的文档。XMLTABLE函数包括一个参数,通过它,应用程序可以传递一个订单文档,使用XPath表达式$d/order/item,XMLTABLE函数为输入文档的每一个条目生成一行数据,然后抽取订单ID,客户ID和订单日期,特殊的列定义FOR ORDINALITY为产生的每一行打上编号。XMLDOCUMENT函数确保每一个条目片段可以作为一个独立的XML文档插入。
上面的代码显示了使用INSERT语句插入XML文档后items表中的数据,下面的代码显示了如何重建原始的订单文档,XMLELEMENT和XMLATTRIBUTES函数使用items表中相关列的值构建的顶部文档,XMLAGG函数组合所有条目,最后形成一个完整的订单文档。注意,XMLAGG在seqno列上包括一个可选的ORDER BY子句,这样可以确保还原后的订单文档和原始文档中的条目显示顺序是一致的。
- SELECT XMLELEMENT(name "order",
- XMLATTRIBUTES(ordID AS "OrderID", odate as "OrderDate"),
- XMLELEMENT(name "customerID", custID)
- XMLAGG(item ORDER BY seqno) )
- FROM items
- WHERE ordID = 9001
- GROUP BY ordID, odate, custID;
使用生成列
DB2 9.7 for LUW中新的IBM DB2 pureXML特性允许你与数据库分区功能(Database Partitioning Feature,DPF),范围分区表和多维集群(MDC)表一起使用XML列,但分区或集群键必须由相关的列组成。前面你已经看到了如何使用INSERT和XMLTABLE从XML文档抽取值到相关的列中,你可以使用这些关联列对表进行分区或集群。如果你更喜欢在程序中使用简单的INSERT语句,并且不知道如何抽取数据时,那你可以考虑使用一个生成的列。
DB2 9.7在用户定义函数(UDF)中支持XML参数,允许你定义生成的列,使用插入的XML文档中的值自动填充。下面的代码显示了一个UDF,它接受一个XML文档作为输入,如前面例子中的订单文档,这个UDF使用XMLCAST和XMLQUERY函数抽取输入文档的OrderDate属性:
- CREATE FUNCTION extractDate(doc XML)
- RETURNS DATE
- LANGUAGE SQL CONTAINS SQL
- NO EXTERNAL ACTION DETERMINISTIC
- RETURN XMLCAST(XMLQUERY('$d/order/@OrderDate'
- PASSING doc AS "d") AS DATE);
你可以在SELECT查询和其它SQL语句中使用这个UDF,但也要定义一个生成列,对于下面的示例,假设检索和插入完整的订单是最关键的操作,在这种情况下,完整地存储订单文档是最好的选择。下面的代码定义了一个使用XML列存储订单的表,并自动抽取订单日期填充到关联的列(odate)中。一条INSERT语句现在可以简单地插入一个XML文档到order列中,不需要考虑抽取值到关联列中:
- CREATE TABLE orders(
- order XML,
- odate DATE GENERATED ALWAYS AS (extractDate(order)));
如果你连续不断地存储许多订单,可能需要对旧订单进行归档,这个时候使用范围分区是最好的选择,下面的代码显示了表order2是通过按odate列的值进行分区的,odate列则产生自XML列,同样,你可以使用生成的列作为分区数据库的分配键,也可以作为MDC表的集群键:
- CREATE TABLE order2(
- order XML,
- odate DATE GENERATED ALWAYS AS (extractDate(order)) NOT NULL)
- PARTITION BY RANGE (odate)
- (PART q109 STARTING('01-01-2009') ENDING ('03-31-2009') INCLUSIVE,
- PART q209 ENDING ('06-30-2009') INCLUSIVE,
- PART q309 ENDING ('09-30-2009') INCLUSIVE,
- PART q409 ENDING ('12-31-2009') INCLUSIVE);
控制XML存储
自定义XML存储有许多好处,将大型XML文档拆分成多个小文档,将会使操作XML数据变得更加容易和高效,使用UDF定义生成列可以简化XML值抽取到关联列,使用生成列还可以帮助你管理分区数据库,范围分区表,或MDC表中的XML。
原文出处:http://www.ibm.com/developerworks/data/library/dmmag/DMMag_2009_Issue3/Tips/index.html
原文名:Customizing XML storage in DB2
作者:Matthias Nicola
Oracle 9i上使用自动管理回滚的错误,简单记录一下。
错误信息为:
Sat May 12 21:54:17 2012
Errors in file /oracle/app/admin/prmdb/bdump/prmdb2_smon_483522.trc:
ORA-01595: error freeing extent (2) of rollback segment (19))
ORA-01594: attempt to wrap into rollback segment (19) extent (2) which is being freed
数据库环境为9208 RAC for Aix,tb跟进MOS文档With AUM Enabled ORA-01594 and ORA-01595 Found in the alert.log [ID 280151.1]的描述,导致问题的原因是在自动回滚管理系统中,如果SMON在尝试收缩一个回滚段时,有新的事务导致回滚段需要扩展,那么这个回收的操作就会报错。因此,可以认为这是一个正常的信息,而非是错误提示,可以简单的忽略这个问题。
Oracle在10g中已经解决了这个问题,在9i中,可以尝试添加更多的回滚空间来解决问题。
客户的数据库出现ORA-600错误,错误函数为1265。
数据库版本为10.2.0.4 for Linux,错误信息为:
Fri Aug 26 22:00:11 2011
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
Fri Aug 26 22:00:13 2011
Errors in file /opt/app/oracletb/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
Fri Aug 26 22:00:13 2011
Trace dumping is performing id=[cdmp_20110826220013]
Fri Aug 26 22:00:14 2011
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-06512: at "USER1.P_PRO", line 5
ORA-04088: error during execution of trigger 'USER1.P_PRO'
Fri Aug 26 22:00:15 2011
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-06512: at "USER1.P_PRO ", line 5
ORA-04088: error during execution of trigger 'USER1.P_PRO'
ORA-06512: at "USER1.U_PRO ", line 25
Fri Aug 26 22:00:17 2011
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-02067: transaction or savepoint rollback required
Fri Aug 26 22:00:18 2011
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [17281], [600], [0x2E134EEC0], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-02067: transaction or savepoint rollback required
这个错误是ORA-600[1265]错误引发的,随后还出现了ORA-600[17281]、ORA-4088和ORA-2067错误。其中ORA-2067的描述为:
$ oerr ora 2067
02067, 00000, "transaction or savepoint rollback required"
// *Cause: A failure (typically a trigger or stored procedure with multiple
// remote updates) occurred such that the all-or-nothing execution
// of a previous Oracle call cannot be guaranteed.
// *Action: rollback to a previous savepoint or rollback the transaction
// and resubmit.
从这个描述和Oracle的报错信息不难判断,Oracle在通过触发器更新远端表时引发了这个600错误。
根据Oracle的MOS文档Bug 5655419 Distributed transaction hits ORA-600:[1265] or ORA-600:[k2gget: downgrade] in 10.2的描述,这个错误和分布式事务有关,确认影响的版本就是当前环境的10.2.0.4。这个错误的产生一般与窗口维护有关,可以看到问题的发生时刻恰好是22点,从这个时刻开始,Oracle进入维护窗口,进行空间回收统计信息收集等后台工作,显然就是因为窗口的变化导致了这个错误的产生。
Oracle在11.1.0.6中FIXED了这个bug。除了版本升级外,可以考虑将包含分布式事务修改的程序放到远离时间窗口改变时间。
客户数据库10.1.0.4碰到这个ORA-600错误。
详细错误信息为:
Sat Feb 4 13:04:31 2006
ALTER DATABASE MOUNT
Sat Feb 4 13:04:31 2006
Errors in file /oracle/admin/orcl/bdump/orcl_ckpt_122986.trc:
ORA-00600: internal error code, arguments: [kccida_kccsgfsz], [], [], [], [], [], [], []
Sat Feb 4 13:04:32 2006
Errors in file /oracle/admin/orcl/bdump/orcl_ckpt_122986.trc:
ORA-00600: internal error code, arguments: [kccida_kccsgfsz], [], [], [], [], [], [], []
Sat Feb 4 13:04:32 2006
CKPT: terminating instance tb due to error 470
Instance terminated by CKPT, pid = 122986
查询MOS发现和文档Alter Database Mount Returns ORA-3113 And ORA-600 [kccida_kccsgfsz] [ID 315112.1]描述的问题一致。导致问题的原因是客户在迁移或断电等因素导致控制文件和数据文件的格式不兼容。
在下次重启时,告警日志中出现的下面的信息也说明了这一点:
Sat Feb 4 13:20:15 2006
alter database mount
Sat Feb 4 13:20:15 2006
Controlfile identified with block size 16384
显然导致这个问题的原因和客户之前的恢复或迁移操作有关。如果如bug所述,数据库是直接从其他平台拷贝到当前环境下,那么正确的方法肯定是通过逻辑备份EXP/EXPDP进行数据库的迁移。
而如果和当前的情况类似,由于异常导致控制文件的损坏,可以考虑从备份中进行恢复或直接重建控制文件。
碰到一个有意思的问题,如果分区表执行过SET UNUSED操作,那么是否还可以进行分区的EXCHANGE操作。
一个简单的测试就可以说明这个问题:
SQL> create table t_part_unused
2 (id number, name varchar2(30), other varchar2(30))
3 partition by range (id)
4 (partition p1 values less than (10),
5 partition pmax values less than (maxvalue));
Table created.
SQL> insert into t_part_unused
2 select rownum, table_name, 'abc'
3 from user_tables;
48 rows created.
SQL> commit;
Commit complete.
SQL> alter table t_part_unused set unused (other);
Table altered.
SQL> desc t_part_unused
Name Null? Type
---------------------------------------- -------- ------------------------
ID NUMBER
NAME VARCHAR2(30)
SQL> create table t_temp_unused as
2 select *
3 from t_part_unused
4 where 1 = 2;
Table created.
SQL> desc t_temp_unused
Name Null? Type
---------------------------------------- -------- ------------------------
ID NUMBER
NAME VARCHAR2(30)
SQL> alter table t_part_unused
2 exchange partition p1
3 with table t_temp_unused;
with table t_temp_unused
*
ERROR at line 3:
ORA-14097: column type or size mismatch in ALTER tb TABLE EXCHANGE PARTITION
SQL> alter table t_temp_unused add (other varchar2(30));
Table altered.
SQL> alter table t_part_unused
2 exchange partition p1
3 with table t_temp_unused;
with table t_temp_unused
*
ERROR at line 3:
ORA-14096: tables in ALTER TABLE EXCHANGE PARTITION must have the same number of columns
SQL> alter table t_temp_unused set unused (other);
Table altered.
SQL> alter table t_part_unused
2 exchange partition p1
3 with table t_temp_unused;
Table altered.
很明显执行了SET UNUSED操作后的表,和普通的表是存在区别的,这种区别导致要求进行EXCHANGE的表必须同样执行SET UNUSED操作,否则就无法执行EXCHANGE的操作。
当目标表中不包含SETE UNUSED的列时,EXCHANGE操作会出现ORA-14097的错误,而如果把列添加到目标表,则会报错ORA-14096,必须在目标表同样对列执行SET UNUSED操作,才能通过EXCHANGE之前的检查。
其实这也不难理解,执行SET UNUSED命令后,数据字典虽然发生了改变,但是tb表上的数据并没有删除,而EXCHANGE操作只是将两个段的数据字典进行互换,因此如果目标表上缺少SET UNUSED列,是无法执行EXCHANGE操作的。
解决问题的方法有两个,第一个就是例子中展示的可以在目标表上建立列然后同样的执行SET UNUSED操作;另外的一个方法就是对于SET UNUSED列执行DROP COLUMN操作,彻底删除该列。
Oracle10g的DBA_TAB_STATISTICS视图的STATTYPE_LOCKED列没有正确的显示结果。
看一个简单的例子:
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE 10.2.0.5.0 Production
TNS for Linux: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production
SQL> create table t_part (id number, name varchar2(30))
2 partition by range (id)
3 (partition p1 values less than (10),
4 partition p2 values less than (20),
5 partition pmax values less than (maxvalue));
Table created.
SQL> select table_name, partition_name, stattype_locked from tb all_tab_statistics where wner = user and table_name = 'T_PART';
TABLE_NAME PARTITION_NAME STATT
------------------------------ ------------------------------ -----
T_PART
T_PART P1
T_PART P2
T_PART PMAX
SQL> exec dbms_stats.lock_partition_stats(user, 'T_PART', 'P1')
PL/SQL procedure successfully completed.
SQL> select table_name, partition_name, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';
TABLE_NAME PARTITION_NAME STATT
------------------------------ ------------------------------ -----
T_PART
T_PART P1
T_PART P2
T_PART PMAX
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART')
PL/SQL procedure successfully completed.
SQL> select table_name, partition_name, last_analyzed, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';
TABLE_NAME PARTITION_NAME LAST_ANAL STATT
------------------------------ ------------------------------ --------- -----
T_PART 16-JUL-12
T_PART P1
T_PART P2 16-JUL-12
T_PART PMAX 16-JUL-12
可以看到在10.2环境中,LOCK_PARTITION_STATS过程是正常工作的,但是DBA_TAB_STATISTICS视图的STATTYPE_LOCKED列并没有正确的显示分区被锁定的结果。
而对于表来说,LOCK_TABLE_STATS过程执行后,STATTYPE_LOCKED的结果显示是正常的:
SQL> exec dbms_stats.lock_table_stats(user, 'T_PART')
PL/SQL procedure successfully completed.
SQL> select table_name, partition_name, last_analyzed, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';
TABLE_NAME PARTITION_NAME LAST_ANAL STATT
------------------------------ ------------------------------ --------- -----
T_PART 16-JUL-12 ALL
T_PART P1 ALL
T_PART P2 16-JUL-12 ALL
T_PART PMAX 16-JUL-12 ALL
这说明在10.2中,Oracle对于分区列的锁定的支持是存在问题的。查询了一下MOS,Oracle将这个问题确认为内部BUG:7240460,这个问题在11.1.0.7中被FIXED。
而在11.2中,这个问题以及不存在了:
SQL> select * from v$version;
BANNER
----------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for Solaris: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
SQL> select owner, table_name, partition_name, stattype_locked
2 from dba_tab_statistics
3 where wner = 'TEST'
4 and table_name = 'T_PART';
OWNER TABLE_NAME PARTITION_NAME STATT
---------- ------------ --------------- -----
TEST T_PART
TEST T_PART P2
TEST T_PART P3
TEST T_PART P4
TEST T_PART P5
TEST T_PART PMAX
6 rows selected.
SQL> exec dbms_stats.lock_partition_stats('TEST', 'T_PART', 'P2')
PL/SQL procedure successfully completed.
SQL> select owner, table_name, partition_name, stattype_locked
2 from dba_tab_statistics
3 where wner = 'TEST'
4 and table_name = 'T_PART';
OWNER TABLE_NAME PARTITION_NAME STATT
---------- ------------ --------------- -----
TEST T_PART
TEST T_PART P2 ALL
TEST T_PART P3
TEST T_PART P4
TEST T_PART P5
TEST T_PART PMAX
6 rows selected
客户数据库出现ORA-4031错误,随后出现了大量的ORA-1003和ORA-604错误。
数据库版本为10.2.0.3 RAC for HP-UX,详细的报错信息为:
Mon Jul 16 15:30:30 2012
Errors in file /u01/app/oracle/admin/ORCL/udump/orcl2_ora_2389.trc:
ORA-00603: ORACLE server session terminated by fatal error
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 32 bytes of shared memory ("shared pool","select name,online$,contents...","sql area","tmp")
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 32 bytes of shared memory ("shared pool","select name,online$,contents...","sql area","tmp")
Mon Jul 16 15:30:32 2012
Errors in file /u01/app/oracle/admin/ORCL/udump/orcl2_ora_2878.trc:
ORA-00603: ORACLE server session terminated by fatal error
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 144 bytes of shared memory ("shared pool","select name,online$,contents...","Typecheck","coldef: qcopCreateCol")
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 144 bytes of shared memory ("shared pool","select name,online$,contents...","Typecheck","coldef: qcopCreateCol")
Mon Jul 16 15:30:32 2012
Errors in file /u01/app/oracle/admin/ORCL/udump/orcl2_ora_10030.trc:
ORA-00603: ORACLE server session terminated by fatal error tb
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 144 bytes of shared memory ("shared pool","select name,online$,contents...","Typecheck","coldef: qcopCreateCol")
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 144 bytes of shared memory ("shared pool","select name,online$,contents...","Typecheck","coldef: qcopCreateCol")
Mon Jul 16 15:30:39 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-04031: unable to allocate 32 bytes of shared memory ("shared pool","select o.owner#,o.name,o.nam...","sql area","kobjn : kkdcchs")
Mon Jul 16 15:30:40 2012
Trace dumping is performing id=[cdmp_20120716153040]
Mon Jul 16 15:32:19 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:33:59 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:35:39 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:37:19 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:39:00 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:40:40 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQ
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: err
Mon Jul 16 15:41:59 2012
Thread 2 advanced to log sequence 61522
Current log# 7 seq# 61522 mem# 0: +ORCL_CTL/orcl/onlinelog/group_7.263.611598065
Mon Jul 16 15:42:20 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:44:00 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:45:40 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQ
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: err
可以看得,开始还是比较“正常”的ORA-4031错误,但是随着CJQ进程出现了ORA-4031的错误,数据库开始每1分40秒报一次ORA-1003和ORA-604的错误。这里CJQ进程似乎陷入了一个死循环中,持续不停的报错。查询MOS发现,在9.2中Oracle存在类似的BUG:Bug 3289063 ORA-1003 every 5 seconds after CJQ hits ORA-4031。这个bug已经在9.2.0.5和10.1.0.2中被FIXED,但是这个bug的现象和当前的错误完全一致,从这点上看,这个问题显然在10.2.0.3中仍然存在。
Oracle给出的解决方案也很简单,重启CJQ进程既可。除了在操作系统去kill -9杀掉cjq0对应的进程之外,直接通过alter system set job_queue_processes=0,然后在设置回原值,也有可能解决该问题。此外,尽量避免ORA-4031错误的产生是最根本的问题解决之道。
可以利用tar命令向磁盘备份文件,同时可以利用tar从磁带中恢复指定文件。
备份的命令为:
tar cvf /dev/rmt/0m filename…
恢复的命令为:
tar xvf /dev/rmt/0m filename…
从磁带上恢复文件的例子:
> tar xvf /dev/rmt/0m /archive/orcl_exp_report_1.dmp /archive/orcl_exp_report_2.dmp
x /archive/temp_exp/acdb_exp_report_1.dmp, 3198353408 bytes, 6246784 tape blocks
x /archive/temp_exp/acdb_exp_report_2.dmp, 3078950912 bytes, 6013576 tape blocks
tb恢复时指定的文件名必须是备份到磁带的文件名。
当用户密码即将过期时,在登录时Oracle会提示ORA-28002错误,但是并不会影响正常的登录。
本来认为这个信息并没有太大的影响,但是没想到这个错误会导致SET AUTOTRACE功能失效:
solaris*orcl-/home/oracle$ sqlplus test/test
SQL*Plus: Release 11.2.0.3.0 Production on Fri Jul 13 11:27:28 2012
Copyright (c) 1982, 2011, Oracle. tb All rights reserved.
ERROR:
ORA-28002: the password will expire within 1 days
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
SQL> set autot on
ERROR:
ORA-28002: the password will expire within 1 days
SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report
SQL> alter user test identified by test;
User altered.
SQL> set autot on
ERROR:
ORA-24315: illegal attribute type
SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report
SQL> exit
Disconnected from Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
solaris*orcl-/home/oracle$ sqlplus test/test
SQL*Plus: Release 11.2.0.3.0 Production on Fri Jul 13 11:27:52 2012
Copyright (c) 1982, 2011, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
SQL> set autot on
SQL> insert into t values (1, 'a');
1 row created.
Execution Plan
----------------------------------------------------------
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 1 | 12 | tb 1 (0)| 00:00:01 |
| 1 | LOAD TABLE CONVENTIONAL | T | | | | |
---------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
71 recursive calls
9 db block gets
75 consistent gets
10 physical reads
1284 redo size
829 bytes sent via SQL*Net to client
785 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
9 sorts (memory)
0 sorts (disk)
1 rows processed
很显然,由于ORA-28002错误导致了SET AUTOTRACE ON功能启用时碰到了错误。当修改了当前的用户密码,则ORA-28002不再出现后,SET AUTOTRACE ON的功能恢复正常。
根据上面的信息其实可以判断,在启用SET AUTOTRACE ON功能时,sqlplus会自动创建一个新的会话来记录当前会话的统计信息。
而启用的新的会话会使用当前会话登录时保存的密码来进行登录,因此可以通过下面的例子来验证这个推论:
solaris*orcl-/home/oracle$ sqlplus test/test
SQL*Plus: Release 11.2.0.3.0 Production on Sun Jul 15 01:28:38 2012
Copyright (c) 1982, 2011, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
SQL> set autot on
SQL> set autot off
SQL> alter user test identified by test1;
User altered.
SQL> set autot on
ERROR:
ORA-01017: invalid username/password; logon denied
SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report
果然,在用户登录后,如果当前的密码被修改,是会导致SET AUTOTRACE ON启用时报错不正确的用户名密码错误的。
一个客户碰到的具体需求,分区表中有些分区所在的表空间被OFFLINE,导致在删除统计信息时报错。
下面通过例子来说明这个问题:
SQL> create table t_part_read (id number)
2 partition by range (id)
3 (partition p1 values less than (10) tablespace ts1,
4 partition p2 values less than (20) tablespace ts2,
5 partition pmax values less than (maxvalue) tablespace users);
Table created.
SQL> insert into t_part_read select rownum from tab;
54 rows created.
SQL> commit;
Commit complete.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
PL/SQL procedure successfully completed.
SQL> alter tablespace ts1 read only;
Tablespace altered.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
PL/SQL procedure successfully completed.
SQL> alter tablespace ts1 offline;
Tablespace altered.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART_READ'); END;
*
ERROR at line 1:
ORA-00376: file 6 cannot be read at this time
ORA-01110: data file 6: '/u01/app/oracle/oradata/ORCL/datafile/tbo1_mf_ts1_7w8l5fz1_.dbf'
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 1
如果将表空间只读,并不会影响到表空间上的表或分区的统计信息的收集,因为收集过程只是读取,而收集的结果信息是写到SYSTEM表空间的。
但是如果分区所在的表空间处于OFFLINE状态,那么在统计信息收集的过程中就会报错。
有一个很简单的方法可以解决这个问题,就是将被OFFLINE影响的分区的统计信息锁定,这样Oracle在收集统计信息时就会跳过锁定的分区,通过这个办法就可以避免统计信息收集过程中的报错:
SQL> exec dbms_stats.lock_partition_stats(user, 'T_PART_READ', 'P1')
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART_READ'); END;
*
ERROR at line 1:
ORA-00376: file 6 cannot be read at this time
ORA-01110: data file 6: '/u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts1_7w8l5fz1_.dbf'
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 1
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ', granularity => 'PARTITION')
PL/SQL procedure successfully completed.
即使锁定分区后,尝试收集统计信息仍然报错,这是因为Oracle默认除了要收集分区上的统计信息以外,还要收集表级的统计信息,而这就会造成被OFFLINE影响的分区也要被读取。
解决方法就是在收集统计信息的时候指定收集的粒度是分区,不收集表上的GLOBAL信息。
第一次碰到Exadata上的bug。
数据库环境Exadata V2-2,数据库版本为11.2.0.2,错误信息为:
Wed Apr 25 11:32:35 2012
Errors in file /u01/app/oracle/diag/rdbms/ods/orcl2/trace/orcl2_ora_9495.trc (incident=304808):
ORA-00600: internal error code, arguments: [kffmXpGet], [145], [69784], [], [], [], [], [], [], [], [], []
ORA-03135: connection lost contact
Incident details in: /u01/app/oracle/diag/rdbms/orcl/orcl2/incident/incdir_304808/orcl2_ora_9495_i304808.trc
Use ADRCI or Support Workbench to package the incident.
See Note 411.1 at My Oracle Support for error and packaging details.
opidcl aborting process tb unknown ospid (9495) as a result of ORA-600
Wed Apr 25 11:32:36 2012
Dumping diagnostic data in directory=[cdmp_20120425113236], requested by (instance=2, sid=9495), summary=[incident=304808].
Wed Apr 25 11:32:36 2012
Sweep [inc][304808]: completed
Sweep [inc2][304808]: completed
Wed Apr 25 11:32:39 2012
Errors in file /u01/app/oracle/diag/rdbms/orcl/orcl2/trace/orcl2_pmon_10797.trc (incident=302224):
ORA-00600: internal error code, arguments: [kffmXpGet], [181], [87276], [], [], [], [], [], [], [], [], []
Incident details in: /u01/app/oracle/diag/rdbms/orcl/orcl2/incident/incdir_302224/orcl2_pmon_10797_i302224.trc
Dumping diagnostic data in directory=[cdmp_20120425113240], requested by (instance=2, sid=10797 (PMON)), summary=[incident=302224].
Use ADRCI or Support Workbench to package the incident.
See Note 411.1 at My Oracle Support for error and packaging details.
Errors in file /u01/app/oracle/diag/rdbms/orcl/orcl2/trace/orcl2_pmon_10797.trc:
ORA-00600: internal error code, arguments: [kffmXpGet], [181], [87276], [], [], [], [], [], [], [], [], []
PMON (ospid: 10797): terminating the instance due to error 472
Wed Apr 25 11:32:41 2012
opiodr aborting process unknown ospid (11248) as a result of ORA-1092
Wed Apr 25 11:32:41 2012
opiodr aborting process unknown ospid (1967) as a result of ORA-1092
Wed Apr 25 11:32:42 2012
ORA-1092 : opitsk aborting process
Wed Apr 25 11:32:43 2012
License high water mark = 573
Instance terminated by PMON, pid = 10797
USER (ospid: 8755): terminating the instance
Instance terminated by USER, pid = 8755
由于ORA-600[kffmXpGet]错误的出现,最终出现了ORA-1092的错误,并致使opitsk进程退出,导致数据库实例崩溃。
Oracle在MOS文档Bug 12387467 instance crash by ORA-600 [kffmxpget]中描述了这个问题,确认影响的版本为11.2.0.1、11.2.0.2、11.2.0.3,Oracle在11.2.0.2的Bundle Patch 16 for Exadata以及11.2.0.3 Bundle Patch 5 for Exadata中fixed了这个问题,Oracle计划在11.2.0.4和12.1中彻底Fixed该问题。
这个错误只会发生在Exadata中,因为导致错误的原因和Smart Scan有关。当会话执行Smart Scan操作时使用了Ctrl + C中止该操作,会导致PMON进程出现这个600错误。Oracle建议在执行数据文件的SHRINK操作前,中止并退出所有执行Exadata Smart Scan的会话。
Eygle的新书经历了4个多月的等待,终于面世了。
我应该是这本书的第一个读者,4个月前Eygle刚刚完成初稿的时候,我就完整的看过一遍了。
我平常看书比较慢,不过那次却看得很快。一方面是由于大部分案例都比较熟悉;另一方面得益于Eygle的文笔,把故障原因、分析过程、解决思路和处理过程描述得非常清晰,给人一种一气呵成的感觉。导致我这个帮忙审稿的,多次都陷入到具体的内容中了,虽然对于我来说看得tb很爽,但是对于审稿而言并不是一件好事。审稿应该始终站在一个中立的角度,而如果在审稿的过程中过于关注内容,就会忽略掉一些细节的问题。好在Eygle对于自己文章的严谨程度很高,因此通篇看完也没有发现多少不妥之处,估计也不会遗漏太多的问题。
书中的所有内容都来自真实的案例,而且其中有三个个重要的案例都是来自2011年12月30日到2011年12月31日这两天。在2012年元旦马上要来临之前,Eygle接连帮助三个客户进行了数据库的恢复,这件事刺激了Eygle,于是元旦回来,Eygle就开始构思并执笔他的新作。一个多月的时间,这本《数据安全警示录》就基本上完成了。
上次看得是电子版,拿到实体书后感觉这次的印刷质量还是很不错的,等有空的话还要再把这本书再看一遍。
客户的数据库出现了ORA-600(kfnsBackground03)错误。
数据库版本为10.2.0.3 RAC for HP-UX 11.23。这个错误在ASM实例和数据库实例都可能出现,如果发生在ASM实例,并不会导致ASM实例的崩溃,但是如果发生在数据库实例,则会导致数据库实例被强制关闭:
Tue May 15 10:28:05 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Tue May 15 10:32:50 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Tue May 15 10:33:05 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Tue May 15 10:34:44 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Tue May 15 10:43:05 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Tue May 15 10:46:13 2012
Errors in file /u01/app/oracle/admin/+ASM/udump/+asm1_ora_18846.trc:
ORA-00600: internal error code, arguments: [kfnsBackground03], [], [], [], [], [], [], []
Tue May tb 15 10:46:14 2012
Trace dumping is performing id=[cdmp_20120515104614]
上面是ASM实例的报错,下面是对应时刻数据库实例的报错:
Tue May 15 10:38:12 2012
kkjcre1p: unable to spawn jobq slave process
Tue May 15 10:38:12 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_cjq0_17957.trc:
Tue May 15 10:42:19 2012
PMON failed to acquire latch, see PMON dump
Tue May 15 10:43:04 2012
found dead shared server 'S006', pid = (90, 4)
Tue May 15 10:43:10 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_j000_19938.trc:
ORA-12012: error on auto execute of job 42579
ORA-27468: "EXFSYS.RLM$EVTCLEANUP" is locked by another process
Tue May 15 10:45:06 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_j002_23628.trc:
ORA-12012: error on auto execute of job 8888975
ORA-27468: "ORCL.P_DATA_C" is locked by another process
Tue May 15 10:45:10 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_j003_23959.trc:
ORA-12012: error on auto execute of job 8855572
ORA-27468: "ORCL.P_DATA" is locked by another process
Tue May 15 10:46:14 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_asmb_18844.trc:
ORA-15064: communication failure with ASM instance
ORA-00600: internal error code, arguments: [kfnsBackground03], [], [], [], [], [], [], []
Tue May 15 10:46:14 2012
ASMB: terminating instance due to error 15064
Tue May 15 10:46:15 2012
System state dump is made for local instance
System State dumped to trace file /u01/app/oracle/admin/ORCL/bdump/orcl1_diag_17903.trc
Tue May 15 10:46:16 2012
Shutting down instance (abort)
License high water mark = 52
如果从这次数据库的实例崩溃看,问题似乎和主机上的资源耗尽有关。在问题发生之前,数据库实例已经出现了kkjcre1p: unable to spawn jobq slave process和PMON failed to acquire latch的问题。
当时其他时刻出现这个错误时,似乎并没有确定的资源不足的信息:
Sat May 26 09:47:49 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Sat May 26 09:49:44 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Sat May 26 09:52:23 2012
Errors in file /u01/app/oracle/admin/+ASM/udump/+asm1_ora_21722.trc:
ORA-00600: internal error code, arguments: [kfnsBackground03], [], [], [], [], [], [], []
Sat May 26 09:52:25 2012
Trace dumping is performing id=[cdmp_20120526095225]
对应这个时刻的数据库告警信息为:
Sat May 26 09:52:24 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_asmb_21720.trc:
ORA-15064: communication failure with ASM instance
ORA-00600: internal error code, arguments: [kfnsBackground03], [], [], [], [], [], [], []
Sat May 26 09:52:24 2012
ASMB: terminating instance due to error 15064
Sat May 26 09:52:25 2012
System state dump is made for local instance
System State dumped to trace file /u01/app/oracle/admin/ORCL/bdump/orcl1_diag_20837.trc
Sat May 26 09:52:26 2012
Shutting down instance (abort)
License high water mark = 46
Sat May 26 09:52:30 2012
Instance terminated by ASMB, pid = 21720
Sat May 26 09:52:31 2012
Instance terminated by USER, pid = 536
这次错误的出现并没有任何其他的信息,数据库实例就直接DOWN掉了。不过每次在出现这个错误时,ASM实例上都会存在告警信息:NOTE: database ORCL1:ORCL failed during msg 19, reply 2。这说明ASM实例和数据库的通信存在了问题。kfnsBackground是Kernel Files Network Service Background的缩写。其中MSG 19是指IOSTAT,而reply 2指的是TIMEOUT,这说明ASM在进行io操作是出现了timeout导致了ASM的异常并导致实例的崩溃。
这个错误相对比较罕见,整个METALINK中,只有3篇文章和这个错误相关,其中两篇是和归档路径空间不足导致系统HANG住,最终导致IO的TIMEOUT,并产生了错误;而另外一篇则没有进一步的信息。其中这三次错误对应的版本分别是10.2.0.4 FOR AIX、10.2.0.4 FOR SOLARIS和10.2.0.3 FOR HPUX,这说明这个错误和平台没有关系,但是问题集中在10.2.0.3和10.2.0.4版本上。
根据上面的分析,应该部署操作系统信息监控工具,以便于随时观察系统资源的使用情况,在出现类似的错误可以进行辅助分析。由于这个问题没有出现在10.2.0.5中的记录,因此把数据库升级到10.2.0.5有可能避开这个问题。
前几天在给公司的员工讲一个案例的提到这个问题。
其实当时提到了这个特点,DROP TABLE会进入回收站,但是DROP PARTITION并不会,因此DROP PARTITION之后,数据无法简单的回复,只能通过逻辑或物理备份的方式来进行数据的回复。
SQL> create table t_drop (id number);
Table created.
SQL> drop table t_drop;
Table dropped.
SQL> select object_name, original_name from recyclebin;
OBJECT_NAME ORIGINAL_NAME
------------------------------ --------------------------------
BIN$xJhZqpmfWZXgRDzZK0pZWw==$0 T_DROP
SQL> create table t_part_drop (id number) partition by range (id)
2 (partition p1 values less than (10),
3 partition p2 values less than (20),
4 partition p3 values less than (30),
5 partition pmax values less than (maxvalue));
Table created.
SQL> insert into t_part_drop tb select rownum from user_objects;
176 rows created.
SQL> commit;
Commit complete.
SQL> alter table t_part_drop drop partition p1;
Table altered.
SQL> select object_name, original_name from recyclebin;
OBJECT_NAME ORIGINAL_NAME
------------------------------ --------------------------------
BIN$xJhZqpmfWZXgRDzZK0pZWw==$0 T_DROP
本来只是普及一下这个常识,不过有人问我Oracle为什么没有实现将删除分区放在回收站中。这个问题问的很好,因为如果这个功能很容易实现,那么Oracle肯定早就实现了,而到了11.2中Oracle仍然没有实现这个功能,那么一定说明这个功能不是无法实现,就是实现的困难太大。
回收站的实现并不复杂,当一张表被删除的时候,Oracle没有直接释放表在表空间上的空间占用,而是将表简单的打了个标识,这样在正常查询数据字典时就不会看到这张被删除的表,而如果需要恢复这张表时,只需要将标识位改回来既可。
那么同样是修改数据字典,为什么不能将被删除的分区通过标识的方法放到回收站中呢,这是因为,对于表而言,删除操作是将一个整理完全删除。而对于分区的删除,是删除整体中的一个部分。对于删除这个动作其实并没有太大的影响,但是回收站的功能不是为了删除,而是为了可以快速的恢复。对表而言,直接恢复整体不存在任何的问题,即使同名对象存在,也只需改个名字既可。而对于删除分区的恢复而言, tb 问题就不那么简单了。由于分区表并没有删除,因此这个表仍然可以继续进行操作,虽然某个分区被删除了,但是除非是范围分区中的MAXVALUE分区和列表分区中的DEFAULT分区,否则再插入原分区对应的数据时,并不会报错,而是会插入到其他分区中:
SQL> select * from t_part_drop partition (p2);
ID
----------
10
11
12
13
14
15
16
17
18
19
10 rows selected.
SQL> insert into t_part_drop values (5);
1 row created.
SQL> select * from t_part_drop partition (p2);
ID
----------
10
11
12
13
14
15
16
17
18
19
5
11 rows selected.
原表应该插入分区P1的数据,由于分区P1被删除,因此现在满足分区P2的条件,被插入到分区P2中,考虑这种情况下,如果直接恢复P1分区会怎样。
显然这不是一个简单的数据字典的修改就能解决的问题,不但涉及到分区数据改变的问题,还必然会带来全局和本地索引失效的问题,更重要的是,可能带来主键冲突的情况。
这还只是分区表进行了DML的情况,如果删除分区后,分区表又进行了DDL,比如新SPLIT了P1分区,那么删除分区的恢复操作就更无法进行了。
如果一个功能觉得很简单就可以实现,但是Oracle却一直没有实现,那么很可能实现这个功能并不像想象的那么简单。
11.2对于CREATE OR REPLACE TYPE语句进行了增加,增加了FORCE选项。
在11.2之前,只要有其他的表或TYPE依赖了当前对象,这个对象就无法进行REPLACE了:
SQL> create type t_num_tab is table of number;
2 /
Type created.
SQL> create type t_record is object (id number, n_tab t_num_tab);
2 /
Type created.
SQL> create or replace type t_num_tab is table of number(5);
2 /
create or replace type t_num_tab is table of number(5);
*
ERROR at line 1:
ORA-02303: cannot drop or replace a type with type or table dependents
这是11.2之前的情况,尝试执行CREATE OR REPLACE将会导致ORA-2303错误,在11.2中Oracle增加了FORCE功能,使得一个仅被TYPE所依赖的对象仍然可以执行REPLACE的操作:
SQL> create or replace type t_num_tab force is tbable of number(5);
2 /
Type created.
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for Solaris: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
有意思的是,FORCE语句的位置不对则不会生效,这时同样会报ORA-2303的错误,而并不会导致语句错误:
SQL> create or replace force type t_num_tab is table of number(5);
create or replace force type t_num_tab is table of number(5)
*
ERROR at line 1:
ORA-02303: cannot drop or replace a type with type or table dependents
SQL> create or replace type t_num_tab is table of number(5) force;
2 /
create or replace type t_num_tab is table of number(5) force;
*
ERROR at line 1:
ORA-02303: cannot drop or replace a type with type or table dependents
最后这个功能只对被TYPE所依赖的对象有效,一旦对象被表所依赖,则FORCE功能也不起作用:
SQL> create table t_type_tab (id number, c_tab t_num_tab)
2 nested table c_tab store tb as c_tab_tab;
Table created.
SQL> create or replace type t_num_tab force is table of number(6);
2 /
create or replace type t_num_tab force is table of number(6);
*
ERROR at line 1:
ORA-22866: cannot replace a type with table dependents
Oracle的错误信息也变成了ORA-22866。其实这时可以预料的,因为一旦创建了表,就相当于进行了实体化的工作,如果依赖的类型发生了变化,将会影响表中已有的数据的读写。不过其实Oracle可以做到更进一步,就是如果表段没有创建或者表中没有插入数据的情况下,允许对依赖的对象进行修改。
最近发现10g的RMAN备份保留REDUNDANCY策略和9i相比发生了改变。
在Oracle9i中,备份保留策略的REDUNDANCY的值,指的是备份冗余的个数。也就是说,如果REDUNDANCY设置为1,那么Oracle会保留2个备份。
但是在10g以后,REDUNDANCY的值,就是最终备份保留的值,手头没有10g的环境,用11g的rman做了一个例子:
solaris*orcl-/home/oracle$ rman target /
Recovery Manager: Release 11.2.0.3.0 - Production on Sun Jul 8 19:04:43 2012
Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved.
connected to target database: ORCL (DBID=1299676637)
RMAN> show retention policy;
using target database control file instead of recovery catalog
RMAN configuration parameters for database with db_unique_name ORCL are:
CONFIGURE RETENTION POLICY TO REDUNDANCY 1; # default
RMAN> backup tablespace ts_32k;
Starting backup at 08-JUL-12
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=180 device type=DISK
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datbafile(s) in backup set
input datafile file number=00005 name=/u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts_32k_7w1w3zmb_.dbf
channel ORA_DISK_1: starting piece 1 at 08-JUL-12
channel ORA_DISK_1: finished piece 1 at 08-JUL-12
piece handle=/u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190559_7zltdqxy_.bkp tag=TAG20120708T190559 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:01
Finished backup at 08-JUL-12
RMAN> backup tablespace ts_32k;
Starting backup at 08-JUL-12
using channel ORA_DISK_1
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datbafile(s) in backup set
input datafile file number=00005 name=/u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts_32k_7w1w3zmb_.dbf
channel ORA_DISK_1: starting piece 1 at 08-JUL-12
channel ORA_DISK_1: finished piece 1 at 08-JUL-12
piece handle=/u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190609_7zltf22b_.bkp tag=TAG20120708T190609 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 08-JUL-12
RMAN> list backup of tablespace ts_32k;
List of Backup Sets
===================
BS Key Type LV Size Device Type Elapsed Time Completion Time
------- ---- -- ---------- ----------- ------------ ---------------
20 Full 2.69M DISK 00:00:01 08-JUL-12
BP Key: 20 Status: AVAILABLE Compressed: NO Tag: TAG20120708T190559
Piece Name: /u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190559_7zltdqxy_.bkp
List of Datafiles in backup set 20
File LV Type Ckp SCN Ckp Time Name
---- -- ---- ---------- --------- ----
5 Full 28932281 08-JUL-12 /u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts_32k_7w1w3zmb_.dbf
BS Key Type LV Size Device Type Elapsed Time Completion Time
------- ---- -- ---------- ----------- ------------ ---------------
21 Full 2.69M DISK 00:00:01 08-JUL-12
BP Key: 21 Status: AVAILABLE Compressed: NO Tag: TAG20120708T190609
Piece Name: /u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190609_7zltf22b_.bkp
List of Datafiles in backup set 21
File LV Type Ckp SCN Ckp Time Name
---- -- ---- ---------- --------- ----
5 Full 28932300 08-JUL-12 /u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts_32k_7w1w3zmb_.dbf
RMAN> delete obsolete;
RMAN retention policy will be applied to the command
RMAN retention policy is set to redundancy 1
using channel ORA_DISK_1
Deleting the following obsolete backups and copies:
Type Key Completion Time Filename/Handle
-------------------- ------ ------------------ --------------------
Backup Set 20 08-JUL-12
Backup Piece 20 08-JUL-12 /u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190559_7zltdqxy_.bkp
Do you really want to delete the above objects (enter YES or NO)? yes
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190559_7zltdqxy_.bkp RECID=20 STAMP=788123159
Deleted 1 objects
可以看到,从10g开始设置的REDUNDANCY的值,就是最终备份保留的个数。为了确认这个问题,特意查询了一下9i和10g的官方文档。
9i的说法是:
The REDUNDANCY parameter specifies that any number of backups or copies beyond a specified number need not be retained.
而10g的文档中,该参数的描述变为:
A redundancy-based backup retention policy determines whether a backup is obsolete based on how many backups of a file are currently on disk.
Oracle改变功能的实现很常见,但是没有想到,对于这种细节的定义也会调整。对于熟悉9i备份策略的DBA需要留神,在设置10g以后的RMAN备份保留策略时,需要在9i的基础上增加1。
客户数据库版本为9208 RAC FOR AIX,客户反应系统缓慢,检查告警日志,发现大量Library cache lock和Library cache load lock等待。
由于客户的原因,这个问题只是远程协助的方式帮忙检查了一下,因此没有留下任何的操作记录,这里只是简单描述一下问题。
客户反应数据库操作响应变慢,平时一个执行很快的基于主键的UPDATE操作也变得异常缓慢,且执行计划本身并未发生改变。
登录数据库后检查两个节点上的告警日志,并未发现任何异常报错。分别检查两个实例的等待信息,发现除了上面提到的大量Library cache lock和Library cache load lock以外,还有明显的gc等待。
但是随后发现,查询V$SESSION和GV$SESSION的结果居然没有区别,接着查询GV$INSTANTBCE视图,发现只有当前的实例存在,而此时恰好连接另一个节点的工具出现了断连,以至于我一度以为另外一个节点上的实例已经DOWN掉,但是随后重新登录到该节点上,发现数据库实例仍然存在,而且登录到数据库实例中也可以进行任何正常的操作。不过发现在当前节点所有的GV$视图都只会返回当前实例的信息,这与另外一个节点的情况完全一样。显然两个节点间的通信出现了问题,当前节点已经不清楚另外一个节点的状态的。
现在再去分析那些等待信息已经没有太多的意义了,因为整个数据库已经处于不正常的状态。不难推断,当前数据库的异常是由于节点间的通信异常导致。由于9i使用的操作系统的CLUSTER,还没有Oracle的clusterware,剩下只能由操作系统或硬件维护人员去进一步跟踪了。
最终数据库和系统在夜间闲时进行了重启操作,重启后数据库恢复正常,GV$视图的结果也恢复了正常。
正常情况下,DBMS_OUTPUT包无法直接输出一个空行。
以前还真没有注意这个问题,前两天想在输出结果的时候进行一下简单的格式化,发现了这个问题:
SQL> set serverout on
SQL> begin
2 dbms_output.put_line('a');
3 dbms_output.put_line(' ');
4 dbms_output.put_line('b');
5 dbms_output.new_line;
6 dbms_output.put_line('c');
7 end;
8 /
a
b
c
PL/SQL procedure successfully completed.
导致问题的原因在于,如果使用DBMS_OUTPUTB包输出的一行都是不可见字符,那么这行内容被DBMS_OUTPUT包忽略掉。
虽然DBMS_OUTPUT包本身并没有提供开关来屏蔽这个属性,不过这个问题依然很容易解决,最简单的方法莫过于直接把回车包含在字符串中:
SQL> begin
2 dbms_output.put_line('a
3
4 b');
5 dbms_output.put_line('
6 c');
7 end;
8 /
a
b
c
PL/SQL procedure successfully completed.
当然这种方法有可能导致PL/SQL代码的可读性变差,也容易影响代码的缩进格式,此外还有一种方式:
SQL> begin
2 dbms_output.put_line('a' || chr(10) || chr(13));
3 dbms_output.put_line('b');
4 dbms_output.put_line(chr(10) || chr(13) || 'c');
5 end;
6 /
a
b
c
PL/SQL procedure successfully completed.
客户10.2.0.4环境告警日志出现ORA-27468错误。
详细错误信息为:
Errors in file /u01/app/oracle/admin/orcl/bdump/orcl1_j000_18724.trc:
ORA-12012: error on auto execute of job 42791
ORA-27468: "EXFSYS.RLM$EVTCLEANUP" is locked by another process
导致这个错误的原因在于升级时没有执行catupgrd.sql,而是执行了建库的部分脚本如catalog.sql和catproc.sql,这导致只有CATALOG视图和系统的PACKAGE以及TYPE的版本更新到10.2.0.4,而其他数据库中组件的版本并没有升级,仍然是10.2.0.1。
在MOS文档ORA-12012 ORA-27468: "SYS.PURGE_LOG" is Locked by Another Process [ID 751884.1]中介绍了这个错误,这个问题可能发生在10.2.0.2到10.2.0.5之间,解决问题的方法很简单,在闲时执行catbupgrd.sql,完成升级组件的后续操作既可。
客户的10.2.0.4 RAC for Hp-un环境碰到了这个错误。
错误信息为:
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_11261.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_32036.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_5935.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_5026.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_7620.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:08 2012
Trace dumping is performing id=[cdmp_20120229194207]
Wed Feb 29 19:42:17 2012
Trace dumping is performing id=[cdmp_20120229194217]
这个ORA-600[qersqCloseRem-2]错误非常罕见,在MOS上居然没有任何记载。不过从tb错误信息进行进一步的分析,这个错误发生在远端数据库的访问异常。
检查进一步的详细信息:
*** 2012-02-29 19:42:05.564
ksedmp: internal or fatal error
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Current SQL statement for this session:
SELECT ACCESS_LOG_SEQUENCE.NEXTVAL@WEBDB.COM FROM DUAL
----- PL/SQL Call Stack -----
object line object
handle number name
0x39b5c3720 5 ECOMMERCE.P_USER_AT
----- Call Stack Trace -----
calling call entry argument values in hex
location type point (? means dubious value)
-------------------- -------- -------------------- ----------------------------
ksedst()+31 call ksedst1() 000000000 ? 000000001 ?
7FBFFF4370 ? 7FBFFF43D0 ?
7FBFFF4310 ? 000000000 ?
ksedmp()+610 call ksedst() 000000000 ? 000000001 ?
7FBFFF4370 ? 7FBFFF43D0 ?
7FBFFF4310 ? 000000000 ?
ksfdmp()+21 call ksedmp() 000000003 ? 000000001 ?
7FBFFF4370 ? 7FBFFF43D0 ?
7FBFFF4310 ? 000000000 ?
.
.
.
0059DF200 ? 683F6E400000001 ?
main()+116 call opimai_real() 000000002 ? 7FBFFFF4E0 ?
000000004 ? 7FBFFFF478 ?
0059DF200 ? 683F6E400000001 ?
__libc_start_main() call main() 000000002 ? 7FBFFFF4E0 ?
+219 000000004 ? 7FBFFFF478 ?
0059DF200 ? 683F6E400000001 ?
_start()+42 call __libc_start_main() 0007139F8 ? 000000002 ?
7FBFFFF628 ? 0052B4BD0 ?
000000000 ? 000000002 ?
--------------------- Binary Stack Dump ---------------------
从详细TRACE分析,在问题发生时刻,正在通过数据库链读取远端序列的值。而此时出现的ORA-3113通信错误,多半与远端数据库状态异常有关。
检查远端数据库的告警日志,果然发现在问题出现时刻,数据库状态异常并最终导致了实例重启:
Wed Feb 29 19:39:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:39:30 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
.
.
.
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:30 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:45:26 2012
PMON failed to acquire latch, see PMON dump
Wed Feb 29 19:46:32 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:33 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:34 2012
PMON failed to acquire latch, see PMON dump
Wed Feb 29 19:46:40 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:43 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:44 2012
Errors in file /opt/app/oracle/admin/orcl/bdump/orcl1_asmb_14614.trc:
ORA-15064: communication failure with ASM instance
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:46:44 2012
ASMB: terminating instance due to error 15064
Wed Feb 29 19:46:44 2012
System state dump is made for local instance
System State dumped to trace file /opt/app/oracle/admin/orcl/bdump/orcl1_diag_14555.trc
Wed Feb 29 19:46:47 2012
Shutting down instance (abort)
License high water mark = 1623
Wed Feb 29 19:46:49 2012
Instance terminated by ASMB, pid = 14614
Wed Feb 29 19:46:52 2012
Instance terminated by USER, pid = 3684
显然远端数据库状态异常是这个ORA-600错误的直接原因。
简单描述一下Oracle VM Server的安装过程。
需要注意,VM 3.0以上版本才支持升级操作,因此在VM 2.2没有办法升级到当前版本,安装VM 3.0将会删除服务器上所有的数据。
将VM Server的光盘放入,并从光盘启动服务器。
在启动界面直输入Enter开始安装过程:
Oracle会提示是否监测截至,这里可以直接SKIP跳过;
键盘选择:选择us;
然后是版权声明,选择Accept后,开始正式的安装步骤;
如果服务器上没有系统,那么会直接进入后面的分区阶段,否则会提示重装系统还是在原有系统上升级;
选择ReInstall后,会显示当前系统磁盘分区信息,首先选择准备进行系统安装的分区,然后选择Remove all partitions and create a new defaultb partition layout,Oracle在格式化分区之前会要求再次确认,并询问是否预览分区空间详细配置,可以完全按照默认推荐值安装,因此这里可以跳过,也可以进入到分区空间修改页面进行自定义的修改;
随后选择Boot Loader配置,选择Master Boot Record;
然后选择一个管理网络接口,手工输入IP和掩码,在下一个页面输入网关、DNS信息,接着是主机名信息;
配置服务器所在时区,配置中找不到北京,可以tb设置Asia/Shanghai代替;
分别输入Agent密码和root密码后,安装操作完成,这是会提示整个安装的日志文件的位置。
在重启界面选择REBOOT,完成整个安装过程。
启动后,进入Oracle VM Server 3.0控制台界面,可以通过Alt + F2进入linux的登录界面。至此VM Server安装完成。
在VM Manager中搜索VM Server时出现这个错误。
按照VM Server以及VM Manager后,通过指定IP地址,让VM Manager自动寻找VM Server,结果JOB运行报错,详细的错误信息为:
Job Construction Phase
----------------------
begin()
Appended operation 'Discover Manager Server Discover' to object 'OVM Foundry : Discover Manager'.
commit()
Completed Step: COMMIT
Objects and Operations
----------------------
Object (IN_USE): [Server] 35:38:33:39:31:34:43:4e:47:31:33:30:53:37:33:42 (server2.zihexin.com)
Object (IN_USE): [DiscoverManager] OVM Foundry : Discover Manager
Operation: Discover Manager Server Discover
Job Running Phase at 18:05 on Fri, Nov 25, 2011
----------------------------------------------
Job Participants: []
Actioner
--------
Starting operation 'Discover Manager Server Discover' on object 'OVM Foundry : Discover Manager'
Setting Context to model only in job with id=1322215534120
Job Internal Error (Operation)com.oracle.ovm.mgr.api.exception.FailedOperationException: OVMAPI_4010E Attempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:474)
at com.oracle.ovm.mgr.action.ActionEngine.sendDiscoverCommand(ActionEngine.java:283)
at com.oracle.ovm.mgr.action.ServerAction.getServerInfo(ServerAction.java:95)
at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:131)
at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:61)
at com.oracle.ovm.mgr.discover.ovm.DiscoverHandler.execute(DiscoverHandler.java:50)
at com.oracle.ovm.mgr.discover.DiscoverEngine.handleDiscover(DiscoverEngine.java:435)
at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverNewServer(DiscoverEngine.java:345)
at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverServer(DiscoverEngine.java:265)
at com.oracle.ovm.mgr.op.manager.DiscoverManagerServerDiscover.action(DiscoverManagerServerDiscover.java:48)
at com.oracle.ovm.mgr.api.job.JobEngine.operationActioner(JobEngine.java:191)
at com.oracle.ovm.mgr.api.job.JobEngine.objectActioner(JobEngine.java:257)
at com.oracle.ovm.mgr.api.job.InternalJobDbImpl.objectCommitter(InternalJobDbImpl.java:1019)
at com.oracle.odof.core.AbstractVessel.invokeMethod(AbstractVessel.java:223)
at com.oracle.odof.core.BasicWork.invokeMethod(BasicWork.java:136)
at com.oracle.odof.command.InvokeMethodCommand.process(InvokeMethodCommand.java:100)
at com.oracle.odof.core.BasicWork.processCommand(BasicWork.java:81)
at com.oracle.odof.core.TransactionManager.processCommand(TransactionManager.java:751)
at com.oracle.odof.core.WorkflowManager.processCommand(WorkflowManager.java:395)
at com.oracle.odof.core.WorkflowManager.processWork(WorkflowManager.java:453)
at com.oracle.odof.io.AbstractClient.run(AbstractClient.java:42)
at java.lang.Thread.run(Thread.java:662)
Caused by: com.oracle.ovm.mgr.api.exception.IllegalOperationException: OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
at com.oracle.ovm.mgr.action.ActionEngine.sendAction(ActionEngine.java:752)
at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:470)
... 24 more
FailedOperationCleanup
----------
Starting failed operation 'Discover Manager Server Discover' cleanup on object 'OVM Foundry : Discover Manager'
Complete rollback operation 'Discover Manager Server Discover' completed with direction=OVM Foundry : Discover Manager
Rollbacker
----------
Objects To Be Rolled Back
-------------------------
Object (IN_USE): [Server] 35:38:33:39:31:34:43:4e:47:31:33:30:53:37:33:42 (server2.zihexin.com)
Object (IN_USE): [DiscoverManager] OVM Foundry : Discover Manager
Completed Step: ROLLBACK
Job failed commit (internal) due to OVMAPI_4010E Attempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
com.oracle.ovm.mgr.api.exception.FailedOperationException: OVMAPI_4010E Atbtempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:474)
at com.oracle.ovm.mgr.action.ActionEngine.sendDiscoverCommand(ActionEngine.java:283)
at com.oracle.ovm.mgr.action.ServerAction.getServerInfo(ServerAction.java:95)
at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:131)
at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:61)
at com.oracle.ovm.mgr.discover.ovm.DiscoverHandler.execute(DiscoverHandler.java:50)
at com.oracle.ovm.mgr.discover.DiscoverEngine.handleDiscover(DiscoverEngine.java:435)
at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverNewServer(DiscoverEngine.java:345)
at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverServer(DiscoverEngine.java:265)
at com.oracle.ovm.mgr.op.manager.DiscoverManagerServerDiscover.action(DiscoverManagerServerDiscover.java:48)
at com.oracle.ovm.mgr.api.job.JobEngine.operationActioner(JobEngine.java:191)
at com.oracle.ovm.mgr.api.job.JobEngine.objectActioner(JobEngine.java:257)
at com.oracle.ovm.mgr.api.job.InternalJobDbImpl.objectCommitter(InternalJobDbImpl.java:1019)
at sun.reflect.GeneratedMethodAccessor1001.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.oracle.odof.core.AbstractVessel.invokeMethod(AbstractVessel.java:223)
at com.oracle.odof.core.BasicWork.invokeMethod(BasicWork.java:136)
at com.oracle.odof.command.InvokeMethodCommand.process(InvokeMethodCommand.java:100)
at com.oracle.odof.core.BasicWork.processCommand(BasicWork.java:81)
at com.oracle.odof.core.TransactionManager.processCommand(TransactionManager.java:751)
at com.oracle.odof.core.WorkflowManager.processCommand(WorkflowManager.java:395)
at com.oracle.odof.core.WorkflowManager.processWork(WorkflowManager.java:453)
at com.oracle.odof.io.AbstractClient.run(AbstractClient.java:42)
at java.lang.Thread.run(Thread.java:662)
Caused by: com.oracle.ovm.mgr.api.exception.IllegalOperationException: OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
at com.oracle.ovm.mgr.action.ActionEngine.sendAction(ActionEngine.java:752)
at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActbionEngine.java:470)
... 24 more
----------
End of Job
----------
由于关键性信息确实,所以无法判断导致错误的原因。即使是在metalink或GOOGLE中查询,也得不到任何有价值的信息。
虽然在VM Manager中得不到有意义的信息,但是在VM Server上,却可以得到更详细的信息,通过检查var/log/ovs-agent.log文件,获取到下面的信息:
[2011-04-16 13:21:46 25970] ERROR (OVSAgentServer:108) Unauthorized access attempt from ('10.0.10.173', 59424)!
Traceback (most recent call last):
File "/opt/ovs-agent-3.0/OVSAgentServer.py", line 103, in do_POST
auth(username, password)
File "/opt/ovs-agent-3.0/OVSAgentServer.py", line 42, in auth
raise Exception('Authorization failed: user does not exist or password error.')
Exception: Authorization failed: user does not exist or password error.
[2011-04-16 13:21:46 25970] INFO (OVSAgentServer:169) code 403, message Unauthorized access attempt from ('10.0.10.173', 59424)!
这次信息就明确多了,显然是由于VM Manager中配置的密码不正确所致,在VM Server上修改oracle用户密码:
[root@server2 ~]# ovs-agent-passwd oracle
Password:
Again:
在搜索VM Server时使用这里修改的密码,VM Manager成功的发现了VM Server信息。
一个客户碰到的具体需求,分区表中有些分区所在的表空间被OFFLINE,tb导致在删除统计信息时报错。
下面通过例子来说明这个问题:
SQL> create table t_part_read (id number)
2 partition by range (id)
3 (partition p1 values less than (10) tablespace ts1,
4 partition p2 values less than (20) tablespace ts2,
5 partition pmax values less than (maxvalue) tablespace users);
Table created.
SQL> insert into t_part_read select rownum from tab;
54 rows created.
SQL> commit;
Commit complete.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
PL/SQL procedure successfully completed.
SQL> alter tablespace ts1 read only;
Tablespace altered.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
PL/SQL procedure successfully completed.
SQL> alter tablespace ts1 offline;
Tablespace altered.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART_READ'); END;
*
ERROR at line 1:
ORA-00376: file 6 cannot be read at this time
ORA-01110: data file 6: '/u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts1_7w8l5fz1_.dbf'
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 1
如果将表空间只读,并不会影响到表空间上的表或分区的统计信息的收集,因为收集过程只是读取,而收集的结果信息是写到SYSTEM表空间的。
但是如果分区所在的表空间处于OFFLINE状态,那么在统计信息收集的过程中就会报错。
有一个很简单的方法可以解决这个问题,就是将被OFFLINE影响的分区的统计信息锁定,这样Oracle在收集统计信息时就会跳过锁定的分区,通过这个办法就可以避免统计信息收集过程中的报错:
SQL> exec dbms_stats.lock_partition_stats(user, 'T_PART_READ', 'P1')
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART_READ'); END;
*
ERROR at line 1:
ORA-00376: file 6 cannot be read at this time
ORA-01110: data file 6: '/u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts1_7w8l5fz1_.dbf'
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 1
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ', granularity => 'PARTITION')
PL/SQL procedure successfully completed.
即使锁定分区后,尝试收集统计信息仍然报错,这是因为Oracle默认除了要收集分区上的统计信息以外,还要收集表级的统计信息,而这就会造成被OFFLINE影响的分区也要被读取。
解决方法就是在收集统计信息的时候指定收集的粒度是分区,不收集表上的GLOBAL信息。
当用户密码即将过期时,在登录时Oracle会提示ORA-28002错误,但是并不会影响正常的登录。
本来认为这个信息并没有太大的影响,但是没想到这个tb错误会导致SET AUTOTRACE功能失效:
solaris*orcl-/home/oracle$ sqlplus test/test
SQL*Plus: Release 11.2.0.3.0 Production on Fri Jul 13 11:27:28 2012
Copyright (c) 1982, 2011, Oracle. All rights reserved.
ERROR:
ORA-28002: the password will expire within 1 days
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
SQL> set autot on
ERROR:
ORA-28002: the password will expire within 1 days
SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report
SQL> alter user test identified by test;
User altered.
SQL> set autot on
ERROR:
ORA-24315: illegal attribute type
SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report
SQL> exit
Disconnected from Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
solaris*orcl-/home/oracle$ sqlplus test/test
SQL*Plus: Release 11.2.0.3.0 Production on Fri Jul 13 11:27:52 2012
Copyright (c) 1982, 2011, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
SQL> set autot on
SQL> insert into t values (1, 'a');
1 row created.
Execution Plan
----------------------------------------------------------
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 1 | 12 | 1 (0)| 00:00:01 |
| 1 | LOAD TABLE CONVENTIONAL | T | | | | |
---------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
71 recursive calls
9 db block gets
75 consistent gets
10 physical reads
1284 redo size
829 bytes sent via SQL*Net to client
785 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
9 sorts (memory)
0 sorts (disk)
1 rows processed
很显然,由于ORA-28002错误导致了SET AUTOTRACE ON功能启用时碰到了错误。当修改了当前的用户密码,则ORA-28002不再出现后,SET AUTOTRACE ON的功能恢复正常。
根据上面的信息其实可以判断,在启用SET AUTOTRACE ON功能时,sqlplus会自动创建一个新的会话来记录当前会话的统计信息。
而启用的新的会话会使用当前会话登录时保存的密码来进行登录,因此可以通过下面的例子来验证这个推论:
solaris*orcl-/home/oracle$ sqlplus test/test
SQL*Plus: Release 11.2.0.3.0 Production on Sun Jul 15 01:28:38 2012
Copyright (c) 1982, 2011, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
SQL> set autot on
SQL> set autot off
SQL> alter user test identified by test1;
User altered.
SQL> set autot on
ERROR:
ORA-01017: invalid username/password; logon denied
SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report
果然,在用户登录后,如果当前的密码被修改,是会导致SET AUTOTRACE ON启用时报错不正确的用户名密码错误的。
Oracle10g的DBA_TAB_STATISTICS视图的STATTYPE_LOCKED列没有tb正确的显示结果。
看一个简单的例子:
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE 10.2.0.5.0 Production
TNS for Linux: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production
SQL> create table t_part (id number, name varchar2(30))
2 partition by range (id)
3 (partition p1 values less than (10),
4 partition p2 values less than (20),
5 partition pmax values less than (maxvalue));
Table created.
SQL> select table_name, partition_name, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';
TABLE_NAME PARTITION_NAME STATT
------------------------------ ------------------------------ -----
T_PART
T_PART P1
T_PART P2
T_PART PMAX
SQL> exec dbms_stats.lock_partition_stats(user, 'T_PART', 'P1')
PL/SQL procedure successfully completed.
SQL> select table_name, partition_name, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';
TABLE_NAME PARTITION_NAME STATT
------------------------------ ------------------------------ -----
T_PART
T_PART P1
T_PART P2
T_PART PMAX
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART')
PL/SQL procedure successfully completed.
SQL> select table_name, partition_name, last_analyzed, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';
TABLE_NAME PARTITION_NAME LAST_ANAL STATT
------------------------------ ------------------------------ --------- -----
T_PART 16-JUL-12
T_PART P1
T_PART P2 16-JUL-12
T_PART PMAX 16-JUL-12
可以看到在10.2环境中,LOCK_PARTITION_STATS过程是正常工作的,但是DBA_TAB_STATISTICS视图的STATTYPE_LOCKED列并没有正确的显示分区被锁定的结果。
而对于表来说,LOCK_TABLE_STATS过程执行后,STATTYPE_LOCKED的结果显示是正常的:
SQL> exec dbms_stats.lock_table_stats(user, 'T_PART')
PL/SQL procedure successfully completed.
SQL> select table_name, partition_name, last_analyzed, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';
TABLE_NAME PARTITION_NAME LAST_ANAL STATT
------------------------------ ------------------------------ --------- -----
T_PART 16-JUL-12 ALL
T_PART P1 ALL
T_PART P2 16-JUL-12 ALL
T_PART PMAX 16-JUL-12 ALL
这说明在10.2中,Oracle对于分区列的锁定的支持是存在问题的。查询了一下MOS,Oracle将这个问题确认为内部BUG:7240460,这个问题在11.1.0.7中被FIXED。
而在11.2中,这个问题以及不存在了:
SQL> select * from v$version;
BANNER
----------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for Solaris: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
SQL> select owner, table_name, partition_name, stattype_locked
2 from dba_tab_statistics
3 where wner = 'TEST'
4 and table_name = 'T_PART';
OWNER TABLE_NAME PARTITION_NAME STATT
---------- ------------ --------------- -----
TEST T_PART
TEST T_PART P2
TEST T_PART P3
TEST T_PART P4
TEST T_PART P5
TEST T_PART PMAX
6 rows selected.
SQL> exec dbms_stats.lock_partition_stats('TEST', 'T_PART', 'P2')
PL/SQL procedure successfully completed.
SQL> select owner, table_name, partition_name, stattype_locked
2 from dba_tab_statistics
3 where wner = 'TEST'
4 and table_name = 'T_PART';
OWNER TABLE_NAME PARTITION_NAME STATT
---------- ------------ --------------- -----
TEST T_PART
TEST T_PART P2 ALL
TEST T_PART P3
TEST T_PART P4
TEST T_PART P5
TEST T_PART PMAX
6 rows selected.
Oracle10g的DBA_TAB_STATISTICS视图的STATTYPE_LOCKED列没有正确的显示结果。
10g中DBA_TAB_STATISTICS的STATTYPE_LOCKED列对分区锁定显示为空:http://yangtingkun.net/?p=1023
上文提到了DBA_TAB_STATISTICS中的STATTYPE_LOCKED列在10g中对于分tb区锁定统计信息显示为空,那么在10g中有没有办法获取到正确的结果呢:
SQL> select table_name, partition_name, last_analyzed, stattype_locked
2 from dba_tab_statistics
3 where wner = user
4 and table_name = 'T_PART';
TABLE_NAME PARTITION_NAME LAST_ANAL STATT
------------------------------ ------------------------------ --------- -----
T_PART 16-JUL-12
T_PART P1
T_PART P2 16-JUL-12
T_PART PMAX 16-JUL-12
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART', partname => 'P1')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART', partname => 'P1'); END;
*
ERROR at line 1:
ORA-20005: object statistics are locked (stattype = ALL)
ORA-06512: at "SYS.DBMS_STATS", line 15027
ORA-06512: at "SYS.DBMS_STATS", line 15049
ORA-06512: at line 1
显然虽然Oracle在DBA_TAB_STATISTICS视图中没有正确的显示分区的锁定状态,但是Oracle在内部确实记录了分区的锁定状态,既然Oracle记录了这个信息,就有办法将这个信息显示出来。
既然11g能够显示该列的值,最简单的方法莫过于对比10g和11g中DBA_TAB_STATISTICS视图的区别,10g视图的结果:
SQL> select text from dba_views where view_name = 'DBA_TAB_STATISTICS';
TEXT
--------------------------------------------------------------------------------
SELECT /* TABLES */
u.name, o.name, NULL, NULL, NULL, NULL, 'TABLE', t.rowcnt,
.
.
.
decode(bitand(t.trigflag, 67108864) + bitand(t.trigflag, 134217728),
0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
FROM
sys.user$ u, sys.obj$ o, sys.tab$ t, sys.tab_stats$ ts, sys.mon_mods_all$ m
WHERE
.
.
.
UNION ALL
SELECT /* PARTITIONS, NOT IOT */
u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
FROM
sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab_stats$ ts, sys.tab$ tab,
sys.mon_mods_all$ m
WHERE
.
.
.
UNION ALL
SELECT /* IOT Partitions */
u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
FROM
sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab, sys.mon_mods_all$ m
WHERE
.
.
.
UNION ALL
SELECT /* COMPOSITE PARTITIONS */
u.name, o.name, o.subname, tcp.part#, NULL, NULL, 'PARTITION',
.
.
.
decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
FROM
sys.user$ u, sys.obj$ o, sys.tabcompartv$ tcp,
sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
WHERE
.
.
.
UNION ALL
SELECT /* SUBPARTITIONS */
u.name, po.name, po.subname, tcp.part#, so.subname, tsp.subpart#,
.
.
.
decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
FROM
sys.user$ u, sys.obj$ po, sys.obj$ so, sys.tabcompartv$ tcp,
sys.tabsubpartv$ tsp, sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
WHERE
.
.
.
UNION ALL
SELECT /* FIXED TABLES */
'SYS', t.kqftanam, NULL, NULL, NULL, NULL, 'FIXED TABLE',
.
.
.
对比一下11g的查询结果tb:
SQL> select text from dba_views where view_name = 'DBA_TAB_STATISTICS';
TEXT
--------------------------------------------------------------------------------
SELECT /* TABLES */
u.name, o.name, NULL, NULL, NULL, NULL, 'TABLE', t.rowcnt,
.
.
.
decode(bitand(t.trigflag, 67108864) + bitand(t.trigflag, 134217728),
0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
FROM
sys.user$ u, sys.obj$ o, sys.tab$ t, sys.tab_stats$ ts, sys.mon_mods_all$ m
WHERE
.
.
.
UNION ALL
SELECT /* PARTITIONS, NOT IOT */
u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
decode(
/*
* Following decode returns 1 if DATA stats locked for partition
* or at table level
*/
decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
/*
* Following decode returns 2 if CACHE stats locked for partition
* or at table level
*/
decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
/* if 0 => not locked, 3 => data and cache stats locked */
0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
FROM
sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab_stats$ ts, sys.tab$ tab,
sys.mon_mods_all$ m
.
.
.
UNION ALL
SELECT /* IOT Partitions */
u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
decode(
/*
* Following decode returns 1 if DATA stats locked for partition
* or at table level
*/
decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
/*
* Following decode returns 2 if CACHE stats locked for partition
* or at table level
*/
decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
/* if 0 => not locked, 3 => data and cache stats locked */
0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
FROM
sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab, sys.mon_mods_all$ m
WHERE
.
.
.
UNION ALL
SELECT /* COMPOSITE PARTITIONS */
u.name, o.name, o.subname, tcp.part#, NULL, NULL, 'PARTITION',
.
.
.
decode(
/*
* Following decode returns 1 if DATA stats locked for partition
* or at table level
*/
decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
/*
* Following decode returns 2 if CACHE stats locked for partition
* or at table level
*/
decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
/* if 0 => not locked, 3 => data and cache stats locked */
0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
FROM
sys.user$ u, sys.obj$ o, sys.tabcompartv$ tcp,
sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
WHERE
.
.
.
UNION ALL
SELECT /* SUBPARTITIONS */
u.name, po.name, po.subname, tcp.part#, so.subname, tsp.subpart#,
.
.
.
decode(
/*
* Following decode returns 1 if DATA stats locked for partition
* or at table level.
* Note that dbms_stats does n't allow locking subpartition stats.
* If the composite partition is locked, all subpartitions are
* considered locked. Hence decode checks for tcp entry.
*/
decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
/*
* Following decode returns 2 if CACHE stats locked for partition
* or at table level
*/
decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
/* if 0 => not locked, 3 => data and cache stats locked */
0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
FROM
sys.user$ u, sys.obj$ po, sys.obj$ so, sys.tabcompartv$ tcp,
sys.tabsubpartv$ tsp, sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
WHERE
.
.
.
UNION ALL
SELECT /* FIXED TABLES */
.
.
显然在11g中Oracle对于分区锁定的显示采用了新的算法,那么可以仿照11g中建立一个视图,来解决10g中分区显示存在错误的问题:
SQL> CREATE OR REPLACE VIEW DBA_TAB_STATISTICS_LOCK
2 (OWNER, TABLE_NAME, PARTITION_NAME,
3 SUBPARTITION_NAME, OBJECT_TYPE, STATTYPE_LOCKED)
4 AS
5 SELECT u.name, o.name, NULL, NULL, 'TABLE',
6 decode(bitand(t.trigflag, 67108864) + bitand(t.trigflag, 134217728),
7 0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL')
8 FROM sys.user$ u, sys.obj$ o, sys.tab$ t
9 WHERE o.owner# = u.user#
10 and o.obj# = t.obj#
11 and bitand(t.property, 1) = 0
12 and o.subname IS NULL
13 and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
14 and bitand(o.flags, 128) = 0
15 UNION ALL
16 SELECT u.name, o.name, o.subname, NULL, 'PARTITION',
17 decode(
18 decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
19 decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
20 0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
21 FROM sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab
22 WHERE o.owner# = u.user#
23 and o.obj# = tp.obj#
24 and tp.bo# = tab.obj#
25 and bitand(tab.property, 64) = 0
26 and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
27 and bitand(o.flags, 128) = 0
28 UNION ALL
29 SELECT u.name, o.name, o.subname, NULL, 'PARTITION',
30 decode(
31 decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
32 decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
33 0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
34 FROM sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab
35 WHERE o.owner# = u.user#
36 and o.obj# = tp.obj#
37 and tp.bo# = tab.obj#
38 and bitand(tab.property, 64) = 64
39 and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
40 and bitand(o.flags, 128) = 0
41 UNION ALL
42 SELECT u.name, o.name, o.subname, NULL, 'PARTITION',
43 decode(
44 decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
45 decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
46 0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
47 FROM sys.user$ u, sys.obj$ o, sys.tabcompartv$ tcp, sys.tab$ tab
48 WHERE o.owner# = u.user#
49 and o.obj# = tcp.obj#
50 and tcp.bo# = tab.obj#
51 and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
52 and bitand(o.flags, 128) = 0
53 UNION ALL
54 SELECT u.name, po.name, po.subname, so.subname, 'SUBPARTITION',
55 decode(
56 decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
57 decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
58 0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
59 FROM sys.user$ u, sys.obj$ po, sys.obj$ so, sys.tabcompartv$ tcp, sys.tabsubpartv$ tsp, sys.tab$ tab
60 WHERE so.obj# = tsp.obj#
61 and po.obj# = tcp.obj#
62 and tcp.obj# = tsp.pobj#
63 and tcp.bo# = tab.obj#
64 and u.user# = po.owner#
65 and bitand(tab.property, 64) = 0
66 and po.namespace = 1 and po.remoteowner IS NULL and po.linkname IS NULL
67 and bitand(po.flags, 128) = 0
68 ;
View created.
SQL> select table_name, partition_name, object_type, stattype_locked
2 from dba_tab_statistics_lock
3 where wner = 'TEST'
4 and table_name = 'T_PART';
TABLE_NAME PARTITION_NAME OBJECT_TYPE STATT
------------------------------ ------------------------------ ------------ -----
T_PART TABLE
T_PART P1 PARTITION ALL
T_PART P2 PARTITION
T_PART PMAX PARTITION
使用新创建的这个视图,就可以解决锁定分区的统计信息显示问题。
碰到一个有意思的问题,如果分区表执行过SET UNUSED操作,那么是否还可以进行分区的EXCHANGE操作。
一个简单的测试就可以说明这个问题:
SQL> create table t_part_unused
2 (id number, name varchar2(30), other varchar2(30))
3 partition by range (id)
4 (partition p1 values less than (10),
5 partition pmax values less than (maxvalue));
Table created.
SQL> insert into t_part_unused
2 select rownum, table_name, 'abc'
3 from user_tables;
48 rows created.
SQL> commit;
Commit complete.
SQL> alter table t_part_unused set unused (other);
Table altered.
SQL> desc t_part_unused
Name Null? Type
---------------------------------------- -------- ------------------------
ID NUMBER
NAME VARCHAR2(30)
SQL> create table t_temp_unused as
2 select *
3 from t_part_unused
4 where 1 = 2;
Table created.
SQL> desc t_temp_unused
Name Null? Type
---------------------------------------- -------- ------------------------
ID NUMBER
NAME VARCHAR2(30)
SQL> alter table t_part_unused
2 exchange partition p1
3 with table t_temp_unused;
with table t_temp_unused
*
ERROR at line 3:
ORA-14097: column type or size mismatch in ALTER TABLE EXCHANGE PARTITION TB
SQL> alter table t_temp_unused add (other varchar2(30));
Table altered.
SQL> alter table t_part_unused
2 exchange partition p1
3 with table t_temp_unused;
with table t_temp_unused
*
ERROR at line 3:
ORA-14096: tables in ALTER TABLE EXCHANGE PARTITION must have the same number of columns
SQL> alter table t_temp_unused set unused (other);
Table altered.
SQL> alter table t_part_unused
2 exchange partition p1
3 with table t_temp_unused;
Table altered.
很明显执行了SET UNUSED操作后的表,和普通的表是存在区别的,这种区别导致要求进行EXCHANGE的表必须同样执行SET UNUSED操作,否则就无法执行EXCHANGE的操作。
当目标表中不包含SETE UNUSED的列时,EXCHANGE操作会出现ORA-14097的错误,而如果把列添加到目标表,则会报错ORA-14096,必须在目标表同样对列执行SET UNUSED操作,才能通过EXCHANGE之前的检查。
其实这也不难理解,执行SET UNUSED命令后,数据字典虽然发生了改变,但是表上的数据并没有删除,而EXCHANGE操作只是将两个段的数据字典进行互换,因此如果目标表上缺少SET UNUSED列,是无法执行EXCHANGE操作的。
解决问题的方法有两个,第一个就是例子中展示的可以在目标表上建立列然后同样的执行SET UNUSED操作;另外的一个方法就是对于SET UNUSED列执行DROP COLUMN操作,彻底删除该列。
如何使用ROWNUM是个老生常谈的问题了,本来没有打算专门强调这个问题,但是最近在看Oracle的官方PL/SQL文档时发现了一个严重的错误,借这个机会还是简单说一下。
首先来看Oracle文档的描述,在10.2的PL/SQL文档中,Oracle关于PL/SQL中直接使用SELECT的查询描述为:
Selecting At Most One Row: SELECT INTO Statement
If you expect a query to only return one row, you can write a regular SQL SELECT statement with an additional INTO clause specifying the PL/SQL variable to hold the result.
If the query might return more than one row, but you do not care about tb values after the first, you can restrict any result set to a single row by comparing the ROWNUM value. If the query might return no rows at all, use an exception handler to specify any actions to take when no data is found.
这个描述是没有问题的,但是到了11.2中,文档的描述变成了:
Single-Row Result Sets
If you expect the query to return only one row, then use the SELECT INTO statement to store values from that row in either one or more scalar variables (see "Assigning Values to Variables with the SELECT INTO Statement") or one record variable (see "SELECT INTO Statement for Assigning Row to Record Variable").
If the query might return multiple rows, but you care about only the nth row, then restrict the result set to that row with the clause WHERE ROWNUM=n. For more information about the ROWNUM pseudocolumn, see Oracle Database SQL Language Reference.
第一个反应是不是我看错了,居然可以通过WHERE ROWNUM = N来限制只返回第N条记录 ,再仔细看了一遍,并和10g的文档对比,发现11.2和10.2中的不同。于是第二个反应是Oracle在11.2中提供了新特性,使得PL/SQL语句中直接SELECT可以通过WHERE ROWNUM来直接控制游标,于是特意在11.2上进行了测试,发现结果和10.2上没有区别,ROWNUM = N是行不通的,除非N等于1。
SQL> select * from v$version;
BANNER
------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for 32-bit Windows: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production
SQL> select * from tab;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
COMPANIES TABLE
MLOG$_T_F TABLE
MLOG$_T_P TABLE
MV_T_ORACLE TABLE
SERVICES TABLE
SERVICE_RATES TABLE
SERVICE_USAGE TABLE
SERVICE_USERS TABLE
T TABLE
T_DEFER TABLE
T_F TABLE
T_LOAD_LOB TABLE
T_P TABLE
T_PART TABLE
已选择14行。
SQL> set serverout on
SQL> declare
2 v_name varchar2(30);
3 begin
4 select tname into v_name from tab where rownum = 5;
5 dbms_output.put_line(v_name);
6 exception
7 when no_data_found then
8 dbms_output.put_line('rownum equal the num bigger than 1 is incorrect!');
9 end;
10 /
rownum equal the num bigger than 1 is incorrect!
PL/SQL过程已成功完成。
显然Oracle文档这里出现了严重的错误,如果要使用ROWNUM来控制返回第几行结果,那么至少需要2层嵌套查询才可以。
最后简单总结一下ROWNUM,很多人都知道ROWNUM只适用于小于或小于等于,如果进行等于判断,那么只能等于1,不能进行大于的比较。但是却并不了解造成这种限制条件的机制是什么。
其实ROWNUM的返回很简单,ROWNUM总是从1开始,不管当前的记录是否满足查询结果,ROWNUM返回的值都是1,如果这条记录的值最终满足所有的条件,那么ROWNUM会递加,下一条记录的ROWNUM会返回2,否则下一条记录的ROWNUM仍然返回1。
理解了这一点,就清楚为什么一般的ROWNUM大于某个值或等于某个不为1的值是无法返回结果的,因此对于每条记录的ROWNUM都是1,而ROWNUM为1不满足查询的结果,所以下一条记录的ROWNUM不会递增,仍然是1,因此所有的记录都不满足条件。
了解了原理,就可以很容易的写出ROWNUM大于某值的例子:
SQL> select * from tab where rownum = 1 or rownum > 1;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
COMPANIES TABLE
MLOG$_T_F TABLE
MLOG$_T_P TABLE
MV_T_ORACLE TABLE
SERVICES TABLE
SERVICE_RATES TABLE
SERVICE_USAGE TABLE
SERVICE_USERS TABLE
T TABLE
T_DEFER TABLE
T_F TABLE
T_LOAD_LOB TABLE
T_P TABLE
T_PART TABLE
朋友解决一个物化视图刷新时碰到的问题。
数据库版本为10.2.0.4,一次本地聚集物化视图的快速刷新执行了3个小时后出现了临时表空间不足的错误:
ORA-12008: error in materialized view refresh path
ORA-01652: unable to extend temp segment by 32 in tablespace TEMP01
这个物化视图以前的刷新是正常的,只是最近偶尔会出现这个错误。上次出现这个错误,tb通过添加临时数据文件并重启数据库解决了问题。目前数据库的临时表空间已经超过1T的大小,如果不找到问题的原因,仅靠通过添加临时文件显然是不现实的。
通过跟踪刷新物化视图的会话,发现问题确实出在物化视图的快速刷新操作上,而会话的等待事件主要集中在临时表空间的写操作上:direct path write temp。
做了一个awrsql报告,检查了SQL语句和执行计划。由于这个SQL本身相对比较复杂,这就使得快速刷新的MERGE语句更加复杂,简单格式化后,语句长度超过300行,这里就不列出来了。这个SQL的执行计划为:
Execution Plan
Id Operation Name Rows Bytes TempSpc Cost (%CPU) Time
0 MERGE STATEMENT 129K(100)
1 MERGE MV_NW_KHXX_YDKH_ALL
2 VIEW
3 NESTED LOOPS OUTER 4 1592 129K (1) 00:38:46
4 VIEW 4 1084 129K (1) 00:38:46
5 TEMP TABLE TRANSFORMATION
6 LOAD AS SELECT
7 VIEW 33 3300 123 (1) 00:00:03
8 WINDOW SORT 33 6600 123 (1) 00:00:03
9 TABLE ACCESS FULL MLOG$_MV_NW_KHXX_YDKH_2 33 6600 122 (0) 00:00:03
10 LOAD AS SELECT
11 VIEW 19151 2244K 3533 (1) 00:01:04
12 WINDOW SORT 19151 4114K 9376K 3533 (1) 00:01:04
13 TABLE ACCESS FULL MLOG$_MV_NW_KHXX_YDKH_1 19151 4114K 3169 (1) 00:00:58
14 SORT GROUP BY 4 1040 125K (1) 00:37:40
15 VIEW 4 1040 125K (1) 00:37:40
16 UNION-ALL
17 HASH JOIN 1 289 21023 (1) 00:06:19
18 HASH JOIN 1 220 20984 (1) 00:06:18
19 TABLE ACCESS FULL MLOG$_DW_YH_JBXX 1 171 20982 (1) 00:06:18
20 VIEW 33 1617 2 (0) 00:00:01
21 TABLE ACCESS FULL SYS_TEMP_0FD9D6744_E9EB0662 33 2013 2 (0) 00:00:01
22 VIEW 19151 1290K 38 (0) 00:00:01
23 TABLE ACCESS FULL SYS_TEMP_0FD9D6745_E9EB0662 19151 1514K 38 (0) 00:00:01
24 HASH JOIN ANTI 1 281 41338 (1) 00:12:25
25 HASH JOIN 1 267 41300 (1) 00:12:24
26 HASH JOIN 1 220 20984 (1) 00:06:18
27 TABLE ACCESS FULL MLOG$_DW_YH_JBXX 1 171 20982 (1) 00:06:18
28 VIEW 33 1617 2 (0) 00:00:01
29 TABLE ACCESS FULL SYS_TEMP_0FD9D6744_E9EB0662 33 2013 2 (0) 00:00:01
30 MAT_VIEW ACCESS FULL MV_NW_KHXX_YDKH_1 9008K 403M 20279 (1) 00:06:06
31 VIEW 19151 261K 38 (0) 00:00:01
32 TABLE ACCESS FULL SYS_TEMP_0FD9D6745_E9EB0662 19151 1514K 38 (0) 00:00:01
33 HASH JOIN ANTI 1 284 21437 (1) 00:06:26
34 HASH JOIN 1 270 21435 (1) 00:06:26
35 HASH JOIN 1 240 21020 (1) 00:06:19
36 TABLE ACCESS FULL MLOG$_DW_YH_JBXX 1 171 20982 (1) 00:06:18
37 VIEW 19151 1290K 38 (0) 00:00:01
38 TABLE ACCESS FULL SYS_TEMP_0FD9D6745_E9EB0662 19151 1514K 38 (0) 00:00:01
39 MAT_VIEW ACCESS FULL MV_NW_KHXX_YDKH_2 283K 8293K 413 (2) 00:00:08
40 VIEW 33 462 2 (0) 00:00:01
41 TABLE ACCESS FULL SYS_TEMP_0FD9D6744_E9EB0662 33 2013 2 (0) 00:00:01
42 HASH JOIN ANTI 1 276 41753 (1) 00:12:32
43 HASH JOIN ANTI 1 262 41714 (1) 00:12:31
44 HASH JOIN 1 248 41711 (1) 00:12:31
45 HASH JOIN 1 201 21396 (1) 00:06:26
46 TABLE ACCESS FULL MLOG$_DW_YH_JBXX 1 171 20982 (1) 00:06:18
47 MAT_VIEW ACCESS FULL MV_NW_KHXX_YDKH_2 283K 8293K 413 (2) 00:00:08
48 MAT_VIEW ACCESS FULL MV_NW_KHXX_YDKH_1 9008K 403M 20279 (1) 00:06:06
49 VIEW 33 462 2 (0) 00:00:01
50 TABLE ACCESS FULL SYS_TEMP_0FD9D6744_E9EB0662 33 2013 2 (0) 00:00:01
51 VIEW 19151 261K 38 (0) 00:00:01
52 TABLE ACCESS FULL SYS_TEMP_0FD9D6745_E9EB0662 19151 1514K 38 (0) 00:00:01
53 MAT_VIEW ACCESS BY INDEX ROWID MV_NW_KHXX_YDKH_ALL 1 127 2 (0) 00:00:01
54 INDEX UNIQUE SCAN I_SNAP$_MV_NW_KHXX_YDKH_AL 1 1 (0) 00:00:01
从执行计划上看出一些疑问,MLOG$_DW_YH_JBXX表的结果只有1行,而实际上这张表的大小超过100W。
在默认情况下,收集SCHEMA的统计信息是不会收集物化视图日志的,而且即使Oracle收集统计信息时可以收集物化视图日志的统计信息,对于当前的情况,也无济于事。因为当前刷新的物化视图是第一个嵌套物化视图,它建立在其他两个物化视图的基础上,也就是说,只有刷新了其他两个物化视图之后,对应的物化视图日志中才会有记录,而其他时候,物化视图日志中的记录都是0。
其实现在问题已经确定了,由于物化视图日志没有统计信息,Oracle认为物化视图日志中记录很少,产生了一个最外层为NESTED LOOP的执行计划,导致刷新效率十分低下。对于这种情况,其实可以通过一次完全刷新来解决问题,但是对于当前的情况,仍然是不可行的。因为这个物化视图是数据仓库系统中的一个中间结果表,下游还有很多物化视图以及其他系统依赖于当前物化视图的增量数据。一旦这个物化视图执行了完全刷新,就会导致所有依赖当前对象的下游物化视图的增量刷新变成了完全刷新。
当前快速刷新碰到的问题其实就是Oracle的默认策略认为物化视图日志中的数据量应该远小于基表的数据量,这样快速刷新才会有性能上的优势,但是当前情况下,物化视图日志的数据量和基表的数据量处于同一个数量级,因此缺少统计信息后,快速刷新的执行计划变得十分的低效。而如果采用完全刷新来解决当前物化视图的问题,那么实际上是把这个问题扩大到下游所有有依赖关系的物化视图上。
为了解决这个问题,首先想到的是optimizer_dynamic_samping参数,通过设置会话级的参数,控制物化视图日志刷新之前,进行详细的动态统计信息采样,使之可以得到一个适合的执行计划。
但是将optimizer_dynamic_samping设置为10后,发现对MLOG$_DW_YH_JBXX表不起任何作用,刷新的执行计划中,MLOG$_DW_YH_JBXX表的行数仍然为1。
看来没有别的办法,只有手工显示的对物化视图日志表执行统计信息的收集工作,当统计信息收集完成后,再次运行物化视图的快速刷新,结果用了不到10分钟的时间,物化视图就刷新成功了。
这次执行计划变为:
Execution Plan
Id Operation Name Rows Bytes TempSpc Cost (%CPU) Time
0 MERGE STATEMENT 141K(100)
1 MERGE MV_NW_KHXX_YDKH_ALL
2 VIEW
3 HASH JOIN OUTER 16997 6606K 4704K 141K (1) 00:42:19
4 VIEW 16997 4498K 129K (1) 00:38:50
5 TEMP TABLE TRANSFORMATION
6 LOAD AS SELECT
7 VIEW 33 3300 123 (1) 00:00:03
8 WINDOW SORT 33 6600 123 (1) 00:00:03
9 TABLE ACCESS FULL MLOG$_MV_NW_KHXX_YDKH_2 33 6600 122 (0) 00:00:03
10 LOAD AS SELECT
11 VIEW 19151 2244K 3533 (1) 00:01:04
12 WINDOW SORT 19151 4114K 9376K 3533 (1) 00:01:04
13 TABLE ACCESS FULL MLOG$_MV_NW_KHXX_YDKH_1 19151 4114K 3169 (1) 00:00:58
14 SORT GROUP BY 16997 4315K 125K (1) 00:37:44
15 VIEW 16997 4315K 125K (1) 00:37:44
16 UNION-ALL
17 HASH JOIN 267 50196 21078 (1) 00:06:20
18 HASH JOIN 191 26549 21076 (1) 00:06:20
19 TABLE ACCESS FULL MLOG$_DW_YH_JBXX 11859 810K 21037 (1) 00:06:19
20 VIEW 19151 1290K 38 (0) 00:00:01
21 TABLE ACCESS FULL SYS_TEMP_0FD9D674D_E9EB0662 19151 1514K 38 (0) 00:00:01
22 VIEW 33 1617 2 (0) 00:00:01
23 TABLE ACCESS FULL SYS_TEMP_0FD9D674C_E9EB0662 33 2013 2 (0) 00:00:01
24 HASH JOIN 16188 2845K 41394 (1) 00:12:26
25 VIEW 33 1617 2 (0) 00:00:01
26 TABLE ACCESS FULL SYS_TEMP_0FD9D674C_E9EB0662 33 2013 2 (0) 00:00:01
27 HASH JOIN RIGHT ANTI 11594 1483K 41391 (1) 00:12:26
28 VIEW 19151 261K 38 (0) 00:00:01
29 TABLE ACCESS FULL SYS_TEMP_0FD9D674D_E9EB0662 19151 1514K 38 (0) 00:00:01
30 HASH JOIN 11594 1324K 41352 (1) 00:12:25
31 TABLE ACCESS FULL MLOG$_DW_YH_JBXX 11859 810K 21037 (1) 00:06:19
32 MAT_VIEW ACCESS FULL MV_NW_KHXX_YDKH_1 9008K 403M 20279 (1) 00:06:06
33 HASH JOIN ANTI 9 1647 21492 (1) 00:06:27
34 HASH JOIN 9 1521 21490 (1) 00:06:27
35 HASH JOIN 191 26549 21076 (1) 00:06:20
36 TABLE ACCESS FULL MLOG$_DW_YH_JBXX 11859 810K 21037 (1) 00:06:19
37 VIEW 19151 1290K 38 (0) 00:00:01
38 TABLE ACCESS FULL SYS_TEMP_0FD9D674D_E9EB0662 19151 1514K 38 (0) 00:00:01
39 MAT_VIEW ACCESS FULL MV_NW_KHXX_YDKH_2 283K 8293K 413 (2) 00:00:08
40 VIEW 33 462 2 (0) 00:00:01
41 TABLE ACCESS FULL SYS_TEMP_0FD9D674C_E9EB0662 33 2013 2 (0) 00:00:01
42 HASH JOIN ANTI 533 93275 41808 (1) 00:12:33
43 HASH JOIN RIGHT ANTI 533 85813 41769 (1) 00:12:32
44 VIEW 33 462 2 (0) 00:00:01
45 TABLE ACCESS FULL SYS_TEMP_0FD9D674C_E9EB0662 33 2013 2 (0) 00:00:01
46 HASH JOIN 533 78351 41766 (1) 00:12:32
47 HASH JOIN 11594 1324K 41352 (1) 00:12:25
48 TABLE ACCESS FULL MLOG$_DW_YH_JBXX 11859 810K 21037 (1) 00:06:19
49 MAT_VIEW ACCESS FULL MV_NW_KHXX_YDKH_1 9008K 403M 20279 (1) 00:06:06
50 MAT_VIEW ACCESS FULL MV_NW_KHXX_YDKH_2 283K 8293K 413 (2) 00:00:08
51 VIEW 19151 261K 38 (0) 00:00:01
52 TABLE ACCESS FULL SYS_TEMP_0FD9D674D_E9EB0662 19151 1514K 38 (0) 00:00:01
53 MAT_VIEW ACCESS FULL MV_NW_KHXX_YDKH_ALL 1701K 206M 3888 (2) 00:01:10
可以看到,最外层的执行计划已经变成了HASH JOIN OUTER,而且虽然物化视图日志MLOG$_DW_YH_JBXX的统计信息仍然少了2个数量级,但是比原本的1条记录要靠谱多了。
至此,问题得以解决,不过为了避免这种情况的再次发生,最好的办法是将物化视图日志的统计信息收集工作放到物化视图刷新之前进行,这样可以确保物化视图的快速刷新可以得到最精确的统计信息,从而得到最优的执行计划。
很多程序员在写代码的时候往往都不注意代码的可读性,让别人在阅读代码时花费更多的时间。其实,只要程序员在写代码的时候,注意为代码加注释,并以合理的格式为代码加注释,这样就方便别人查看代码,也方便自己以后查看了。下面分享十个加注释的技巧:
1. 逐层注释
为每个代码块添加注释,并在每一层使用统一的注释方法和风格。例如:
针对每个类:包括摘要信息、作者信息、以及最近修改日期等;
针对每个方法:包括用途、功能、参数和返回值等。
在团队工作中,采用标准化的注释尤为重要。当然,使用注释规范和工具(例如C#里的XML,Java里的Javadoc)可以tb更好的推动注释工作完成得更好。
2. 使用分段注释
如果有多个代码块,而每个代码块完成一个单一任务,则在每个代码块前添加一个注释来向读者说明这段代码的功能。例子如下:
// Check that all data records
// are correct
foreach (Record record in records)
...{
if (rec.checkStatus()==Status.OK)
...{
. . .
}
}
// Now we begin to perform
// transactions
Context ctx = new ApplicationContext();
ctx.BeginTransaction();
. . .
3. 在代码行后添加注释
如果多行代码的每行都要添加注释,则在每行代码后添加该行的注释,这将很容易理解。例如:
const MAX_ITEMS = 10; // maximum number of packets
const MASK = 0x1F; // mask bit TCP
在分隔代码和注释时,有的开发者使用tab键,而另一些则使用空格键。然而由于tab键在各编辑器和IDE工具之间的表现不一致,因此最好的方法还是使用空格键。
4. 不要侮辱读者的智慧
避免以下显而易见的注释:写这些无用的注释会浪费你的时间,并将转移读者对该代码细节的理解。
if (a == 5) // if a equals 5
counter = 0; // set the counter to zero
5. 礼貌点
避免粗鲁的注释,如:“注意,愚蠢的使用者才会输入一个负数”或“刚修复的这个问题出于最初的无能开发者之手”。这样的注释能够反映到它的作者是多么的拙劣,你也永远不知道谁将会阅读这些注释,可能是:你的老板,客户,或者是你刚才侮辱过的无能开发者。
6. 关注要点
不要写过多的需要转意且不易理解的注释。避免ASCII艺术,搞笑,诗情画意,hyperverbosity的注释。简而言之,保持注释简单直接。
7. 使用一致的注释风格
一些人坚信注释应该写到能被非编程者理解的程度。而其他的人则认为注释只要能被开发人员理解就行了。无论如何,Successful Strategies for Commenting Code已经规定和阐述了注释的一致性和针对的读者。就个人而言,我怀疑大部分非编程人员将会去阅读代码,因此注释应该是针对其他的开发者而言。
8. 使用特有的标签
在一个团队工作中工作时,为了便于与其它程序员沟通,应该采用一致的标签集进行注释。例如,在很多团队中用TODO标签表示该代码段还需要额外的工作。
int Estimate(int x, int y)
...{
// TODO: implement the calculations
return 0;
}
注释标签切忌不要用于解释代码,它只是引起注意或传递信息。如果你使用这个技巧,记得追踪并确认这些信息所表示的是什么。
9. 在代码时添加注释
在写代码时就添加注释,这时在你脑海里的是清晰完整的思路。如果在代码最后再添加同样注释,它将多花费你一倍的时间。而“我没有时间写注释”,“我很忙”和“项目已经延期了”这都是不愿写注释而找的借口。一些开发者觉得应该write comments before code,用于理清头绪。例如:
public void ProcessOrder()
...{
// Make sure the products are available
// Check that the customer is valid
// Send the order to the store
// Generate bill
}
10. 为自己注释代码
当注释代码时,要考虑到不仅将来维护你代码的开发人员要看,而且你自己也可能要看。用Phil Haack大师的话来说就是:“一旦一行代码显示屏幕上,你也就成了这段代码的维护者”。因此,对于我们写得好(差)的注释而言,我们将是第一个受益者(受害者)。
这里只是介绍了php-fpm的优化方法的,但一般情况下和nginx组合使用的时候,单独优化其中一项的话,作用不是特别的大,同时还需要对nginx进行优化.nginx的做法方法参考:http://blog.haohtml.com/archives/6213.上面的优化前和优化后的图,看得出前后差距还是特别的大的.
导致nginx 502 bad gateway的PHP-CGI(FASTCGI)
NGINX频爆502 BAD GATEWAY的错误,看了网上的教程,仍没有彻底解决。
目前我总结的解决502 BAD GATEWAY的方式有:
1.视服务器的性能,在php-fmp.conf里增加max_children的值,我目前用的15.
2.用reload参数定时重载php-fpm。这个主要原因是php脚本执行时间过长造成的,重载php-fpm能杜绝这个问题。如何彻底解决php-cgi脚本占用大量内存从而导致502错误的产生还值得进一步探讨,目前该做法不失为一种好办法。
具体的做法是,用crontab让php-fpm平滑重启,从而不影响PHP脚本的tb运行。
*/10 * * * * /usr/local/php/sbin/php-fpm reload
=================== 优化设置 =========================
When you running a highload website with PHP-FPM via FastCGI, the following tips may be useful to you : )
如果您高负载网站使用PHP-FPM管理FastCGI,这些技巧也许对您有用:)
1. Compile PHP’s modules as less as possible, the simple the best (fast);
1.尽量少安装PHP模块,最简单是最好(快)的
2. Increas PHP FastCGI child number to 100 and even more. Sometime, 200 is OK! ( On 4GB memory server);
2.把您的PHP FastCGI子进程数调到100或以上,在4G内存的服务器上200就可以
注:我的1g测试机,开64个是最好的,建议使用压力测试获取最佳值
3. Using SOCKET PHP FastCGI, and put into /dev/shm on Linux;
3.使用socket连接FastCGI,linux操作系统可以放在 /dev/shm中
注:在php-fpm.cnf里设置<value name=”listen_address”>/tmp/nginx.socket</value>就可以通过socket连接FastCGI了,/dev/shm是内存文件系统,放在内存中肯定会快了.记得这时也要在nginx里的配置里进行修改,保持一致.
location ~ .*\.(php|php5)?$
{
#将Nginx与FastCGI的通信方式由TCP改为Unix Socket。TCP在高并发访问下比Unix Socket稳定,但Unix Socket速度要比TCP快。
fastcgi_pass unix:/tmp/php-cgi.sock;
#fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fcgi.conf;
}
4. Increase Linux “max open files”, using the following command (must be root):
# echo ‘ulimit -HSn 65536′ >> /etc/profile
# echo ‘ulimit -HSn 65536 >> /etc/rc.local
# source /etc/profile
4.调高linux内核打开文件数量,可以使用这些命令(必须是root帐号)
echo ‘ulimit -HSn 65536′ >> /etc/profile
echo ‘ulimit -HSn 65536′ >> /etc/rc.local
source /etc/profile
注:我是修改/etc/rc.local,加入ulimit -SHn 51200的
5. Increase PHP-FPM open file description rlimit:
# vi /path/to/php-fpm.conf
Find “<value name=”rlimit_files”>1024</value>”
Change 1024 to 4096 or higher number.
Restart PHP-FPM.
5. 增加 PHP-FPM 打开文件描述符的限制:
# vi /path/to/php-fpm.conf
找到“<value name=”rlimit_files”>1024</value>”
把1024 更改为 4096 或者更高.
重启 PHP-FPM.
6. Using PHP code accelerator, e.g eAccelerator, XCache. And set “cache_dir” to /dev/shm on Linux.
6.使用php代码加速器,例如 eAccelerator, XCache.在linux平台上可以把`cache_dir`指向 /dev/shm
至于其它的优化见李宴的bltbog一篇文章:http://blog.s135.com/post/375/
昨天在调试 WAP 网站时发现,在增加了 GB2312 到 UTF-8 转化以后,有些页面显示不正常了——有些页面只有一半的内容,另一半被截掉了。因为被截掉的部分包含了<p>的后半个标签</p>,因此整个页面都显示不出来,而报告错误。经过猜测、尝试,最后终于把问题集中在了 iconv 函数上。在经过高人指点以后,发现这个函数的第二个参数,除了可以指定要转化到的编码以外,还可以增加两个后缀://TRANSLIT 和 //IGNORE,其中 //TRANSLIT 会自动将不能直接转化的字符变成一个或多个近似的字符,//IGNORE 会忽略掉不能转化的字符,而默认效果是从第一个非法字符截断。但是我尝试了//TRANSLIT 和 //IGNORE 这两个后缀,效果还是不对。于是我想问题可能不是出在这里。
从 GB2312 到 UTF-8 转化应该不会有不能转化的字符,因为 UTF-8 的字符集完全包含了 GB2312 中的字符,所以我tb想大概是前面要转化的字符集指定错了,于是我尝试着把 GB2312 改成 GBK
$ary=addslashes(iconv("GB2312", "UTF-8", $ary));
问题解决!虽然那两个后缀在这里没派上用场,不过也算学了一招,以后肯定会用到的。补记:改成 GBK 后,发现仍然有一封邮件的内容解析不正确。在另一位高人指点下,先换成 GB18030,问题依旧,然后改用 mb_convert_encoding 进行转换,问题解决!不知道是 mb_convert_encoding 问题,还是我的系统问题,我用 mb_convert_encoding 时不支持 GB18030 编码。另外,用 GBK 或者GB18030 作为输入编码,并在输出编码中加上 //IGNORE 后缀,用 iconv 函数也能解决那封含有错误编码的邮件内容解析不正确的问题。不过用 mb_convert_encoding 可以指定多种输入编码,它会根据内容自动识别,这个比 iconv 要好的多。 这里可以将iconv改成从gbk到utf8的转换,不使用gb2312.
$ary=addslashes(iconv("GBK", "UTF-8", $ary));
其实,同事在生成图片文字水印的时候也遇到了这种问题,同事最初用的是GB2312字符集,结果直接报错,说是字符串的offset有问题,但仔细检查后却没有这种问题。后来才发现是直接调用的这个iconv转换出错了。
原来的转换是从gb2312往 UTF8转换,表面上确实没有什么问题,然而,现在的人特别爱装酷,受影响的那位同志,用的是繁体字,繁体字的字库大多情况是属于GBK的,所以后来换成GBK后就正常了。
估计以后再遇上用火星文的朋友,就真的只能使用andot提出的这种方法了。转换成18030,再使用ignore参数。哈哈
mbstring好象最初的版本里没有使用,如果换成这个,估计代码工作量非常大,先将就着点了
一不小心将VSS 6 admin用户的密码忘记(再此证明我的粗心),Google了一番,找到以下信息
the secret is to hack the um.dat file to remove the Admin password
from offset 80 the bytes are (all numbers are hex)
0:80 55 55 bc 7f 41 64 6d 69 6e 00 00 00 00 00 00 00
0:90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0:a0 00 00 00 00 90 6e 00 00 a8 01 00 00 00 00 00 00
Just load the um.dat file into a hex editor and change the bytes from =
offset 80 to exactly what is shown above. When the SourceSafe admin tool =
starts it believes that the admin password has never been set.
the hex values above are taken from a 'virgin' um.dat file
of course, you didn't get this from me....and ALWAYS backup first (just =
in case I'm wrong)
有朋友看不懂,我用中文解释一下
如果忘记了密码,打开你vss数据库所在的文件夹,打开data目录,
找到um.dat文件,用hex编辑器打开编辑它,从offset 80的55 55 开始
将值改为如上文所述的样子,然后保存,这样um.dat文件就回到了
初始状态(virgin ? :)),然后打开vss admin,用admin用户登录,tb不需要
密码了。
$(function(){
var validator = $("#enterRegForm").validate({
debug:false, //调试模式取消submit的默认提交功能
//errorClass: "error",//默认为错误的样式类为:error
//validClass: "check",//验证成功后的样式,默认字符串valid
focusInvalid: true,//表单提交时,焦点会指向第一个没有通过验证的域
//focusCleanup:true;//焦点指向错误域时,隐藏错误信息,不可与focusInvalid一起使用!
onkeyup: true,
errorElement: "div",
submitHandler: function(form){ //表单提交句柄,为一回调函数,带一个参数:form
form.submit(); //提交表单
},
rules: {
"enterprise.enName": {
required: true,
minlength: 6,
remote:{
url: "/nameServlet", //后台处理程序
type: "get", //数据发送方式
dataType: "json", //接受数据格式
data: { //要传递的数据
enName: function() {
return $("#enName").val();
}
}
}
},
"user.passWord":{
required:true,
rangelength:[6,18]
},
passWordConf:{
required:true,
rangelength:[6,18],
equalTo:"#passWord" //此处的passWord 是<input id="passWord"> 一开始还以为是name的值呢,气死了
}
},
messages: { //自定义验证消息
"enterprise.enName": {
required: "请填写企业名称!",
minlength: $.format("至少要{0}个字符!"),
remote:$.format("该企业名称已存在!")
},
"user.passWord":{
required:"请填写确认密码!",
rangelength:$.format("密码要在{0}-{1}个字符之间!")
},
passWordConf:{
required:"请填写确认密码!",
rangelength:$.format("确认密码要在{0}-{1}个字符之间!"),
equalTo:"确认密码要和密码一致!"
},
errorPlacement: function(error, element) { //验证消息放置的地方
//error.appendTo( element.parent("td").next("td").children(".msg") );
error.appendTo( element.parent(".field").next("div"));
},
highlight: function(element, errorClass) { //针对验证的表单设置高亮
$(element).addClass(errorClass);
},
success: function(div) {
div.addClass("valid");
}
});
});
自定义方法;
新建一个js文件:$(document).ready(function(){
// 字符最小长度验证(一个中文字符长度为2)
jQuery.validator.addMethod("stringMinLength", function(value, element, param) {
var length = value.length;
for ( var i = 0; i < value.length; i++) {
if (value.charCodeAt(i) > 127) {
length++;
}
}
return this.optional(element) || (length >= param);
}, $.validator.format("长度不能小于{0}!"));
// 字符最大长度验证(一个中文字符长度为2)
jQuery.validator.addMethod("stringMaxLength", function(value, element, param) {
var length = value.length;
for ( var i = 0; i < value.length; i++) {
if (value.charCodeAt(i) > 127) {
length++;
}
}
return this.optional(element) || (length <= param);
}, $.validator.format("长度不能大于{0}!"));
// 字符验证
jQuery.validator.addMethod("stringCheck", function(value, element) {
return this.optional(element) || /^[\u0391-\uFFE5\w]+$/.test(value);
}, "只能包括中文字、英文字母、数字和下划线");
// 中文字两个字节
jQuery.validator.addMethod("byteRangeLength", function(value, element, param) {
var length = value.length;
for(var i = 0; i < value.length; i++){
if(value.charCodeAt(i) > 127){
length++;
}
}
return this.optional(element) || ( length >= param[0] && length <= param[1] );
}, "请确保输入的值在3-15个字节之间(一个中文字算2个字节)");
// 字符验证
jQuery.validator.addMethod("string", function(value, element) {
return this.optional(element) || /^[\u0391-\uFFE5\w]+$/.test(value);
}, "不允许包含特殊符号!");
// 必须以特定字符串开头验证
jQuery.validator.addMethod("begin", function(value, element, param) {
var begin = new RegExp("^" + param);
return this.optional(element) || (begin.test(value));
}, $.validator.format("必须以 {0} 开头!"));
// 验证两次输入值是否不相同
jQuery.validator.addMethod("notEqualTo", function(value, element, param) {
return value != $(param).val();
}, $.validator.format("两次输入不能相同!"));
// 验证值不允许与特定值等于
jQuery.validator.addMethod("notEqual", function(value, element, param) {
return value != param;
}, $.validator.format("输入值不允许为{0}!"));
// 验证值必须大于特定值(不能等于)
jQuery.validator.addMethod("gt", function(value, element, param) {
return value > param;
}, $.validator.format("输入值必须大于{0}!"));
// 验证值小数位数不能超过两位
jQuery.validator.addMethod("decimal", function(value, element) {
var decimal = /^-?\d+(\.\d{1,2})?$/;
return this.optional(element) || (decimal.test(value));
}, $.validator.format("小数位数不能超过两位!"));
//字母数字
jQuery.validator.addMethod("alnum", function(value, element) {
return this.optional(element) || /^[a-zA-Z0-9]+$/.test(value);
}, "只能包括英文字母和数字");
// 汉字
jQuery.validator.addMethod("chcharacter", function(value, element) {
var tel = /^[\u4e00-\u9fa5]+$/;
return this.optional(element) || (tel.test(value));
}, "请输入汉字");
// 身份证号码验证
jQuery.validator.addMethod("isIdCardNo", function(value, element) {
return this.optional(element) || /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$/.test(value)||/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[A-Z])$/.test(value);
}, "请正确输入您的身份证号码");
// 手机号码验证
jQuery.validator.addMethod("isMobile", function(value, element) {
var length = value.length;
var mobile = /^(((13[0-9]{1})|(15[0-9]{1}))+\d{8})$/;
return this.optional(element) || (length == 11 && mobile.test(value));
}, "请tb正确填写您的手机号码");
// 电话号码验证
jQuery.validator.addMethod("isTel", function(value, element) {
var tel = /^\d{3,4}-?\d{7,9}$/; //电话号码格式010-12345678
return this.optional(element) || (tel.test(value));
}, "请正确填写您的电话号码");
// 联系电话(手机/电话皆可)验证
jQuery.validator.addMethod("isPhone", function(value,element) {
var length = value.length;
var mobile = /^(((13[0-9]{1})|(15[0-9]{1}))+\d{8})$/;
var tel = /^\d{3,4}-?\d{7,9}$/;
return this.optional(element) || (tel.test(value) || mobile.test(value));
}, "请正确填写您的联系电话");
// 邮政编码验证
jQuery.validator.addMethod("isZipCode", function(value, element) {
var tel = /^[0-9]{6}$/;
return this.optional(element) || (tel.test(value));
}, "请正确填写您的邮政编码");
});
该文件要先被引用再对form进行验证。
注意:remote 中的url 为servlet,但是这个servlet该怎么写呢
一开始以为是跟平时的一样:out.println("用户名"+name+"已存在!");
虽然可以验证,但是还是存在问题:输入个不存在的name还是提示存在。
后来还是Google,看到别人也是犯了这样的错误,解决方法时返回true,false.
但是 doGet 是void类型的,return true 是行不通的。
后来又请教群里的高手,正确写法是out.println("true");就可以了。
哎,费了老长时间
总算是把问题给解决了。
$("#category ul").find("li").each(
function() {
$(this).mouseover(
function() {
$("#" + this.id + "_menu").show();
$(this).addClass("a");
}
);
$(this).mouseout(
function() {
$(this).removeClass("a");
$("#" + this.id + "_menu").hide();
}
);
}
);
浏览器之间的不兼容一直令前端开发者的头疼,而 IE 更是噩梦。鼠标在下拉菜单移动时菜单会不断闪烁,tb说明不断触发了 mouseover 和 mouseout 事件。
这貌似涉及到所谓的“事件冒泡”,我不懂 JavaScript,就不在误人子弟了,详情请自己 Google,这里只给出解决方法:将 mouseover 改成 mouseenter,mouseout 改成 mouseleave。
$("#category ul").find("li").each(
function() {
$(this).mouseenter(
function() {
$("#" + this.id + "_menu").show();
$(this).addClass("a");
}
);
$(this).mouseleave(
function() {
$(this).removeClass("a");
$("#" + this.id + "_menu").hide();
}
);
}
);
最后指出一点,mouseenter 和 mouseleave 事件是 jQuery 库中实现的,似乎(再次声明我不懂 JavaScript,所以这里用“似乎”)并不是浏览器的原生事件,所以如果你想自己写 JavaScript 实现的话,请自行 Google,或者查看 jQuery 源码,如果你能看懂的话。
参考链接:JQuery HowTo: Problems with jQuery mouseover / mouseout events
转自:http://demon.tw/programming/jquery-mouseover-mouseout.html
以前在使用 js 日历时,没有使用过 jQuery Datepicker,今天第一次使用发现非常的好用。使用时需要将日历文字显示为中文,打开前边的链接在文章底部就可以看到将 jQuery Datepicker 文字显示为中文的方法,在http://jquery-ui.googlecode.com/svn/trunk/ui/i18n/可以看到各种版本的语言,中文文件内容如下:
jQuery(function($){
$.datepicker.regional['zh-CN'] = {
closeText: '关闭',
prevText: '<上月',
nextText: '下月>',
currentText: '今天',
monthNames: ['一月','二月','三月','四月','五月','六月',
'七月','八月','九月','十月','十一月','十二月'],
monthNamesShort: ['一','二','三','四','五','六',
'七','八','九','十','十一','十二'],
dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'],
dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'],
dayNamesMin: ['日','一','二','三','四','五','六'],
weekHeader: '周',
dateFormat: 'yy-mm-dd',
firstDay: 1,
isRTL: false,
showMonthAfterYear: true,
yearSuffix: '年'};
$.datepicker.setDefaults($.datepicker.regional['zh-CN']);
});
使用时引入该文件, jQuery Datepicker 就会显示简体中文,但为了tb网站性能,最好将它整合到一个 js 文件中。
$(document).ready(function() {
$("#sdate,#edate").datepicker();
});
使用时也可以添加其他的选项,如下所示,具体的可以参考 jQuery Datepicker 官方文档。
$(document).ready(function () {
$("#sdate,#edate").datepicker({
numberOfMonths: 2, //显示两个月
minDate: 0 //从当前日期起可选
});
});
本篇简单介绍了下 jQuery UI Datepicker 显示中文的方法,希望能对遇到这个问题又不太熟悉英文的朋友带来一点帮助
addinput.js
//======================
//功能:在表单中input file控件
//参数:parentID---要插入input file控件的父元素ID
// inputID----input file控件的ID,这个一定要和name保持一致的,否则会出问题
// maxNum --- 最大数量 0为不限制
//======================
function createInput(parentID,inputFileID, maxNum){
if (maxNum > 0) {
x=document.getElementsByName(inputFileID);
y=x.length;
if (y >= maxNum) {
alert('最多只允许添加' + maxNum + '个');
return false;
}
}
var parent=$G(parentID);//获取父元素
var div=document.createElement("div");//创建一个div容器tb用于包含input file
var x=parseInt(Math.random()*(80-1))+1;
var divName=inputFileID+x.toString();//随机div容器的名称
div.name=divName;
div.id=divName;
var aElement=document.createElement("input"); //创建input
aElement.name=inputFileID;
aElement.id=inputFileID;
aElement.type="text";//设置类型为file
aElement.className = "dynInputLen";
var delBtn=document.createElement("input");//再创建一个用于删除input file的Button
delBtn.type="button";
delBtn.value=" ";
delBtn.className = "btn_del";
delBtn.onclick=function(){ removeInput(parentID,divName)};//为button设置tbonclick方法
div.appendChild(aElement);//将input file加入div容器
div.appendChild(delBtn);//将删除按钮加入div容器
parent.appendChild(div);//将div容器加入父元素
}
//============================
//功能:删除一个包含input file的div 容器
//参数:parentID---input file控件的父元素ID
// DelDivID----个包含input file的div 容器ID
//============================
function removeInput(parentID,DelDivID){
var parent=$G(parentID);
parent.removeChild($G(DelDivID));
}
//通过元素ID获取文档中的元素
function $G(v){return document.getElementById(v);}
在html里引入addinput.js文件.html代码如下:
<div id="div_zc" class="dynInput"> |
|
<input name="zhuancheng[]" type="text" id="zhuancheng" value="" maxlength="50" /> |
|
</div> |
|
<div style="clear:both;margin-top:10px; padding-left:5px;"> |
|
<input type="button" onClick="createInput('div_zc','zhuancheng[]', 3)" name="button" id="button" value="+ 添加(限3条)" class="btn_add"> |
可以设定最多可以添加多少个input,如果设置为0的话,则表示不限制数量。
在网上,看到一个问题 “什么是全局变量?”说实话,这个问题我想了一会儿。觉得有二义性,可能大部分人会说,只要是在堆栈外面定义的 就是全局。 我觉得,全局这个概念,我想反问提问者,是线程全局?进程全局? 好吧,我今天想说说后者的。其实,在写hook的时候,大家都知道,进程需要共享dll中的全局变量。没错,就是它,我觉得被进程共享的 才叫全局。
- #pragma comment (linker, "/SECTION:GlobalValue,RWS")
-
- #include <iostream>
-
- #pragma data_seg("GlobalValue")
- int g_iCnt = 1000;
- #pragma data_seg()
-
- int PlusShow()
- {
- return printf("PlusShow %d",++g_iCnt);
- }
-
- int Show()
- {
- return printf("Show %d",g_iCnt);
- }
-
- int main(int argc)
- {
- if(argc < 2)
- {
- return Show();
- }
-
- PlusShow();
- getchar();
- return 0;
- }
#pragma comment (linker, "/SECTION:GlobalValue,TBRWS")
#include <iostream>
#pragma data_seg("GlobalValue")
int g_iCnt = 1000;
#pragma data_seg()
int PlusShow()
{
return printf("PlusShow %d",++g_iCnt);
}
int Show()
{
return printf("Show %d",g_iCnt);
}
int main(int argc)
{
if(argc < 2)
{
return Show();
}
PlusShow();
getchar();
return 0;
}
其实 不同段放的 不仅仅是变量。可以是函数 或者对象,非常灵活。看你自己怎么用了:)
- @charset "utf-8";
-
- /* =Reset default browser CSS. Based on work by Eric Meyer:http://meyerweb.com/eric/tools/css/reset/index.html
- -------------------------------------------------------------- */
- html, body, div, span, applet, object, iframe,
- h1, h2, h3, h4, h5, h6, p, blockquote, pre,
- a, abbr, acronym, address, big, cite, code,
- del, dfn, em, font, ins, kbd, q, s, samp,
- small, strike, strong, sub, sup, tt, var,
- dl, dt, dd, ol, ul, li,
- fieldset, form, label, legend,
- table, caption, tbody, tfoot, thead, tr, th, td { margin:0; padding:0;}
- :focus { outline:0;}
- ol, ul { list-style:none;}
- h1, h2, h3, h4, h5, h6 { font-size:100%; font-weight:normal;}
- /* tables still need 'cellspacing="0"' in the markup */
- table { border-collapse:separate; border-spacing:0;}
- caption, th, td { font-weight:normal; text-align:left;}
- blockquote:before, blockquote:after,
- q:before, q:after { content:'';}
- blockquote, q { quotes:'' '';}
- a img { border:0;}
- article, aside, details, figcaption, figure,
- footer, header, hgroup, menu, nav, section { display:block;}
-
- /* =Structure
- ----------------------------------------------- */
- body { font:12px/1.5 'Microsoft yahei', Tahoma, Geneva, sans-serif; color:#666; background:#d5d8dd;}
- a { text-decoration:none; color:#4a5566;}
- a:hover { text-decoration:underline; color:#3061b0;}
- .area { width:960px; margin:auto;}
- .pt { padding-top:20px; !important;}
- .mb { margin-bottom:20px !important;}
- .clearfix:after { content:'.'; display:block; height:0; clear:both; visibility:hidden; font-size:0;}
- .clearfix { min-height:1%;}
- .red { color:#c33;}
- .tright { text-align:right;}
- .dis { display:none !importantb;}
-
- /* = Layout
- ----------------------------------------------- */
- /* header */
-
-
- /* body */
-
-
- /* footer */
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Calendar;
- import java.util.Date;
-
- public class A {
- public static void main(String[] args) throws ParseException{
- Date d1 = Calendar.getInstance().getTime();
- String s1 = new SimpleDateFormat("yyyy"+"-"+"MM"+"-"+"dd HH" + ":" + "mm" + ":" + "ss").format(d1);
- System.out.println("s1==="+stb1);
-
- String time = "2011-08-13 14:42:43";
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- Date dd= sdf.parse(time.toString());
- String ss = new SimpleDateFormat("yyyy"+"-"+"MM"+"-"+"dd HH" + ":" + "mm" + ":" + "ss").format(dd);
- System.out.println("ss==="+ss);
-
- int i = d1.compareTo(dd);
- if(i<0){
- System.out.println("d1更早");
- }else if(i==0){
- System.out.println("d1一样");
- }else{
- System.out.println("dd更早");
- }
-
- }
- }
1.将jdbc操作改成批处理 addBatch(); //添加批处理
2.使用PreparedStatement
代码:
eg:
- Connection conn = DBUtils.getInstance().getConnetion();
-
- conn.setAutoCommit(false );
-
- PreparedStatement pstmt = null;
-
- try
-
- pstmt = conn.preparedStatement("insert into test1(a,b) vlaues (?,?)");
-
- pstmt.clearBatch();
-
- for(int i = 0; i<100000;i++){
-
- pstmt.setInt(1,i);
-
- pstmt.setString(2,"value"+i);
-
- pstmt.addBatch();
-
- if(i % 10000){
-
- pstmt.executbeBatch();
-
- }
-
-
-
- }
-
-
-
-
-
- pstmt.executeBatch();
-
-
-
- conn.commit();
-
- } catch(Exception e) {
-
- conn.rollback();
-
- } finally {
-
- conn.setAutocommit(true);
-
- }
数组有四种定义的方式
使用构造函数:
var a = new Array();
var b = new Array(8);
var c = new Array("first", "second", "third");
或者数组直接量:
var d = ["first", "second", "third"];
属性
Array只有一个属性,就是length,length表示的是数组所占内存空间的数目,而不仅仅是数组中元素的个数,在刚才定义的数组中,b.length的值为8
<script>
var a = new Array("first", "second", "third")
a[48] = "12"
document.write(a.length)
//显示的结果是49
</script>
数组的length属性是可写的,这是一个非常有意思的属性,我们可以通过这种方法来截取数组
<script>
var a = new Array("first", "second", "third")
delete a[1]
document.write(a.length)
//显示的结果是3,说明即使删除也无法改变数组的长度
var a = new Array("first", "second", "third")
a.length = 1
document.write(a.length)
//显示的结果是1,说明只剩下一个元素了
</script>
方法
这里并没有包括IE和FF并不兼容的一些方法:
toString():把数组转换成一个字符串
toLocaleString():把数组转换成一个字符串
join():把数组转换成一个用符号连接的字符串
shift():将数组头部的一个元素移出
unshift():在数组的头部插入一个元素
pop():从数组尾部删除一个元素
push():把一个元素添加到数组的尾部
concat():给数组添加元素
slice():返回数组的部分
reverse():将数组反向排序
sort():对数组进行排序操作
splice():插入、删除或者替换一个数组元素
toString()方法,toLocaleString()方法的作用类似,FF下的作用是完全相同的,IE的话如果元素是字符串,会在“,”后面加上一个空格,如果元素是数字,会扩展到两位小数,两者都会改变字符串的length属性,所以考虑到兼容性,尽量不要使用toLocaleString()方法。
<script>
var a = new Array(1, 2, 3, [4, 5, [6, 7]])
var b = a.toString() //b为字符串形式的 "1, 2, 3, 4, 5, 6, 7"
var c = new Array(1, 2, 3, [4, 5, [6, 7]])
var d = c.toLocaleString() //d为字符串形式的 "1, 2, 3, 4, 5, 6, 7"
//toString()方法和toLocaleString()方法都可以拆解多维数组
</script>
join()方法将数组中的所有元素转换成字符串,然后连接起来,这刚好和String的split()方法是一个相反的操作。join()默认是使用“,”作为分隔符,当然你也可以在方法中指定分隔符
<script>
var a = new Array("first", "second", "third")
var s = a.join("...")
document.write(s)
//显示的结果是“first...second...third”
</script>
pop()方法可以从数组尾部删除若干个元素,push()方法把一个元素添加到数组的尾部,这两个方法刚好是两个相反的操作。两个都是对原来的数组进行操作,但是要注意push()方法返回的是新的数组的长度,而pop()方法则返回被删去的那个元素。
<script>
var a = new Array(1, 2, 3)
var b = a.push(4,5,[6,7]) //a为[1, 2, 3, 4, 5, [6, 7]] b为6 注意tbpush()方法不会帮你打开一个数组
var c = new Array(1, 2, 3, 4, "first")
var d = c.pop() //c为[1, 2, 3, 4] d为字符串形式的"first"
</script>
shift()方法可以从数组头部删除一个元素,unshift()方法把若干元素添加到数组的头部,这两个方法刚好是两个相反的操作。两个都是对原来的数组进行操作,但是要注意unshift()方法返回的是新的数组的长度,而shift()方法则返回被删去的那个元素。
<script>
var a = new Array(1, 2, 3)
var b = a.unshift(4,5,[6,7]) //a为[4, 5, [6, 7], 1, 2, 3] b为6 注意unshift()方法不会帮你打开一个数组,还有就是被插入数值的顺序
var c = new Array("first", 1, 2, 3, 4)
var d = c.shift() //c为[1, 2, 3, 4] d为字符串形式的"first"
</script>
concat()方法可以返回一个在原有数组上增添了元素的数组,元素用“,”分隔,元素中如果有数组,将被展开并继续添加,但不支持多维数组形式的展开添加
<script>
var a = new Array("first", "second", "third")
s = a.concat("fourth",["fifth", "sixth"],["seventh", ["eighth", "ninth"]])
document.write(s[7])
//显示的结果是“eighth, ninth”,说明“eighth, ninth”是以数组的形式被添加了进去,此是s的值为["first", "second", "third", "fourth", "fifth", "sixth", "seventh", ["eighth", "ninth"]]
</script>
slice()方法返回数组的一个片断,或者说是子数组。slice()的参数表示字数组的始末位置,如果只有一个参数,就表示从该处开始一直取到最后,如果参数出现负数,则表示倒数的某个位置。slice(start,end) //表示数组从从下标为start(包含这个)的地方开始到end(不包含这个)
<script>
var a = new Array(1, 2, 3, 4, 5)
var b = a.slice(3) //b为[4, 5]
var c = a.slice(-3) //c为[3, 4, 5]
var d = a.slice(1,-1) //d为[2, 3, 4]
var e = a.slice(-3,-1) //e为[3, 4]
</script>
reverse()方法将数组反向排序,他并不创建和返回一个新的数组,而是在原有的数组上进行操作
<script>
var a = new Array("first", "second", "third")
a.reverse()
document.write(a)
//显示的结果是“third,second,first”,这时候数组的顺序已经颠倒了
</script>
sort()方法的作用是对数组进行排序,这是一个非常奇特的方法,我不知道当初创作他的人是出于懒惰还是聪明,这是一个让我印象深刻的方法。
sort()方法的参数是一个有两个参数,并且有返回值的函数,如果返回的值大于零,则说明前一个参数比后一个参数大,等于零则相等,小于零说明前一个参数比后一个小,而相对小的那个参数将出现在排序的前列。
sort()方法直接在数组上进行操作,同时也返回值,但是两者似乎是等价的。sort()方法默认是用字母的顺序进行排序
<script>
var a = new Array(33, 4, 111, 543)
a.sort(way)
function way(x, y){
if (x % 2 ==0)
return 1;
if (x % 2 !=0)
return -1;
}
//排序的结果是使奇数在前偶数在后
</script>
splice()方法的作用是插入、删除或者替换一个数组元素,他tb不光会在原有的数组上进行修改,还会返回被处理掉的内容,因此这是一个功能强大,但是不容易使用的方法,splice()方法用前两个参数进行定位,余下的参数表示插入部分。
<script>
var a = new Array(1, 2, 3, 4, 5)
var b = a.splice(2) //a为[1, 2] b为[3, 4, 5]
var c = new Array(1, 2, 3, 4, 5)
var d = c.splice(2,2) //c为[1, 2, 5] d为[3, 4]
var e = new Array(1, 2, 3, 4, 5)
var f = f.splice(-4,2) //e为[1, 4, 5] f为[2, 3]
var g = new Array(1, 2, 3, 4, 5)
var h = g.splice(-2,-2) //第二个参数表示长度,因此负数在此无效
var i = new Array(1, 2, 3, 4, 5)
var j = i.splice(2,2,"first","second","third") //i为[1, 2, "first", "second", "third", 5] j为[3, 4] 后面部分会自动前后移动,以保持数组的连续性
var k = new Array(1, 2, 3, 4, 5)
var l = k.splice(2,2,["first","second"],"third") //k为[1, 2, ["first", "second"], "third", 5] l为[3, 4] splice()方法不会展开数组,只直接写入
</script>
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import org.w3c.dom.Document;
import org.w3c.dom.DOMException;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
/***
* @author xuechong
* 6/11/2010 16:58
* DomXMLString.java
* 概述:纯java方式访问远程WebService接口返回的xml格式的数据保存在本地
*/
public class DomXMLString{
private static String SERVICES_HOST = "www.webxml.com.cn";
//远程WebService接口url
private static String NETDATA_URL = "http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx/getRegionProvince";
//访问远程WebService接口返回的xml格式的数据保存在本地的绝对路径
private static String LOCAL_PC_SAVEFILE_URL = "E:dataTest/netDataToLocalFile.xml";
private DomXMLString(){}
public static void main(String[] args) throws Exception{
Document document = getProvinceCode(NETDATA_URL);
helloOK(document, LOCAL_PC_SAVEFILE_URL);
}
/*返回一个Document对象*/
public static Document getProvinceCode(String netXMLDataURL){
Document document = null;
DocumentBuilderFactory documentBF = DocumentBuilderFactory.newInstance();
documentBF.setNamespaceAware(true);
try{
DocumentBuilder documentB = documentBF.newDocumentBuilder();
InputStream inputStream = getSoapInputStream(netXMLDataURL); //具体webService相关
document = documentB.parse(inputStream);
inputStream.close();
}catch(DOMException e){
e.printStackTrace();
return null;
}catch(ParserConfigurationException e){
e.printStackTrace();
return null;
}catch (SAXException e){
e.printStackTrace();
return null;
}catch(IOException e){
e.printStackTrace();
return null;
}
return document;
}
/*返回InputStream对象*/
public static InputStream getSoapInputStream(String url){
InputStream inputStream = null;
try{
URL urlObj = new URL(url);
URLConnection urlConn = urlObj.openConnection();
urlConn.setRequestProperty("Host", SERVICES_HOST); //具体webService相关
urlConn.connect();
inputStream = urlConn.getInputStream();
}catch(MalformedURLException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
return inputStream;
}
/*访问远程(WebService)xml数据后返回的xml格式字符串并生成为tb本地文件*/
public static void helloOK(Document document, String savaFileURL){
TransformerFactory transF = TransformerFactory.newInstance();
try{
Transformer transformer = transF.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "YES");
PrintWriter pw = new PrintWriter(new FileOutputStream(savaFileURL));
StreamResult result = new StreamResult(pw);
transformer.transform(source, result);
System.out.println("生成xml文件成功!");
}catch(TransformerConfigurationException e){
System.out.println(e.getMessage());
}catch(IllegalArgumentException e){
System.out.println(e.getMessage());
}catch(FileNotFoundException e){
System.out.println(e.getMessage());
}catch(TransformerException e){
System.out.println(e.getMessage());
}
}
}
在应用程序开发过程中,使用的最多的数据类型就是字符串 。在Java语言平台中也是如此。为此掌握字符串的处理技巧,无疑是一位数据库管理员必须要掌握的技能。笔者这里就给大家介绍如何利用字符串连接来解决一些实际的问题。
一、 字符串连接概述。
在编写应用程序的时候,我们往往需要将多个字符串连接起来,来完成特定的功能。如现在有两个字符串变量,分别为名字(变量名为name)和年龄(变量名为age)。现在需要在屏幕上输出“我的名字是某某,年龄多少”这个字符串。这个语句该如何写呢?可以写为“我的名字是”+name+“,年龄为”+age。也就是说,在Java语言中,可以通过+号将多个字符串(包括变量)连接成一个字符串。在屏幕上打印出来或者将其赋值给一个新的字符串变量。在进行这个字符串连接的时候,大家需要注意一点。也许有些程序员以前学过其他的开发语言,如SQL语言等等。不同的语言采用的字符串连接字符是不同的。如在SQL中采用的就是||符号。有时候,在开发应用程序的时候,开发人员可能会混淆。如在利用JAVA语言开发应用程序,在代码中需要加入SQL语句从数据库中查询数据。此时就有可能在JAVA代码中和SQL代码中都要用到字符串连接的情况(SQL代码中要将多个字段的内容连接为一个字段)。由于两个代码所采用的连接符号不同,所以可能会搞错。
二、 利用字符串连接解决分行问题。
在JAVA开发平台中,可以利用+号将多个字符串合并为一个字符串。不过在实际工作中,这个合并功能往往还可以帮助系统开发人员解决一些棘手的问题。如根据语法规定,JAVA代码中一句相连的字符串不能够分开在两行中书写。这是什么意思呢?如现在有一个字串比较长,为“我的名字叫萝卜,今年29岁,在宁波工作”。如果将这个字符串写在同一行的话,那么这一行会很长,从而影响代码的阅读。但是,笔者希望其在屏幕上输出或者保存在文件中时,则是在同一行。在这种情况下,该如何处理呢?
确实,这是应用程序开发人员在程序开发过程中经常遇到的一个问题。因为在书写代码时,一个好的程序开发人员应该兼顾到其代码的美观,这有助于程序开发人员阅读代码。毕竟有时候需要多个程序开发人员一同完成某个任务。而不能够只完成某个特定的功能就可以了。此时,为了代码的美观与更好的阅读性,有经验的程序开发人员往往会利用这个+号连接字符来处理这个分行的问题。
由于对于长的字符串,JAVA代码不能够分为两行来写。否则的话,系统就会提示语法错误。遇到这种情况时,为了提高阅读性,我们往往要求程序开发人员把他们分为两行,同时利用加号将他们连接起来。如下所示:
“我的名字叫萝卜,今年29岁,”+
“在宁波工作”
也就是说,应用程序开发人员可以使用+号将两个字符串连接起来,然后在+号处换行,从而将两个字符串连接起来。此时,JAVA编译器会认为这是合法的,允许程序开发人员采用这个技巧来对字符串进行合理的分行。所以说,利用字符串连接可以有效的解决分行问题。
三、 利用字符串连接来实现数据类型的自动转换。
在各种开发语言中,都有各种各样的数据类型。有时候为了满足特定的需要,要对他们进行数据类型的转换。如需要将数字类型的数据转换为字符串类型的数据;再如将某个字符串“2432”转换为数值类型的数据。在JAVA语言的开发平台中,数据类型的转换有两种方式,分别为隐式转换与显示转换。一般情况下,从低级数据类型向高级类型的转换,系统会自动进行转换,而不需要程序员进行任何的手工操作或者指定。这个从低级数据类型向高级数据类型的转换,就叫做隐式转换。在对数据类型进行转换的时候,程序开发人员需要遵守严格的规则,否则的话容易出现错误。如从低精度数据类型向高精度数据类型转换的时候,则永远不会发生溢出,通常情况下都会成功。而如果把高精度数据类型转换为低精度数据类型的话,则就会发生溢出错误,从而导致部分信息丢失,甚至无法正常转换。
不过在JAVA开发平台中,除了以上这两种转换方式外,还有一种数据类型的转换方法。就是如果某个字符串和某个变量一起(这个变量可能是数值型的数据或者是日期型的数据)利用+号连接起来时,系统会自动对这个变量进行转换,会将其转换为字符串数据类型,然后再与原先的字符串连接起来。
也就是说,字符串也可同其他基本数据类型进行连接。如果将字符串同这些数据类型进行连接时(如数值型的数据),会将这些数据直接转换成字符串。如上面那个例子中“我的名字是”+name+“,年龄为”+age这个字符串,age这个变量为数值型的数据,而其他为字符串的数据。现在程序开发人员就可以利用这个+号将不同数据类型的字符串连接起来。不过最后的数据类型都会字符串的数据类型。即系统会先将变量age转换为字符型数据,然后再跟其他字符串数据类型进行连接。那么在后台中,这到底是如何实现呢?如果在利用+号来进行连接的时候,如果某个变量不是字符串的数据类型,则系统回自动调用一个toSring方法,将非字符串的数据类型转换成字符串的数据类型,再进行合并。由于这个过程是系统自动完成的,所以程序开发人员需要特别的注意。一般来说,只要将+号运算符的一个操作数是字符串,编译器就会将另一个操作数转换成字符串形式。所以程序开发人员应谨慎地将其他数据类型与字符串相连,以免得到意想不到的结果。如将这tb个利用+号连接起来的内容,如果其中有个操作数是字符型数据的话,那么将其赋值给数值型的变量时,就会导致莫名其妙的问题。这种问题很可能存在。因为这个+号,除了可以连接字符串,还可以用来对数值型的数据进行四则运法运算。所以,在进行四则运法运算时,要确保各个操作数都是数值型的。否则的话,这个后果就可想而知了。
另外需要再提一句的是,如果在进行加法运算时,需要注意其数据类型的隐式转换。如现在有三个操作数,其中两个操作数其小数点保留两位;另外一个操作数其小数点保留为1位。而最后赋值给一个变量,其保留小数位数3位。这就是一个涉及到不同精度的数据类型的数值型数据转换问题。那么最终的结果是什么呢?笔者这里卖个关,大家若想知道结果的话,可以回去编个程序测试一下。有时候,笔者告诉你们答案,读者并不一定记得住。所以,笔者在这里就点倒为止。
总之一句话,+号这个连接符号,不仅可以连接各个字符串,而且还用来完成分行与数据类型的隐式转换。为此笔者建议,各个程序开发人员需要注意这方面细节,一定能够获得不少的收获。
对于J2EE,我们知道当开发应用时,在架构设计阶段的决定将对应用的性能和可扩展性产生深远的影响。现在当开发一个应用项目时,我们越来越多地注意到了性能和可扩展性的问题。应用性能的问题比应用功能的不丰富问题往往更为严重,前者会影响到所有用户,而后者只会影响到碰巧使用该功能的那些用户。
作为应用系统的负责人,一直被要求"要少花钱多办事"----用更少的硬件,更少的网络带宽,以及更短的时间完成更多的任务。J2EE通过提供组件方式和通用的中间件服务是目前首选的最优方式。而要能够构建一个具有高性能和可扩展性的J2EE应用,需要遵循一些基本的架构策略。
缓存(Caching)
简单地说,缓存中存放着频繁访问的数据,在应用的整个生命周期中,这些数据存放在持久性存储器或存放在内存中。在实际环境中,典型的现象是在分布式系统中每个JVM中有一个缓存的实例或者在多个JVM中有一个缓存的实例。
缓存数据是通过避免访问持久性存储器来提高性能的,否则会导致过多的磁盘访问和过于频繁网络数据传输。
复制
复制是通过在多台物理机器上创建指定应用服务的多个拷贝来获得整体更大吞吐效率。理论上看,如果一个服务被复制成两个服务,那么系统将可处理两倍的请求。复制是通过单一服务的多个实例的方式从而减少每个服务的负载来提高性能的。
并行处理
并行处理将一个任务分解为更为简单的子任务,并能够同时在不同的线程中执行。
并行处理是通过利用J2EE层执行模式的多线程和多CPU特点来提高性能。
tb与使用一个线程或CPU处理任务相比,以并行方式处理多个子任务可以使操作系统在多个线程或处理器中进行分配这些子任务。
异步处理
应用功能通常被设计为同步或串行方式。异步处理只处理那些非常重要的任务部分,然后将控制立即返回给调用者,其他任务部分将在稍后执行。
异步处理是通过缩短那些在将控制返回给用户之前必须处理的时间来提高性能的。虽然都做同样多的事情,但是用户不必等到整个过程完成就可以继续发出请求了。
资源池
资源池技术使用的是一套准备好的资源。与在请求和资源之间维持1:1的关系的不同,这些资源可被所有请求所共享。资源池的使用是有条件的,需要衡量下面两种方式的代价:
A、维持一套可被所有请求共享资源的代价
B、为每个请求都重新创建一个资源的代价
当前者小于后者时,使用资源池才是有效率的。
希望通过本文介绍的是J2EE中的五个核心策略,对你有帮助。
线程的同步是保证多线程安全访问竞争资源的一种手段。
线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些原则问题需要考虑,是否有竞争资源被同时改动的问题?
在本文之前,请参阅《Java线程:线程的同步与锁》,本文是在此基础上所写的。
对于同步,在具体的Java代码中需要完成一下两个操作:
把竞争访问的资源标识为private;
同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。
当然这不是唯一控制并发安全的途径。
synchronized关键字使用说明
synchronized只能标记非抽象的方法,不能标识成员变量。
为了演示同步方法的使用,构建了一个信用卡账户,起初信用额为100w,然后模拟透支、存款等多个操作。显然银行账户User对象是个竞争资源,而多个并发操作的是账户方法oper(int x),当然应该在此方法上加上同步,并将账户的余额设为私有变量,禁止直接访问。
/**
* Java线程:线程的同步
*
* @author leizhimin 2009-11-4 11:23:32
*/
public class Test {
public static void main(String[] args) {
User u = new User("张三", 100);
MyThread t1 = new MyThread("线程A", u, 20);
MyThread t2 = new MyThread("线程B", u, -60);
MyThread t3 = new MyThread("线程C", u, -80);
MyThread t4 = new MyThread("线程D", u, -30);
MyThread t5 = new MyThread("线程E", u, 32);
MyThread t6 = new MyThread("线程F", u, 21);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class MyThread extends Thread {
private User u;
private int y = 0;
MyThread(String name, User u, int y) {
super(name);
this.u = u;
this.y = y;
}
public void run() {
u.oper(y);
}
}
class User {
private String code;
private int cash;
User(String code, int cash) {
this.code = code;
this.cash = cash;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
/**
* 业务方法
* @param x 添加x万元
*/
public synchronized void oper(int x) {
try {
Thread.sleep(10L);
this.cash += x;
System.out.println(Thread.currentThread().getName() + "运行结束,增加“" + x + "”,当前用户账户余额为:" + cash);
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "User{" +
"code='" + code + '\'' +
", cash=" + cash +
'}';
}
}
输出结果:
线程A运行结束,增加“20”,当前用户账户余额为:120
线程F运行结束,增加“21”,当前用户账户余额为:141
线程E运行结束,增加“32”,当前用户账户余额为:173
线程C运行结束,增加“-80”,当前用户账户余额为:93
线程B运行结束,增加“-60”,当前用户账户余额为:33
线程D运行结束,增加“-30”,当前用户账户余额为:3
Process finished with exit code 0
反面教材,不同步的情况,也就是去掉oper(int x)方法的synchronized修饰符,然后运行程序,结果如下:
线程A运行结束,增加“20”,当前用户账户余额为:61
线程D运行结束,增加“-30”,当前用户账户余额为:63
线程B运行结束,增加“-60”,当前用户账户余额为:3
线程F运行结束,增加“21”,当前用户账户余额为:61
线程E运行结束,增加“32”,当前用户账户余额为:93
线程C运行结束,增加“-80”,当前用户账户余额为:61
Process finished with exit code 0
很显然,上面的结果是错误的,导致错误的原因是多个线程并发访问了竞争资源u,
tb并对u的属性做了改动。
可见同步的重要性。
注意:
通过前文可知,线程退出同步方法时将释放掉方法所属对象的锁,但还应该注意的是,同步方法中还可以使用特定的方法对线程进行调度。这些方法来自于java.lang.Object类。
void notify()
唤醒在此对象监视器上等待的单个线程。
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
void wait()
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
void wait(long timeout)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。
void wait(long timeout, int nanos)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
结合以上方法,处理多线程同步与互斥问题非常重要,著名的生产者-消费者例子就是一个经典的例子,任何语言多线程必学的例子。
摘要: 本篇博文讲两件事情,一个是推荐在博客园经常写博客的童鞋们一个很棒的工具--“百度统计”,另一个是“拷贝百度统计”的页面框架。
首先讲第一个事情,我的博客里有不少文章都是讲“用户行为分析”的,虽然现在不做这个方向的项目,但是对它的兴趣不减,所以我今天在自己博客里部署了百度的用户行为分析系统“百度统计̶...
阅读全文
对1.0版进行了重构,去掉了一些花销的功能,优化了页面样式,现有功能:
1)、一次性把数据加载到页面内存,在页面进行分页。
2)、使用jquery的ajax每次从服务器取数据分页。
3)、支持自定义分页条样式,插件默认实现两种样式可供选择。
- <table id="table2" >
- <thead>
- <tr><th width="200px">网站名称</th>
- <th width="100px">网址</th>
- <th width="100px">知名度</th>
- <th width="120px">访问量</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
-
- $("#table2").bigPage({ajaxData:{url:"ajax.php"}});
<table id="table2" >
<thead>
<tr><th width="200px">网站名称</th>
<th width="100px">网址</th>
<th width="100px">tb知名度</th>
<th width="120px">访问量</th>
</tr>
</thead>
<tbody></tbody>
</table>
$("#table2").bigPage({ajaxData:{url:"ajax.php"}});
更多的例子及代码下载地址:
http://bigui4.sinaapp.com/index.html
摘要: 简介
Spring提供的DAO(数据访问对象)支持主要的目的是便于以标准的方式使用不同的数据访问技术,如JDBC,Hibernate或者JDO等。它不仅可以让你方便地在这些持久化技术间切换, 而且让你在编码的时候不用考虑处理各种技术中特定的异常。
一致的异常层次
Spring提供了一种方便的方法,把特定于某种技术的异常,如SQLException, 转化为自己的异常,这种异常属于以 ...
阅读全文
目前,最常用的三种动态网页语言有ASP(Active Server Pages),JSP(JavaServer Pages),PHP (Hypertext Preprocessor)。
简 介 :
ASP全名Active Server Pages,是一个WEB服务器端的开发环境,利用它可以产生和执行动态的、互动的、高性能的WEB服务应用程序
(1)ASP采用脚本语言VBScript(Java script)作为自己的开发语言。
(2)PHP是一种跨平台的服务器端的嵌入式脚本语言。它大量地借用C, Java和Perl语言的语法, 并耦合PHP自己的特性,使WEB开发者能够快速地写出动态产生页面。它支持目前绝大多数数据库。还有一点,PHP是完全免费的,不用花钱,你可以从PHP官方站点(http: //www.php.net)自由下载。而且你可以不受限制地获得源码,甚至可以从中加进你自己需要的特色。
(3)JSP是Sun公司推出的新一代网站开发语言,Sun公司借助自己在Java上的不凡造诣,将Java从Java应用程序和Java Applet之外,又有新的硕果,就是JSP,Java Server Page。JSP可以在Serverlet和JavaBean的支持下,完成功能强大的站点程序。
三者都提供在 HTML代码中混合某种程序代码、由语言引擎解释执行程序代码的能力。但JSP代码被编译成 Servlet并由Java虚拟机解释执行,这种编译操作仅在对JSP页面的第一次请求时发生。在ASP 、PHP、JSP环境下,HTML代码主要负责描述信息的显示样式,而程序代码则用来描述处理逻辑。普通的 HTML页面只依赖于Web服务器,而ASP 、PHP、JSP页面需要附加的语言引擎分析和执行程序代码。程序代码的执行结果被重新嵌入到HTML代码中,然后一起发送给浏览器。ASP 、PHP、JSP三者都是面向Web服务器的技术,客户端浏览器不需要任何附加的软件支持。
技术特点
ASP:
1. 使用VBScript 、 JScript等简单易懂的脚本语言,结合HTML代码,即可快速地完成网站的应用程序。
2. 无须compile编译,容易编写,可在服务器端直接执行。
3. 使用普通的文本编辑器,如Windows的记事本,即可进行编辑设计。
4. 与浏览器无关(Browser Independence), 客户端只要使用可执行HTML码的浏览器,即可浏览Actbive Server Pages所设计的网页内容。Active ServerPages 所使用的脚本语言(VBScript 、 Jscript)均在WEB服务器端执行,客户端的浏览器不需要能够执行这些脚本语言。
5.Active Server Pages能与任何ActiveX scripting语言兼容。除了可使用VB Script或JScript语言来设计外,还通过plug-in的方式,使用由第三方所提供的其它脚本语言,譬如REXX 、Perl 、Tcl等。脚本引擎是处理脚本程序的COM(Component Object Model) 对象。
6. 可使用服务器端的脚本来产生客户端的脚本。
7. ActiveX Server Components(ActiveX 服务器组件 )具有无限可扩充性。可以使用Visual Basic 、Java 、Visual C++ 、COBOL等程序设计语言来编写你所需要的ActiveX Server Component 。
PHP:
1. 数据库连接
PHP可以编译成具有与许多数据库相连接的函数。PHP与MySQL是现在绝佳的群组合。你还可以自己编写外围的函数去间接存取数据库。通过这样的途径当你更换使用的数据库时,可以轻松地修改编码以适应这样的变化。PHPLIB就是最常用的可以提供一般事务需要的一系列基库。但PHP提供的数据库接口支持彼此不统一,比如对Oracle, MySQL,Sybase的接口,彼此都不一样。这也是PHP的一个弱点。
JSP:
1. 将内容的产生和显示进行分离
使用JSP技术,Web页面开发人员可以使用HTML或者XML标识来设计和格式化最终页面。使用JSP标识或者小脚本来产生页面上的动态内容。产生内容的逻辑被封装在标识和JavaBeans群组件中,并且捆绑在小脚本中,所有的脚本在服务器端执行。如果核心逻辑被封装在标识和Beans中,那么其它人,如Web管理人员和页面设计者,能够编辑和使用JSP页面,而不影响内容的产生。在服务器端,JSP引擎解释JSP标识,产生所请求的内容(例如,通过存取JavaBeans群组件,使用JDBC技术存取数据库),并且将结果以HTML(或者XML)页面的形式发送回浏览器。这有助于作者保护自己的代码,而又保证任何基于HTML的Web浏览器的完全可用性。
2. 强调可重用的群组件
绝大多数JSP页面依赖于可重用且跨平台的组件(如:JavaBeans或者Enterprise JavaBeans)来执行应用程序所要求的更为复杂的处理。开发人员能够共享和交换执行普通操作的组件,或者使得这些组件为更多的使用者或者用户团体所使用。基于组件的方法加速了总体开发过程,并且使得各种群组织在他们现有的技能和优化结果的开发努力中得到平衡。
3. 采用标识简化页面开发
Web页面开发人员不会都是熟悉脚本语言的程序设计人员。JavaServer Page技术封装了许多功能,这些功能是在易用的、与JSP相关的XML标识中进行动态内容产生所需要的。标准的JSP标识能够存取和实例化 JavaBeans组件,设定或者检索群组件属性,下载Applet,以及执行用其它方法更难于编码和耗时的功能。
通过开发定制化标识库,JSP技术是可以扩展的。今后,第三方开发人员和其它人员可以为常用功能建立自己的标识库。这使得Web页面开发人员能够使用熟悉的工具和如同标识一样的执行特定功能的构件来工作。
JSP技术很容易整合到多种应用体系结构中,以利用现存的工具和技巧,并且扩展到能够支持企业级的分布式应用。作为采用Java技术家族的一部分,以及Java 2EE的一个成员,JSP技术能够支持高度复杂的基于Web的应用。
由于JSP页面的内置脚本语言是基于Java程序设计语言的,而且所有的JSP页面都被编译成为Java Servlet,JSP页面就具有Java技术的所有好处,包括健壮的存储管理和安全性。
作为Java平台的一部分,JSP拥有Java程序设计语言“一次编写,各处执行”的特点。随着越来越多的供货商将JSP支持加入到他们的产品中,您可以使用自己所选择的服务器和工具,修改工具或服务器并不影响目前的应用。
应用范围
ASP是Microsoft开发的动态网页语言,也继承了微软产品的一贯传统,只能执行于微软的服务器产品,IIS(Internet Information Server)(windows NT)和PWS(Personal Web Server)(windows 98)上。Unix下也有ChiliSoft的组件来支持ASP,但是ASP本身的功能有限,必须通过ASP+COM的群组合来扩充,Unix下的COM实现起来非常困难。
PHP3可在Windows,Unix,Linux的Web服务器上正常执行,还支持IIS,Apache等一般的Web服务器,用户更换平台时,无需变换PHP3代码,可即拿即用。
JSP同PHP3类似,几乎可以执行于所有平台。如Win NT,Linux,Unix。在NT下IIS通过一个外加服务器,例如JRUN或者ServletExec,就能支持JSP。知名的Web服务器Apache已经能够支持JSP。由于Apache广泛应用在NT、Unix和Linux上,因此JSP有更广泛的执行平台。虽然现在NT操作系统占了很大的市场份额,但是在服务器方面Unix的优势仍然很大,而新崛起的Linux更是来势不小。从一个平台移植到另外一个平台,JSP和JavaBean甚至不用重新编译,因为Java字节码都是标准的与平台无关的。
性能比较
有人做过试验,对这三种语言分别做回圈性能测试及存取Oracle数据库测试。
在循环性能测试中,JSP只用了令人吃惊的四秒钟就结束了20000*20000的回圈。而ASP、PHP测试的是2000*2000循环(少一个数量级),却分别用了63秒和84秒。(参考PHPLIB)。
数据库测试中,三者分别对 Oracle 8 进行 1000 次 Insert,Update,Select和Delete: JSP 需要 13 秒,PHP 需要 69 秒,ASP则 需要73 秒。
前景分析
目前在国内PHP与ASP应用最为广泛。而JSP由于是一种较新的技术,国内采用的较少。但在国外,JSP已经是比较流行的一种技术,尤其是电子商务类的网站,多采用JSP。
采用PHP的网站如新浪网(sina)、中国人(Chinaren)等,但由于PHP本身存在的一些缺点,使得它不适合应用于大型电子商务站点,而更适合一些小型的商业站点。首先,PHP缺乏规模支持。其次,缺乏多层结构支持。对于大负荷站点,解决方法只有一个:分布计算。数据库、应用逻辑层、表示逻辑层彼此分开,而且同层也可以根据流量分开,群组成二维数组。而PHP则缺乏这种支持。还有上面提到过的一点,PHP提供的数据库接口支持不统一,这就使得它不适合运用在电子商务中。
ASP和JSP则没有以上缺陷,ASP可以通过Microsoft Windowsd的COM/DCOM获得ActiveX规模支持,通过DCOM和Transcation Server获得结构支持;JSP可以通过SUN Java的Java Class和EJB获得规模支持,通过EJB/CORBA以及众多厂商的Application Server获得结构支持。
三者中,JSP应该是未来发展的趋势。世界上一些大的电子商务解决方案提供商都采用JSP/Servlet。比较出名的如IBM的E-business,它的核心是采用JSP/Servlet的Web Sphere。它们都是通过CGI来提供支持的。但去年10月后它推出了Enfinity,一个采用JSP/Servlet的电子商务Application Server,而且声言不再开发传统软件。
总之,ASP,PHP,JSP三者都有相当数量的支持者,由此也可以看出三者各有所长。正在学习或使用动态页面的朋友可根据三者的特点选择一种适合自己的语言。
1、数据库当中的表设计
2、对应数据表的实体Bean (id为主键)
- public class EnginConfigVO {
- int id = 0;
- int THREADS_COUNT;
-
-
-
-
- public int primaryGetId() {
- return id;
- }
-
-
-
- public void primarySetId(int id) {
- this.id = id;
- }
-
-
-
- public int getTHREADS_COUNT() {
- return THREADS_COUNT;
- }
-
-
-
-
- public void setTHREADS_COUNT(int tHREADS_COUNT) {
- THREADS_COUNT = tHREADS_COUNT;
- }
- }
public class EnginConfigVO {
int id = 0;
int THREADS_COUNT;
/**
* @return the id
*/
public int primaryGetId() {
return id;
}
/**
* @param id the id to set
*/
public void primarySetId(int id) {
this.id = id;
}
/**
* @return the tHREADS_COUNT
*/
public int getTHREADS_COUNT() {
return THREADS_COUNT;
}
/**
* @param tHREADS_COUNT the tHREADS_COUNT to set
*/
public void setTHREADS_COUNT(int tHREADS_COUNT) {
THREADS_COUNT = tHREADS_COUNT;
}
}
由于没有像hibernate那样的注解机制,所以只能在主键的setter和getter方法上动动手脚primaryGetId() ,primarySetId(int id)
而实体bean的类名在与数据表的匹配上最后多了“vo” 两个字母,所以在tb下面方法中将这两个字母剪裁掉。
反射方法:
T o 对应的就是实体Bean,这样的方法当然是写在DAO层中,供上层的service调用,传入需要修改的实体Bean
- public <T> void updatePropertiesValues(T o) {
- StringBuilder sd = new StringBuilder("update ");
- sd.append(o.getClass().getSimpleName().toLowerCase().substring(0, o.getClass().getSimpleName().length()-2)).append(" ");
- sd.append("set ");
- StringBuilder id = new StringBuilder("where ");
- try {
- for(Method m : o.getClass().getDeclaredMethods()) {
- String name = m.getName();
- if (name.startsWith("get")) {
- sd.append(name.substring(3).toLowerCase()).append("=");
- if(m.invoke(o) instanceof String) {
- sd.append("'").append(m.invoke(o)).append("', ");
- }else {
- sd.append(m.invoke(o)).append(", ");
- }
- }
- if(name.startsWith("primaryGet")) {
- id.append(name.substring(10).toLowerCase()).append("=");
- if(m.invoke(o) instanceof String) {
- id.append("'").append(m.invoke(o)).append("';");
- }else {
- id.append(m.invoke(o)).append(";");
- }
- }
- }
- sd.delete(sd.length()-2, sd.length());
- sd.append(" ");
- sd.append(id);
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
-
- executeTrans(sd.toString());
-
- }
public <T> void updatePropertiesValues(T o) {
StringBuilder sd = new StringBuilder("update ");
sd.append(o.getClass().getSimpleName().toLowerCase().substring(0, o.getClass().getSimpleName().length()-2)).append(" ");
sd.append("set ");
StringBuilder id = new StringBuilder("where ");
try {
for(Method m : o.getClass().getDeclaredMethods()) {
String name = m.getName();
if (name.startsWith("get")) {
sd.append(name.substring(3).toLowerCase()).append("=");
if(m.invoke(o) instanceof String) {
sd.append("'").append(m.invoke(o)).append("', ");
}else {
sd.append(m.invoke(o)).append(", ");
}
}
if(name.startsWith("primaryGet")) {
id.append(name.substring(10).toLowerCase()).append("=");
if(m.invoke(o) instanceof String) {
id.append("'").append(m.invoke(o)).append("';");
}else {
id.append(m.invoke(o)).append(";");
}
}
}
sd.delete(sd.length()-2, sd.length());
sd.append(" ");
sd.append(id);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
executeTrans(sd.toString());
}
这样以后便可以拼凑出完整的sql语句,为我们解决了功能相似代码的冗余。。
另外在查找时,我们还可以利用发射机制,将数据库返回的resultset 对象包装成List<T>
- public static <T> List<T> getObjectsList(ResultSet rs, Class<T> k)
- throws SQLException {
- List<T> bl = new ArrayList<T>();
- if (rs != null) {
- while (rs.next()) {
-
- T o = null;
- try {
- o = k.newInstance();
- for (Method m : k.getDeclaredMethods()) {
- String name = m.getName();
- if (name.startsWith("set")) {
-
- m.invoke(o, rs.getObject(name.substring(3)));
- }
- }
- bl.add(o);
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- }
- return bl;
- }
- return null;
- }
public static <T> List<T> getObjectsList(ResultSet rs, Class<T> k)
throws SQLException {
List<T> bl = new ArrayList<T>();
if (rs != null) {
while (rs.next()) {
// System.out.println("result is not null");
T o = null;
try {
o = k.newInstance();
for (Method m : k.getDeclaredMethods()) {
String name = m.getName();
if (name.startsWith("set")) {
// System.out.println(rs.getObject(name.substring(3)).getClass().getName());
m.invoke(o, rs.getObject(name.substring(3)));
}
}
bl.add(o);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return bl;
}
return null;
}
这样,我们就可以从底层直接获得包装好的List<T>集合。。不足之处,欢迎大家讨论。。
java 单例加锁方法:
ScheduleEngine是个单例类,在获得实例的方法getinstance中,两次判断其是否为空,有利于多线程的并发操作。
使得实例化时,只在第一次加锁,这样效率会有提高。
- class ScheduleEngine{
-
- private static Lock instanceLock=new ReentrantLock();
-
- private ScheduleEngine() {
- setproperties;
- }
-
- public static ScheduleEngine getInstance(int temphandlerType) {
- if(null==engine) {
- instanceLock.lock();
- try
- {
- if(null==engine)
- {
- handlerType=temphandlerType;
- engine=new ScheduleEngine(temphandlerType);
- }
-
- }
- finally
- {
- instanceLock.unlock();
- }
- }
- return engine;
- }
- }
class ScheduleEngine{
private static Lock instanceLock=new ReentrantLock();
private ScheduleEngine() {
setproperties;
}
public static ScheduleEngine getInstance(int temphandlerType) {
if(null==engine) {
instanceLock.lock();
try
{
if(null==engine)
{
handlerType=temphandlerType;
engine=new ScheduleEngine(temphandlerType);
}
}
finally
{
instanceLock.unlock();
}
}
return engine;
}
}
初始实例化 单例c3p0对象的方法,常用的是
public final class ConnectionManager {
private static ConnectionManager instance;
private static ComboPooledDataSource cpds;
private static String c3p0Properties;
/**
* 从数据库连接池取连接
* @throws Exception
*/
private ConnectionManager() throws Exception {
Properties p = new Properties();
c3p0Properties = System.getProperty("user.dir") +
"/mom_project_config/database.properties";
// p.load(this.getClass().getClassLoader().getResourceAsStream(c3p0Properties));
p.load(new BufferedInputStream(new FileInputStream(c3p0Properties)));
// String url = p.getProperty("url") + p.getProperty("database");
String url = p.getProperty("url") + p.getProperty("database")+"?useUnicode=true&characterEncoding=UTF-8";
cpds = new ComboPooledDataSource();
cpds.setDriverClass(p.getProperty("driverclass"));
cpds.setJdbcUrl(url);
cpds.setUser(p.getProperty("user"));
cpds.setPassword(p.getProperty("password"));
// 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 acquireIncrement
cpds.setAcquireIncrement(Integer.valueOf(p
.getProperty("acquireincrement")));
// 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 acquireRetryAttempts
cpds.setAcquireRetryAttempts(Integer.valueOf(p
.getProperty("acquireretryattempts")));
// 两次连接中间隔时间,tb单位毫秒。Default: 1000 acquireRetryDelay
cpds.setAcquireRetryDelay(Integer.valueOf(p
.getProperty("acquireretrydelay")));
// 自动提交事务;连接关闭时默认将所有未提交的操作回滚。Default: false autoCommitOnClose
cpds.setAutoCommitOnClose(Boolean.valueOf(p
.getProperty("autocommitonclose")));
// 当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,
// 如设为0则无限期等待.单位毫秒,默认为0
cpds.setCheckoutTimeout(Integer.valueOf(p
.getProperty("checkouttimeout")));
// 每多少秒检查所有连接池中的空闲连接。默认为0表示不检查。Default: 0 idleConnectionTestPeriod
cpds.setIdleConnectionTestPeriod(Integer.valueOf(p
.getProperty("idleconnectiontestperiod")));
// 最大空闲时间,25000秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 maxIdleTime
cpds.setMaxIdleTime(Integer.valueOf(p.getProperty("maxidletime")));
// 初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 initialPoolSize
cpds.setInitialPoolSize(Integer.valueOf(p
.getProperty("initialpoolsize")));
// 连接池中保留的最小连接数。
cpds.setMinPoolSize(Integer.valueOf(p.getProperty("minpoolsize")));
// 连接池中保留的最大连接数。Default: 15 maxPoolSize
cpds.setMaxPoolSize(Integer.valueOf(p.getProperty("maxpoolsize")));
// JDBC的标准参数,用以控制数据源内加载的PreparedStatement数据.但由于预缓存的Statement属于单个Connection而不是整个连接池.所以
// 设置这个参数需要考滤到多方面的因素,如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭.默认为0;
cpds.setMaxStatements(Integer.valueOf(p.getProperty("maxstatements")));
// 连接池内单个连接所拥有的最大缓存被关闭.默认为0;
cpds.setMaxStatementsPerConnection(Integer.valueOf(p
.getProperty("maxstatementsperconnection")));
// C3P0是异步操作的,缓慢的JDBC操作通过帮助进程完成.扩展这些操作可以有效的提升性能,通过多数程实现多个操作同时被执行.默为为3
cpds.setNumHelperThreads(Integer.valueOf(p
.getProperty("numhelperthreads")));
// 用户修改系统配置参数执行前最多等待的秒数.默认为300;
cpds.setPropertyCycle(Integer.valueOf(p.getProperty("propertycycle")));
// 如果设为true那么在取得连接的同时将校验连接的有效性。Default: false testConnectionOnCheckin
cpds.setTestConnectionOnCheckin(Boolean.valueOf(p
.getProperty("testconnectiononcheckin")));
// 因性能消耗大请只在需要的时候使用它。
// 如果设为true那么在每个connection提交的时候都将校验其有效性。
// 建议使用idleConnectionTestPeriod或automaticTestTable等方法来提升连接测试的性能。Default:
// false testConnectionOnCheckout
cpds.setTestConnectionOnCheckout(Boolean.valueOf(p
.getProperty("testconnectionOncheckout")));
// 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。
// 但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。
// 如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。Default: false
// breakAfterAcquireFailure
cpds.setBreakAfterAcquireFailure(Boolean.valueOf(p
.getProperty("breakafteracquirefailure")));
}
/**
* 获得ConnectionManager单例对象
* @return
*/
public synchronized static ConnectionManager getInstance() {
if (instance == null) {
try {
instance = new ConnectionManager();
} catch (Exception e) {
e.printStackTrace();
}
}
return instance;
}
/**
* 获得连接
* @return
*/
public Connection getContection() {
try {
return cpds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
在初始化获得单例的方法上面加锁,不利于并发操作的执行,用第一段代码两次判断是否为空的方式,可以减少代码执行中锁的引用。 不足之处烦请朋友们指正。。
Java在网络编程这个地方做的很好,java的主要目的也是为了网络而生的,它能方便的访问网络上的资源。我们这节课来介绍网络通讯的两种机制:URL通信机制,Socket通信机制。
URL表示了Internet上一个资源的引用或地址。Java网络应用程序也是使用URL来定位要访问的Internet的资源。在jdk里面java.net.URL也是一个类,它来封装URL的一些细节。目前大家可以把URL理解为网址,default.aspx 这就是个URL.http是协议名(超文本传输协议)用“://”隔开www.tbwshc.com 是主机名。Default.aspx是文件名。它的端口号没有写,默认是80.
实践:
import java.net.*;
public class ParseURL {
public static void main(String[] args) throws MalformedURLException{
URL url = new URL("http://www.100jq.com:45175/default.aspx");
System.out.println("协议是 "+url.getProtocol());
System.out.println("主机是 "+url.getHost());
System.out.println("文件名是 "+url.getFile());
System.out.println("端口号是 "+url.getPort());
}}
/*
URL这个对象中提供了很多方法像是
getProtocol()
getHost()
getFile()
getPort()
*/ |
我们可以通过URL对文件或资源读取,tb也可以通过URLConnection读取,也可以通过这个写入数据限于cgi脚本。
实践:
import java.net.*;
import java.io.*;
public class URLConnectionReader {
public static void main(String[] args) throws IOException {
URL google = new URL("");
URLConnection g = google.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(g.getInputStream()));
String inputLine;
while ((inputLine=in.readLine())!=null)
System.out.println(inputLine);
in.close();
}} |
URL和URLConnection类提供了较高层次的网络访问。有时候需要进行较低层次的访问。编写C/S模型的程序时,就要使用Socket通信机制了。因为在网络上不一定非得访问文件。
实践:
//先写个客户端的应用
import java.net.*;
import java.io.*;
public class SimpleClient {
public static void main(String args[]) {
try {
// 在5432端口打开服务器连接
// 在这里用localhost与127.0.0.1是一个意思
Socket s1 = new Socket("127.0.0.1", 5432);
// 对这个端口连接一个reader,注意端口不能够占用别的
BufferedReader br = new BufferedReader(
new InputStreamReader(s1.getInputStream()));
// 读取输入的数据并且打印在屏幕上
System.out.println(br.readLine());
//当完成时关闭流和连接
br.close();
s1.close();
} catch (ConnectException connExc) {
System.err.println("Could not connect to the server.");
} catch (IOException e) {
// ignore
}}}
//这是服务端的应用
import java.net.*;
import java.io.*;
public class SimpleServer {
public static void main(String args[]) {
ServerSocket s = null;
// 注册服务端口为5432
try {
s = new ServerSocket(5432);
} catch (IOException e) {
e.printStackTrace();
}
// 运行监听器并接收,永远循环下去。因为服务器总要开启的
while (true) {
try {
// 等待一个连接的请求
Socket s1 = s.accept();
// 得到端口的输出流
OutputStream s1out = s1.getOutputStream();
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(s1out));
// 发送一个字符串
bw.write("百家拳软件项目研究室欢迎您!\n");
// 关闭这个连接, 但不是服务端的socket
bw.close();
s1.close();
} catch (IOException e) {
e.printStackTrace();
}}}} |
下载 href="http://java.chinaitlab.com/download/07072213182935.rar" target=_blank>上述例子打包下载
执行这个程序和其它的不太一样,先用javac将两个文件编译之后。然后敲start开启另一个窗口。用start命令开启的窗口继承了原来窗口的特性。如图26-1所示
图26-1
接着在原来的窗口上执行服务端程序java SimpleServer.在新窗口中执行java SimpleClient 就会看到结果了。注意如果如果在启动服务端的时候抛出bindException则说明5432这个端口已经被别的程序占用着,改成别的端口号就可以了。通常选用端口的时候,其数字最好不要小于1024,1024一下的端口很多都是专用的端口。
现在的java界,很多东西叫××let,××let的意思都是些小程序的意思。例如:applet应用程序的小程序,servlet服务器端的小程序,midlet手机中的小程序,portlet门户容器端的小程序。这节我们介绍applet.这个东西用的不是很多,但是在java的体系结构中是很有意义的。这个东西是能够在浏览器里运行的,可以潜入到HTML页面里。我们知道普通的Application要有main()作为入口点运行,而Applet要在浏览器里运行,或者开发时查看的时候用appletviewer运行。举个例子,实践:
import java.awt.*;
import java.applet.*;
@SuppressWarnings("serial") //抑制警告
//所有的Applet,都继承了java.applet.Applet
public class HelloApplet extends Applet {
public void paint(Graphics g){
g.drawString("百家拳软件项目研究室!",30,30);
}} |
还需要建立一个html文件,因为刚才说了它可以嵌入在浏览器里面。用记事本建立一个hello.html代码如下:
<applet code="HelloApplet.class" width=150 heightb=150></applet>
之后照样用javac编译刚才那个类。最后在命令行中输入appletviewer hello.html可以看到结果。
这种小程序弥补了B/S模型的不足,用浏览器可以执行客户端的东西。因为它功能强大,所以是个不错的东西。可是功能太强大了,又引发了一些安全性的问题。所以浏览器也会对applet做了一些安全性的限制。Applet还有一种叫做沙箱模型的机制,它使得没有访问权限的资源,不能访问。保证了安全性。同时开发时也不是那么方便。Applet又跨平台的特性。
而且微软的IE浏览器里面在运行applet的时候速度不是很快,不如activex的方便。界面也不是太漂亮。不过它的这种在浏览器中运行的思想还是比较不错的。
1 、对象的初始化
(1 )非静态对象的初始化
在创建对象时,对象所在类的所有数据成员会首先进行初始化。
基本类型:int 型,初始化为0.
如果为对象:这些对象会按顺序初始化。
※在所有类成员初始化完成之后,才调用本类的构造方法创建对象。
构造方法的作用就是初始化。
(2 )静态对象的初始化
程序中主类的静态变量会在main方法执行前初始化。
不仅第一次创建对象时,类中的所有静态变量都初始化,并且第一次访问某
类(注意此时未创建此类对象)的静态对象时,所有的静态变量也要按它们在类
中的顺序初始化。
2 、继承时,对象的初始化过程
(1 )主类的超类由高到低按顺序初始化静态成员,无论静态成员是否为private.
(2 )主类静态成员的初始化。
(3 )主类的超类由高到低进行默认构造方法的调用。注意,在调用每一个
超类的默认构造方法前,先进行对此超类进行非静态对象的初始化。
(4 )主类非静态成员的初始化。
(5 )调用主类的构造方法。
3 、关于构造方法
(1 )类可以没有构造方法,但如果有多个构造方法,就应该要有默认的构
造方法,否则在继承此类时,需要在子类中显式调用父类的某一个非默认的构造
方法了。
(2 )在一个构造方法中,只能调用一次其他的构造方法,并且调用构造方
法的语句必须是第一条语句。
4 、有关public、private 和protected
(1 )无public修饰的类,可以被其他类访问的条件是:a.两个类在同一文
件中,b.两个类在同一文件夹中,c.两个类在同一软件包中。
(2 )protected :继承类和同一软件包的类可访问。
(3 )如果构造方法为private ,那么在其他类中不能创建该类的对象。
5 、抽象类
(1 )抽象类不能创建对象。
(2 )如果一个类中一个方法为抽象方法,则这个类必须为abstract抽象类。
(3 )继承抽象类的类在类中必须实现抽象类中的抽象方法。
(4 )抽象类中可以有抽象方法,也可有非抽象方法。抽象方法不能为private.
(5 )间接继承抽象类的类可以不给出抽象方法的定义。
6 、final 关键字
(1 )一个对象是常量,不代表不能转变对象的成员,仍可以其成员进行操
作。
(2 )常量在使用前必须赋值,但除了在声明的同时初始化外,就只能在构
造方法中初始化。
(3 )final 修饰的方法不能被重置(在子类中不能出现同名方法)。
(4 )如果声明一个类为final ,则所有的方法均为final ,无论其是否被
final 修饰,但数据成员可为final 也可不是。
7 、接口interface (用implements来实现接口)
(1 )接口中的所有数据均为static和final 即静态常量。尽管可以不用这
两个关键字修饰,但必须给常量赋初值。
(2 )接口中的方法均为public,在实现接口类中,实现方法必须可public
关键字。
(3 )如果使用public来修饰接口,则接口必须与文件名相同。
8 、多重继承
(1 )一个类继承了一个类和接口,那么必须将类写在前面,接口写在后面,
接口之间用逗号分隔。
(2 )接口之间可多重继承,注意使用关键字extends.
(3 )一个类虽只实现了一个接口,但不仅要实现这个接口的所有方法,还
要实现这个接口继承的接口的方法,接口中的所有方法均须在类中实现。
9 、接口的嵌入
(1 )接口嵌入类中,可以使用private 修饰。此时,接口只能在所在的类
中实现,其他类不能访问。
(2 )嵌入接口中的接口一定要为public.
10、类的嵌入
(1 )类可以嵌入另一个类中,但不能嵌入接口中。
(2 )在静态方法或其他方法中,不能直接创建内部类tb对象,需通过手段来
取得。
手段有两种:
class A { class B {} B getB () { B b = new B(); return b ; }
} static void m () { A a = new A(); A.B ab = a.getB(); // 或者
是 A.B ab = a.new B (); }
(3 )一个类继承了另一个类的内部类,因为超类是内部类,而内部类的构
造方法不能自动被调用,这样就需要在子类的构造方法中明确的调用超类的构造
方法。接上例:
class C extends A.B { C () { new A()。super (); // 这一句就
实现了对内部类构造方法的调用。 } }
构造方法也可这样写:
C (A a ) { a.super(); } // 使用这个构造方法创建对象,要写成C
c = new C (a ); a是A 的对象。
11、异常类
JAVA中除了RunTimeException类,其他异常均须捕获或抛出。
从很小都认识苹果机了,我不记得我是否在小学的时候学过Basic,只记得大学实验室里的苹果机我的确是摸过(得益于我是教师子弟,有“特殊待遇”),也看到计算机系的学生们编写的游戏。初中,有了自己的游戏机。玩过魂斗罗,坦克。当时觉得很不过瘾,心想以后能自己编写游戏就好了,于是立志以后做个程序员。
高考不顺利,只考上了普通学校电力专业。这还是幸亏当时学校的罗老师(那四年,她一直都在帮助我)看到我以前的成绩还不错,决定要下我,否则,我就往下落的更厉害了。电力专业几乎没有计算机课程。等到学校关于自动化的时候,开始接触了汇编,和自学了C.当时很羡慕学计算机的那个女老乡,姓杨,呵呵,因为羡慕,还被别人误认为我喜欢她,其实完全不是,她根本对计算机没有兴趣,毕业后也去了当公务员,可惜啊,早知道如此,她何必要高出几分,占据我喜欢的专业呢,我甚至为此感到暗自不爽。 不过大学还是学到了一些计算机皮毛知识,C程序写的很好,记得写了一个模仿TT的打字程序。汇编也不错,写个文件病毒,源代码10K,编译链接后3K多,很大,AV95能识别出来,我想大概是我写的太烂,别的杀毒程序,象KV300,都不认为这是个病毒。不管怎么样,我没有拿这个干啥坏事情。这始终是不光彩的事情。
该毕业了,家乡的供电局没能进去。我怨我老妈没有帮我跑关系,其实我跟我老妈都不愿意我去,我老妈是不想让我回铜仁,我自己也不想做电力职工(虽然在我们那是一等的暴有钱的工作),我还是喜欢去做个程序员,为此也退掉了别的电力相关的工作。但是,我始终不到该如何入门。毕业了,门卫老头开始赶我们出去,我工作无着落,同学们都已经回到家乡开始上班了,我还在跟老头打游击。他进我退,他退我上床休息,有次晚上洗澡,被他发现,吓得我光着屁股从三楼跑到5楼,再跑回三楼。呵呵,那时候整个宿舍楼都空了,所以也不算丢脸了。
好运终于坚持到了,网上碰到一网友,后来我叫他秦哥,他说他需要一个人帮他做个网站。我便毛遂自荐了一下,其实,那时候我不懂做网站,不懂ASP,不过我相信我的能力,果然,一段适应时间后,我成了他得力的帮手,我也开始正式进入程序员这个行业了。相比现在的很多学生,我觉得他们比我幸运多了,在大学的时候都已经学习到很多知识,甚至是已经有一定的实践了。刚毕业就能踏入这行,还能有地方住,要知道我不光要跟老头打游击,有时候还睡在电脑城广场的板凳上,早上起来看的第一眼便是保安和他身边对我俯视眈眈的狼狗。
搞懂了ASP和网站后,开始考虑学更多的东西,这时候我已经放弃了我编写游戏程序的梦想了,因为我跟本不知道如何去追逐这个梦想。我也放弃了我比较擅长的单片机开发(现在应该叫嵌入式)。我转向了Java.俗话说,女怕嫁错狼,男怕入错行。8年前的这个时候,我算是马马虎虎开始我的JavaEE道路吧,这儿有点体会就是一定要坚持自己的理想,而这个理想,应该能养活你的,能让你有兴趣做的事情。
初学Java,有些迷惑,当时微软有个VJ++,我先买了一本介绍这样的书看,结构后来发现它主要是桌面程序的,而且,跟我知道的JSP不太一样。当时也没有想到可以找人问或者论坛上发给帖子问。幸好后来明智的转到了JSP,挺简单,跟ASP差不多,tb概念都能通用(毕竟处理的问题都一样嘛),比起现在的孩子来说,我当时学的东西太少了,不用学习hibernate,spring,j2ee,也不用学习ant,Junit什么的,呵呵,关键还是当时书太少,见识少,也没有这么多新玩意。好处就是我能深入JSP技术,为以后理解这些Web框架打下了很好的基础。不象现在的孩子,还搞不懂JSP,就去弄MVC,搞的本末倒置了。
J2EE技术得到提高得益于后来到了北京,去了ZZ公司,现在看来,好不夸张的说,从这个公司出来的程序员,都有一定创新能力和解决问题能力。一到这公司,就做了一个算是大的项目,几十个人,还包括国防科技大学的数十个博士,当时用到了很多J2EE技术,象EJB,JMS都用到了,当时不懂这些,费了很多力气去学,还好项目本身就是个很好的学习材料。通过专研项目代码学到了不少东西,远比看书强多了。现在的很多培训方式都是通过做独立完成项目来学习技术,这是很有道理的。当时那个项目做了一年,期间我对自己要求蛮高的,总会多学点东西,比如学了EJB 无状态会话Bean,虽然项目没有用到有状态Bean,但还是花时间去搞明白。这个项目期间,头一次知道了还有英文资料这么一说,象什么Weblogic使用说明,Java文档都,我都会强迫自己去看,有时候打印下来,躺在小床,打开台灯看,那感觉真是美阿。
对于J2ME编程开发平台,在其他平台风生水起的时候,J2ME编程开发平台似乎很沉寂。本文将简单介绍一下几个J2ME编程开发平台的重要概念。
内存
我们一直在强调,移动信息设备的内存非常小,使用起来应该加倍的珍惜,但是我们却很少知道这些内存是如何分类的,下面将做详细的介绍。事实上MIDP设备的内存分为三种,
1.ProgrammeMemory、Heap、persistentStorage
ProgrammeMemory是移动信息设备分配给MIDletsuite的空间,因为MIDletsuite是以jar文件进行发布的,所以这个文件的大小可以认为是ProgrammeMemory的大小。一些厂商对ProgrammeMemory的最大值是有限制的,例如我的Nokia6108的最大值是64k,超过的话将不能进行安装。减小MIDletsuite的大小非常重要,一个便捷的方法就是使用混淆器对应用程序进行混淆,这样可以减小jar文件的大小。在以后的文章中我会谈到如何使用Proguard.
Heap是应用程序在运行过程中存放所创建的对象的存储空间,tb本地变量和成员变量也是放在Heap上的,MIDP设备中提供的Heap空间大概在几十k到几百K.
PersistentStorage的空间是用来实现MIDP应用程序的本地数据持久性存储的,在RecordManagementSystem从入门到精通中我做了详细的介绍这里就不再多说了。
2.ConnectedLimitedDeviceConfiguration
CLDC包括一个Java虚拟机和一系列的基础类,J2ME的专家组经过对移动信息设备进行硬件抽象后得到他们的特点,然后设计并实现了在移动信息设备上运行的java虚拟机,通常我们把它叫做KVM.在CLDC1.0还同时提供了由java.io、java.lang、javax.microediton.io、java.util组成的基础类。在CLDC1.1里面添加了java.lang.ref.
3.MobileInfomationDeviceProfile
MIDP是运行在CLDC基础之上的,在MIDP中定义了应用程序的生命周期、用户图形界面、数据管理系统等子集,从而构建起了J2ME平台。通常,J2ME平台由一个CLDC和一个或者多个Profile构成。
Jquery是继prototype之后又一个优秀的Javascrīpt框架。它是轻量级的js库,除了兼容CSS3外,还兼容各种浏览器+)。jQuery使用户能更方便地处理HTML documents、events、实现动画效果,并且方便地为网站提供AJAX交互。
HTML5 中新定义的 HTML 元素,可以用来在 HTML 页面中通过 JavaScriptb 绘制图形、制作动画。现在要推荐的 jCanvas 就是一个 jQuery 的绘图插件,它封装了一些绘制图形的方法,只需编写几行代码即可生成图形。
以下是JCanvas 绘制窗口并对其监听的程序代码分享
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
class JCanvas extends JComponent
{
public JCanvas()
{
setDoubleBuffered(true);
}
public void paintComponent(Graphics g)
{
Dimension size = getSize();
g.setColor(getBackground());
g.fillRect(0,0,size.width,size.height);
}
}
class TestJCanvas
{
public static void main(String s[] )
{
MyWindowListener l = new MyWindowListener();
JCanvas c = new JCanvas();
c.setBackground(Color.yellow);
JFrame f = new JFrame("Test JCanvas...");
f.addWindowListener(l);
f.getContentPane().add(c,BorderLayout.CENTER);
f.pack();
f.setSize(500,400);
f.show();
}
}
class MyWindowListener extends WindowAdapter
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
虽然jQuery上手简单,相比于其他库学习起来较为简单,但是要全面掌握,却不轻松。因为它涉及到网页开发的方方面面,提供的方法和内部变化有上千种之多。初学者常常感到,入门很方便,提高很困难。本文的目标是将jQuery选择器做一个系统的梳理,试图理清jQuery的设计思想,找出学习的脉络,使读者从入门到精通。
jQuery是什么
简单的说,jQuery是一个JavaScript框架,它的宗旨是:写更少的代码,做更多的事情。对于Web开发人员而言,jQuery是一个功能强大的JavaScript库,能更加快速开发相关应用,例如AJAX交互,JavaScript动画效果等。对于Web设计师而言,jQuery封装了Javascript源码细节,实现了与HTML标签的有效分离,便于设计师更加专注于Web页面设计效果。基于此,网页的用户体验大大增强,包括网页的交互性,视觉效果等等。
jQuery的核心设计思想是:选择某个网页元素,然后对其进行某种操作。那么如何选择、定位某个网页元素呢?对于JavaScript开发人员而言,通常的一种手段是document.getElementById()。而在jQuery语法中,使用的是美元符号“$”,等价的选择表达式写法为:
var someElement = $("#myId");
jQuery之所以称之为“jQuery”,主要就是因为它强大的选择器,也就是Javascript Query的意思。下面,我们具体介绍jQuery选择器相关的设计思想:
一、jQuery基本选择器
前面提到,选择器是jQuery的特色。jQuery的基本选择器主要分为tb以下五种类型:
1. $(“#myId”) // 选择ID为myId的网页元素
2. $(“标签名”) // 例如$(“div”)获取的就是HTML文档中的所有的div元素的jQuery对象集合
3. $(“.myClass”) // 获取的是HTML文档中所有的class为“myClass”的元素集合
4. $(“*”) // 这个获取的是HTML文档中的所有的元素
5. $(“selector1,selector2,selector3…selectorN “) // 这种选择器叫做组选择器。例如:$(“span,#two”)
// 选取所有的span标签元素和id=two的元素。
二、jQuery层次选择器
无论何时,通过jQuery选择器获取的jQuery对象任何时候都是一组元素。jQuery的层次选择器主要分为以下两种类型:
1. $(“ancestor descendant”):选取parent元素后所有的child元素。ancestor的中文意思是“祖先”,descendant的中文意思是“后代”。例如:
$(“body div”) 选取body元素下所有的div元素。
$(“div#test div”) 选取id为“test”的div所包含的所有的div子元素
2. $(“parent > child”):选取parent元素后所有的第一个child元素。例如:
$(“body > div”) 选取body元素下所有的第一级div元素。
$(“div#test > div”) 选取id为“test”的div所包含的所有的第一级div子元素
三、jQuery过滤选择器
jQuery最基本过滤选择器包括:
1. :first // 选取第一个元素。$(“div:first”)选取所有div元素中的第一个div元素
2. :last // 选取最后一个元素。$(“div:last”)选取所有div元素中的最后一个div元素
3. :even // 选取索引是偶数的所有元素。$(“input:even”)选取索引是偶数的input元素。
4. :odd // 选取索引是奇数的所有元素。$(“input:odd”)选取索引是奇数的input元素。
5. :eq(index) // 选取索引等于index的元素。$(“input:eq(1)”)选取索引等于1的input元素。
6. :gt(index) // 选取索引大于index的元素。$(“input:gt(1)”)选取索引大于1的input元素。
7. :lt(index) // 选取索引小于index的元素。$(“input:lt(3)”)选取索引小于3的input元素。
jQuery内容过滤选择器,可以轻松地对DOM文档中的文本内容进行筛选,从而准确地选取我们所需要的元素。
1. :contains(text) // 选取含有文本内容为“text”元素。$(“div:contains(‘你’)”)选取含有文本“你”的div元素。
2. :empty // 选取不包含子元素和文本的空元素。$(“div:empty”)选取不包含子元素(包括文本元素)的div空元素。
3. :parent // 选取含有子元素或者文本的元素。$(“div:parent”)选取拥有子元素(包括文本元素)的div元素。
可以看见,jQuery内容过滤选择器的过滤规则主要体现在它所包含的子元素或文本内容上。
jQuery可见性过滤选择器的用法如下:
1. :hidden // 选取所有不可见的元素。$(“:hidden”)选取网页中所有不可见的元素。
2. :visible // 选取所有可见元素。$(“div:visible”)选取所有可见的div元素。
jQuery属性过滤选择器的过滤规则是通过元素的属性来获取相应的元素。
1. [attribute] // 选择拥有此属性的元素。$(“div[id]“)选取拥有属性id的元素。
2. [attribute=value] // 选取属性值为value的元素。$(“div[name=test]“)选取属性name的值为“test”的div元素。
3. [attribute!value] // 选取属性值不等于value的元素。
4.[attribute^=value] // 选取属性的值以value开始的元素。
5. [attribute$=value] // 选取属性的值以value为结束的元素。
6. [attribute*=value] // 选取属性的值含有value的元素。
7. [selector1] [selector2] [selectorN] //复合属性选择器。$(“div[id][name*=test]“)选取拥有属性id,且属性name的值含有“test”的div元素
jQuery子元素过滤选择器的过滤规则相对于其它的选择器稍微有些复杂。
1. :nth-child(index/even/odd/equation) // 选取每个父元素下的第index个子元素或者奇偶元素。
2. :first-child // 选取每个父元素的第一个子元素
3. :last-child // 选取每个父元素的最后一个子元素
jQuery表单对象属性过滤选择器主要是对所选择的表单元素进行过滤,例如选择不可用的表单元素、被选中的下拉框、多选框等等。
1. :enabled // 选取所有可用的表单元素。$(“#form1 :enabled”)选取id为“form1”的表单内的所有可用元素。
2. :disabled // 选取所有不可用的表单元素。
3. :checked // 选取所有被选中的元素。$(“input:checked”)选取所有被选中的元素。
4. :selected // 选取所有被选中的选项元素。$(“select :selected”)选取所有被选中的选项元素(option)。
jQuery中引入了表单选择器,让我们能极其方便地获取到一个表单中的某个或某类型的元素。
在前一节实现异步调用的基础上 , 现在我们来看一下一个完善的 Java 异步消息处理机制 .
[ 写在本节之前 ]
在所有这些地方 , 我始终没有提到设计模式这个词 , 而事实上 , 多线程编程几乎每一步都在应该设计模式 . 你只要能恰如其份地应用它 , 为什么要在意你用了某某名称的模式呢 ?
一个说书人它可以把武功招数说得天花乱坠 , 引得一班听书客掌声如雷 , 但他只是说书的 . 真正的武林高手也许并不知道自己的招式在说书人口中叫什么 , 这不重要 , 重要的是他能在最恰当的时机把他不知名的招式发挥到极致 !
你了解再多的设计模式 , 或你写出了此类的著作 , 并不重要 , 重要的是你能应用它设计出性能卓越的系统 .
本节的这个例子 , 如果你真正的理解了 , 不要怀疑自己 , 你已经是 Java 高手的行列了 . 如果你抛开本节的内容 , 五天后能自己独立地把它再实现一次 , 那你完全可以不用再看我写的文章系列了 , 至少是目前 , 我再也没有更高级的内容要介绍了 .
上节的 Java 异步调用为了简化调用关系 , 很多角色被合并到一个类中实现 , 为了帮助大家改快地抓住核心的流程 . 那么一个真正的异步消息处理器 , 当然不是这样的简单 .
一. 它要能适应不同类型的请求 :
本节用 makeString 来说明要求有返回值的请求 . 用 displayString 来说明不需要返回值的请求 .
二. 要能同时并发处理多个请求 , 并能按一定机制调度 :
本节将用一个队列来存放请求 , 所以只能按 FIFO 机制调度 , 你可以改用 LinkedList, 就可以简单实现一个优先级 ( 优先级高的 addFirst, 低的 addLast).
三. 有能力将调用的边界从线程扩展到机器间 (RMI)
四. 分离过度耦合 , 如分离调用句柄 ( 取货凭证 ) 和真实数据的实现 . 分离调用和执行的过程 , 可以尽快地将调返回 .
现在看具体的实现 :
public interface Axman {
Result resultTest(int count,char c);
void noResultTest(String str);
}
这个接口有两个方法要实现 , 就是有返回值的调用 resultTest 和不需要返回值的调用
noResultTest, 我们把这个接口用一个代理类来实现 , 目的是将方法调用转化为对象 , 这样就可以将多个请求 ( 多个方法调 ) 放到一个容器中缓存起来 , 然后统一处理 , 因为 Java 不支持方法指针 , 所以把方法调用转换为对象 , 然后在这个对象上统一执行它们的方法 , 不仅可以做到异步处理 , 而且可以将代表方法调用的请求对象序列化后通过网络传递到另一个机器上执行 (RMI). 这也是 Java 回调机制最有力的实现 .
一个简单的例子 .
如果 1: 做 A
如果 2: 做 B
如果 3: 做 C
如果有 1000 个情况 , 你不至于用 1000 个 case 吧 ? 以后再增加呢 ?
所以如果 C/C++ 程序员 , 会这样实现 : (c 和 c++ 定义结构不同 )
type define struct MyStruct{
int mark;
(*fn) ();
} MyList;
然后你可以声明这个结构数据 :
{1,A,
2,B
3,C
}
做一个循环 :
for(i=0;i<length;i++) {
if( 数据组 [i].mark == 传入的值 ) ( 数据组 [i].*fn)();
}
简单说 c/c++ 中将要被调用的涵数可以被保存起来 , 然后去访问 , 调用 , 而 Java 中 , 我们无法将一个方法保存 , 除了直接调用 , 所以将要调用的方法用子类来实现 , 然后把这些子类实例保存起来 , 然后在这些子类的实现上调用方法 :
interface My{
void test();
}
class A implements My{
public void test(){
System.out.println(“A”):
}
}
class B implements My{
public void test(){
System.out.println(“B”):
}
}
class C implements My{
public void test(){
System.out.println(“C”):
}
}
class MyStruct {
int mark;
My m;
public MyStruct(int mark,My m){this.mark = amrk;this.m = m}
}
数组 :
{ new MyStruct(1,new A()),new MyStruct(2,new B()),new MyStruct(3,new C())}
for(xxxxxxxxx) if( 参数 == 数组 [i].mark) 数组 [i].m.test();
这样把要调用的方法转换为对象的保程不仅仅是可以对要调用的方法进行调度 , 而且可以把对象序列化后在另一台机器上执行 , 这样就把调用边界从线程扩展到了机器 .
回到我们的例子 :
class Proxy implements Axman{
private final Scheduler scheduler;
private final Servant servant;
public Proxy(Scheduler scheduler,Servant servant){
this.scheduler = scheduler;
this.servant = servant;
}
public Result resultTest(int count,char c){
FutureResult futrue = new FutureResult();
this.scheduler.invoke(new ResultRequest(servant,futrue,count,c));
return futrue;
}
public void noResultTest(String str){
this.scheduler.invoke(new NoResultRequest(this.servant,str));
}
}
其中 scheduler 是管理对调用的调度 , servant 是真正的对方法的执行 :
Servant 就是去真实地实现方法 :
class Servant implements Axman{
public Result resultTest(int count,char c){
char[] buf = new char[count];
for(int i = 0;i < count;i++){
buf[i] = c;
try{
Thread.sleep(100);
}catch(Throwable t){}
}
return new RealResult(new String(buf));
}
public void noResultTest(String str){
try{
System.out.println("displayString :" + str);
Thread.sleep(10);
}catch(Throwable t){}
}
}
在 scheduler 将方法的调用 (invkoe) 和执行 (execute) 进行了分离 , 调用就是开始 ” 注册 ” 方法到要执行的容器中 , 这样就可以立即返回出来 . 真正执行多久就是 execute 的事了 , 就象一个人点燃爆竹的引信就跑了 , 至于那个爆竹什么时候爆炸就不是他能控制的了 .
public class Scheduler extends Thread {
private final ActivationQueue queue;
public Scheduler(ActivationQueue queue){
this.queue = queue;
}
public void invoke(MethodRequest request){
this.queue.putRequest(request);
}
public void run(){
while(true){
// 如果队列中有请求线程 , 测开始执行请求
MethodRequest request = this.queue.takeRequest();
request.execute();
}
}
}
在 schetbduler 中只用一个队列来保存代表方法和请求对象 , 实行简单的 FIFO 调用 , 你要实更复杂的调度就要在这里重新实现 :
class ActivationQueue{
private static final int MAX_METHOD_REQUEST = 100;
private final MethodRequest[] requestQueue;
private int tail;
private int head;
private int count;
public ActivationQueue(){
this.requestQueue = new MethodRequest[MAX_METHOD_REQUEST];
this.head = this.count = this.tail = 0;
}
public synchronized void putRequest(MethodRequest request){
while(this.count >= this.requestQueue.length){
try {
this.wait();
}
catch (Throwable t) {}
}
this.requestQueue[this.tail] = request;
tail = (tail + 1)%this.requestQueue.length;
count ++ ;
this.notifyAll();
}
public synchronized MethodRequest takeRequest(){
while(this.count <= 0){
try {
this.wait();
}
catch (Throwable t) {}
}
MethodRequest request = this.requestQueue[this.head];
this.head = (this.head + 1) % this.requestQueue.length;
count --;
this.notifyAll();
return request;
}
}
为了将方法调用转化为对象 , 我们通过实现 MethodRequestb 对象的 execute 方法来方法具体方法转换成具体对象 :
abstract class MethodRequest{
protected final Servant servant;
}
本文的目的并不是介绍使用的什么技术,而是重点阐述其实现原理。
一、 异步和同步
讲通俗点,异步就是不需要等当前执行的动作完成,就可以继续执行后面的动作。
通常一个程序执行的顺序是:从上到下,依次执行。后面的动作必须等前面动作执行完成以后方可执行。这就是和异步相对的一个概念——同步。
案例:
A、张三打电话给李四,让李四帮忙写份材料。
B、李四接到电话的时候,手上有自己的工作要处理,但他答应张三,忙完手上的工作后马上帮张三写好材料,并传真给张三。
C、通完电话后,张三外出办事。
说明:
张三给李四通完电话后,就出去办事了,他并不需要等李四把材料写好才外出。那么张三让李四写材料的消息就属于异步消息。
相反,如果张三必须等李四把材料写好才能外出办事的话,那么这个消息就属于同步消息了。
二、 异步的实现
传统的程序执行代码都是从上到下,一条一条执行的。
但生活中有很多情况并不是这样,以上的案例中,如果李四需要几个小时以后才能帮张三写好材料的话,那张三就必须等几个小时,这样张三可能会崩溃或者抓狂。
这种一条龙似的处理,显示不太合理。
可以使用以下办法来处理这种问题:
张三找王五去给李四打电话,等李四写好材料后,由王五转交给张三。这样张三就可以外出办其他的事情了。
问题得到了合理的解决,之前张三一条线的工作,由张三和王五两条线来完成了,两边同时进行,彼此不耽误。
三、 计算机语言的实现
办法有了,如何用程序来模拟实现呢?
A、以前由一个线程来处理的工作,可以通过新增一个线程来达到异步的目的。这也就是JAVA中的多线程技术。
B、最后李四写好的材料必须交给张三,以做他用。这就是回调。
回调你可以这样来理解:
A发送消息给B,B处理好A要求的事情后,将结果返回给A,A再对B返回的结果来做进一步的处理。
四、 Java代码的实现
A、 回调的实现
Java代码
tb
public interface CallBack {
public void execute(Object... objects );
}
public interface CallBack { public void execute(Object... objects ); }
Java是面向对象的语言,因此回调函数就变成了回调接口。
B、 消息的发送者
Javatb代码
public class Local implements CallBack,Runnable{
private Remote remote;
private String message;
public Local(Remote remote, String message) {
super();
this.remote = remote;
this.message = message;
}
public void sendMessage()
{
System.out.println(Thread.currentThread().getName());
Thread thread = new Thread(this);
thread.start();
System.out.println("Message has been sent by Local~!");
}
public void execute(Object... objects