The important thing in life is to have a great aim , and the determination

常用链接

统计

IT技术链接

保险相关

友情链接

基金知识

生活相关

最新评论

#

海量数据库的查询优化及分页算法方案 (转载)

海量数据库的查询优化及分页算法方案

随着“金盾工程”建设的逐步深入和公安信息化的高速发展,公安计算机应用系统被广泛应用在各警种、各部门。与此同时,应用系统体系的核心、系统数据的存放地――数据库也随着实际应用而急剧膨胀,一些大规模的系统,如人口系统的数据甚至超过了1000万条,可谓海量。那么,如何实现快速地从这些超大容量的数据库中提取数据(查询)、分析、统计以及提取数据后进行数据分页已成为各地系统管理员和数据库管理员亟待解决的难题。

在以下的文章中,我将以“办公自动化”系统为例,探讨如何在有着1000万条数据的MS SQL SERVER数据库中实现快速的数据提取和数据分页。以下代码说明了我们实例中数据库的“红头文件”一表的部分数据结构:

CREATE TABLE [dbo].[TGongwen] ( --TGongwen是红头文件表名

[Gid] [int] IDENTITY (1, 1) NOT NULL ,
--本表的id号,也是主键

[title] [varchar] (80) COLLATE Chinese_PRC_CI_AS NULL ,
--红头文件的标题

[fariqi] [datetime] NULL ,
--发布日期

[neibuYonghu] [varchar] (70) COLLATE Chinese_PRC_CI_AS NULL ,
--发布用户

[reader] [varchar] (900) COLLATE Chinese_PRC_CI_AS NULL ,

--需要浏览的用户。每个用户中间用分隔符“,”分开

) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

下面,我们来往数据库中添加1000万条数据:

declare @i int

set @i=1

while @i<=250000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-2-5','通信科','通信科,办公室,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经侦支队,户政科,治安支队,外事科','这是最先的25万条记录')

set @i=@i+1

end

GO

declare @i int

set @i=1

while @i<=250000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-9-16','办公室','办公室,通信科,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经侦支队,户政科,外事科','这是中间的25万条记录')

set @i=@i+1

end

GO

declare @h int

set @h=1

while @h<=100

begin

declare @i int

set @i=2002

while @i<=2003

begin

declare @j int

set @j=0

while @j<50

begin

declare @k int

set @k=0

while @k<50

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values(cast(@i as varchar(4))+'-8-15 3:'+cast(@j as varchar(2))+':'+cast(@j as varchar(2)),'通信科','办公室,通信科,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经侦支队,户政科,外事科','这是最后的50万条记录')

set @k=@k+1

end

set @j=@j+1

end

set @i=@i+1

end

set @h=@h+1

end

GO

declare @i int

set @i=1

while @i<=9000000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-5-5','通信科','通信科,办公室,王局长,刘局长,张局长,admin,刑侦支队,特勤支队,交巡警支队,经侦支队,户政科,治安支队,外事科','这是最后添加的900万条记录')

set @i=@i+1000000

end

GO

通过以上语句,我们创建了25万条由通信科于2004年2月5日发布的记录,25万条由办公室于2004年9月6日发布的记录,2002年和2003年各100个2500条相同日期、不同分秒的由通信科发布的记录(共50万条),还有由通信科于2004年5月5日发布的900万条记录,合计1000万条。

一、因情制宜,建立“适当”的索引

建立“适当”的索引是实现查询优化的首要前提。

索引(index)是除表之外另一重要的、用户定义的存储在物理介质上的数据结构。当根据索引码的值搜索数据时,索引提供了对数据的快速访问。事实上,没有索引,数据库也能根据SELECT语句成功地检索到结果,但随着表变得越来越大,使用“适当”的索引的效果就越来越明显。注意,在这句话中,我们用了“适当”这个词,这是因为,如果使用索引时不认真考虑其实现过程,索引既可以提高也会破坏数据库的工作性能。

(一)深入浅出理解索引结构

实际上,您可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,也称非聚类索引、非簇集索引)。下面,我们举例来说明一下聚集索引和非聚集索引的区别:

其实,我们的汉语字典的正文本身就是一个聚集索引。比如,我们要查“安”字,就会很自然地翻开字典的前几页,因为“安”的拼音是“an”,而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字;同样的,如果查“张”字,那您也会将您的字典翻到最后部分,因为“张”的拼音是“zhang”。也就是说,字典的正文部分本身就是一个目录,您不需要再去查其他目录来找到您需要找的内容。

我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。

如果您认识某个字,您可以快速地从自动中查到这个字。但您也可能会遇到您不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而需要去根据“偏旁部首”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“张”字,我们可以看到在查部首之后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码却是63页,“张”的下面是“弩”字,页面是390页。很显然,这些字并不是真正的分别位于“张”字的上下方,现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后再翻到您所需要的页码。

我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非聚集索引”。

通过以上例子,我们可以理解到什么是“聚集索引”和“非聚集索引”。

进一步引申一下,我们可以很容易的理解:每个表只能有一个聚集索引,因为目录只能按照一种方法进行排序。

(二)何时使用聚集索引或非聚集索引

下面的表总结了何时使用聚集索引或非聚集索引(很重要)。

动作描述
使用聚集索引
使用非聚集索引

列经常被分组排序



返回某范围内的数据

不应

一个或极少不同值
不应
不应

小数目的不同值

不应

大数目的不同值
不应


频繁更新的列
不应


外键列



主键列



频繁修改索引列
不应


事实上,我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如:返回某范围内的数据一项。比如您的某个表有一个时间列,恰好您把聚合索引建立在了该列,这时您查询2004年1月1日至2004年10月1日之间的全部数据时,这个速度就将是很快的,因为您的这本字典正文是按日期进行排序的,聚类索引只需要找到要检索的所有数据中的开头和结尾数据即可;而不像非聚集索引,必须先查到目录中查到每一项数据对应的页码,然后再根据页码查到具体内容。

(三)结合实际,谈索引使用的误区

理论的目的是应用。虽然我们刚才列出了何时应使用聚集索引或非聚集索引,但在实践中以上规则却很容易被忽视或不能根据实际情况进行综合分析。下面我们将根据在实践中遇到的实际问题来谈一下索引使用的误区,以便于大家掌握索引建立的方法。

1、主键就是聚集索引

这种想法笔者认为是极端错误的,是对聚集索引的一种浪费。虽然SQL SERVER默认是在主键上建立聚集索引的。

通常,我们会在每个表中都建立一个ID列,以区分每条数据,并且这个ID列是自动增大的,步长一般为1。我们的这个办公自动化的实例中的列Gid就是如此。此时,如果我们将这个列设为主键,SQL SERVER会将此列默认为聚集索引。这样做有好处,就是可以让您的数据在数据库中按照ID进行物理排序,但笔者认为这样做意义不大。

显而易见,聚集索引的优势是很明显的,而每个表中只能有一个聚集索引的规则,这使得聚集索引变得更加珍贵。

从我们前面谈到的聚集索引的定义我们可以看出,使用聚集索引的最大好处就是能够根据查询要求,迅速缩小查询范围,避免全表扫描。在实际应用中,因为ID号是自动生成的,我们并不知道每条记录的ID号,所以我们很难在实践中用ID号来进行查询。这就使让ID号这个主键作为聚集索引成为一种资源浪费。其次,让每个ID号都不同的字段作为聚集索引也不符合“大数目的不同值情况下不应建立聚合索引”规则;当然,这种情况只是针对用户经常修改记录内容,特别是索引项的时候会负作用,但对于查询速度并没有影响。

在办公自动化系统中,无论是系统首页显示的需要用户签收的文件、会议还是用户进行文件查询等任何情况下进行数据查询都离不开字段的是“日期”还有用户本身的“用户名”。

通常,办公自动化的首页会显示每个用户尚未签收的文件或会议。虽然我们的where语句可以仅仅限制当前用户尚未签收的情况,但如果您的系统已建立了很长时间,并且数据量很大,那么,每次每个用户打开首页的时候都进行一次全表扫描,这样做意义是不大的,绝大多数的用户1个月前的文件都已经浏览过了,这样做只能徒增数据库的开销而已。事实上,我们完全可以让用户打开系统首页时,数据库仅仅查询这个用户近3个月来未阅览的文件,通过“日期”这个字段来限制表扫描,提高查询速度。如果您的办公自动化系统已经建立的2年,那么您的首页显示速度理论上将是原来速度8倍,甚至更快。

在这里之所以提到“理论上”三字,是因为如果您的聚集索引还是盲目地建在ID这个主键上时,您的查询速度是没有这么高的,即使您在“日期”这个字段上建立的索引(非聚合索引)。下面我们就来看一下在1000万条数据量的情况下各种查询的速度表现(3个月内的数据为25万条):

(1)仅在主键上建立聚集索引,并且不划分时间段:

Select gid,fariqi,neibuyonghu,title from tgongwen

用时:128470毫秒(即:128秒)

(2)在主键上建立聚集索引,在fariq上建立非聚集索引:

select gid,fariqi,neibuyonghu,title from Tgongwen

where fariqi> dateadd(day,-90,getdate())

用时:53763毫秒(54秒)

(3)将聚合索引建立在日期列(fariqi)上:

select gid,fariqi,neibuyonghu,title from Tgongwen

where fariqi> dateadd(day,-90,getdate())

用时:2423毫秒(2秒)

虽然每条语句提取出来的都是25万条数据,各种情况的差异却是巨大的,特别是将聚集索引建立在日期列时的差异。事实上,如果您的数据库真的有1000万容量的话,把主键建立在ID列上,就像以上的第1、2种情况,在网页上的表现就是超时,根本就无法显示。这也是我摒弃ID列作为聚集索引的一个最重要的因素。

得出以上速度的方法是:在各个select语句前加:declare @d datetime

set @d=getdate()

并在select语句后加:

select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())

2、只要建立索引就能显著提高查询速度

事实上,我们可以发现上面的例子中,第2、3条语句完全相同,且建立索引的字段也相同;不同的仅是前者在fariqi字段上建立的是非聚合索引,后者在此字段上建立的是聚合索引,但查询速度却有着天壤之别。所以,并非是在任何字段上简单地建立索引就能提高查询速度。

从建表的语句中,我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同记录。在此字段上建立聚合索引是再合适不过了。在现实中,我们每天都会发几个文件,这几个文件的发文日期就相同,这完全符合建立聚集索引要求的:“既不能绝大多数都相同,又不能只有极少数相同”的规则。由此看来,我们建立“适当”的聚合索引对于我们提高查询速度是非常重要的。

3、把所有需要提高查询速度的字段都加进聚集索引,以提高查询速度

上面已经谈到:在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户名”。既然这两个字段都是如此的重要,我们可以把他们合并起来,建立一个复合索引(compound index)。

很多人认为只要把任何字段加进聚集索引,就能提高查询速度,也有人感到迷惑:如果把复合的聚集索引字段分开查询,那么查询速度会减慢吗?带着这个问题,我们来看一下以下的查询速度(结果集都是25万条数据):(日期列fariqi首先排在复合聚集索引的起始列,用户名neibuyonghu排在后列)

(1)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5'

查询速度:2513毫秒

(2)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5' and neibuyonghu='办公室'

查询速度:2516毫秒

(3)select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu='办公室'

查询速度:60280毫秒

从以上试验中,我们可以看到如果仅用聚集索引的起始列作为查询条件和同时用到复合聚集索引的全部列的查询速度是几乎一样的,甚至比用上全部的复合索引列还要略快(在查询结果集数目一样的情况下);而如果仅用复合聚集索引的非起始列作为查询条件的话,这个索引是不起任何作用的。当然,语句1、2的查询速度一样是因为查询的条目数一样,如果复合索引的所有列都用上,而且查询结果少的话,这样就会形成“索引覆盖”,因而性能可以达到最优。同时,请记住:无论您是否经常使用聚合索引的其他列,但其前导列一定要是使用最频繁的列。

(四)其他书上没有的索引使用经验总结

1、用聚合索引比用不是聚合索引的主键速度快

下面是实例语句:(都是提取25万条数据)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

使用时间:3326毫秒

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid<=250000

使用时间:4470毫秒

这里,用聚合索引比用不是聚合索引的主键速度快了近1/4。

2、用聚合索引比用一般的主键作order by时速度快,特别是在小数据量情况下

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi

用时:12936

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid

用时:18843

这里,用聚合索引比用一般的主键作order by时,速度快了3/10。事实上,如果数据量很小的话,用聚集索引作为排序列要比使用非聚集索引速度快得明显的多;而数据量如果很大的话,如10万以上,则二者的速度差别不明显。

3、使用聚合索引内的时间段,搜索时间会按数据占整个数据表的百分比成比例减少,而无论聚合索引使用了多少个

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1'

用时:6343毫秒(提取100万条)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-6-6'

用时:3170毫秒(提取50万条)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

用时:3326毫秒(和上句的结果一模一样。如果采集的数量一样,那么用大于号和等于号是一样的)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1' and fariqi<'2004-6-6'

用时:3280毫秒

4 、日期列不会因为有分秒的输入而减慢查询速度

下面的例子中,共有100万条数据,2004年1月1日以后的数据有50万条,但只有两个不同的日期,日期精确到日;之前有数据50万条,有5000个不同的日期,日期精确到秒。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1' order by fariqi

用时:6390毫秒

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi<'2004-1-1' order by fariqi

用时:6453毫秒

(五)其他注意事项

“水可载舟,亦可覆舟”,索引也一样。索引有助于提高检索性能,但过多或不当的索引也会导致系统低效。因为用户在表中每加进一个索引,数据库就要做更多的工作。过多的索引甚至会导致索引碎片。

所以说,我们要建立一个“适当”的索引体系,特别是对聚合索引的创建,更应精益求精,以使您的数据库能得到高性能的发挥。

当然,在实践中,作为一个尽职的数据库管理员,您还要多测试一些方案,找出哪种方案效率最高、最为有效。

二、改善SQL语句

很多人不知道SQL语句在SQL SERVER中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解。比如:

select * from table1 where name='zhangsan' and tID > 10000

和执行:

select * from table1 where tID > 10000 and name='zhangsan'

一些人不知道以上两条语句的执行效率是否一样,因为如果简单的从语句先后上看,这两个语句的确是不一样,如果tID是一个聚合索引,那么后一句仅仅从表的10000条以后的记录中查找就行了;而前一句则要先从全表中查找看有几个name='zhangsan'的,而后再根据限制条件条件tID>10000来提出查询结果。

事实上,这样的担心是不必要的。SQL SERVER中有一个“查询分析优化器”,它可以计算出where子句中的搜索条件并确定哪个索引能缩小表扫描的搜索空间,也就是说,它能实现自动优化。

虽然查询优化器可以根据where子句自动的进行查询优化,但大家仍然有必要了解一下“查询优化器”的工作原理,如非这样,有时查询优化器就会不按照您的本意进行快速查询。

在查询分析阶段,查询优化器查看查询的每个阶段并决定限制需要扫描的数据量是否有用。如果一个阶段可以被用作一个扫描参数(SARG),那么就称之为可优化的,并且可以利用索引快速获得所需数据。

SARG的定义:用于限制搜索的一个操作,因为它通常是指一个特定的匹配,一个值得范围内的匹配或者两个以上条件的AND连接。形式如下:

列名 操作符 <常数 或 变量>



<常数 或 变量> 操作符列名

列名可以出现在操作符的一边,而常数或变量出现在操作符的另一边。如:

Name=’张三’

价格>5000

5000<价格

Name=’张三’ and 价格>5000

如果一个表达式不能满足SARG的形式,那它就无法限制搜索的范围了,也就是SQL SERVER必须对每一行都判断它是否满足WHERE子句中的所有条件。所以一个索引对于不满足SARG形式的表达式来说是无用的。

介绍完SARG后,我们来总结一下使用SARG以及在实践中遇到的和某些资料上结论不同的经验:

1、Like语句是否属于SARG取决于所使用的通配符的类型

如:name like ‘张%’ ,这就属于SARG

而:name like ‘%张’ ,就不属于SARG。

原因是通配符%在字符串的开通使得索引无法使用。

2、or 会引起全表扫描

Name=’张三’ and 价格>5000 符号SARG,而:Name=’张三’ or 价格>5000 则不符合SARG。使用or会引起全表扫描。

3、非操作符、函数引起的不满足SARG形式的语句

不满足SARG形式的语句最典型的情况就是包括非操作符的语句,如:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等,另外还有函数。下面就是几个不满足SARG形式的例子:

ABS(价格)<5000

Name like ‘%三’

有些表达式,如:

WHERE 价格*2>5000

SQL SERVER也会认为是SARG,SQL SERVER会将此式转化为:

WHERE 价格>2500/2

但我们不推荐这样使用,因为有时SQL SERVER不能保证这种转化与原始表达式是完全等价的。

4、IN 的作用相当与OR

语句:

Select * from table1 where tid in (2,3)



Select * from table1 where tid=2 or tid=3

是一样的,都会引起全表扫描,如果tid上有索引,其索引也会失效。

5、尽量少用NOT

6、exists 和 in 的执行效率是一样的

很多资料上都显示说,exists要比in的执行效率要高,同时应尽可能的用not exists来代替not in。但事实上,我试验了一下,发现二者无论是前面带不带not,二者之间的执行效率都是一样的。因为涉及子查询,我们试验这次用SQL SERVER自带的pubs数据库。运行前我们可以把SQL SERVER的statistics I/O状态打开。

(1)select title,price from titles where title_id in (select title_id from sales where qty>30)

该句的执行结果为:

表 'sales'。扫描计数 18,逻辑读 56 次,物理读 0 次,预读 0 次。

表 'titles'。扫描计数 1,逻辑读 2 次,物理读 0 次,预读 0 次。

(2)select title,price from titles where exists (select * from sales where sales.title_id=titles.title_id and qty>30)

第二句的执行结果为:

表 'sales'。扫描计数 18,逻辑读 56 次,物理读 0 次,预读 0 次。

表 'titles'。扫描计数 1,逻辑读 2 次,物理读 0 次,预读 0 次。

我们从此可以看到用exists和用in的执行效率是一样的。

7、用函数charindex()和前面加通配符%的LIKE执行效率一样

前面,我们谈到,如果在LIKE前面加上通配符%,那么将会引起全表扫描,所以其执行效率是低下的。但有的资料介绍说,用函数charindex()来代替LIKE速度会有大的提升,经我试验,发现这种说明也是错误的:

select gid,title,fariqi,reader from tgongwen where charindex('刑侦支队',reader)>0 and fariqi>'2004-5-5'

用时:7秒,另外:扫描计数 4,逻辑读 7155 次,物理读 0 次,预读 0 次。

select gid,title,fariqi,reader from tgongwen where reader like '%' + '刑侦支队' + '%' and fariqi>'2004-5-5'

用时:7秒,另外:扫描计数 4,逻辑读 7155 次,物理读 0 次,预读 0 次。

8、union并不绝对比or的执行效率高

我们前面已经谈到了在where子句中使用or会引起全表扫描,一般的,我所见过的资料都是推荐这里用union来代替or。事实证明,这种说法对于大部分都是适用的。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' or gid>9990000

用时:68秒。扫描计数 1,逻辑读 404008 次,物理读 283 次,预读 392163 次。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

union

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid>9990000

用时:9秒。扫描计数 8,逻辑读 67489 次,物理读 216 次,预读 7499 次。

看来,用union在通常情况下比用or的效率要高的多。

但经过试验,笔者发现如果or两边的查询列是一样的话,那么用union则反倒和用or的执行速度差很多,虽然这里union扫描的是索引,而or扫描的是全表。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' or fariqi='2004-2-5'

用时:6423毫秒。扫描计数 2,逻辑读 14726 次,物理读 1 次,预读 7176 次。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

union

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-2-5'

用时:11640毫秒。扫描计数 8,逻辑读 14806 次,物理读 108 次,预读 1144 次。

9、字段提取要按照“需多少、提多少”的原则,避免“select *”

我们来做一个试验:

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc

用时:4673毫秒

select top 10000 gid,fariqi,title from tgongwen order by gid desc

用时:1376毫秒

select top 10000 gid,fariqi from tgongwen order by gid desc

用时:80毫秒

由此看来,我们每少提取一个字段,数据的提取速度就会有相应的提升。提升的速度还要看您舍弃的字段的大小来判断。

10、count(*)不比count(字段)慢

某些资料上说:用*会统计所有列,显然要比一个世界的列名效率低。这种说法其实是没有根据的。我们来看:

select count(*) from Tgongwen

用时:1500毫秒

select count(gid) from Tgongwen

用时:1483毫秒

select count(fariqi) from Tgongwen

用时:3140毫秒

select count(title) from Tgongwen

用时:52050毫秒

从以上可以看出,如果用count(*)和用count(主键)的速度是相当的,而count(*)却比其他任何除主键以外的字段汇总速度要快,而且字段越长,汇总的速度就越慢。我想,如果用count(*), SQL SERVER可能会自动查找最小字段来汇总的。当然,如果您直接写count(主键)将会来的更直接些。

11、order by按聚集索引列排序效率最高

我们来看:(gid是主键,fariqi是聚合索引列)

select top 10000 gid,fariqi,reader,title from tgongwen

用时:196 毫秒。 扫描计数 1,逻辑读 289 次,物理读 1 次,预读 1527 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc

用时:4720毫秒。 扫描计数 1,逻辑读 41956 次,物理读 0 次,预读 1287 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc

用时:4736毫秒。 扫描计数 1,逻辑读 55350 次,物理读 10 次,预读 775 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc

用时:173毫秒。 扫描计数 1,逻辑读 290 次,物理读 0 次,预读 0 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc

用时:156毫秒。 扫描计数 1,逻辑读 289 次,物理读 0 次,预读 0 次。

从以上我们可以看出,不排序的速度以及逻辑读次数都是和“order by 聚集索引列” 的速度是相当的,但这些都比“order by 非聚集索引列”的查询速度是快得多的。

同时,按照某个字段进行排序的时候,无论是正序还是倒序,速度是基本相当的。

12、高效的TOP

事实上,在查询和提取超大容量的数据集时,影响数据库响应时间的最大因素不是数据查找,而是物理的I/0操作。如:

select top 10 * from (

select top 10000 gid,fariqi,title from tgongwen

where neibuyonghu='办公室'

order by gid desc) as a

order by gid asc

这条语句,从理论上讲,整条语句的执行时间应该比子句的执行时间长,但事实相反。因为,子句执行后返回的是10000条记录,而整条语句仅返回10条语句,所以影响数据库响应时间最大的因素是物理I/O操作。而限制物理I/O操作此处的最有效方法之一就是使用TOP关键词了。TOP关键词是SQL SERVER中经过系统优化过的一个用来提取前几条或前几个百分比数据的词。经笔者在实践中的应用,发现TOP确实很好用,效率也很高。但这个词在另外一个大型数据库ORACLE中却没有,这不能说不是一个遗憾,虽然在ORACLE中可以用其他方法(如:rownumber)来解决。在以后的关于“实现千万级数据的分页显示存储过程”的讨论中,我们就将用到TOP这个关键词。



到此为止,我们上面讨论了如何实现从大容量的数据库中快速地查询出您所需要的数据方法。当然,我们介绍的这些方法都是“软”方法,在实践中,我们还要考虑各种“硬”因素,如:网络性能、服务器的性能、操作系统的性能,甚至网卡、交换机等。

三、实现小数据量和海量数据的通用分页显示存储过程

建立一个web 应用,分页浏览功能必不可少。这个问题是数据库处理中十分常见的问题。经典的数据分页方法是:ADO 纪录集分页法,也就是利用ADO自带的分页功能(利用游标)来实现分页。但这种分页方法仅适用于较小数据量的情形,因为游标本身有缺点:游标是存放在内存中,很费内存。游标一建立,就将相关的记录锁住,直到取消游标。游标提供了对特定集合中逐行扫描的手段,一般使用游标来逐行遍历数据,根据取出数据条件的不同进行不同的操作。而对于多表和大表中定义的游标(大的数据集合)循环很容易使程序进入一个漫长的等待甚至死机。

更重要的是,对于非常大的数据模型而言,分页检索时,如果按照传统的每次都加载整个数据源的方法是非常浪费资源的。现在流行的分页方法一般是检索页面大小的块区的数据,而非检索所有的数据,然后单步执行当前行。

最早较好地实现这种根据页面大小和页码来提取数据的方法大概就是“俄罗斯存储过程”。这个存储过程用了游标,由于游标的局限性,所以这个方法并没有得到大家的普遍认可。

后来,网上有人改造了此存储过程,下面的存储过程就是结合我们的办公自动化实例写的分页存储过程:

CREATE procedure pagination1

(@pagesize int, --页面大小,如每页存储20条记录

@pageindex int --当前页码

)

as

set nocount on

begin

declare @indextable table(id int identity(1,1),nid int) --定义表变量

declare @PageLowerBound int --定义此页的底码

declare @PageUpperBound int --定义此页的顶码

set @PageLowerBound=(@pageindex-1)*@pagesize

set @PageUpperBound=@PageLowerBound+@pagesize

set rowcount @PageUpperBound

insert into @indextable(nid) select gid from TGongwen where fariqi >dateadd(day,-365,getdate()) order by fariqi desc

select O.gid,O.mid,O.title,O.fadanwei,O.fariqi from TGongwen O,@indextable t where O.gid=t.nid

and t.id>@PageLowerBound and t.id<=@PageUpperBound order by t.id

end

set nocount off

以上存储过程运用了SQL SERVER的最新技术――表变量。应该说这个存储过程也是一个非常优秀的分页存储过程。当然,在这个过程中,您也可以把其中的表变量写成临时表:CREATE TABLE #Temp。但很明显,在SQL SERVER中,用临时表是没有用表变量快的。所以笔者刚开始使用这个存储过程时,感觉非常的不错,速度也比原来的ADO的好。但后来,我又发现了比此方法更好的方法。

笔者曾在网上看到了一篇小短文《从数据表中取出第n条到第m条的记录的方法》,全文如下:

从publish 表中取出第 n 条到第 m 条的记录:
SELECT TOP m-n+1 *
FROM publish
WHERE (id NOT IN
    (SELECT TOP n-1 id
     FROM publish))

id 为publish 表的关键字

我当时看到这篇文章的时候,真的是精神为之一振,觉得思路非常得好。等到后来,我在作办公自动化系统(ASP.NET+ C#+SQL SERVER)的时候,忽然想起了这篇文章,我想如果把这个语句改造一下,这就可能是一个非常好的分页存储过程。于是我就满网上找这篇文章,没想到,文章还没找到,却找到了一篇根据此语句写的一个分页存储过程,这个存储过程也是目前较为流行的一种分页存储过程,我很后悔没有争先把这段文字改造成存储过程:

CREATE PROCEDURE pagination2
(
@SQL nVARCHAR(4000), --不带排序语句的SQL语句
@Page int, --页码
@RecsPerPage int, --每页容纳的记录数
@ID VARCHAR(255), --需要排序的不重复的ID号
@Sort VARCHAR(255) --排序字段及规则
)
AS

DECLARE @Str nVARCHAR(4000)

SET @Str='SELECT TOP '+CAST(@RecsPerPage AS VARCHAR(20))+' * FROM ('+@SQL+') T WHERE T.'+@ID+'NOT IN
(SELECT TOP '+CAST((@RecsPerPage*(@Page-1)) AS VARCHAR(20))+' '+@ID+' FROM ('+@SQL+') T9 ORDER BY '+@Sort+') ORDER BY '+@Sort

PRINT @Str

EXEC sp_ExecuteSql @Str
GO

其实,以上语句可以简化为:

SELECT TOP 页大小 *

FROM Table1

WHERE (ID NOT IN

(SELECT TOP 页大小*页数 id

FROM 表

ORDER BY id))

ORDER BY ID

但这个存储过程有一个致命的缺点,就是它含有NOT IN字样。虽然我可以把它改造为:

SELECT TOP 页大小 *

FROM Table1

WHERE not exists

(select * from (select top (页大小*页数) * from table1 order by id) b where b.id=a.id )

order by id

即,用not exists来代替not in,但我们前面已经谈过了,二者的执行效率实际上是没有区别的。

既便如此,用TOP 结合NOT IN的这个方法还是比用游标要来得快一些。

虽然用not exists并不能挽救上个存储过程的效率,但使用SQL SERVER中的TOP关键字却是一个非常明智的选择。因为分页优化的最终目的就是避免产生过大的记录集,而我们在前面也已经提到了TOP的优势,通过TOP 即可实现对数据量的控制。

在分页算法中,影响我们查询速度的关键因素有两点:TOP和NOT IN。TOP可以提高我们的查询速度,而NOT IN会减慢我们的查询速度,所以要提高我们整个分页算法的速度,就要彻底改造NOT IN,同其他方法来替代它。

我们知道,几乎任何字段,我们都可以通过max(字段)或min(字段)来提取某个字段中的最大或最小值,所以如果这个字段不重复,那么就可以利用这些不重复的字段的max或min作为分水岭,使其成为分页算法中分开每页的参照物。在这里,我们可以用操作符“>”或“<”号来完成这个使命,使查询语句符合SARG形式。如:

Select top 10 * from table1 where id>200

于是就有了如下分页方案:

select top 页大小 *

from table1

where id>

(select max (id) from

(select top ((页码-1)*页大小) id from table1 order by id) as T

)

order by id

在选择即不重复值,又容易分辨大小的列时,我们通常会选择主键。下表列出了笔者用有着1000万数据的办公自动化系统中的表,在以GID(GID是主键,但并不是聚集索引。)为排序列、提取gid,fariqi,title字段,分别以第1、10、100、500、1000、1万、10万、25万、50万页为例,测试以上三种分页方案的执行速度:(单位:毫秒)

页 码
方案1
方案2
方案3

1
60
30
76

10
46
16
63

100
1076
720
130

500
540
12943
83

1000
17110
470
250

1万
24796
4500
140

10万
38326
42283
1553

25万
28140
128720
2330

50万
121686
127846
7168


从上表中,我们可以看出,三种存储过程在执行100页以下的分页命令时,都是可以信任的,速度都很好。但第一种方案在执行分页1000页以上后,速度就降了下来。第二种方案大约是在执行分页1万页以上后速度开始降了下来。而第三种方案却始终没有大的降势,后劲仍然很足。

在确定了第三种分页方案后,我们可以据此写一个存储过程。大家知道SQL SERVER的存储过程是事先编译好的SQL语句,它的执行效率要比通过WEB页面传来的SQL语句的执行效率要高。下面的存储过程不仅含有分页方案,还会根据页面传来的参数来确定是否进行数据总数统计。

-- 获取指定页的数据

CREATE PROCEDURE pagination3

@tblName varchar(255), -- 表名

@strGetFields varchar(1000) = '*', -- 需要返回的列

@fldName varchar(255)='', -- 排序的字段名

@PageSize int = 10, -- 页尺寸

@PageIndex int = 1, -- 页码

@doCount bit = 0, -- 返回记录总数, 非 0 值则返回

@OrderType bit = 0, -- 设置排序类型, 非 0 值则降序

@strWhere varchar(1500) = '' -- 查询条件 (注意: 不要加 where)

AS

declare @strSQL varchar(5000) -- 主语句

declare @strTmp varchar(110) -- 临时变量

declare @strOrder varchar(400) -- 排序类型

if @doCount != 0

begin

if @strWhere !=''

set @strSQL = "select count(*) as Total from [" + @tblName + "] where "+@strWhere

else

set @strSQL = "select count(*) as Total from [" + @tblName + "]"

end

--以上代码的意思是如果@doCount传递过来的不是0,就执行总数统计。以下的所有代码都是@doCount为0的情况

else

begin

if @OrderType != 0

begin

set @strTmp = "<(select min"

set @strOrder = " order by [" + @fldName +"] desc"

--如果@OrderType不是0,就执行降序,这句很重要!

end

else

begin

set @strTmp = ">(select max"

set @strOrder = " order by [" + @fldName +"] asc"

end

if @PageIndex = 1

begin

if @strWhere != ''

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from [" + @tblName + "] where " + @strWhere + " " + @strOrder

else

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from ["+ @tblName + "] "+ @strOrder

--如果是第一页就执行以上代码,这样会加快执行速度

end

else

begin

--以下代码赋予了@strSQL以真正执行的SQL代码

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from ["

+ @tblName + "] where [" + @fldName + "]" + @strTmp + "(["+ @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + " ["+ @fldName + "] from [" + @tblName + "]" + @strOrder + ") as tblTmp)"+ @strOrder

if @strWhere != ''

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from ["

+ @tblName + "] where [" + @fldName + "]" + @strTmp + "(["

+ @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + " ["

+ @fldName + "] from [" + @tblName + "] where " + @strWhere + " "

+ @strOrder + ") as tblTmp) and " + @strWhere + " " + @strOrder

end

end

exec (@strSQL)

GO

上面的这个存储过程是一个通用的存储过程,其注释已写在其中了。

在大数据量的情况下,特别是在查询最后几页的时候,查询时间一般不会超过9秒;而用其他存储过程,在实践中就会导致超时,所以这个存储过程非常适用于大容量数据库的查询。

笔者希望能够通过对以上存储过程的解析,能给大家带来一定的启示,并给工作带来一定的效率提升,同时希望同行提出更优秀的实时数据分页算法。



四、聚集索引的重要性和如何选择聚集索引

在上一节的标题中,笔者写的是:实现小数据量和海量数据的通用分页显示存储过程。这是因为在将本存储过程应用于“办公自动化”系统的实践中时,笔者发现这第三种存储过程在小数据量的情况下,有如下现象:

1、分页速度一般维持在1秒和3秒之间。

2、在查询最后一页时,速度一般为5秒至8秒,哪怕分页总数只有3页或30万页。

虽然在超大容量情况下,这个分页的实现过程是很快的,但在分前几页时,这个1-3秒的速度比起第一种甚至没有经过优化的分页方法速度还要慢,借用户的话说就是“还没有ACCESS数据库速度快”,这个认识足以导致用户放弃使用您开发的系统。

笔者就此分析了一下,原来产生这种现象的症结是如此的简单,但又如此的重要:排序的字段不是聚集索引!

本篇文章的题目是:“查询优化及分页算法方案”。笔者只所以把“查询优化”和“分页算法”这两个联系不是很大的论题放在一起,就是因为二者都需要一个非常重要的东西――聚集索引。

在前面的讨论中我们已经提到了,聚集索引有两个最大的优势:

1、以最快的速度缩小查询范围。

2、以最快的速度进行字段排序。

第1条多用在查询优化时,而第2条多用在进行分页时的数据排序。

而聚集索引在每个表内又只能建立一个,这使得聚集索引显得更加的重要。聚集索引的挑选可以说是实现“查询优化”和“高效分页”的最关键因素。

但要既使聚集索引列既符合查询列的需要,又符合排序列的需要,这通常是一个矛盾。

笔者前面“索引”的讨论中,将fariqi,即用户发文日期作为了聚集索引的起始列,日期的精确度为“日”。这种作法的优点,前面已经提到了,在进行划时间段的快速查询中,比用ID主键列有很大的优势。

但在分页时,由于这个聚集索引列存在着重复记录,所以无法使用max或min来最为分页的参照物,进而无法实现更为高效的排序。而如果将ID主键列作为聚集索引,那么聚集索引除了用以排序之外,没有任何用处,实际上是浪费了聚集索引这个宝贵的资源。

为解决这个矛盾,笔者后来又添加了一个日期列,其默认值为getdate()。用户在写入记录时,这个列自动写入当时的时间,时间精确到毫秒。即使这样,为了避免可能性很小的重合,还要在此列上创建UNIQUE约束。将此日期列作为聚集索引列。

有了这个时间型聚集索引列之后,用户就既可以用这个列查找用户在插入数据时的某个时间段的查询,又可以作为唯一列来实现max或min,成为分页算法的参照物。

经过这样的优化,笔者发现,无论是大数据量的情况下还是小数据量的情况下,分页速度一般都是几十毫秒,甚至0毫秒。而用日期段缩小范围的查询速度比原来也没有任何迟钝。

聚集索引是如此的重要和珍贵,所以笔者总结了一下,一定要将聚集索引建立在:

1、您最频繁使用的、用以缩小查询范围的字段上;

2、您最频繁使用的、需要排序的字段上。


结束语:

本篇文章汇集了笔者近段在使用数据库方面的心得,是在做“办公自动化”系统时实践经验的积累。希望这篇文章不仅能够给大家的工作带来一定的帮助,也希望能让大家能够体会到分析问题的方法;最重要的是,希望这篇文章能够抛砖引玉,掀起大家的学习和讨论的兴趣,以共同促进,共同为公安科技强警事业和金盾工程做出自己最大的努力。

最后需要说明的是,在试验中,我发现用户在进行大数据量查询的时候,对数据库速度影响最大的不是内存大小,而是CPU。在我的P4 2.4机器上试验的时候,查看“资源管理器”,CPU经常出现持续到100%的现象,而内存用量却并没有改变或者说没有大的改变。即使在我们的HP ML 350 G3服务器上试验时,CPU峰值也能达到90%,一般持续在70%左右。

posted @ 2007-05-23 23:28 鸿雁| 编辑 收藏

Web Service学习笔记之----JAX-RPC (转载CN-JAVA)

导读
本文是我对学习jwsdp-1.2时所做笔记的整理,其中主要是一些指导性的内容,并没有多少概念以及原理的介绍,读者可能觉得略显简单,如果想要学习基本概念可以参考网上有关Web Service的资料。本文例子所使用的开发环境是WindowXP+JWSDP-1.2。

一.Web Service简介
1.定义
由两部分组成
·SOAP--Web Service之间的基本通信协议。
·WSDL--Web Service描述语言,它定义了Web Service做什么,怎么做和查询的信息。
2.简单的Web Service实现
包含四个基本步骤
·创建Web Service的商业逻辑(通常是一些Java类)
·将这些Java类部署到一个SOAP服务器上
·生成客户访问代码
·部署客户应用
注意:WSDL等文件的生成通常是利用厂商提供的工具来完成
3.WSDL解析
WSDL描述语言一般包含三部分
·What部分--包括了type、message和portType元素
Type:定义了Web Service使用的数据结构(使用XML Schema定义)
Message:一个Message是SOAP的基本通信元素。每个Message可以有一个或多个Part,每个Part代表一个参数。
PortType:消息汇总为不同的操作并归入到一个被称为portType的实体中。一个portType代表一个接口(Web Service支 持的操作集合),每个Web Service可以有多个接口,它们都使用portType表示。每个操作又包含了input和 output部分。
·How部分--包含binding元素
binding元素将portType绑定到特定的通信协议上(如HTTP上的SOAP协议)
·Where部分--由service元素组成
它将portType,binding以及Web Service实际的位置(URI)放在一起描述
4.客户端
通常Web Service可以有三种类型的客户
·商业伙伴(Business Partner)--包括分发商,零售商以及大型消费者)
此类客户通过SOAP、WSDL、ebXML、UDDI等XML技术与Web Service连接
·瘦客户--包括Web浏览器、PDA以及无线设备
该类客户通常经由轻量协议(如HTTP)与Web Service连接
·肥客户--包括Applet、各类应用以及现存系统
通常使用重量级协议(如IIOP)连接Web Service

二.使用JAX-RPC开发Web Service
1.JAX-RPC支持的数据类型
JAX-RPC除了支持Java的基本数据类型外还支持一些自定义对象,但这些对象有一些条件限制
·有缺省构造函数的对象
·没有实现java.rmi.Remote接口
·字段必须是JAX-RPC支持的类型
·公有字段不能声明为final或transient
·非公有字段必须有对应的setter和getter方法
2.使用JAX-RPC创建Web Service
·基本步骤
A. 编写服务端接口并实现
一个服务的end-point有一些规定:必须实现java.rmi.Remot接口而且每个方法需要抛出RemoteException异常。
B. 编译、生成并且将所有服务需要的类和文件打包成WAR文件
C. 部署包含服务的WAR文件
·如何创建服务
A. 编译服务所需的类文件
B. 生成服务所需文件
可以使用wscompile工具生成model.gz文件,它包含了描述服务的内部数据结构命令如下
wscompile -define -d build -nd build -classpath build config.xml
-model build/model.gz
define标志告诉工具读取服务的 endpoint接口并且创建WSDL文件。-d和-nd标志告诉工具将输出文件写入指定的目录build。工具需要读以下的config.xml文件
<?xml version=”1.0” encoding=”UTF-8”?>
<configuration xmlns=”http://java.sun.com/xml/ns/jax-rpc/ri/config”>
<service
name=”HelloService”
targetNamespace=”urn:Star”
typeNamespace=”urn:Star”
packageName=”helloservice”>
<interface name=”helloservice.HelloIF”/>
</service>
</configuration>
该文件告诉wscompile创建model文件所需的信息
·服务名称:MyHelloService
·WSDL名字空间:urn:Star
·HelloService的所有类在包helloservice中
·服务的端点(endpoint)接口:helloservice.HelloIF
C. 将服务打包为WAR文件
WEB-INF/classes/hello/HelloIF.class
WEB-INF/classes/hello/HelloImpl.class
WEB-INF/jaxrpc-ri.xml
WEB-INF/model.gz
WEB-INF/web.xml
jaxrpc-ri.xml文件如下所述
<?xml version=”1.0” encoding=”UTF-8”?>
<webServices xmlns=”http://java.sun.com/xml/ns/jax-rpc/ri/dd”
version=”1.0”
targetNamespaceBase=”urn:Star”
typeNamespaceBase=”urn:Star”
urlPatternBase=”webservice”>
<endpoint name=”Hello”
displayName=”HelloWorld Service”
description=”A simple web service”
interface=”helloservice.HelloIF”
model=”/WEB-INF/model.gz”
implementation=”helloservice.HelloImpl”/>
<endpointMapping endpointName=”Hello” urlPattern=”/hello”/>
</webServices>
D. 处理WAR文件
使用命令行
wsdeploy -o hello-jaxrpc.war hello-jaxrpc-original.war
wsdeploy工具完成以下几个任务
·读 hello-jaxrpc-original.war作为输入
·从jaxrpc-ri.xml文件中获得信息
·为服务生成tie classes
·生成名为HelloService.wsdl的WSDL文件
·将tie classes和HelloService.wsdl文件打包到新的war文件中
E. 在服务器上部署服务
如果你使用的是TOMCAT,你可以将WAR文件拷贝到webapps目录下,然后可以在
<http://localhost:8080/[context]/[servicename>上看是否配置成功
·如何使用JAX-RPC创建Web Service客户端
通常有三种类型的客户:Static Stub、Dynamic Proxy和Dynamic Invocation Interface(DII)
Static Stub客户
·生成Stub
通过使用config-wsdl.xml和wscompile工具,可以生成stub
wscompile -gen:client -d build -classpath build config-wsdl.xml
config-wsdl.xml文件如下
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl location="http://localhost:8080/helloWS/hello?WSDL"
packageName="staticstub"/>
</configuration>
wscompile工具读取服务器上的WSDL文件并生成stub
·编写静态客户代码
Stub stub=(Stub)(new HelloService_Impl().getHelloIFPort());
HelloIF hello=(HelloIF)stub;
Hello.sayHello(“starchu”);
注意:HelloService_Impl类由wscompile生成
·编译代码
·运行客户端(需要包含saaj API和JAX-RPC API运行)
Dynamic Proxy客户
·生成接口
通过使用config-wsdl.xml文件和wscompile工具,可以生成客户所需的接口
wscompile -import -d build -classpath build config-wsdl.xml
config-wsdl.xml和前面列出的文件内容相同。
·编写动态客户代码
ServiceFactory factory=ServiceFactory.newInstance();
URL wsdlUrl=new URL(“<your web service wsdl url>”);
Service service=factory.createService(wsdlUrl,
new QName(“urn:Star”,”HelloService”));
HelloIF hello=(HelloIF)service.getPort(
new QName(“urn:Star”,”HelloIFPort”),HelloIF.class);
Hello.sayHello(“starchu”);
注意:这里不再需要静态客户代码的HelloService_Impl类
·编译代码
·运行客户端(需要包含saaj API和JAX-RPC API运行)
Dynamic Invocation Interface客户
这个方法为我们提供了更有弹性的客户调用方式,客户代码不在需要由wscompile工具生成的运行时类,当然这种代码更加复杂。具体步骤如下:
·创建ServiceFactory实例
ServiceFactory factory=ServiceFactory.newInstance();

·创建Service(利用服务名的Qname)
Service service=factory.createService(new QName(“HelloService”));

·创建Call对象(使用端点接口的Qname)
Call call=service.createCall(new QName(“HelloIF”));

·设置端点的地址和一些Call对象属性
call.setTargetEndpointAddress(args[0]);
call.setProperty(Call.SOAPACTION_USE_PROPERTY,new Boolean(true));
call.setProperty(Call.SOAPACTION_URI_PROPERTY,””);
call.setProperty(“javax.xml.rpc.encodingstyle.namespace.uri”,
“http://schemas.xmlsoap.org/soap/encoding/”);

·设置远程调用的返回类型、操作名和参数
QName stringType=new Qname(“http://www.w3.org/2001/XMLSchema”,”string”)
call.setReturnType(stringType);
call.setOperationName(new Qname(“urn:Star”,”sayHello”));
call.addParameter(“String_1”,stringType,ParameterMode.IN);

·调用call的invoke方法
String [] param={ “ starchu “ };
String retValue=call.invoke(param);

·编译代码并对Main方法设置<http://localhost:8080/helloWS/hello参数(服务器需有效>)

3.SOAP Message Handler的例子
通常使用JAX-RPC建立的Web Service并不需要开发人员自己处理SOAP消息,但是JAX-RPC提供了一种机制可以使程序员获得这种处理能力,这就是所谓的消息处理器。总的来说,像日志和加解密功能可以通过SOAP消息处理器实现,除此之外,你根本不需要处理SOAP消息。
·基本Handler处理过程
SOAP请求
·客户端处理器在请求消息发送到服务器前被调用
·服务端处理器在请求消息分发到端点前被调用
SOAP应答
·服务端处理器在应答消息发送回客户前被调用
·客户端处理器在应答消息转换成Java方法返回前被调用
SOAP错误
处理过程与SOAP应答的方式一样
注意:处理器可以在任意端组成处理器链
A.Handler基本编程模型
服务端
·编写服务端点接口代码、实现服务并且实现服务端处理器类
·创建jaxrpc-ri.xml文件,以便wscompile使用,其中包含了Handler的信息
·创建web.xml文件
·编译所有代码
·将文件打包为WAR文件
·用wsdeploy工具将原始war文件替换为完整可部署的war文件
·在服务器上部署war文件
客户端
·编写客户程序以及客户端处理器代码
·创建config.xml文件以便wscompile使用,它包含了客户端处理器的信息
·编译代码
·运行wscompile生成服务端点接口和客户类
·编译所有代码,并运行客户应用

B.建立客户端处理器
处理器必须扩展javax.xml.rpc.handler.GenericHandler类并且提供至少两个方法的实现init和getHandlers。此外,你可以利用handleXXX方法处理请求、应答和错误SOAP消息。基本步骤如下
·编写客户端处理器代码
Public class ClientHandler extends GenericHandler{
Public void init(HandlerInfo info){
This.info=info;
}
public QName[] getHeaders(){
return info.getHeaders();
}
public boolean handleRequest(MessageContext context){
SOAPMessageContext smc=(SOAPMessageContext)context;
SOAPMessage message=smc.getMessage();
file://You can use SOAP API to implement your own logic
file://such as logging and encrypt
……
file://Set a logger element in the SOAPHeader
SOAPHeaderElement loggerElement=
header.addHeaderElement(envelope.createName(“loginfo”,
“ns1”,”urn:Star:headprops”));
loggerElement.setMustUnderstand(true);
loggerElement.setValue(“10”);
file://Set a name element in the SOAP Header
SOAPHeaderElement nameElement=
Header.addHeaderElement(envelope.createName(“client”,
“ns1”,”urn:Star:headprops”));
nameElement.addTextNode(“Star chu”);
}
}
·编辑config.xml文件
<?xml version=”1.0” encoding=”UTF-8”?>
<configuration xmlns=”http://java.sun.com/xml/ns/jax-rpc/ri/config”?>
<wsdl location=”http://localhost:8080/handlerWS/handler?WSDL
packageName=”client”>
<handlerChains>
<chain runAt=”client”>
<handler className=”client.ClientHandler”>
<property name=”name” value=”client handler”/>
</handler>
</chain>
</handlerChains></wsdl></configuration>
·编写静态客户
C.建立服务端处理器
·编写服务端处理器(与客户端结构类似)
Public boolean handleRequest(MessageContext context){
SOAPMessageContext smc=(SOAPMessageContext)context;
……
Iterator it=header.examineAllHeaderElements();
While(it.hasNext()){
SOAPElement element=(SOAPElement)it.next();
If(element name is loginfo and must understand it){
element.getValue();
element.detach();
file://Invoke only when the setMustUnderstand(true)
}
}
}
detach方法用来移除元素,这个需求仅当一个元素设置了mustUnderstand属性在必要。
·编辑jaxrpc-ri.xml文件
<?xml version=”1.0” encoding=”UTF-8”?>
<webServices xmlns=”http://java.sun.com/jax-rpc/config/ri/dd”
version=”1.0”
targetNamespaceBase=”urn:Star:wsdl”
typeNamespaceBase=”urn:Star:types”
urlPatternBase=”/handler”>
<endpoint name=”HandlerTest”
displayName=”Handler Test”
description=” … …”
interface=”service.HandlerTest”
model=”/WEB-INF/model.gz”
implementation=”service.HandlerTestImpl”>
<handlerChains>
<chain runAt=”server”>
<handler className=”service.LoggerHandler”
headers=”ns1:loginfo”
xmlns:ns1=”urn:Star:headerprops”>
<property name=”name” value=”Logger”/>
</handler>
<handler className=”service.NameHandler”>
<propery name=”name” value=”Name”/>
</handler>
</chain>
</handlerChains>
</endpoint>
<endpointMapping endpointName=”HandlerTest”
urlPattern=”/handler”/>
</webServices>
在第一个处理器中,XML使用了属性 headers描述头信息。这是因为客户代码告诉服务端,logger头必须被理解,否则客户将收到SOAP错误消息
·生成WAR文件并部署到服务器上
4.源代码
·HelloIF.java(endpoint接口)
package helloservice;

import java.rmi.RemoteException;
import java.rmi.Remote;

public interface HelloIF extends Remote{
public String sayHello(String target) throws RemoteException;
}
·HelloImpl.java
package helloservice;

public class HelloImpl implements HelloIF{
private String message="Hello";
public String sayHello(String target){
return message+target;
}
}
·StaticClient.java
package staticstub;
import javax.xml.rpc.Stub;
public class StaticClient{
private static String endpointAddress;
public static void main(String [] args){
if(args.length!=1){
System.err.println("Usage : java HelloClient [endpoint address]");
System.exit(-1);
}
endpointAddress=args[0];
System.out.println("Connect to :"+endpointAddress);
try{
Stub stub=createStub();
stub._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,
endpointAddress);
HelloIF hello=(HelloIF)stub;
System.out.println(hello.sayHello(" Starchu!"));
}catch(Exception e){System.err.println(e.toString());}
}
private static Stub createStub(){
return (Stub)(new HelloService_Impl().getHelloIFPort());
}
}
·DynamicClient.java
package dynamicproxy;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.JAXRPCException;
import staticstub.HelloIF;

public class DynamicClient{
private static String wsdl;
private static String namespaceUri="urn:Star:wsdl";
private static String serviceName="HandlerService";
private static String portName="HandlerTestPort";

public static void main(String [] args){
if(args.length!=1){
System.err.println("Usage : java DynamicClient [server Url]");
System.exit(-1);
}
System.out.println("Connect to :"+args[0]);
helloWsdl=args[0]+"?WSDL";
try{
URL wsdlUrl=new URL(wsdl);
ServiceFactory serviceFactory=ServiceFactory.newInstance();
Service service=
serviceFactory.createService(wsdlUrl,
new QName(namespaceUri,serviceName));
HandlerTest proxy=(HandlerTest)service.getPort(
new QName(namespaceUri,portName),HandlerTest.class);
proxy.test();
}catch(Exception e){
System.err.println(e.toString());
}
}
}
·DIIClient.java
package dii;
import javax.xml.rpc.*;
import javax.xml.namespace.*;

public class DIIClient{
private static String qnameService = "HelloService";
private static String qnamePort = "HelloIF";
private static String BODY_NAMESPACE_VALUE ="urn:Star";
private static String ENCODING_STYLE_PROPERTY ="javax.xml.rpc.encodingstyle.namespace.uri";
private static String NS_XSD ="http://www.w3.org/2001/XMLSchema";
private static String URI_ENCODING ="http://schemas.xmlsoap.org/soap/encoding/";

public static void main(String [] args){
try{

ServiceFactory factory=ServiceFactory.newInstance();
Service service=factory.createService(new QName(qnameService));
QName port=new QName(qnamePort);
Call call=service.createCall(port);
call.setTargetEndpointAddress(args[0]);
call.setProperty(Call.SOAPACTION_USE_PROPERTY,new Boolean(true));
call.setProperty(Call.SOAPACTION_URI_PROPERTY,"");
call.setProperty(ENCODING_STYLE_PROPERTY,URI_ENCODING);
QName qnameTypeString=new QName(NS_XSD,"string");
call.setReturnType(qnameTypeString);
call.setOperationName(new QName(BODY_NAMESPACE_VALUE,"sayHello"));
call.addParameter("String_1",qnameTypeString,ParameterMode.IN);
String [] params = { "Starchu" };
System.out.println((String)call.invoke(params));
}catch(Exception e){
System.err.println(e.toString());
}
}
}
·Ant文件build.xml
<project name="helloWS" basedir="." default="deploy">
<property file="build.properties"/>
<property name="build" value="build"/>
<property name="dist" value="${build}\classes"/>
<property name="lib" value="${build}\lib"/>
<property name="src" value="src"/>
<property name="etc" value="${src}\etc"/>

<target name="clean">
<delete dir="${build}"/>
</target>

<target name="init">
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${lib}"/>
</target>

<path id="classpath">
<fileset dir="${tomcat.home}">
<include name="jaxrpc/**/*.jar"/>
<include name="jaxb/**/*.jar"/>
<include name="jaxp/**/*.jar"/>
<include name="saaj/**/*.jar"/>
<include name="jwsdp-shared/lib/**/*.jar"/>
</fileset>
<pathelement path="${dist}"/>
<pathelement location="${lib}"/>
</path>

<target name="compile-service" depends="init">
<javac srcdir="${src}" destdir="${dist}" includes="HelloService/**/*.java"/>
</target>

<target name="generate-sei-service" depends="compile-service">
<exec executable="wscompile.bat">
<arg line="-define -d ${build} -nd ${build} -classpath ${dist} ${etc}\config-interface.xml -model ${build}\model.gz"/>
</exec>
<copy todir="${build}">
<fileset dir="${etc}" includes="*.xml"/>
</copy>
</target>

<target name="package-service" depends="generate-sei-service">
<war warfile="${build}\${ant.project.name}-portable.war"
webxml="${build}\web.xml">
<webinf dir="${build}" includes="*.xml,*.gz,*.wsdl" excludes="web.xml"/>
<classes dir="${dist}" includes="**/*.class" defaultexcludes="no"/>
</war>
</target>

<target name="process-war" depends="package-service">
<exec executable="wsdeploy.bat">
<arg line="-o ${ant.project.name}.war ${build}\${ant.project.name}-portable.war"/>
</exec>
</target>

<target name="deploy">
<copy file="${ant.project.name}.war" todir="${server}"/>
</target>

<target name="undeploy">
<delete file="${server}\${ant.project.name}.war"/>
</target>

<!-- Generating Static Client -->
<target name="generate-stubs" depends="init">
<exec executable="wscompile.bat">
<arg line="-gen:client -d ${dist} -classpath ${dist} ${etc}\config-wsdl.xml"/>
</exec>
</target>

<target name="compile-client" depends="generate-stubs">
<javac srcdir="${src}" destdir="${dist}" includes="StaticStub/**/*.java">
<classpath refid="classpath"/>
</javac>
</target>

<target name="package-client" depends="compile-client">
<jar destfile="${lib}\client.jar" basedir="${dist}" excludes="HelloService/**/*.class"/>
</target>

<target name="run-client" depends="package-client">
<java classname="staticstub.HelloClient"
classpathref="classpath"
fork="true">
<sysproperty key="endpoint" value="${endpoint}"/>
<arg value="${server.port.url}"/>
</java>
</target>

<!-- Generating Dynamic Client -->
<target name="generate-interface" depends="init">
<exec executable="wscompile.bat">
<arg line="-import -d ${dist} -classpath ${dist} ${etc}\config-wsdl.xml"/>
</exec>
</target>

<target name="compile-dynamic-client" depends="generate-interface">
<javac srcdir="${src}" destdir="${dist}" includes="DynamicProxy/**/*.java">
<classpath refid="classpath"/>
</javac>
</target>

<target name="package-dynamic-client" depends="compile-dynamic-client">
<jar destfile="${lib}\client.jar" basedir="${dist}" includes="**/HelloIF.class,**/DynamicClient.class"/>
</target>

<target name="run-dynamic-client" depends="package-dynamic-client">
<java classname="dynamicproxy.DynamicClient"
classpathref="classpath"
fork="true">
<sysproperty key="endpoint" value="${endpoint}"/>
<arg value="${server.port.url}"/>
</java>
</target>

<!-- Generating Dynamic Invocation Interface -->

<target name="compile-dii">
<javac srcdir="${src}" destdir="${dist}" includes="DII/**/*.java">
<classpath refid="classpath"/>
</javac>
</target>

<target name="run-dii" depends="compile-dii">
<java classname="dii.DIIClient"
classpathref="classpath"
fork="true">
<sysproperty key="endpoint" value="${endpoint}"/>
<arg value="${server.port.url}"/>
</java>
</target>

</project>
·属性文件(build.xml文件使用)
server=C:/Java/jwsdp-1.2/webapps
tomcat.home=C:/Java/jwsdp-1.2
endpoint=http://localhost:8080/helloWS/hello
server.port.url=http://localhost:8080/helloWS/hello

参考资料
1. Developing Web Service Series <http://www.theserverside.com/resources/article.jsp?l=Systinet-web-services-part-1> www.theserverside.com
2. JWSDP-1.2 Tutorial java.sun.com

posted @ 2007-05-18 15:24 鸿雁| 编辑 收藏

性格是否影响了你的职业生涯?

谈到职业生涯,最基础的是要看一个人和环境之间的适应性。现代的职业观当然不再局限于一个工作,而是找到那些能让自己发挥能力、技术、能表达自己想法、能在某一方面承担某一角色的环境;而对环境的适应也是因性格的不同而相异的。  

  一、传统型:这种个性类型的人在事务性的职业中最为常见。这一类人容易组织起来,喜欢和数据型及数字型的事实打交道,喜欢明确的目标,不能接受模棱两可的状态。这些人可以用这一类的词语来表述他们:服从的,有秩序的,有效率的,实际的。如果用不太客气的话说,就是缺乏想像,能自我控制,无灵活性。出纳员就是这种类型的典型代表。  

  二、艺术型:这种类型与传统型形成最强烈的反差。他们喜欢选择音乐、艺术、文学、戏剧等方面的职业。他们认为自己富有想像力,直觉强,易冲动,好内省,有主见。这一类型的人语言方面的资质强于数学方面。如果用消极一些的语言描述,这类人是感情极丰富的、无组织纪律的。  

  三、现实主义型:这种类型的人真诚坦率,较稳定,讲求实利,害羞,缺乏洞察力,容易服从。他们一般具有机械方面的能力,乐于从事半技术性的或手工性的职业(如管道工、装配线工人等),这类职业的特点是有连续性的任务需要却很少有社会性的需求,如谈判和说服他人等。  

  四、社会型:社会型的人与现实主义型的人几乎是相反的两类。这类型喜欢为他人提供信息,帮助他人,喜欢在秩序井然、制度化的工作环境中发展人际关系和工作。这些人除了爱社交之外,还有机智老练、友好、易了解、乐于助人等特点。其个性中较消极的一面是独断专行,爱操纵别人。社会型的人适于从事护理、教学、市场营销、销售、培训与开发等工作。  

  五、创新型(企业家型):这种类型的人与社会型的人相似之处在于他(她)也喜欢与人合作。其主要的区别是创新型的人喜欢领导和控制他人(而不是区帮助他人),其目的是为了达到特定的组织目标。这种类型的人自信,有雄心,精力充沛,健谈。其个性特点中较消极的一面是专横,权力欲过强,易于冲动。  

  六、调查研究型:这种类型与创新型几乎相反。这一类型的人为了知识的开发与理解而乐于从事现象的观察与分析工作。这些人思维复杂,有创见,有主见,但无纪律性,不切实际,易于冲动。生物学家、社会学家、数学家多属于这种类型。在商业性组织中,这类人经常担任的是研究与开发职务及咨询参谋之职。这些职务需要的是复杂的分析,而不必去说服取信于他人。  

  当然,一个人往往不是单一地表现某种类型,常常是两三种类型的组合,但不管怎样,总要往积极的性格方向发展,要让自己选择工作,而不是工作选择自己。  

posted @ 2007-05-09 13:08 鸿雁| 编辑 收藏

越老越值钱的七大职业

医师:治病+保健最赚钱医生从事的是高科技和高风险的职业,人才培养周期也大于其它行业,因此,做医师自然越做越值钱。

  律师:向专业领域发展

  律师的工作性质类似于医生,属于经验行业。律师今后的发展方向应努力朝某一个专业领域发展,把握该领域的发展趋势,成为某一方面的专家里手。尤其是商业律师、企业法律顾问等专业方向相对容易获得高薪。

  教师:应用型+方法论吃香

  在许多大中以上城市,教师的收入水平已经超过一般白领。今后,应用型的教师将更容易获得高薪,因此,教师也必须经常走进企业,不断积累“实战”经验,以此丰富自己的知识结构。而中小学教师面对的是有升学压力的学生,故而传授“方法”显得极其重要。

  古玩鉴定师:等于“古董”职业

  古玩鉴定行业的门槛较高,需要具备相当深厚的中国传统文化知识,而这些知识通过突击不能获得,因此,称该职业为“越老越值钱”的“古董”职业一点也不为过……

  精算师:年薪起步价20万

  普通和初级财务人员目前供大于求,但高端财务人才却千金难觅。如果想在这个行业发展,考取相应的资格证书只是第一步,丰富的经验能赋予你高效的管理能力、处理突发事件的应急能力。

  建筑设计师:稀缺人才

  属于稀缺性人才,目前市场上对建筑设计人才大多要求5年以上的工作经验,有多年的研究和实践经验的支持,薪水一定能层层攀高。

  咨询顾问:新兴行业

  咨询顾问的工作就是替企业诊断“疑难杂症”,并开出“药方”,要用最前沿的眼光为企业做出最具战略意义的分析。

posted @ 2007-05-09 12:22 鸿雁| 编辑 收藏

胸部小怎么办?

无论你正值豆蔻年华,还是浓情绽放,抑或花期已过,都应该做些胸部保养,因为它是女性的第二张脸。先检查一下这张脸是否足够结实、挺拔、浑圆?皮肤是否柔软细腻有弹性?是否因产后哺乳导致胸部下垂变形?是否因减肥不当导致胸部消瘦缩小?是否因为工作压力大造成胸部皮肤弹性张力减低而松弛、下垂
、变形?是否因年龄关系体内激素分泌减少产生萎缩?总之,无论是先天发育不佳,抑或健康、压力、产后哺乳,更年期等原因造成的不良影响,都会危及到你的玲珑美丽。

紫颖倡导由内而外的自然美丽。采用法国天使丰姿技法与点穴原理创新结合的自然丰胸技法,效果看得见,已为众多女性带来完美曲线。结合传统循经点穴手法,紫颖将这一西方经典的美胸技术融入传统的点穴丰胸之中,由专家指点操作于背部、胸部,疏通经络,加之理性互动按摩,促进血液循环;进一步使胸大肌强健,提高乳房基础质量;促进胸部脂肪细胞吸收更多营养,同时加大脂肪细胞体积;从而达到理想的丰胸目的。更多精彩内容可上WWW.ZIYING.COM.CN。

posted @ 2007-05-08 13:07 鸿雁| 编辑 收藏

报关员入行前要三思

每年6月是报关员资格全国统一考试的报名时间,那么报关员工作容易找吗?报关员是“金领职业”吗?什么样的人适合从事这一行?
  带着这些问题,我们走访了上海报关协会培训部负责人季岳生先生和长发国际货运有限公司空运部陶伟明先生,从行业管理和企业用人的角度,他们给想要入行的人士提供了有价值的建议。

  【考试】
  门槛不高过关率低

  随着我国外贸进出口量的不断扩张,报关员考试的热度也不断在上升。从上海地区报名情况看,2001年有1.1万多人报名,到2003年,人数已经上升到2万多人。
  季岳生认为,单从报名的热度上评价这个行业的热度有一定片面性。可以看到,接连几年上升的报名人数,一方面与外贸行业的人才需求息息相关,另一方面,也与目前不容乐观的大中专毕业生就业形势相映衬,在增加的报名人数中,大专生、“三校生”占到了很大比例。
  同时,每年报关员考试的过关率并不高,全国平均仅在10%左右,即使是直接从上海海关教育培训中心出来的考生,能一次通过考试的也只有23%。报关员考试最大的难度在“报关实务”部分,这个内容占到考试内容的60%,很多没有实际操作经验的人往往在这上面丢分。
  不过,也有人指出,以中专及高中学历为界的报名门槛使得考生队伍平均素质不高,报关员考试通过率较低也是在所难免的,有志于此者不必让通过率吓倒了。

  【求职】
  证书为经验锦上添花

  由于报关员考试报名热度不断升温,报关员考试培训也成了“香饽饽”,不少培训机构表示,只要考到证,工作就一片光明。
  对此,季岳生先生认为应该客观地来看待报关员证书的分量。一方面,据他的了解,目前在上海,拥有报关员证书而找不到工作的人几乎没有,他表示:“如果有找不到工作的,可以到报关协会来联系,我们可以帮他们与企业牵个线。”但另一方面,季先生承认,报关员证书能带来的职业前景并非像一些培训机构所说的如此光明,毕竟这个市场容量是有限度的,特别是随着电子化报关时代的到来,技术升级会降低人工的需求,而对个人技能的要求越来越高。
  作为用人单位,长发国际货运有限公司空运部陶伟明先生把报关员证看得更淡。他表示在招聘中,工作经验才是最关键的因素。像他们招聘的空运报关员,更是强调经验,因为航班可不等人,万一有一个环节弄错了,就很难弥补了。如果是新手,多半只能“跟着别人跑”,一些审单工作,往往需要有三五年工作积累的报关员。

  【工作】
  薪资不均“金饭碗”太沉
  社会上流传着“报关员是个金饭碗”的说法,不过,业内人士同时指出,这个说法有点偏颇。
  陶先生表示,报关员的工作相当辛苦,由于海关实行的是“5+2”通关,天天不关门,报关员必须跟着市场走,没有固定的休息日,加班是常事。另外,和白领比,报关员工作环境并不理想。通关口岸往往人声鼎沸,而通关手续又繁杂,长期从事这个工作,耐压能力和心态调整非常重要。
  季先生从薪资角度提出疑议。据他透露,报关员薪资差别非常大。一般来说,跑单的,月薪1000元左右很正常,而负责制单的,月薪能到三四千元。经验越多,薪水还会往上走。
  另外,自理报关员的收入与企业的效益相关,报关员所在的外贸企业效益好,报关员的收入也不低,很多高薪的说法大多从这些公司传出来;比较而言,代理报关公司的薪水就较难保证,代理公司竞争激烈,相互间压价厉害,薪资反而不如自理报关员。
  季先生也坦言,在就业难背景下,这个行业确实在迅速扩张中,因此,短期之间,报关员职业的热度不会退。

  优秀报关员基本条件
  1.熟悉外贸知识,了解产品属性和贸易流程,正确填写报关单。
  2.熟悉海关各类政策、条款及相关流程,及时了解海关政策调整。
  3.要具有出色的人际沟通能力,这样在办理通关手续时,能清楚回答海关问题,解释疑惑。
  4.工作耐心细致,把准备工作做在前面,避免返工,加速通关。
  5.良好的廉政意识,切忌对海关工作人员行贿。

posted @ 2007-05-08 11:44 鸿雁| 编辑 收藏

健康生活:电脑族每天宜喝四杯茶

面对电脑时间长了不好,那该怎么办?其实每天四杯茶,不但可以对抗辐射的侵害,还可以保护眼睛。

1、上午一杯绿茶:绿茶中含强效的抗氧化剂以及维生素C,不但可以清除体内的自由基,还能分泌出对抗紧张压力的激素。绿茶中所含的少量咖啡因可以刺激中枢神经,振奋精神。不过最好在白天饮用,以免影响睡眠。

2、下午一杯菊花茶:菊花有明目清肝的作用,有些人就干脆用菊花加上枸杞一起泡来喝,或是在菊花茶中加入蜂蜜,都对解郁有帮助。

3、疲劳了一杯枸杞茶:枸杞子含有丰富的β胡萝卜素、维生素B1、维生素C、钙、铁,具有补肝、益肾、明目的作用。其本身具有甜味,可以泡茶也可以像葡萄干一样作零食,对解决电脑族眼睛涩、疲劳都有功效。

4、晚间一杯决明茶:决明子有清热、明目、补脑髓、镇肝气、益筋骨的作用。

posted @ 2007-05-08 10:47 鸿雁| 编辑 收藏

新华人寿推出富贵人生两全保险(A款)(分红型)

喜庆祥和的2007“金猪”年刚至,新华保险隆重推出一款集快速见利、祝寿养老、高额保障、分红增值等多重功能于一体、特别适合在春节期间销售的拳头产品。该产品充分吸收了目前市场上热销的快速返还型产品特色,保险期间至被保险人88周岁保单生效对应日。产品的交费期限分10年交、20年交两种,其中10年交投保年龄为30天以上-50周岁,20年交投保年龄为30天以上-45周岁。

富贵人生两全保险(A款)(分红型)产品具有快速见利、六六祝寿、年金养老、高额保障、分红增值和特别豁免六大特点。分别是:

快速见利:66周岁前每两年领取有效保额的8%,补充日常开销。

六六祝寿:66周岁领取100%有效保额作为祝寿金,实现晚年心愿。

年金养老:66周岁开始每年领取有效保额的8%,以年金形式补充养老基金。

高额保障:66周岁前300%有效保额的身价保障;随着家庭责任的减轻,66周岁开始还拥有150%有效保额的身价保障。

分红增值:通过保额分红,复利递增,使领取金和身价保障不断提高,保值增值。

特别豁免:全残免交续期保费,继续享受所有保障利益。

富贵人生两全保险(A款)(分红型)产品到底能给客户带来什么利益呢?笔者专程走访了新华保险公司产品专家,据其介绍,该产品设计科学、合理,可解决客户的零用、保障、养老和人生规划四大需求。分别为:1、零用。提供零花钱。66周岁前有效保额8%的领取可补贴日常开支,或实现固定的小额开销规划,如门诊医疗金、年度专项体检金、家庭旅游金等。2、保障。提供高额保障。66周岁前300%有效保额的身价保障,66周岁开始150%有效保额的身价保障,抵御未知风险。3、养老。66周岁祝寿金和以后每年有效保额8%的年金领取,补充养老金。4、人生规划。以现有资金,规划未来不同阶段的生活,抵御各种未知风险。

该理财专家还介绍了富贵人生产品与众不同的五大优势:一是返还基础不同。“富贵人生”是以有效保额作为返还基础,可有效缓解通货膨胀压力。二是返还频率和额度不同。“富贵人生”设计更科学合理。 66周岁前每两年返有效保额的8%,既解决客户的部分现金领取需求,又不会对账户的长期收益造成很大影响;66周岁后每年返有效保额的8%,有效补充养老金。三是祝寿金设计不同。“富贵人生”领取适时,设计切合客户退休后规划生活的需求,66周岁返还100%有效保险金额,为客户开始一段新的人生提供资金支持。四是身故保障的设计不同。“富贵人生”66周岁前300%有效保额的身价保障,66周岁开始150%有效保额的保障,切合客户不同阶段的人生责任。五是独具保费豁免功能。“富贵人生”设计更加人性化,特别增加了全残豁免保费的功能,面临人生变故,无须继续交费,仍然可以享受全部保险利益。

据理财专家介绍,当前百姓投资渠道狭窄,房地产投资风险加剧,股市及基金专业性强,投资市场正处于不太明朗、潜藏风险的状况之下,新华保险《富贵人生两全保险(A款)(分红型)》的面世为广大市民理财提供了一个新的选择。

posted @ 2007-04-20 21:05 鸿雁| 编辑 收藏

分红险现金分红保额分红方式不同分红差异大

同样是分红险,保额、保费差不多,分到的红利却相差十余倍?目前,在加息预期下,分红型保险成为大热险种,但其实,目前,市场上的分红型保险产品虽然同样名曰“分红险”,可实质上却使用两种不同的分红方式,一种是现金分红,一种是保额分红,市民在选择分红险的时候,要结合自己的实际情况进行挑选。

两种分红方式

美式分红:即通常所说的现金分红,中国人寿(行情论坛)、平安保险(行情论坛)、友邦保险等大多数保险公司均采用美式分红。

英式分红:即通常所说的保额分红,新华人寿、信诚人寿、太平人寿的一些产品则采用英式分红。这两款产品的分红之所以差异如此之大,就在于它们根本采取了不同的分红方式。

案例

保费相近 红利相差数百元

市民赵先生去年投保了一份分红型寿险,分20年交保费,年缴保费3600元,保险金额10万元,最新接到的一份保险公司的分红通知让赵先生不禁大为欣喜,今年的分红有900多元,按照自己3600元的投入,相当于一年的收益率超过了20%!保险的回报率竟然有这么高?

可在赵先生印象中,自己的朋友小唐同样是去年投保了一款分红型寿险,保险金额10万元,一年要缴保费3000多元,拿到的分红也就只有几十元而已。

同样是分红型保险,为什么红利的差距如此悬殊?赵先生颇费思量。

两种分红方式的计算

以投保人同样买10万元保额分红险为例,20年缴,年保费3000元,假设分红率均为1%。

若采用美式分红,保险公司以现金价值的多少为依据计算分红,并以现金的形式派发当年分红。粗略来看,第一年的年终分红为3000×1%=30元;第二年为3000×2×1%=60元,以此类推。

而若采取英式分红,保险公司根据保额计算分红,并将当年分红自动累加到保户的保额上,将红利变成保额,复利递增。

举例来看,年终分红可以这样计算,假定每年分红率为1%,由于保险金额为10万元,第一年的年度红利为10万元×1%=1000元;第二年的年度红利为(10万元+1000元)×1%=1010元;而第三年的年度红利为(10万元+1000元+1010元)×1%=1020.1元,以此类推。

现金分红 PK 保额分红

收益性

保额分红短期收益有优势

从计算红利的基数来看,现金分红的计算基数为“保单的现金价值”,保额分红的计算基数是保险金额,由于投保初期保单的现金价值比较小,因此,现金分红险投保初期现金红利很小,没有保额分红的优势。但从长期看,随着所交保费越来越多,与保额分红的差异会逐步缩小。

从“分红率”来看,由于现金分红,每年派发现金红利需要较高流动性,可能会制约保险公司投资收益的空间,而保额分红派发的红利会直接增加到保额上,这样保险公司可以增加长期资产的投资比例,在某种程度上可增加投资收益,使被保险人能保持较高且稳定的投资收益率。

保障性

保额分红保障性较高

从保障的角度来看,保额分红的优势则比较明显。

保额分红是以保额为基础进行分红,同时,会将当期红利增加到保单的现有保额之上。这就相当于使投保人在保障期内,无需核保和申请增加保额,保额自动增加,可在一定程度上缓解因通胀导致的保障贬值。

灵活性

现金分红可留也可取

保额分红的红利领取不如现金分红灵活。

以赵先生和小唐的例子来看,第一年的分红,赵先生所获得的红利是900多元,而小唐的分红只有几十元,但由于这笔钱转成了保额,赵先生实际上是无法从保险公司拿到这笔钱的,只能在发生保险事故、保险期满或退保时,才能拿回所分配的红利。

小唐所能获得的红利看起来不多,但采取现金分红的方式,红利性质比较灵活,可留也可取,只要小唐想领取现金红利,就可以拿到这笔分红。而目前现金分红险可提供累计生息、抵交保费、购买缴清增额等方式。

建议

流动性要求高选现金分红

如果市民不急于将红利取出,可选择保额分红产品,长期将获得更高的保障,并有望获得更好的收益。但如市民对现金的流动性要求较高,则可选择现金分红产品,灵活性更高。

此外,不管是“现金分红”还是“保额分红”,能否给投保人带来更多的利益,关键还在于保险公司的运营情况。如果保险公司的资金运营能取得更高的收益率,分红自然也会水涨船高,市民不要被分红方式这种表面上的差别所迷惑,而更应该关注所投保的保险公司的运营能力。

posted @ 2007-04-20 20:33 鸿雁| 编辑 收藏

测试:出生日期看你的感情表达能力

  •   凭出生日期的数字测出内在性格和潜在力量的测验,看似不可思议,但近来在日本十分流行,而且被测者都发现能找到准确而奇妙的答案,不信?玩过就知。

玩法:请从你出生年、月、日中,找出不同的数字。例如:1978年12月9日,就有(2个1)(1个2)(1个7)(1个8)和(2个9),然后请参阅下文,就会知道你的感情表达能力、思维能力、失恋治疗能力等内在玄妙了。

分析:

(1)代表感情表达能力

(1个1):你属于固执而不懂表达感情的人,故经常暗恋人。由于你都算理智,甚少被情所伤。

(2个1):你善于表达感情,面对心仪对象,往往能大胆示爱。由于你喜怒形于色,恋爱过程亦见顺利。

(3个1):你不易透露心底秘密,往往经过深思熟虑,才会将事情告知他人,所以做你的情人要有十足耐性。

(4个1):你十分敏感,情绪起伏不定,毫不掩饰自己的喜怒哀乐,容易意志消沉,需要情人不时地鼓励。

(5个1):你极度情绪化,容易伤害别人,作为你的情人,一定要对你耐心关怀,才能彼此沟通无阻。

(2)代表直觉度

(1个2):你懂得顾及别人的感受,善于洞悉别人的想法,是一个可靠的朋友和情人。

(2个2):你善解人意,乐于助人,爱付出。对于异性来说,你的细心体贴甚具吸引力。

(3个2或以上):你的直觉一般,幸好反应能力强。你喜欢多姿多彩的生活,对神秘的爱情也心向往之。

(3)代表思维能力和想象力

(1个3):超强的想象力令你能散发独特的魅力,而你同时向往浪漫而甜蜜的恋爱。

(2个3):言行常超出常规,常胡思乱想。与爱侣相处时,经常云游太虚,令对方觉得无趣。

(3个3或以上):你智商很高,思维清晰,无法忍受单调的生活,若没机会发挥才能,会变得精神紧张。

(4)代表行动力

(1个4):热情澎湃,言出必行,自信十足,你会大胆表露内心感情,性欲亦旺盛。

(2个4或以上):做人缺乏自信心,对于爱侣忠心耿耿。不会有越轨的念头,亦期望另一半对你专心不二。

(5)代表意志坚定度

(1个5):思想单纯,即使情人见异思迁,你亦不会放弃,希望有守得云开见月明的一日。

(2个5):你的意志并不坚定,容易半途而废,往往事倍功半,想好好发挥才能,最好将精力放在创意活动上。

(3个5或以上):内心有着无法克制的热情,做事冲动,决不会改变自己决定的事情,还要另一半听你指示。

(6)代表自我价值

(1个6):你天性敏感,喜欢被别人欣赏,只有这样,你才能感受到自己存在的价值。

(2个6):你多愁善感,缺乏自信,伴侣对你的爱护,是你发挥才能的推动力。

(3个6或以上):你有绝对的自信心,为了令自己与众不同,永远全力以赴,喜欢出风头。

(7)代表失恋治疗能力

(1个7):谈恋爱时,你会为对方周全考虑,失恋治疗能力亦强,对人欢笑,背人垂泪。

(2个7):由于你每次恋爱都很投入,故失恋时,往往伤得很深,需要向别人倾诉,才能解开郁结。

(3个7或以上):你不易爱上人,但一旦恋爱,会是十分专情的情人。如果被抛弃,你会对曾经的一切念念不忘。

(8)代表智力和逻辑性

(1个8):你智力一般,但逻辑性强,做事喜欢循序渐进,不喜欢预期以外的变化。

(2个8或以上):你聪明独立,表达能力强,有决断能力,有领导才华,做事往往得心应手。

(9)代表体贴度

(1个9):你尝试理解别人对感情的渴求,然后尽量配合。

(2个9或以上):无论智力或精力,你都非常旺盛,但缺点是经常沉醉于自己的想法中,令情人觉得你难以捉摸。

(10)代表精神力量

(1个0):你一生的时间和精神力量,均被情人及朋友瓜分,紧记要在友情和爱情之间找出一个平衡点。

(2个0或以上):你的自我精神极强,在逆境时往往能看出事情的真相,甚少作出错误决定

posted @ 2007-04-20 15:33 鸿雁| 编辑 收藏

仅列出标题
共18页: First 上一页 7 8 9 10 11 12 13 14 15 下一页 Last