<< 一月 2014 | 首页 | 三月 2014 >>

预测算法用java实现 - Knowledge&&Imagination - ITeye技术网站

一,简易平均法,是一种简便的时间序列法。是以一定观察期的数据求得平均数,并以所求平均数为基础,预测未来时期的预测值。简易平均法是最简单的定量预测方法。简易平均法的运算过程简单,不需要进行复杂的模型设计和数学运用,常在市场的近期预测、短期预测中使用。 
  1、算术平均法 
  算术平均法,就是以观察期数据之和除以求和时使用的数据个数(或资料期数),求得平均数进行预测的方法。 

  运用算术平均法求平均数,有两种形式: 

  (一)以最后一年的每月平均值或数年的每月平均值,作为次年的每月预测值 

  为了确定合理的误差,用公式估计出预测的标准差。 

  按公式计算某种可靠程度要求时的预测区间。 

  (二)以观察期的每月平均值作为预测期对应月份的预测值 

  当时间序列资料在年度内变动显著或呈季节性变化时,用第一种方法求平均值进行预测的话,势必影响预测值的精确度,同时也不能反映出年度内不同月、季的情况。 

  2、几何平均法 
  几何平均法,就是运用几何平均数求出预测目标的发展速度。 

  几何平均数,就是将观察期n个环比速度资料数相乘,开n次方,所得的n次方根。 

  根据几何平均数建立预测模型进行预测。 

  3、加权平均法 
  加权平均法,就是在求平均数时,根据观察期各资料重要性的不同,分别给以不同的权数后加以平均的方法。

  其特点是:所求得的平均数,已包含了长期趋势变动。 


二,移动平均法 
移动平均法是用一组最近的实际数据值来预测未来一期或几期内公司产品的需求量、公司产能等的一种常用方法。移动平均法适用于即期预测。当产品需求既不快速增长也不快速下降,且不存在季节性因素时,移动平均法能有效地消除预测中的随机波动,是非常有用的。移动平均法根据预测时使用的各元素的权重不同 

  移动平均法是一种简单平滑预测技术,它的基本思想是:根据时间序列资料、逐项推移,依次计算包含一定项数的序时平均值,以反映长期趋势的方法。因此,当时间序列的数值由于受周期变动和随机波动的影响,起伏较大,不易显示出事件的发展趋势时,使用移动平均法可以消除这些因素的影响,显示出事件的发展方向与趋势(即趋势线),然后依趋势线分析预测序列的长期趋势。 

移动平均法的种类 
  移动平均法可以分为:简单移动平均和加权移动平均。 

1、简单移动平均法 
  简单移动平均的各元素的权重都相等。简单的移动平均的计算公式如下: Ft=(At-1+At-2+At-3+…+At-n)/n式中, 

•Ft--对下一期的预测值; 
•n--移动平均的时期个数; 
•At-1--前期实际值; 
•At-2,At-3和At-n分别表示前两期、前三期直至前n期的实际值。 
2、加权移动平均法 
  加权移动平均给固定跨越期限内的每个变量值以不同的权重。其原理是:历史各期产品需求的数据信息对预测未来期内的需求量的作用是不一样的。除了以n为周期的周期性变化外,远离目标期的变量值的影响力相对较低,故应给予较低的权重。 加权移动平均法的计算公式如下: 

  Ft=w1At-1+w2At-2+w3At-3+…+wnAt-n式中, 

•w1--第t-1期实际销售额的权重; 
•w2--第t-2期实际销售额的权重; 
•wn--第t-n期实际销售额的权 
•n--预测的时期数;w1+ w2+…+ wn=1 
  在运用加权平均法时,权重的选择是一个应该注意的问题。经验法和试算法是选择权重的最简单的方法。一般而言,最近期的数据最能预示未来的情况,因而权重应大些。例如,根据前一个月的利润和生产能力比起根据前几个月能更好的估测下个月的利润和生产能力。但是,如果数据是季节性的,则权重也应是季节性的。 

移动平均法的优缺点 
  使用移动平均法进行预测能平滑掉需求的突然波动对预测结果的影响。但移动平均法运用时也存在着如下问题: 

  1、 加大移动平均法的期数(即加大n值)会使平滑波动效果更好,但会使预测值对数据实际变动更不敏感; 

  2、 移动平均值并不能总是很好地反映出趋势。由于是平均值,预测值总是停留在过去的水平上而无法预计会导致将来更高或更低的波动; 

  3、 移动平均法要由大量的过去数据的记录。 
什么是指数平滑法 
  指数平滑法是布朗(Robert G..Brown)所提出,布朗(Robert G..Brown)认为时间序列的态势具有稳定性或规则性,所以时间序列可被合理地顺势推延;他认为最近的过去态势,在某种程度上会持续到最近的未来,所以将较大的权数放在最近的资料。 

 三,指数平均法 
指数平滑法是生产预测中常用的一种方法。也用于中短期经济发展趋势预测,所有预测方法中,指数平滑是用得最多的一种。简单的全期平均法是对时间数列的过去数据一个不漏地全部加以同等利用;移动平均法则不考虑较远期的数据,并在加权移动平均法中给予近期资料更大的权重;而指数平滑法则兼容了全期平均和移动平均所长,不舍弃过去的数据,但是仅给予逐渐减弱的影响程度,即随着数据的远离,赋予逐渐收敛为零的权数。 

  也就是说指数平滑法是在移动平均法基础上发展起来的一种时间序列分析预测法,它是通过计算指数平滑值,配合一定的时间序列预测模型对现象的未来进行预测。其原理是任一期的指数平滑值都是本期实际观察值与前一期指数平滑值的加权平均。 
    指数平滑法的基本公式是:St=ayt+(1-a)St-1 式中, 

•St--时间t的平滑值; 
•yt--时间t的实际值; 
•St − 1--时间t-1的平滑值; 
•a--平滑常数,其取值范围为[0,1]; 
  由该公式可知: 

  1.St是yt和 St − 1的加权算数平均数,随着a取值的大小变化,决定yt和 St − 1对St的影响程度,当a取1时,St = yt;当a取0时,St = St − 1。 

  2.St具有逐期追溯性质,可探源至St − t + 1为止,包括全部数据。其过程中,平滑常数以指数形式递减,故称之为指数平滑法。指数平滑常数取值至关重要。平滑常数决定了平滑水平以及对预测值与实际结果之间差异的响应速度。平滑常数a越接近于1,远期实际值对本期平滑值影响程度的下降越迅速;平滑常数a越接近于 0,远期实际值对本期平滑值影响程度的下降越缓慢。由此,当时间数列相对平稳时,可取较大的a;当时间数列波动较大时,应取较小的a,以不忽略远期实际值的影响。生产预测中,平滑常数的值取决于产品本身和管理者对良好响应率内涵的理解。 

  3.尽管St包含有全期数据的影响,但实际计算时,仅需要两个数值,即yt和 St − 1,再加上一个常数a,这就使指数滑动平均具逐期递推性质,从而给预测带来了极大的方便。 

  4.根据公式S1=ay1+(1-a)S0,当欲用指数平滑法时才开始收集数据,则不存在y0。无从产生S0,自然无法据指数平滑公式求出S1,指数平滑法定义S1为初始值。初始值的确定也是指数平滑过程的一个重要条件。 

  如果能够找到y1以前的历史资料,那么,初始值S1的确定是不成问题的。数据较少时可用全期平均、移动平均法;数据较多时,可用最小二乘法。但不能使用指数平滑法本身确定初始值,因为数据必会枯竭。 

  如果仅有从y1开始的数据,那么确定初始值的方法有: 

  1)取S1等于y1; 

  2)待积累若干数据后,取S1等于前面若干数据的简单算术平均数,如:S1=(y1+ y2+y3)/3等等。 

指数平滑的预测公式 
  据平滑次数不同,指数平滑法分为:一次指数平滑法、二次指数平滑法和三次指数平滑法等。 
(一) 一次指数平滑预测 
  当时间数列无明显的趋势变化,可用一次指数平滑预测。其预测公式为: 

  yt+1'=ayt+(1-a)yt' 式中, 

•yt+1'--t+1期的预测值,即本期(t期)的平滑值St ; 
•yt--t期的实际值; 
•yt'--t期的预测值,即上期的平滑值St-1 。 
  该公式又可以写作:yt+1'=yt'+a(yt- yt')。可见,下期预测值又是本期预测值与以a为折扣的本期实际值与预测值误差之和。 

(二) 二次指数平滑预测 
  二次指数平滑是对一次指数平滑的再平滑。它适用于具线性趋势的时间数列。其预测公式为: 

  yt+m=(2+am/(1-a))yt'-(1+am/(1-a))yt=(2yt'-yt)+m(yt'-yt) a/(1-a) 

  式中,yt= ayt-1'+(1-a)yt-1 

  显然,二次指数平滑是一直线方程,其截距为:(2yt'-yt),斜率为:(yt'-yt) a/(1-a),自变量为预测天数。 

(三) 三次指数平滑预测 
  三次指数平滑预测是二次平滑基础上的再平滑。其预测公式是: 

  yt+m=(3yt'-3yt+yt)+[(6-5a)yt'-(10-8a)yt+(4-3a)yt]*am/2(1-a)2+ (yt'-2yt+yt')*a2m2/2(1-a)2 

  式中,yt=ayt-1+(1-a)yt-1 

  它们的基本思想都是:预测值是以前观测值的加权和,且对不同的数据给予不同的权,新数据给较大的权,旧数据给较小的权。 

[编辑]指数平滑法的趋势调整 
  一段时间内收集到的数据所呈现的上升或下降趋势将导致指数预测滞后于实际需求。通过趋势调整,添加趋势修正值,可以在一定程度上改进指数平滑预测结果。调整后的指数平滑法的公式为: 

  包含趋势预测(YITt)=新预测(Yt)+趋势校正(Tt) 

  进行趋势调整的指数平滑预测有三个步骤: 

  1、 利用前面介绍的方法计算第t期的简单指数平滑预测(Yt); 

  2、 计算趋势。其公式为: Tt=(1-b)Tt-1+b(Yt-Yt-1)其中, 

•Tt=第t期经过平滑的趋势; 
•Tt-1=第t期上期经过平滑的趋势; 
•b=选择的趋势平滑系数; 
•Yt=对第t期简单指数平滑预测; 
•Yt-1=对第t期上期简单指数平滑预测。 
  3、计算趋势调整后的指数平滑预测值(YITt)。计算公式为:YITt=Yt+Tt。 
四,线性回归法 
1. 一元线性回归预测模型 
 一元线性回归预测法是分析一个因变量与一个自变量之间的线性关系的预测方法。 常用统计指标:平均数、增减量、平均增减量。 

  确定直线的方法是最小二乘法 最小二乘法的基本思想:最有代表性的直线应该是直线到各点的距离最近。然后用这条直线进行预测。 

1、选取一元线性回归模型的变量 ;    
2、绘制计算表和拟合散点图 ;    
3、计算变量间的回归系数及其相关的显著性 ;    
4、回归分析结果的应用 。 

、经济意义检验:就是根据模型中各个参数的经济含义,分析各参数的值是否与分析对象的经济含义相符。    
2、回归标准差检验    
3、拟合优度检验   
4、回归系数的显著性检验 
利用回归预测模型进行预测 
  可以分为:点预测和置信区间预测法    
1、点预测法:将自变量取值带入回归预测模型求出因变量的预测值。   
 2、置信区间预测法:估计一个范围,并确定该范围出现的概率。置信区间的大小的影响的因素:a、因变量估计值;b、回归标准差;C、概率度t。 
模型分析 
  一元线性回归分析预测法,是根据自变量x和因变量Y的相关关系,建立x与Y的线性回归方程进行预测的方法。由于市场现象一般是受多种因素的影响,而并不是仅仅受一个因素的影响。所以应用一元线性回归分析预测法,必须对影响市场现象的多种因素做全面分析。只有当诸多的影响因素中,确实存在一个对因变量影响作用明显高于其他因素的变量,才能将它作为自变量,应用一元相关回归分析市场预测法进行预测。一元线性回归分析法的预测模型为:yt=b+axt 式中,xt代表t期自变量的值;yt代表t期因变量的值; a、b代表一元线性回归方程的参数。 a、b参数由下列公式求得(用代表):  为简便计算,我们作以下定义:   (2)   式中:   这样定义a、b后,参数由下列公式求得:   将a、b代入一元线性回归方程Yt = a + bxt,就可以建立预测模型,那么,只要给定xt值,即可求出预测值。   在回归分析预测法中,需要对X、Y之间相关程度作出判断,这就要计算相关系数Y,其公式如下:   相关系数r的特征有:   ①相关系数取值范围为:-1≤r≤1 。   ②r与b符合相同。当r>0,称正线性相关,Xi上升,Yi呈线性增加。当r<0,称负线性相关,Xi上升,Yi呈线性减少。   ③|r|=0,X与Y无线性相关关系;|r|=1,完全确定的线性相关关系;0<|r|<1,X与Y存在一定的线性相关关系;|r|&gt;0.7,为高度线性相关;0.3<|r|≤0.7,为中度线性相关;|r|≤0.3,为低度线性相关. 

阅读全文……

Linux上Oracle 11g安装步骤图解 - gaojun - 博客园

另,可参考:

Redhat Linux安装Oracle 11g R2数据库

 

1、检查硬件

内存

# grep MemTotal /proc/meminfo

交换空间

# grep SwapTotal /proc/meminfo

磁盘空间

# df -ah

2、下载软件

Oracle Database 10g Release 2 (10.2.0.1) Software下载位置:

http://otn.oracle.com/software/products/database/oracle10g/index.html

3、修改host文件

查看/etc/hosts文件中必须包含a fully qualified name for the server

4、安装软件包检查

1、检查下列包是否安装,若未安装则要先安装:

binutils-2.17.50.0.6-2.el5

compat-libstdc++-33-3.2.3-61

elfutils-libelf-0.125-3.el5

elfutils-libelf-devel-0.125

glibc-2.5-12

glibc-common-2.5-12

glibc-devel-2.5-12

gcc-4.1.1-52

gcc-c++-4.1.1-52

libaio-0.3.106

libaio-devel-0.3.106

libgcc-4.1.1-52

libstdc++-4.1.1

libstdc++-devel-4.1.1-52.e15

make-3.81-1.1

sysstat-7.0.0

unixODBC-2.2.11

unixODBC-devel-2.2.11

 

# rpm -qa | grep make gcc glibc compat openmotif21 setarch 等等

 

5libstdc包的链接

(老版本需求,新版本oracle11不要设置):

ln -s /usr/lib/libstdc++.so.6.0.3 /usr/lib/libstdc++.so.5           

#检测存在如果不做此链接,在安装过程中建好数据库,将会出现不能链接不上数据库问题。

 

.参数设置

1、设置核心参数

/etc/sysctl.conf文件中加入下列行:

#kernel.core_uses_pid = 1

kernel.shmall = 2097152

kernel.shmmax = 2147483648 -- (以字节为单位,物理内存数量*1024*1024*2,为内存的2)

kernel.shmmni = 4096

# semaphores: semmsl, semmns, semopm, semmni

kernel.sem = 250 32000 100 128

fs.file-max = 65536

net.ipv4.ip_local_port_range = 1024 65000

net.core.rmem_default=8388608

net.core.rmem_max=8388608

net.core.wmem_default=262144

net.core.wmem_max=262144

esc :wq!

# sysctl -p #运行下列命令改变核心参数

 

2、设置Shell Limits(系统资源限制),提高软件的运行效率

 

a./etc/security/limits.conf文件中加入下列红色行:

oracle soft nofile 65536

oracle hard nofile 65536

oracle soft nproc 16384

oracle hard nproc 16384

 

b./etc/pam.d/login文件中加入下列行,如果里面没有的话:

session required /lib/security/pam_limits.so

session required pam_limits.so

c./etc/profile后加入以下语句:

vi /etc/profile

if [ $USER = "oracle" ]; then

if [ $SHELL = "/bin/ksh" ]; then

ulimit -p 16384

ulimit -n 65536

else

ulimit -u 16384 -n 65536

fi

fi

3.创建用户和组及相关目录

--创建dba/oper/oinstall

/usr/sbin/groupadd dba

/usr/sbin/groupadd oper

/usr/sbin/groupadd oinstall

--创建oracle用户并设置用户所属组

/usr/sbin/usermod -g oinstall -G dba oracle

/usr/sbin/useradd -g oinstall -G dba oracle

passwd oracle

# id oracle

uid=501(oracle) gid=501(oinstall) groups=501(oinstall),502(dba)

--创建相关安装目录

 

mkdir -p /opt/oracle/product

mkdir -p /opt/oracle/product/OraHome

mkdir -p /opt/oraInventory                      #(the default inventory folder)

mkdir -p /opt/oracle/oradata                   #(change the right file owner)

mkdir -p /var/opt/oracle

 

--设置目录所有者和权限

chown -R oracle.oinstall /opt/oracle

chown -R oracle.oinstall /opt/oracle/oradata

chown -R oracle.oinstall /opt/oracle/product/OraHome

chown -R oracle.dba /opt/oraInventory

chown oracle.dba /var/opt/oracle

chmod -R 775 /opt/oracle

chmod -R 755 /var/opt/oracle

 

4.设置安装环境变量

# su - oracle --suoracle用户下,

更改用户的一些配置

$vi .bash_profile

添加以下参数,主要是配置oracle软件运环境参数

# Get the aliases and functions

if [ -f ~/.bashrc ]; then

       . ~/.bashrc

fi

 

export ORACLE_BASE=/opt/oracle

export ORACLE_HOME=$ORACLE_BASE/product/OraHome

export ORACLE_SID=orcl

export ORACLE_OWNER=oracle

export ORACLE_TERM=vt100

export PATH=$PATH:$ORACLE_HOME/bin:$HOME/bin

export PATH=$ORACLE_HOME/bin:$ORACLE_HOME/Apache/Apache/bin:$PATH

LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib:/usr/local/lib

export LD_LIBRARY_PATH

CLASSPATH=$ORACLE_HOME/JRE:$ORACLE_HOME/jlib:$ORACLE_HOME/rdbms/jlib

CLASSPATH=$CLASSPATH:$ORACLE_HOME/network/jlib

export CLASSPATH

 

:

11g:ORA_NLS33=$ORACLE_HOME/nls/admin/data

10gORA_NLS33=$ORACLE_HOME/ocommon/nls/admin/data

9iORA_NLS33=/oracle/app/ora92/ocommon/nls/admin/data

保存退出

--执行以下命令让配置马上生效或以oracle用户登录使设置生效

$ source $HOME/.bash_profile

阅读全文……

标签 : , ,

Silent Oracle 11g R2 install using only the command line | Beyond Oracle

1. Add Oracle public yum repository.

[root@localhost ~]$ cd /etc/yum.repos.d
[root@localhost ~]$ wget http://public-yum.oracle.com/public-yum-el5.repo

# edit the file public-yum-el5.repo and enable both the [el5_u5_base] and the [ol5_u5_base] repositories in the yum configuration file by changing enable=0 to enable=1 in those sections

2. Install Oracle validated packages

[root@localhost ~]$ yum install oracle-validated

3. Download and Install Oracle software on command line

[root@localhost ~]$ su - oracle
[oracle@localhost ~]$ cd /software/11gr2/database
[oracle@localhost database]$ ./runInstaller -silent -force \
FROM_LOCATION=/software/11gr2/database/stage/products.xml \
oracle.install.option=INSTALL_DB_SWONLY \
UNIX_GROUP_NAME=oinstall \
INVENTORY_LOCATION=/home/oracle/oraInventory \
ORACLE_HOME=/u01/app/oracle/product/11.2/db_1 \
ORACLE_HOME_NAME="OraDb11g_Home1" \
ORACLE_BASE=/u01/app/oracle \
oracle.install.db.InstallEdition=SE \
oracle.install.db.isCustomInstall=false \
oracle.install.db.DBA_GROUP=dba \
oracle.install.db.OPER_GROUP=dba \
DECLINE_SECURITY_UPDATES=true
[oracle@localhost database]$ exit
# As a root user, execute the following script(s):
[root@localhost ~]$ /home/oracle/oraInventory/orainstRoot.sh
[root@localhost ~]$ /u01/app/oracle/product/11.2/db_1/root.sh

阅读全文……

标签 : , ,

linux下IPTABLES配置详解 - 草原和大树 - 博客园

(1)查看本机关于IPTABLES的设置情况
[root@tp ~]# iptables -L -n
Chain INPUT (policy ACCEPT)
target       prot opt source                 destination         
Chain FORWARD (policy ACCEPT)
target       prot opt source                 destination         
Chain OUTPUT (policy ACCEPT)
target       prot opt source                 destination         
Chain RH-Firewall-1-INPUT (0 references)
target       prot opt source                 destination         
ACCEPT       all    --    0.0.0.0/0              0.0.0.0/0           
ACCEPT       icmp --    0.0.0.0/0              0.0.0.0/0             icmp type 255 
ACCEPT       esp    --    0.0.0.0/0              0.0.0.0/0           
ACCEPT       ah     --    0.0.0.0/0              0.0.0.0/0           
ACCEPT       udp    --    0.0.0.0/0              224.0.0.251           udp dpt:5353 
ACCEPT       udp    --    0.0.0.0/0              0.0.0.0/0             udp dpt:631 
ACCEPT       all    --    0.0.0.0/0              0.0.0.0/0             state RELATED,ESTABLISHED 
ACCEPT       tcp    --    0.0.0.0/0              0.0.0.0/0             state NEW tcp dpt:22 
ACCEPT       tcp    --    0.0.0.0/0              0.0.0.0/0             state NEW tcp dpt:80 
ACCEPT       tcp    --    0.0.0.0/0              0.0.0.0/0             state NEW tcp dpt:25 
REJECT       all    --    0.0.0.0/0              0.0.0.0/0             reject-with icmp-host-prohibited 
可以看出我在安装linux时,选择了有防火墙,并且开放了22,80,25端口.
如果你在安装linux时没有选择启动防火墙,是这样的
[root@tp ~]# iptables -L -n
Chain INPUT (policy ACCEPT)
target       prot opt source                 destination         
Chain FORWARD (policy ACCEPT)
target       prot opt source                 destination         
Chain OUTPUT (policy ACCEPT)
target       prot opt source                 destination  
什么规则都没有.
(2)清除原有规则.
不管你在安装linux时是否启动了防火墙,如果你想配置属于自己的防火墙,那就清除现在filter的所有规则.
[root@tp ~]# iptables -F        清除预设表filter中的所有规则链的规则
[root@tp ~]# iptables -X        清除预设表filter中使用者自定链中的规则
我们在来看一下
[root@tp ~]# iptables -L -n
Chain INPUT (policy ACCEPT)
target       prot opt source                 destination         
Chain FORWARD (policy ACCEPT)
target       prot opt source                 destination         
Chain OUTPUT (policy ACCEPT)
target       prot opt source                 destination      
什么都没有了吧,和我们在安装linux时没有启动防火墙是一样的.(提前说一句,这些配置就像用命令配置IP一样,重起就会失去作用),怎么保存.
[root@tp ~]# /etc/rc.d/init.d/iptables save
 
这样就可以写到/etc/sysconfig/iptables文件里了.写入后记得把防火墙重起一下,才能起作用.
[root@tp ~]# service iptables restart
 
现在IPTABLES配置表里什么配置都没有了,那我们开始我们的配置吧
(3)设定预设规则
[root@tp ~]# iptables -p INPUT DROP
[root@tp ~]# iptables -p OUTPUT ACCEPT
[root@tp ~]# iptables -p FORWARD DROP
上面的意思是,当超出了IPTABLES里filter表里的两个链规则(INPUT,FORWARD)时,不在这两个规则里的数据包怎么处理呢,那就是DROP(放弃).应该说这样配置是很安全的.我们要控制流入数据包
而对于OUTPUT链,也就是流出的包我们不用做太多限制,而是采取ACCEPT,也就是说,不在着个规则里的包怎么办呢,那就是通过.
可以看出INPUT,FORWARD两个链采用的是允许什么包通过,而OUTPUT链采用的是不允许什么包通过.
这样设置还是挺合理的,当然你也可以三个链都DROP,但这样做我认为是没有必要的,而且要写的规则就会增加.但如果你只想要有限的几个规则是,如只做WEB服务器.还是推荐三个链都是DROP.
注:如果你是远程SSH登陆的话,当你输入第一个命令回车的时候就应该掉了.因为你没有设置任何规则.
怎么办,去本机操作呗!
(4)添加规则.
首先添加INPUT链,INPUT链的默认规则是DROP,所以我们就写需要ACCETP(通过)的链
为了能采用远程SSH登陆,我们要开启22端口.
[root@tp ~]# iptables -A INPUT -p tcp --dport 22 -j ACCEPT
[root@tp ~]# iptables -A OUTPUT -p tcp --sport 22 -j ACCEPT (注:这个规则,如果你把OUTPUT 设置成DROP的就要写上这一部,好多人都是望了写这一部规则导致,始终无法SSH.在远程一下,是不是好了.
其他的端口也一样,如果开启了web服务器,OUTPUT设置成DROP的话,同样也要添加一条链:
[root@tp ~]# iptables -A OUTPUT -p tcp --sport 80 -j ACCEPT ,其他同理.)
如果做了WEB服务器,开启80端口.
[root@tp ~]# iptables -A INPUT -p tcp --dport 80 -j ACCEPT
如果做了邮件服务器,开启25,110端口.
[root@tp ~]# iptables -A INPUT -p tcp --dport 110 -j ACCEPT
[root@tp ~]# iptables -A INPUT -p tcp --dport 25 -j ACCEPT

如果做了FTP服务器,开启21端口
[root@tp ~]# iptables -A INPUT -p tcp --dport 21 -j ACCEPT
[root@tp ~]# iptables -A INPUT -p tcp --dport 20 -j ACCEPT
如果做了DNS服务器,开启53端口
[root@tp ~]# iptables -A INPUT -p tcp --dport 53 -j ACCEPT
如果你还做了其他的服务器,需要开启哪个端口,照写就行了.
上面主要写的都是INPUT链,凡是不在上面的规则里的,都DROP
允许icmp包通过,也就是允许ping,
[root@tp ~]# iptables -A OUTPUT -p icmp -j ACCEPT (OUTPUT设置成DROP的话)
[root@tp ~]# iptables -A INPUT -p icmp -j ACCEPT    (INPUT设置成DROP的话)
允许loopback!(不然会导致DNS无法正常关闭等问题)
IPTABLES -A INPUT -i lo -p all -j ACCEPT (如果是INPUT DROP)
IPTABLES -A OUTPUT -o lo -p all -j ACCEPT(如果是OUTPUT DROP)
下面写OUTPUT链,OUTPUT链默认规则是ACCEPT,所以我们就写需要DROP(放弃)的链.
减少不安全的端口连接
[root@tp ~]# iptables -A OUTPUT -p tcp --sport 31337 -j DROP
[root@tp ~]# iptables -A OUTPUT -p tcp --dport 31337 -j DROP
有些些特洛伊木马会扫描端口31337到31340(即黑客语言中的 elite 端口)上的服务。既然合法服务都不使用这些非标准端口来通信,阻塞这些端口能够有效地减少你的网络上可能被感染的机器和它们的远程主服务器进行独立通信的机会
还有其他端口也一样,像:31335、27444、27665、20034 NetBus、9704、137-139(smb),2049(NFS)端口也应被禁止,我在这写的也不全,有兴趣的朋友应该去查一下相关资料.
 
当然出入更安全的考虑你也可以包OUTPUT链设置成DROP,那你添加的规则就多一些,就像上边添加
允许SSH登陆一样.照着写就行了.
 
下面写一下更加细致的规则,就是限制到某台机器
如:我们只允许192.168.0.3的机器进行SSH连接
[root@tp ~]# iptables -A INPUT -s 192.168.0.3 -p tcp --dport 22 -j ACCEPT
如果要允许,或限制一段IP地址可用 192.168.0.0/24 表示192.168.0.1-255端的所有IP.
24表示子网掩码数.但要记得把 /etc/sysconfig/iptables 里的这一行删了.
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT 因为它表示所有地址都可以登陆.
或采用命令方式:
[root@tp ~]# iptables -D INPUT -p tcp --dport 22 -j ACCEPT
然后保存,我再说一边,反是采用命令的方式,只在当时生效,如果想要重起后也起作用,那就要保存.写入到/etc/sysconfig/iptables文件里.
[root@tp ~]# /etc/rc.d/init.d/iptables save
这样写 !192.168.0.3 表示除了192.168.0.3的ip地址
其他的规则连接也一样这么设置.
 
在下面就是FORWARD链,FORWARD链的默认规则是DROP,所以我们就写需要ACCETP(通过)的链,对正在转发链的监控.
开启转发功能,(在做NAT时,FORWARD默认规则是DROP时,必须做)
[root@tp ~]# iptables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
[root@tp ~]# iptables -A FORWARD -i eth1 -o eh0 -j ACCEPT
丢弃坏的TCP包
[root@tp ~]#iptables -A FORWARD -p TCP ! --syn -m state --state NEW -j DROP
处理IP碎片数量,防止攻击,允许每秒100个
[root@tp ~]#iptables -A FORWARD -f -m limit --limit 100/s --limit-burst 100 -j ACCEPT
设置ICMP包过滤,允许每秒1个包,限制触发条件是10个包.
[root@tp ~]#iptables -A FORWARD -p icmp -m limit --limit 1/s --limit-burst 10 -j ACCEPT
我在前面只所以允许ICMP包通过,就是因为我在这里有限制.
二,配置一个NAT表放火墙
1,查看本机关于NAT的设置情况
[root@tp rc.d]# iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target       prot opt source                 destination         
Chain POSTROUTING (policy ACCEPT)
target       prot opt source                 destination         
SNAT         all    --    192.168.0.0/24         anywhere              to:211.101.46.235
Chain OUTPUT (policy ACCEPT)
target       prot opt source                 destination    
我的NAT已经配置好了的(只是提供最简单的代理上网功能,还没有添加防火墙规则).关于怎么配置NAT,参考我的另一篇文章
当然你如果还没有配置NAT的话,你也不用清除规则,因为NAT在默认情况下是什么都没有的
如果你想清除,命令是
[root@tp ~]# iptables -F -t nat
[root@tp ~]# iptables -X -t nat
[root@tp ~]# iptables -Z -t nat
 
2,添加规则
添加基本的NAT地址转换,(关于如何配置NAT可以看我的另一篇文章),
添加规则,我们只添加DROP链.因为默认链全是ACCEPT.
防止外网用内网IP欺骗
[root@tp sysconfig]# iptables -t nat -A PREROUTING -i eth0 -s 10.0.0.0/8 -j DROP
[root@tp sysconfig]# iptables -t nat -A PREROUTING -i eth0 -s 172.16.0.0/12 -j DROP
[root@tp sysconfig]# iptables -t nat -A PREROUTING -i eth0 -s 192.168.0.0/16 -j DROP

如果我们想,比如阻止MSN,QQ,BT等的话,需要找到它们所用的端口或者IP,(个人认为没有太大必要)
例:
禁止与211.101.46.253的所有连接
[root@tp ~]# iptables -t nat -A PREROUTING    -d 211.101.46.253 -j DROP
禁用FTP(21)端口
[root@tp ~]# iptables -t nat -A PREROUTING -p tcp --dport 21 -j DROP
这样写范围太大了,我们可以更精确的定义.
[root@tp ~]# iptables -t nat -A PREROUTING    -p tcp --dport 21 -d 211.101.46.253 -j DROP
这样只禁用211.101.46.253地址的FTP连接,其他连接还可以.如web(80端口)连接.
按照我写的,你只要找到QQ,MSN等其他软件的IP地址,和端口,以及基于什么协议,只要照着写就行了.
 
最后:
drop非法连接
[root@tp ~]# iptables -A INPUT     -m state --state INVALID -j DROP
[root@tp ~]# iptables -A OUTPUT    -m state --state INVALID -j DROP
[root@tp ~]# iptables-A FORWARD -m state --state INVALID -j DROP

允许所有已经建立的和相关的连接
[root@tp ~]# iptables-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
[root@tp ~]# iptables-A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

[root@tp ~]# /etc/rc.d/init.d/iptables save

这样就可以写到/etc/sysconfig/iptables文件里了.写入后记得把防火墙重起一下,才能起作用.

[root@tp ~]# service iptables restart


阅读全文……

标签 : ,

lucene修改相似度实现:去掉文本长度和重复词的影响 - sling2007的日志 - 网易博客

文档的分值代表了该文档在特定查询词下对应的相关性高低,他关联着信息检索向量空间模型中的向量夹角的接近度。一个文档越与查询词相关,得分越高。分值计算公式如下:

score(q,d)   =   coord(q,d) · queryNorm(q) ·   ∑ ( tf(t in d) · idf(t)2 · t.getBoost() · norm(t,d) ) 
                                                                      t in q  

其中

tf(t in d)

这个值衡量着Term在文档中出现的频率,也就是词频。关键词在文档中出现的次数越多,得分越高,这个值在DefaultSimilarity的计算公式如下(词频的平方根):

tf(t in d)   =    frequency½

idf(t)

代表着该词的逆词频,这个值衡量了该词在整个文档库中出现的频度。这意味着,一个词出现的越少,根据香农的信息公示,他越珍稀。同时将贡献更多的分值给总 分值。默认的计算公式如下(其中numDocs代表整个文档的数量,docFreq代表了含有Term t的文档数量):

                                   numDocs 
idf(t) =    1 + log    ( –––––––––   )  
                                  docFreq+1 

coord(q,d)

一次查询可能查询好多条件,产生多个子查询。文档命中的子查询数越多,说明越符合查询,得分应该越高。

比如title:中国 || keyword:中国 || author:作者,这个查询有三个子查询,命中一个条件即为0.333,两个就是0.6667,都命中则为1

coord(q,d) = overlap / maxOverlap

queryNorm(q)

这个标准化因子用于在多个查询器中进行比较。它并不影响文档的排名。它的主要作用在于多个查询器返回的结果进行比较,甚至是结果来自多个索引时。这是搜索时的权重因子,当给查询器设置权重时就是通过这个因子进行影响的。默认的实现公式如下:

                                                                                                           1 
queryNorm(q)   =   queryNorm(sumOfSquaredWeights)   =   –––––––––––––– 
                                                                                         sumOfSquaredWeights½

其中的sumOfSquaredWeights的计算公式如下:(可以清晰的看到获取query的boost,当没给查询器设置值时,默认为1,不起作用)

sumOfSquaredWeights   =   q.getBoost() 2 ·   ∑ ( idf(t) · t.getBoost() ) 2  
                                                                      t in q

t.getBoost()

该值是一个搜索时权重因子,可以在查询时给不同的Term设置不同的权重,可以通过lucene语法(具体参见我翻译的另外一篇文章:hi.baidu.com/expertsearch/blog/item/8d4f7d355a2e413c5ab5f547.html),也可以通过setBost()函数,注意,在多Term查询器中,是没有获取单一Term权重的函数的,所以如果需要获取,只能调用相应的子查询器函数的getBoost()函数。

norm(t,d)

封装了一些索引时因子以及长度因子。
Document boost - 在索引时,添加到Index前可以通过doc.setBoost()设置,衡量了Document的重要程度。. 
Field boost - 在将字段加入到文档前可以通过调用field.setBoost()来设置字段的权重。
lengthNorm(field) - 该值在将文档添加到索引时,根据所有文档中特定字段的Term数来计算。所以默认更短的字段将贡献更多的分值。

                                                   1 
lengthNorm(field) =   –––––––––––––– 
                                           numTerms½

当文档加入索引时,以上因子将相乘,如果一个文档中有多个同名的字段,那么将多个多同的权重也相乘。

norm(t,d)   =   doc.getBoost() · lengthNorm(field) ·   ∏ f.getBoost() 
                                                                       field f in d named as t

可 是还有件值得注意的事情,这个值在索引时计算完毕后将编码为一个Byte存储起来,在搜索时,再从文件中读取出该值并解码成float。在这个过程中,可 能会造成精度的缺失,并不能保证decode(encode(x)) = x,比如,有可能decode(encode(0.89)) = 0.75,同样值得注意的是,在搜索时改变此值已经太晚了。例如,用一个不同于DefaultSimilarity的实现。

阅读全文……

标签 :

有关Lucene的问题(4):影响Lucene对文档打分的四种方式 - forfuture1978的专栏 - 博客频道 - CSDN.NET

在索引阶段设置Document Boost和Field Boost,存储在(.nrm)文件中。

如果希望某些文档和某些域比其他的域更重要,如果此文档和此域包含所要查询的词则应该得分较高,则可以在索引阶段设定文档的boost和域的boost值。

这些值是在索引阶段就写入索引文件的,存储在标准化因子(.nrm)文件中,一旦设定,除非删除此文档,否则无法改变。

如果不进行设定,则Document Boost和Field Boost默认为1。

Document Boost及FieldBoost的设定方式如下:

Document doc = new Document();

Field f = new Field("contents", "hello world", Field.Store.NO, Field.Index.ANALYZED);

f.setBoost(100);

doc.add(f);

doc.setBoost(100);

两者是如何影响Lucene的文档打分的呢?

让我们首先来看一下Lucene的文档打分的公式:

score(q,d)   =   coord(q,d)  ·  queryNorm(q)  ·  ( tf(t in d)  ·  idf(t)2  ·  t.getBoost() ·  norm(t,d) )

                                                                       t in q

Document Boost和Field Boost影响的是norm(t, d),其公式如下:

norm(t,d)   =   doc.getBoost()  ·  lengthNorm(field)  ·  f.getBoost()

field f in d named as t

它包括三个参数:

  • Document boost:此值越大,说明此文档越重要。
  • Field boost:此域越大,说明此域越重要。
  • lengthNorm(field) = (1.0 / Math.sqrt(numTerms)):一个域中包含的Term总数越多,也即文档越长,此值越小,文档越短,此值越大。

其中第三个参数可以在自己的Similarity中影响打分,下面会论述。

当然,也可以在添加Field的时候,设置Field.Index.ANALYZED_NO_NORMS或Field.Index.NOT_ANALYZED_NO_NORMS,完全不用norm,来节约空间。

根据Lucene的注释,No norms means that index-time field and document boosting and field length normalization are disabled.  The benefit is less memory usage as norms take up one byte of RAM per indexed field for every document in the index, during searching.  Note that once you index a given field with norms enabled, disabling norms will have no effect. 没有norms意味着索引阶段禁用了文档boost和域的boost及长度标准化。好处在于节省内存,不用在搜索阶段为索引中的每篇文档的每个域都占用一个字节来保存norms信息了。但是对norms信息的禁用是必须全部域都禁用的,一旦有一个域不禁用,则其他禁用的域也会存放默认的norms值。因为为了加快norms的搜索速度,Lucene是根据文档号乘以每篇文档的norms信息所占用的大小来计算偏移量的,中间少一篇文档,偏移量将无法计算。也即norms信息要么都保存,要么都不保存。

下面几个试验可以验证norms信息的作用:

试验一:Document Boost的作用

public void testNormsDocBoost() throws Exception { 
  File indexDir = new File("testNormsDocBoost"); 
  IndexWriter writer = new IndexWriter(FSDirectory.open(indexDir), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); 
  writer.setUseCompoundFile(false); 
  Document doc1 = new Document(); 
  Field f1 = new Field("contents", "common hello hello", Field.Store.NO, Field.Index.ANALYZED); 
  doc1.add(f1); 
  doc1.setBoost(100); 
  writer.addDocument(doc1); 
  Document doc2 = new Document(); 
  Field f2 = new Field("contents", "common common hello", Field.Store.NO, Field.Index.ANALYZED_NO_NORMS); 
  doc2.add(f2); 
  writer.addDocument(doc2); 
  Document doc3 = new Document(); 
  Field f3 = new Field("contents", "common common common", Field.Store.NO, Field.Index.ANALYZED_NO_NORMS); 
  doc3.add(f3); 
  writer.addDocument(doc3); 
  writer.close();

  IndexReader reader = IndexReader.open(FSDirectory.open(indexDir)); 
  IndexSearcher searcher = new IndexSearcher(reader); 
  TopDocs docs = searcher.search(new TermQuery(new Term("contents", "common")), 10); 
  for (ScoreDoc doc : docs.scoreDocs) { 
    System.out.println("docid : " + doc.doc + " score : " + doc.score); 
  } 
}

如果第一篇文档的域f1也为Field.Index.ANALYZED_NO_NORMS的时候,搜索排名如下:

docid : 2 score : 1.2337708 
docid : 1 score : 1.0073696 
docid : 0 score : 0.71231794

如果第一篇文档的域f1设为Field.Index.ANALYZED,则搜索排名如下:

docid : 0 score : 39.889805 
docid : 2 score : 0.6168854 
docid : 1 score : 0.5036848

试验二:Field Boost的作用

如果我们觉得title要比contents要重要,可以做一下设定。

public void testNormsFieldBoost() throws Exception { 
  File indexDir = new File("testNormsFieldBoost"); 
  IndexWriter writer = new IndexWriter(FSDirectory.open(indexDir), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); 
  writer.setUseCompoundFile(false); 
  Document doc1 = new Document(); 
  Field f1 = new Field("title", "common hello hello", Field.Store.NO, Field.Index.ANALYZED); 
  f1.setBoost(100); 
  doc1.add(f1); 
  writer.addDocument(doc1); 
  Document doc2 = new Document(); 
  Field f2 = new Field("contents", "common common hello", Field.Store.NO, Field.Index.ANALYZED_NO_NORMS); 
  doc2.add(f2); 
  writer.addDocument(doc2); 
  writer.close();

  IndexReader reader = IndexReader.open(FSDirectory.open(indexDir)); 
  IndexSearcher searcher = new IndexSearcher(reader); 
  QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "contents", new StandardAnalyzer(Version.LUCENE_CURRENT)); 
  Query query = parser.parse("title:common contents:common"); 
  TopDocs docs = searcher.search(query, 10); 
  for (ScoreDoc doc : docs.scoreDocs) { 
    System.out.println("docid : " + doc.doc + " score : " + doc.score); 
  } 
}

如果第一篇文档的域f1也为Field.Index.ANALYZED_NO_NORMS的时候,搜索排名如下:

docid : 1 score : 0.49999997 
docid : 0 score : 0.35355338

如果第一篇文档的域f1设为Field.Index.ANALYZED,则搜索排名如下:

docid : 0 score : 19.79899 
docid : 1 score : 0.49999997

试验三:norms中文档长度对打分的影响

public void testNormsLength() throws Exception { 
  File indexDir = new File("testNormsLength"); 
  IndexWriter writer = new IndexWriter(FSDirectory.open(indexDir), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); 
  writer.setUseCompoundFile(false); 
  Document doc1 = new Document(); 
  Field f1 = new Field("contents", "common hello hello", Field.Store.NO, Field.Index.ANALYZED_NO_NORMS); 
  doc1.add(f1); 
  writer.addDocument(doc1); 
  Document doc2 = new Document(); 
  Field f2 = new Field("contents", "common common hello hello hello hello", Field.Store.NO, Field.Index.ANALYZED_NO_NORMS); 
  doc2.add(f2); 
  writer.addDocument(doc2); 
  writer.close();

  IndexReader reader = IndexReader.open(FSDirectory.open(indexDir)); 
  IndexSearcher searcher = new IndexSearcher(reader); 
  QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "contents", new StandardAnalyzer(Version.LUCENE_CURRENT)); 
  Query query = parser.parse("title:common contents:common"); 
  TopDocs docs = searcher.search(query, 10); 
  for (ScoreDoc doc : docs.scoreDocs) { 
    System.out.println("docid : " + doc.doc + " score : " + doc.score); 
  } 
}

当norms被禁用的时候,包含两个common的第二篇文档打分较高:

docid : 1 score : 0.13928263 
docid : 0 score : 0.09848769

当norms起作用的时候,虽然包含两个common的第二篇文档,由于长度较长,因而打分较低:

docid : 0 score : 0.09848769 
docid : 1 score : 0.052230984

试验四:norms信息要么都保存,要么都不保存的特性

public void testOmitNorms() throws Exception { 
  File indexDir = new File("testOmitNorms"); 
  IndexWriter writer = new IndexWriter(FSDirectory.open(indexDir), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); 
  writer.setUseCompoundFile(false); 
  Document doc1 = new Document(); 
  Field f1 = new Field("title", "common hello hello", Field.Store.NO, Field.Index.ANALYZED); 
  doc1.add(f1); 
  writer.addDocument(doc1); 
  for (int i = 0; i < 10000; i++) { 
    Document doc2 = new Document(); 
    Field f2 = new Field("contents", "common common hello hello hello hello", Field.Store.NO, Field.Index.ANALYZED_NO_NORMS); 
    doc2.add(f2); 
    writer.addDocument(doc2); 
  } 
  writer.close(); 
}

当我们添加10001篇文档,所有的文档都设为Field.Index.ANALYZED_NO_NORMS的时候,我们看索引文件,发现.nrm文件只有1K,也即其中除了保持一定的格式信息,并无其他数据。

[图]全部OmitNorms

 

当我们把第一篇文档设为Field.Index.ANALYZED,而其他10000篇文档都设为Field.Index.ANALYZED_NO_NORMS的时候,发现.nrm文件又10K,也即所有的文档都存储了norms信息,而非只有第一篇文档。

[图]部分OmitNorms

 

在搜索语句中,设置Query Boost.

在搜索中,我们可以指定,某些词对我们来说更重要,我们可以设置这个词的boost:

common^4 hello

使得包含common的文档比包含hello的文档获得更高的分数。

由于在Lucene中,一个Term定义为Field:Term,则也可以影响不同域的打分:

title:common^4 content:common

使得title中包含common的文档比content中包含common的文档获得更高的分数。

实例:

public void testQueryBoost() throws Exception { 
  File indexDir = new File("TestQueryBoost"); 
  IndexWriter writer = new IndexWriter(FSDirectory.open(indexDir), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); 
  Document doc1 = new Document(); 
  Field f1 = new Field("contents", "common1 hello hello", Field.Store.NO, Field.Index.ANALYZED); 
  doc1.add(f1); 
  writer.addDocument(doc1); 
  Document doc2 = new Document(); 
  Field f2 = new Field("contents", "common2 common2 hello", Field.Store.NO, Field.Index.ANALYZED); 
  doc2.add(f2); 
  writer.addDocument(doc2); 
  writer.close();

  IndexReader reader = IndexReader.open(FSDirectory.open(indexDir)); 
  IndexSearcher searcher = new IndexSearcher(reader); 
  QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "contents", new StandardAnalyzer(Version.LUCENE_CURRENT)); 
  Query query = parser.parse("common1 common2")
  TopDocs docs = searcher.search(query, 10); 
  for (ScoreDoc doc : docs.scoreDocs) { 
    System.out.println("docid : " + doc.doc + " score : " + doc.score); 
  } 
}

根据tf/idf,包含两个common2的第二篇文档打分较高:

docid : 1 score : 0.24999999 
docid : 0 score : 0.17677669

如果我们输入的查询语句为:"common1^100 common2",则第一篇文档打分较高:

docid : 0 score : 0.2499875 
docid : 1 score : 0.0035353568

那Query Boost是如何影响文档打分的呢?

根据Lucene的打分计算公式:

score(q,d)   =   coord(q,d)  ·  queryNorm(q)  · ( tf(t in d)  ·  idf(t)2  ·  t.getBoost() ·  norm(t,d) )

                                                                      t in q

注:在queryNorm的部分,也有q.getBoost()的部分,但是对query向量的归一化(见向量空间模型与Lucene的打分机制[http://forfuture1978.javaeye.com/blog/588721])。

继承并实现自己的Similarity

Similariy是计算Lucene打分的最主要的类,实现其中的很多借口可以干预打分的过程。

(1) float computeNorm(String field, FieldInvertState state)

(2) float lengthNorm(String fieldName, int numTokens)

(3) float queryNorm(float sumOfSquaredWeights)

(4) float tf(float freq)

(5) float idf(int docFreq, int numDocs)

(6) float coord(int overlap, int maxOverlap)

(7) float scorePayload(int docId, String fieldName, int start, int end, byte [] payload, int offset, int length)

它们分别影响Lucene打分计算的如下部分:

score(q,d)   =   (6)coord(q,d)  ·  (3)queryNorm(q)  · ( (4)tf(t in d)  ·  (5)idf(t)2  ·  t.getBoost() ·  (1)norm(t,d) )

t in q

norm(t,d)   =   doc.getBoost()  ·  (2)lengthNorm(field)  ·  f.getBoost()

                                                                           field f in d named as t

下面逐个进行解释:

(1) float computeNorm(String field, FieldInvertState state)

影响标准化因子的计算,如上述,他主要包含了三部分:文档boost,域boost,以及文档长度归一化。此函数一般按照上面norm(t, d)的公式进行计算。

(2) float lengthNorm(String fieldName, int numTokens)

主要计算文档长度的归一化,默认是1.0 / Math.sqrt(numTerms)。

因为在索引中,不同的文档长度不一样,很显然,对于任意一个term,在长的文档中的tf要大的多,因而分数也越高,这样对小的文档不公平,举一个极端的例子,在一篇1000万个词的鸿篇巨著中,"lucene"这个词出现了11次,而在一篇12个词的短小文档中,"lucene"这个词出现了10次,如果不考虑长度在内,当然鸿篇巨著应该分数更高,然而显然这篇小文档才是真正关注"lucene"的。

因而在此处是要除以文档的长度,从而减少因文档长度带来的打分不公。

然而现在这个公式是偏向于首先返回短小的文档的,这样在实际应用中使得搜索结果也很难看。

于是在实践中,要根据项目的需要,根据搜索的领域,改写lengthNorm的计算公式。比如我想做一个经济学论文的搜索系统,经过一定时间的调研,发现大多数的经济学论文的长度在8000到10000词,因而lengthNorm的公式应该是一个倒抛物线型的,8000到10000词的论文分数最高,更短或更长的分数都应该偏低,方能够返回给用户最好的数据。

(3) float queryNorm(float sumOfSquaredWeights)

这是按照向量空间模型,对query向量的归一化。此值并不影响排序,而仅仅使得不同的query之间的分数可以比较。

(4) float tf(float freq)

freq是指在一篇文档中包含的某个词的数目。tf是根据此数目给出的分数,默认为Math.sqrt(freq)。也即此项并不是随着包含的数目的增多而线性增加的。

(5) float idf(int docFreq, int numDocs)

idf是根据包含某个词的文档数以及总文档数计算出的分数,默认为(Math.log(numDocs/(double)(docFreq+1)) + 1.0)。

由于此项计算涉及到总文档数和包含此词的文档数,因而需要全局的文档数信息,这给跨索引搜索造成麻烦。

从下面的例子我们可以看出,用MultiSearcher来一起搜索两个索引和分别用IndexSearcher来搜索两个索引所得出的分数是有很大差异的。

究其原因是MultiSearcher的docFreq(Term term)函数计算了包含两个索引中包含此词的总文档数,而IndexSearcher仅仅计算了每个索引中包含此词的文档数。当两个索引包含的文档总数是有很大不同的时候,分数是无法比较的。

public void testMultiIndex() throws Exception{ 
  MultiIndexSimilarity sim = new MultiIndexSimilarity(); 
  File indexDir01 = new File("TestMultiIndex/TestMultiIndex01"); 
  File indexDir02 = new File("TestMultiIndex/TestMultiIndex02"); 
  IndexReader reader01 = IndexReader.open(FSDirectory.open(indexDir01)); 
  IndexReader reader02 = IndexReader.open(FSDirectory.open(indexDir02)); 
  IndexSearcher searcher01 = new IndexSearcher(reader01); 
  searcher01.setSimilarity(sim); 
  IndexSearcher searcher02 = new IndexSearcher(reader02); 
  searcher02.setSimilarity(sim); 
  MultiSearcher multiseacher = new MultiSearcher(searcher01, searcher02); 
  multiseacher.setSimilarity(sim); 
  QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "contents", new StandardAnalyzer(Version.LUCENE_CURRENT)); 
  Query query = parser.parse("common"); 
  TopDocs docs = searcher01.search(query, 10); 
  System.out.println("----------------------------------------------"); 
  for (ScoreDoc doc : docs.scoreDocs) { 
    System.out.println("docid : " + doc.doc + " score : " + doc.score); 
  } 
  System.out.println("----------------------------------------------"); 
  docs = searcher02.search(query, 10); 
  for (ScoreDoc doc : docs.scoreDocs) { 
    System.out.println("docid : " + doc.doc + " score : " + doc.score); 
  } 
  System.out.println("----------------------------------------------"); 
  docs = multiseacher.search(query, 20); 
  for (ScoreDoc doc : docs.scoreDocs) { 
    System.out.println("docid : " + doc.doc + " score : " + doc.score); 
  }

结果为:

------------------------------- 
docid : 0 score : 0.49317428 
docid : 1 score : 0.49317428 
docid : 2 score : 0.49317428 
docid : 3 score : 0.49317428 
docid : 4 score : 0.49317428 
docid : 5 score : 0.49317428 
docid : 6 score : 0.49317428 
docid : 7 score : 0.49317428 
------------------------------- 
docid : 0 score : 0.45709616 
docid : 1 score : 0.45709616 
docid : 2 score : 0.45709616 
docid : 3 score : 0.45709616 
docid : 4 score : 0.45709616 
------------------------------- 
docid : 0 score : 0.5175894 
docid : 1 score : 0.5175894 
docid : 2 score : 0.5175894 
docid : 3 score : 0.5175894 
docid : 4 score : 0.5175894 
docid : 5 score : 0.5175894 
docid : 6 score : 0.5175894 
docid : 7 score : 0.5175894 
docid : 8 score : 0.5175894 
docid : 9 score : 0.5175894 
docid : 10 score : 0.5175894 
docid : 11 score : 0.5175894 
docid : 12 score : 0.5175894

如果几个索引都是在一台机器上,则用MultiSearcher或者MultiReader就解决问题了,然而有时候索引是分布在多台机器上的,虽然Lucene也提供了RMI,或用NFS保存索引的方法,然而效率和并行性一直是一个问题。

一个可以尝试的办法是在Similarity中,idf返回1,然后多个机器上的索引并行搜索,在汇总结果的机器上,再融入idf的计算。

如下面的例子可以看出,当idf返回1的时候,打分可以比较了:

class MultiIndexSimilarity extends Similarity {

  @Override 
  public float idf(int docFreq, int numDocs) { 
    return 1.0f; 
  }

----------------------------- 
docid : 0 score : 0.559017 
docid : 1 score : 0.559017 
docid : 2 score : 0.559017 
docid : 3 score : 0.559017 
docid : 4 score : 0.559017 
docid : 5 score : 0.559017 
docid : 6 score : 0.559017 
docid : 7 score : 0.559017 
----------------------------- 
docid : 0 score : 0.559017 
docid : 1 score : 0.559017 
docid : 2 score : 0.559017 
docid : 3 score : 0.559017 
docid : 4 score : 0.559017 
----------------------------- 
docid : 0 score : 0.559017 
docid : 1 score : 0.559017 
docid : 2 score : 0.559017 
docid : 3 score : 0.559017 
docid : 4 score : 0.559017 
docid : 5 score : 0.559017 
docid : 6 score : 0.559017 
docid : 7 score : 0.559017 
docid : 8 score : 0.559017 
docid : 9 score : 0.559017 
docid : 10 score : 0.559017 
docid : 11 score : 0.559017 
docid : 12 score : 0.559017

(6) float coord(int overlap, int maxOverlap)

一次搜索可能包含多个搜索词,而一篇文档中也可能包含多个搜索词,此项表示,当一篇文档中包含的搜索词越多,则此文档则打分越高。

public void TestCoord() throws Exception { 
  MySimilarity sim = new MySimilarity(); 
  File indexDir = new File("TestCoord"); 
  IndexWriter writer = new IndexWriter(FSDirectory.open(indexDir), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); 
  Document doc1 = new Document(); 
  Field f1 = new Field("contents", "common hello world", Field.Store.NO, Field.Index.ANALYZED); 
  doc1.add(f1); 
  writer.addDocument(doc1); 
  Document doc2 = new Document(); 
  Field f2 = new Field("contents", "common common common", Field.Store.NO, Field.Index.ANALYZED); 
  doc2.add(f2); 
  writer.addDocument(doc2); 
  for(int i = 0; i < 10; i++){ 
    Document doc3 = new Document(); 
    Field f3 = new Field("contents", "world", Field.Store.NO, Field.Index.ANALYZED); 
    doc3.add(f3); 
    writer.addDocument(doc3); 
  } 
  writer.close();

  IndexReader reader = IndexReader.open(FSDirectory.open(indexDir)); 
  IndexSearcher searcher = new IndexSearcher(reader); 
  searcher.setSimilarity(sim); 
  QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "contents", new StandardAnalyzer(Version.LUCENE_CURRENT)); 
  Query query = parser.parse("common world"); 
  TopDocs docs = searcher.search(query, 2); 
  for (ScoreDoc doc : docs.scoreDocs) { 
    System.out.println("docid : " + doc.doc + " score : " + doc.score); 
  } 
}

class MySimilarity extends Similarity {

  @Override 
  public float coord(int overlap, int maxOverlap) { 
    return 1; 
  }

}

如上面的实例,当coord返回1,不起作用的时候,文档一虽然包含了两个搜索词common和world,但由于world的所在的文档数太多,而文档二包含common的次数比较多,因而文档二分数较高:

docid : 1 score : 1.9059997 
docid : 0 score : 1.2936771

而当coord起作用的时候,文档一由于包含了两个搜索词而分数较高:

class MySimilarity extends Similarity {

  @Override 
  public float coord(int overlap, int maxOverlap) { 
    return overlap / (float)maxOverlap; 
  }

}

docid : 0 score : 1.2936771 
docid : 1 score : 0.95299983

(7) float scorePayload(int docId, String fieldName, int start, int end, byte [] payload, int offset, int length)

由于Lucene引入了payload,因而可以存储一些自己的信息,用户可以根据自己存储的信息,来影响Lucene的打分。

payload的定义

我们知道,索引是以倒排表形式存储的,对于每一个词,都保存了包含这个词的一个链表,当然为了加快查询速度,此链表多用跳跃表进行存储。

Payload信息就是存储在倒排表中的,同文档号一起存放,多用于存储与每篇文档相关的一些信息。当然这部分信息也可以存储域里(stored Field),两者从功能上基本是一样的,然而当要存储的信息很多的时候,存放在倒排表里,利用跳跃表,有利于大大提高搜索速度。

Payload的存储方式如下图:

[图]payload 

 

由payload的定义,我们可以看出,payload可以存储一些不但与文档相关,而且与查询词也相关的信息。比如某篇文档的某个词有特殊性,则可以在这个词的这个文档的position信息后存储payload信息,使得当搜索这个词的时候,这篇文档获得较高的分数。

要利用payload来影响查询需要做到以下几点,下面举例用标记的词在payload中存储1,否则存储0:

首先要实现自己的Analyzer从而在Token中放入payload信息:

class BoldAnalyzer extends Analyzer {

  @Override 
  public TokenStream tokenStream(String fieldName, Reader reader) { 
    TokenStream result = new WhitespaceTokenizer(reader); 
    result = new BoldFilter(result); 
    return result; 
  }

}

class BoldFilter extends TokenFilter { 
  public static int IS_NOT_BOLD = 0; 
  public static int IS_BOLD = 1;

  private TermAttribute termAtt; 
  private PayloadAttribute payloadAtt;

  protected BoldFilter(TokenStream input) { 
    super(input); 
    termAtt = addAttribute(TermAttribute.class); 
    payloadAtt = addAttribute(PayloadAttribute.class); 
  }

  @Override 
  public boolean incrementToken() throws IOException { 
    if (input.incrementToken()) {

      final char[] buffer = termAtt.termBuffer(); 
      final int length = termAtt.termLength();

      String tokenstring = new String(buffer, 0, length); 
      if (tokenstring.startsWith("") && tokenstring.endsWith("")) { 
        tokenstring = tokenstring.replace("", ""); 
        tokenstring = tokenstring.replace("
", ""); 
        termAtt.setTermBuffer(tokenstring); 
        payloadAtt.setPayload(new Payload(int2bytes(IS_BOLD))); 
      } else { 
        payloadAtt.setPayload(new Payload(int2bytes(IS_NOT_BOLD))); 
      } 
      return true; 
    } else 
      return false; 
  }

  public static int bytes2int(byte[] b) { 
    int mask = 0xff; 
    int temp = 0; 
    int res = 0; 
    for (int i = 0; i < 4; i++) { 
      res <<= 8; 
      temp = b[i] & mask; 
      res |= temp; 
    } 
    return res; 
  }

  public static byte[] int2bytes(int num) { 
    byte[] b = new byte[4]; 
    for (int i = 0; i < 4; i++) { 
      b[i] = (byte) (num >>> (24 - i * 8)); 
    } 
    return b; 
  }

}

然后,实现自己的Similarity,从payload中读出信息,根据信息来打分。

class PayloadSimilarity extends DefaultSimilarity {

  @Override 
  public float scorePayload(int docId, String fieldName, int start, int end, byte[] payload, int offset, int length) { 
    int isbold = BoldFilter.bytes2int(payload); 
    if(isbold == BoldFilter.IS_BOLD){ 
      System.out.println("It is a bold char."); 
    } else { 
      System.out.println("It is not a bold char."); 
    } 
    return 1; 
  } 
}

最后,查询的时候,一定要用PayloadXXXQuery(在此用PayloadTermQuery,在Lucene 2.4.1中,用BoostingTermQuery),否则scorePayload不起作用。

public void testPayloadScore() throws Exception { 
  PayloadSimilarity sim = new PayloadSimilarity(); 
  File indexDir = new File("TestPayloadScore"); 
  IndexWriter writer = new IndexWriter(FSDirectory.open(indexDir), new BoldAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED); 
  Document doc1 = new Document(); 
  Field f1 = new Field("contents", "common hello world", Field.Store.NO, Field.Index.ANALYZED); 
  doc1.add(f1); 
  writer.addDocument(doc1); 
  Document doc2 = new Document(); 
  Field f2 = new Field("contents", "common hello world", Field.Store.NO, Field.Index.ANALYZED); 
  doc2.add(f2); 
  writer.addDocument(doc2); 
  writer.close();

  IndexReader reader = IndexReader.open(FSDirectory.open(indexDir)); 
  IndexSearcher searcher = new IndexSearcher(reader); 
  searcher.setSimilarity(sim); 
  PayloadTermQuery query = new PayloadTermQuery(new Term("contents", "hello"), new MaxPayloadFunction()); 
  TopDocs docs = searcher.search(query, 10); 
  for (ScoreDoc doc : docs.scoreDocs) { 
    System.out.println("docid : " + doc.doc + " score : " + doc.score); 
  } 
}

如果scorePayload函数始终是返回1,则结果如下,不起作用。

It is not a bold char. 
It is a bold char. 
docid : 0 score : 0.2101998 
docid : 1 score : 0.2101998

如果scorePayload函数如下:

class PayloadSimilarity extends DefaultSimilarity {

  @Override 
  public float scorePayload(int docId, String fieldName, int start, int end, byte[] payload, int offset, int length) { 
    int isbold = BoldFilter.bytes2int(payload); 
    if(isbold == BoldFilter.IS_BOLD){ 
      System.out.println("It is a bold char."); 
      return 10; 
    } else { 
      System.out.println("It is not a bold char."); 
      return 1; 
    } 
  } 
}

则结果如下,同样是包含hello,包含加粗的文档获得较高分:

It is not a bold char. 
It is a bold char. 
docid : 1 score : 2.101998 
docid : 0 score : 0.2101998

继承并实现自己的collector

以上各种方法,已经把Lucene score计算公式的所有变量都涉及了,如果这还不能满足您的要求,还可以继承实现自己的collector。

在Lucene 2.4中,HitCollector有个函数public abstract void collect(int doc, float score),用来收集搜索的结果。

其中TopDocCollector的实现如下:

public void collect(int doc, float score) { 
  if (score > 0.0f) { 
    totalHits++; 
    if (reusableSD == null) { 
      reusableSD = new ScoreDoc(doc, score); 
    } else if (score >= reusableSD.score) { 
      reusableSD.doc = doc; 
      reusableSD.score = score; 
    } else { 
      return; 
    } 
    reusableSD = (ScoreDoc) hq.insertWithOverflow(reusableSD); 
  } 
}

此函数将docid和score插入一个PriorityQueue中,使得得分最高的文档先返回。

我们可以继承HitCollector,并在此函数中对score进行修改,然后再插入PriorityQueue,或者插入自己的数据结构。

比如我们在另外的地方存储docid和文档创建时间的对应,我们希望当文档时间是一天之内的分数最高,一周之内的分数其次,一个月之外的分数很低。

我们可以这样修改:

public static long milisecondsOneDay = 24L * 3600L * 1000L;

public static long millisecondsOneWeek = 7L * 24L * 3600L * 1000L;

public static long millisecondsOneMonth = 30L * 24L * 3600L * 1000L;

public void collect(int doc, float score) { 
  if (score > 0.0f) {

    long time = getTimeByDocId(doc);

    if(time < milisecondsOneDay) {

        score = score * 1.0;

    } else if (time < millisecondsOneWeek){

        score = score * 0.8;

    } else if (time < millisecondsOneMonth) {

        score = score * 0.3;

    } else {

        score = score * 0.1;

    }

    totalHits++; 
    if (reusableSD == null) { 
      reusableSD = new ScoreDoc(doc, score); 
    } else if (score >= reusableSD.score) { 
      reusableSD.doc = doc; 
      reusableSD.score = score; 
    } else { 
      return; 
    } 
    reusableSD = (ScoreDoc) hq.insertWithOverflow(reusableSD); 
  } 
}

在Lucene 3.0中,Collector接口为void collect(int doc),TopScoreDocCollector实现如下:

public void collect(int doc) throws IOException { 
  float score = scorer.score(); 
  totalHits++; 
  if (score <= pqTop.score) { 
    return; 
  } 
  pqTop.doc = doc + docBase; 
  pqTop.score = score; 
  pqTop = pq.updateTop(); 
}

同样可以用上面的方式影响其打分。

阅读全文……

标签 :

lucene之排序、设置权重、优化、分布式搜索_孤独浪子_新浪博客

2. 多字段搜索

使用 MultiFieldQueryParser 可以指定多个搜索字段。

Query query = MultiFieldQueryParser.Parse("name*", new string[] { FieldName, FieldValue }, analyzer);

IndexReader reader = IndexReader.Open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query);


3. 多条件搜索

除了使用 QueryParser.Parse 分解复杂的搜索语法外,还可以通过组合多个 Query 来达到目的。

Query query1 = new TermQuery(new Term(FieldValue, "name1")); // 词语搜索
Query query2 = new WildcardQuery(new Term(FieldName, "name*")); // 通配符
//Query query3 = new PrefixQuery(new Term(FieldName, "name1")); // 字段搜索 Field:Keyword,自动在结尾添加 *
//Query query4 = new RangeQuery(new Term(FieldNumber, NumberTools.LongToString(11L)), new Term(FieldNumber, NumberTools.LongToString(13L)), true); // 范围搜索
//Query query5 = new FilteredQuery(query, filter); // 带过滤条件的搜索
      
BooleanQuery query = new BooleanQuery();
query.Add(query1, BooleanClause.Occur.MUST);
query.Add(query2, BooleanClause.Occur.MUST);

IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query);


4. 设置权重

可以给 Document 和 Field 增加权重(Boost),使其在搜索结果排名更加靠前。缺省情况下,搜索结果以 Document.Score 作为排序依据,该数值越大排名越靠前。Boost 缺省值为 1。

Score = Score * Boost


通过上面的公式,我们就可以设置不同的权重来影响排名。

如下面的例子中根据 VIP 级别设定不同的权重。

Document document = new Document();
switch (vip)
{
   case VIP.Gold: document.SetBoost(2F); break;
   case VIP.Argentine: document.SetBoost(1.5F); break;
}


只要 Boost 足够大,那么就可以让某个命中结果永远排第一位,这就是百度等网站的"收费排名"业务。明显有失公平,鄙视一把。 [no]

5. 排序

通过 SortField 的构造参数,我们可以设置排序字段,排序条件,以及倒排。

Sort sort = new Sort(new SortField(FieldName, SortField.DOC, false));

IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query, sort);


排序对搜索速度影响还是很大的,尽可能不要使用多个排序条件。

6. 过滤

使用 Filter 对搜索结果进行过滤,可以获得更小范围内更精确的结果。

举个例子,我们搜索上架时间在 2005-10-1 到 2005-10-30 之间的商品。
对于日期时间,我们需要转换一下才能添加到索引库,同时还必须是索引字段。

// index
document.Add(FieldDate, DateField.DateToString(date), Field.Store.YES, Field.Index.UN_TOKENIZED);

//...

// search
Filter filter = new DateFilter(FieldDate, DateTime.Parse("2005-10-1"), DateTime.Parse("2005-10-30"));
Hits hits = searcher.Search(query, filter);


除了日期时间,还可以使用整数。比如搜索价格在 100 ~ 200 之间的商品。
Lucene.Net NumberTools 对于数字进行了补位处理,如果需要使用浮点数可以自己参考源码进行。

// index
document.Add(new Field(FieldNumber, NumberTools.LongToString((long)price), Field.Store.YES, Field.Index.UN_TOKENIZED));

//...

// search
Filter filter = new RangeFilter(FieldNumber, NumberTools.LongToString(100L), NumberTools.LongToString(200L), true, true);
Hits hits = searcher.Search(query, filter);


使用 Query 作为过滤条件。

QueryFilter filter = new QueryFilter(QueryParser.Parse("name2", FieldValue, analyzer));


我们还可以使用 FilteredQuery 进行多条件过滤。

Filter filter = new DateFilter(FieldDate, DateTime.Parse("2005-10-10"), DateTime.Parse("2005-10-15"));
Filter filter2 = new RangeFilter(FieldNumber, NumberTools.LongToString(11L), NumberTools.LongToString(13L), true, true);

Query query = QueryParser.Parse("name*", FieldName, analyzer);
query = new FilteredQuery(query, filter);
query = new FilteredQuery(query, filter2);

IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query);


7. 分布搜索

我们可以使用 MultiReader 或 MultiSearcher 搜索多个索引库。

MultiReader reader = new MultiReader(new IndexReader[] { IndexReader.Open(@"c:\index"), IndexReader.Open(@"\\server\index") });
IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query);


IndexSearcher searcher1 = new IndexSearcher(reader1);
IndexSearcher searcher2 = new IndexSearcher(reader2);
MultiSearcher searcher = new MultiSearcher(new Searchable[] { searcher1, searcher2 });
Hits hits = searcher.Search(query);


还可以使用 ParallelMultiSearcher 进行多线程并行搜索。

8. 合并索引库

将 directory1 合并到 directory2 中。

Directory directory1 = FSDirectory.GetDirectory("index1", false);
Directory directory2 = FSDirectory.GetDirectory("index2", false);

IndexWriter writer = new IndexWriter(directory2, analyzer, false);
writer.AddIndexes(new Directory[] { directory });
Console.WriteLine(writer.DocCount());
writer.Close();


9. 显示搜索语法字符串

我们组合了很多种搜索条件,或许想看看与其对等的搜索语法串是什么样的。

BooleanQuery query = new BooleanQuery();
query.Add(query1, true, false);
query.Add(query2, true, false);
//...

Console.WriteLine("Syntax: {0}", query.ToString());


输出:
Syntax: +(name:name* value:name*) +number:[0000000000000000b TO 0000000000000000d]

呵呵,就这么简单。

10. 操作索引库

删除 (软删除,仅添加了删除标记。调用 IndexWriter.Optimize() 后真正删除。)

IndexReader reader = IndexReader.Open(directory);

// 删除指定序号(DocId)的 Document。
reader.Delete(123);

// 删除包含指定 Term 的 Document。
reader.Delete(new Term(FieldValue, "Hello"));

// 恢复软删除。
reader.UndeleteAll();

reader.Close();


增量更新 (只需将 create 参数设为 false,即可往现有索引库添加新数据。)

Directory directory = FSDirectory.GetDirectory("index", false);
IndexWriter writer = new IndexWriter(directory, analyzer, false);
writer.AddDocument(doc1);
writer.AddDocument(doc2);
writer.Optimize();
writer.Close();


11. 优化

批量向 FSDirectory 增加索引时,增大合并因子(mergeFactor )和最小文档合并数(minMergeDocs)有助于提高性能,减少索引时间。

IndexWriter writer = new IndexWriter(directory, analyzer, true);

writer.maxFieldLength = 1000; // 字段最大长度
writer.mergeFactor = 1000;
writer.minMergeDocs = 1000;

for (int i = 0; i < 10000; i++)
{
   // Add Documentes...
}

writer.Optimize();
writer.Close();

阅读全文……

标签 :

五、CXF WebService整合Spring - hoojo - 博客园

applicationContext-client.xml配置文件,文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans >
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://cxf.apache.org/jaxws 
    http://cxf.apache.org/schemas/jaxws.xsd"
    
    <import resource="classpath:META-INF/cxf/cxf.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
    
    <jaxws:client id="userWsClient" serviceClass="com.hoo.service.IComplexUserService" 
        address="http://localhost:8080/CXFWebService/Users"/>
</beans>

阅读全文……

标签 : ,

BSTEK Development Framework (BDF) - 资料库

BDF开发框架文档

BDF的应用目标是基于J2EE的B/S项目,所以只要您是要做的项目是J2EE架构的B/S项目,同时又打算使用dorado7,那么就可以采用BDF发行包快速搭建一个包含BDF各个功能模块的dorado7项目。

要配置一个包含BDF的dorado7应用,那么添加BDF发行包下的com.bstek.bdf.corecom.bstek.bdf.core.dorado7两个模块是必须的。

BDF发行包中包含若干个目录,每个目录分别表示各个不同模块,比如com.bstek.bdf.security就表示BDF的安全模块,在这些模块当中,有的需要管理界面,BDF提供了基于Dorado7的管理页面,这些页面则放在以dorado7结尾的模块当中,比如安全模块的管理界面所在目录就是com.bstek.bdf.security.dorado7;在后续的叫法当中,我们将省略com.bstek.bdf前缀,比如对安全模块,我们就直接称之为security模块,对于安全模块的管理界面则称之为security.dorado7模块,依次类推。

阅读全文……

标签 :

Quartz与Spring整合进行热部署的实现 - pigwing - 博客园

先来几张实现图

任务管理页.

新建任务管理.目前实现叫简单的需求...若各位同学要实现复杂的设计...quartz都有提供强大的支持.小弟目前的需求做到这已经够用了.

接下来.我们如何实现quartz的热部署编码呢?

小弟是利用spring整合quzrtz实现的.但是会产生两个小问题.

我们先看看quartz如何与spring整合

复制代码
<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="applicationContextSchedulerContextKey" value="applicationContextKey"/>
<property name="configLocation" value="classpath:quartz.properties"/><!--
这个是必须的,QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动
-->
<property name="startupDelay" value="30"/><!--
这个是可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
-->
<property name="overwriteExistingJobs" value="true"/>
<property name="jobDetails" >
<list>
<ref bean="xxxJobDetail"/>

</list>
</property>
</bean>
复制代码

首先.我们需要添加一个数据源给quzrtz.允许其序列化JobDetail到数据库.之后有注释.呵呵.我就不必多说了.

 

复制代码
<bean id="xxxJobDetail" class="frameworkx.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!--shouldRecover属性为true,则当Quartz服务被中止后,再次启动任务时会尝试恢复执行之前未完成的所有任务-->
<!--<property name="shouldRecover" value="true"/>-->
<!-- 标识job是持久的,删除触发器的时候不被删除 -->
<property name="durable" value="true"/>
<property name="targetObject" ref="xxxxJob"/>
  
<!-- 此处是需要执行的任务的方法 -->
<property name="targetMethod" value="executeJob"/>
</bean>
复制代码

 

凡是使用过quartz跟spring整合的同学会发现.为什么class的命名空间不是org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean呢?

因为spring技术小组的class会报NotSerializableException.且功能不强shouldRecover,durable这些基础属性不提供...大概spring的MethodInvokingJobDetailFactoryBean根本不支持JobDetail的序列化...想详细了解原因.可以看spring的源码.目前小弟使用的spring3,spring小组依然没解决这问题,应该说还不支持JobDetail序列化.但国外牛人们已经帮我们解决好了.详细见下连接

http://jira.springframework.org/browse/SPR-3797

好了.接下来我们需要配置quzrtz的properties(放到classpath下.quzrtz就能找到)

 

复制代码
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export
= false
org.quartz.scheduler.rmi.proxy
= false
org.quartz.scheduler.wrapJobExecutionInUserTransaction
= false

org.quartz.threadPool.
class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount
= 10
org.quartz.threadPool.threadPriority
= 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread
= true

org.quartz.jobStore.misfireThreshold
= 60000

#org.quartz.jobStore.
class = org.quartz.simpl.RAMJobStore

org.quartz.jobStore.
class = org.quartz.impl.jdbcjobstore.JobStoreTX
#org.quartz.jobStore.driverDelegateClass
=org.quartz.impl.jdbcjobstore.HSQLDBDelegate
org.quartz.jobStore.driverDelegateClass
=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#org.quartz.jobStore.useProperties
= true
org.quartz.jobStore.tablePrefix
= QRTZ_
org.quartz.jobStore.isClustered
= false
org.quartz.jobStore.maxMisfiresToHandleAtATime
=1
复制代码

 

我们这次是选择org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

而不是默认的org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore简单存储在内存中.

接来下,我们需要一个quartz的scheduler管理类.

 

复制代码
protected final Log log = LogFactory.getLog(getClass());
private Scheduler scheduler;
private QuartzDao quartzDao;

private static final Logger logger = LoggerFactory.getLogger(SchedulerServiceImpl.class);



public void setScheduler(Scheduler scheduler) {
this.scheduler = scheduler;
}


public void setQuartzDao(QuartzDao quartzDao) {
this.quartzDao = quartzDao;
}

/**
* 根据job的名称获取job,进而添加到trigger
*
@param name
*
@param jobName
*
@param cronExpression
*
@param group
*
@throws SchedulerException
*/

public void schedule(String name, String jobName, String cronExpression,String group) throws SchedulerException {
if (name == null || name.trim().equals("")) {
name
= UUID.randomUUID().toString();
}

try {
JobDetail jobDetail
= scheduler.getJobDetail(jobName, group);

if(jobDetail != null) {
scheduler.addJob(jobDetail,
true);

CronTrigger cronTrigger
= new CronTrigger(name, group, jobDetail.getName(),
Scheduler.DEFAULT_GROUP);
cronTrigger.setCronExpression(
new CronExpression(cronExpression));
scheduler.scheduleJob(cronTrigger);
scheduler.rescheduleJob(cronTrigger.getName(), cronTrigger.getGroup(), cronTrigger);
}
else
log.error(
"无法找到对应的job.所以无法建立trigger");


}
catch (SchedulerException e) {
log.error(e.getMessage());
throw new SchedulerException();
}
catch (ParseException e) {
log.error(e.getMessage());
}
}

public List<Map<String, Object>> getQrtzTriggers(){
return quartzDao.getQrtzTriggers();
}


public void pauseTrigger(String triggerName,String group) throws SchedulerException{
try {
scheduler.pauseTrigger(triggerName, group);
//停止触发器
} catch (SchedulerException e) {
log.error(e.getMessage());
throw new SchedulerException();
}
}


public void resumeTrigger(String triggerName,String group) throws SchedulerException{
try {
scheduler.resumeTrigger(triggerName, group);
//重启触发器
} catch (SchedulerException e) {
log.error(e.getMessage());
throw new SchedulerException();
}
}


public boolean removeTrigdger(String triggerName,String group) throws SchedulerException{
try {

scheduler.pauseTrigger(triggerName, group);
//停止触发器
return scheduler.unscheduleJob(triggerName, group);//移除触发器
} catch (SchedulerException e) {
log.error(e.getMessage());
throw new SchedulerException();
}
}


public String[] getJobNames(String group) throws SchedulerException {
String[] jobNames
= null;
try {
jobNames
= scheduler.getJobNames(group);
}
catch (SchedulerException e) {
log.error(e.getMessage());
throw new SchedulerException();
}
return jobNames;
}
复制代码

 

但前只是简单实现通过cornexpression.若有复杂配置trigger规则的.或需要对trigger添加calendar...可以自己进行扩展.

一下是quzrtzDao的实现

 

复制代码
private DataSource dataSource;

public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}

public List<Map<String, Object>> getQrtzTriggers() {
List
<Map<String, Object>> results = getJdbcTemplate().queryForList("select * from QRTZ_TRIGGERS order by start_time");
long val = 0;
String temp
= null;
for (Map<String, Object> map : results) {
temp
= MapUtils.getString(map, "trigger_name");
if(StringUtils.indexOf(temp, "#") != -1){
map.put(
"display_name", StringUtils.substringBefore(temp, "#"));
}
else{
map.put(
"display_name", temp);
}

val
= MapUtils.getLongValue(map, "next_fire_time");
if (val > 0) {
map.put(
"next_fire_time", DateFormatUtils.format(val, "yyyy-MM-dd HH:mm:ss"));
}

val
= MapUtils.getLongValue(map, "prev_fire_time");
if (val > 0) {
map.put(
"prev_fire_time", DateFormatUtils.format(val, "yyyy-MM-dd HH:mm:ss"));
}

val
= MapUtils.getLongValue(map, "start_time");
if (val > 0) {
map.put(
"start_time", DateFormatUtils.format(val, "yyyy-MM-dd HH:mm:ss"));
}

val
= MapUtils.getLongValue(map, "end_time");
if (val > 0) {
map.put(
"end_time", DateFormatUtils.format(val, "yyyy-MM-dd HH:mm:ss"));
}

map.put(
"statu",status.get(MapUtils.getString(map, "trigger_state")));
}

return results;
}


public static final Map<String,String> status = new HashMap<String,String>();


static{
status.put(
"ACQUIRED", "运行");
status.put(
"PAUSED", "暂停");
status.put(
"WAITING", "等待");

}

private JdbcTemplate getJdbcTemplate() {
return new JdbcTemplate(this.dataSource);
}
复制代码

 

同学们可以根据自己的风格,修改一下其代码.

此时我们就可以创建一个简单的quzrtz的热部署管理.

我之前讲过会有两个问题.第二个问题是什么呢?还记得讲过我们需要讲jobDetail序列化到数据库吗?因为我们使用了spring 系统中的manager类通过spring的IOC依赖注入.那我们的跟quartz相关的manager都需要声明实现Serializable序列化接口.

此时我们可以想到用ContextLoader.getCurrentWebApplicationContext()把相应的manage通过bean id get出来.

 

复制代码
public class SpringBeanProvide implements Serializable/*, ApplicationContextAware*/{


/**
*
*/
private static final long serialVersionUID = 8430477279431126488L;
private ApplicationContext context;

@SuppressWarnings(
"unchecked")
public <T> T getBean(Class<T> clazz, String beanName){
context
= ContextLoader.getCurrentWebApplicationContext();
return (T)context.getBean(beanName);
}

public ServletContext getServletContext() {
WebApplicationContext webContext
= ContextLoader.getCurrentWebApplicationContext();
return webContext.getServletContext();
}

/*@Autowired
public void setApplicationContext(ApplicationContext context)
throws BeansException {
this.context = context;

}
*/
}
复制代码

 

我们看看ContextLoader.getCurrentWebApplicationContext()的源码

 

复制代码
/**
* Obtain the Spring root web application context for the current thread
* (i.e. for the current thread's context ClassLoader, which needs to be
* the web application's ClassLoader).
*
@return the current root web application context, or <code>null</code>
* if none found
*
@see org.springframework.web.context.support.SpringBeanAutowiringSupport
*/
public static WebApplicationContext getCurrentWebApplicationContext() {
ClassLoader ccl
= Thread.currentThread().getContextClassLoader();
if (ccl != null) {
WebApplicationContext ccpt
= currentContextPerThread.get(ccl);
if (ccpt != null) {
return ccpt;
}
}
return currentContext;
}
复制代码

 

currentContextPerThread是一个final的ConcurrentHashMap.也是IOC容器存储bean的一个hash表.

到底什么时候才把ccl设置到currentContextPerThread中呢?

我们往上看源码

 

复制代码
try {
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);

// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
this.context);

ClassLoader ccl
= Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext
= this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl,
this.context);
}
复制代码

其中...currentContextPerThread.put(ccl, this.context);是在ApplicationContext parent = loadParentContext(servletContext);之下.

 

ContextLoader.getCurrentWebApplicationContext()只能在容器完全初始化后才能使用..这对于单元测试时...ccpt返回出来的是一个null...

这就是我为什么会在SpringBeanProvide出现注释了ApplicationContextAware的原因...因为本人有点懒...哈哈..没重新整理一份spring的bean xml作为单元测试使用...所以...在单元测试时...改一下注释...就去跑了..呵呵...此时...我们已经可以使用quzrtz的热部署.不必重启服务器就可以对任务进行管理了.

阅读全文……

标签 : , ,