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

极客制作的技术“书”

$
0
0

世上的爱书之人,为之癫狂陶醉、夜不能寐,本人自认为没有达到那种高度,实不敢以爱书人自居。不过身在此行,毕竟耳濡目染,“书”本身这个概念对我而言也有了非凡的意义。不论是有关写书、做书还是卖书,如在生活日常中碰到有人提及,必竖起耳朵;有文谈及,必扫上一眼。近年来,看到的“书”真是千姿百态、乱花迷眼。但是本文并不准备谈及实体书籍的新样式(奢华、文艺的实体”书“近年来真是层出不穷,实体书的”奢饰品“面目也逐渐显山露水),而是想谈一谈近来见到的一些极客写出来的技术”书“。

之所以选择技术“书”这个门类,主要基于以下考虑:所谓的呆萌geek已经在近十几年来几乎把世界翻了个个儿,他们颠覆了通讯、社交、音乐、视频、商品交易;正在逐渐颠覆着金融、医疗、教育、出版和房地产。这些geek们对书又是怎么个看法呢?他们心目中的书应该是个什么样子?他们是怎样写书的?他们又是怎样把书呈现到读者眼前的?他们的想法代表着未来的趋势,他们的某些尝试会成为未来的行业标准。目前世道就是这样:You’ve got to do what is the next. 不管你对当下的事情做得再好,也无济于事,看看可怜的柯达、诺基亚,就都明白了。

极客对书的改造首先体现在技术”书“上,因为他们要看、要用、要写、要分享。以下几条是我总结的极客制作电子书的特点。(注:本人见识有限,很容易犯 可得性偏差的错误,如有错漏,敬请指教。)

极客做书的几个特点

1. 不用Word来写书

使用word的人口基数还是大啊……但是如果用word导出为html的话,那些凌乱的代码,看起来让人烦躁。所见即所得的编辑模式虽然直观,但是排版是要用鼠标点来点去,选来划去,对于键盘流的coder来说,无法行云流水地施展,实在是不够炫酷啊!另外,短篇的word文档,排排版尚可。长篇大作,再用word的排版工具,真的很繁琐。word格式也不利于跨平台、跨媒介的分享。对于技术人员来讲,word格式真的不是进行文字编辑的首选。

那么geek们是怎么写书的呢?自然是用代码啦!但是对于大多数没有太多互动功能的书来说,这里所说的”代码“只是一种固定撰写内容的格式罢了。目前势头正劲的markdown是很多写书极客的选择。在大名鼎鼎的代码托管站Github上写文章,用的就是markdown,大多数码农一定都不会陌生。国内专注于写文章的网站 简书和走在技术书籍出版前沿的 图灵社区都是支持并鼓励markdown格式的。我自己写这篇文章时用的也是这种格式。学习markdown的曲线并不陡峭,十分容易上手。以前习惯了word的自己,在用了一段时间的纯文本方式写作之后,也逐渐能够感觉到这种至简之中所透露出的优雅和美感。

除了markdown以外,有些geek会使用更为复杂的模式进行写作。DocBook就是一种非常有名的格式,O’Reilly出版社默认使用的就是这种格式,算得上一种事实上的标准。DocBook的写作要符合xml格式,上手没有markdown那么容易。 这里有一篇DocBook文件写作的入门教程,略扫一眼就知道这种写作方式是多么nerd了。

除了以上两种格式以外,还有很多其他格式,例如reStructuredText以及在学术出版中备受推崇的Latex等。

【近距离观察】很多人可能对于上述看来麻烦的写作方式嗤之以鼻。毕竟写小说与写技术书籍是两回事。但是,重要的并不是使用的究竟是哪种格式,而是为什么geek会选择用文本编辑器来编写格式相对复杂的文字,而非使用word来写作。我想归根结底,是由于形式与内容相分离的趋势造成的。正如目前发生在前端网页制作中的内容、样式和行为,即html、css和javascript的分离,在出版业内,这种趋势也愈演愈烈。doc格式是非常优秀且直观的文件形式,但是内容的逻辑结构是通过样式来展现的,依靠的是肉眼的分辨。例如通过字号大小等字体格式的调整,段落前后的间隙以及文字的缩进等实现。在word中,进行标题、引用等的设置选项就是在“样式”设置的分类下面的,这就很说明问题。word中的文字拷贝到txt文件中时,由于样式没有保留,其内容的逻辑结构也随之掩盖在文字之间,难以辨识。而word导出的XML文档中却涵盖了太多与样式相关的内容。尽管doc文件使用广泛,人们喜闻乐见,但是以它为基础进行内容逻辑与样式的分离,则是一件相对困难和繁琐的事情。极客们用文本编辑器写出的文本可以视为纯之又纯的内容逻辑,其中逻辑的展现不是依赖人眼的感受识别,而是通过固定符号和标签,以电脑可以处理的显性方式进行存储。这种内容与样式相分离带来的直接好处就是下面要讲到的多种文件格式的转换以及跨平台的展示。

2. 多种格式的转换

在维基的 comparison of ebook formats一文中,列出的格式达31种之多。但是大多要求生成的最终格式主要有以下三种:PDF、mobi和epub。PDF是现行书籍出版的标准,可以直接用其进行高质量的书籍印刷,可以算得上是一种必备格式。由于amazon kindle阅读器的热销,mobi格式也迅速突出重围,获得举足轻重的地位。epub则是除了kindle阅读器以外,其他各类阅读器和应用都支持的电子书阅读格式,使用范围也非常的广泛。其中值得注意的是mobi格式可以通过epub格式生成。因此,格式转换的关注点应放在能够便捷完美转换成为epub和pdf格式。这里的“便捷”、“完美”有两方面所指:1. 转换出来的样式美观,没有产生过多格式问题;2. 转换期间没有或极少有人工参与,不太需要人力进行细节的格式修正。

支持格式转换的工具很多,其中 Pandoc脱颖而出,成为各类格式转换的瑞士军刀。在Pandoc的首页上,言明其支持的源文件格式和最终格式:

源文件格式

  • markdown
  • reStructuredText
  • textile
  • HTML
  • DocBook
  • LaTeX
  • MediaWiki markup
  • OPML
  • Haddock markup

目标文件格式

  • HTML格式(支持使用 Slidy), reveal.js, Slideous, S5, 或 DZSlides显示的HTML幻灯
  • 文字处理格式 Docx, ODT, OpenDocument XML
  • 电子书格式 epub, FictionBook2
  • 文档格式 DocBook, GNU TexInfo, Groff man pages, Haddock markup
  • 大纲格式 OPML
  • TeX格式 LaTeX, ConTeXt
  • PDF (通过LaTex中间格式转)
  • 轻量标记格式 Markdown, reStructuredText, AsciiDoc, MediaWiki markup, Emacs Org-Mode, Textile
  • 自定义格式 自定义用户可以用lua编写

值得注意的是上述的源文件格式,都是比较理想的存档格式。有了这些格式的文件,常见的word、pdf、epub和mobi格式都不在话下。而word、pdf等格式,虽然常见,但只能被单向生成,无法实现双向转换,因此并不是理想的存档格式,不利于后续多用途加工。

3. 分享形式的推陈出新

说到技术书籍的分享,把豆瓣这么文艺的平台拨到一边, 图灵社区这一片洋溢着知性、理性和极客风范的净土浮上水平线。图灵文化对于纸质书似乎不那么执着,在电子书那一栏中,有些电子书有纸质版在售,有些则没有。例如《 Angular JS入门教程》和《 Sketch中文手册》这两本就是完全免费的电子书。由于正在为图灵译书,所以晓得图灵的后台图书编辑系统是支持Markdown的,因此图灵的书不仅有纸质版,还有PDF版和对应的Kindle版,对于他们而言,这种多格式的运作方式成本并不高。所有这些书在社区中,直接在书籍内容旁边就有Tab页集合了勘误、评论和延伸阅读的文章,使得书籍本身价值得以提升,呈现的内容更为爆满,也使人与作者、编辑和书之间的沟通变得更为顺畅。

除了图灵社区以外,github也是一片技术书籍诞生的热土。例如阮一峰的这本 《ECMAScript 6入门》,就是其中之一。据说这本书不久就会由电子工业出版社出版纸质版本,不过电子版本已经优先在网路面世。这本书的简介中称这本书是“开源”的javascript教程。任何人都可以在保持原作者署名和非商用的前提下,自由地阅读、分享、修改本书。最近看过的另外一本 《JavaScript Patterns》一书则是直接在Github中集体翻译完成并共享的。在 这里,你能看到更多基于git、github和markdown制作的互动书籍gitbook。

另外,网上各种语言的文档其实也可以看作典型的技术书籍的范本。例如HAML的这份 参考文档,不就是一本典型的小手册吗?这种文档真是遍地都是,如果你的mac机上装有 Dash的话,还会看到更多实例。

各类支持自出版的平台也层出不穷。亚马逊的自出版计划Kindle Direct Publishing 自不必提, Leanpub就是一个支持敏捷出版的网站,出版格式多种多样,目前大多数是与技术相关的书籍。个人认为首页右下角创始人的演讲视频很值得一看。

这里一本 docbook,那里一本 wikibook,似乎一下子蹦出来很多后面跟了book的新词儿。感谢呆萌的技术宅,把出书这件事儿也整的这么有技术范这么性感。其实说到底,技术并不是把书跟人分得越来越远,而是把书与人越拉越近。只要你动动鼠标,打打键盘,自己的书似乎就在网络上成形了,接着就有人能看到,能评论。这无疑是将著书立说这件事情变得更容易。写到最后,附上一个段子吧!首先声明,段子决没有取笑技术宅之意,要说真是意有所指的话,那也是那些高高在上磨不开面子拉不下脸面的出版爷们:

“老师老师,现在写书的根本就不用笔写了,称为笔者已经不对了啊,那叫什么好呢?”“叫键人!”“那么那些只用鼠标的呢?”“叫鼠辈!”“那只用触屏手机的呢?”“叫触生!”“。。。”

奉劝一句,别再装“笔”者,俯身当“键”人!

极客制作的技术“书”,首发于 极客范 - GeekFan.net


安装和使用 Elasticsearch

$
0
0

Elasticsearch是开源搜索平台的新成员,实时数据分析的神器,发展迅猛,基于 Lucene、RESTful、分布式、面向云计算设计、实时搜索、全文搜索、稳定、高可靠、可扩展、安装+使用方便,介绍都说的很好听,好不好用拿出来遛一遛。

做了个简单测试,在两台完全一样的虚拟机上,2000万条左右数据,Elasticsearch 插入数据速度比 MongoDB 慢很多(可以忍受),但是搜索/查询速度快10倍以上,这只是单机情况,多机集群情况下 Elasticsearch 表现更好一些。以下安装步骤在 Ubuntu Server 14.04 LTS 上完成。

安装 Elasticsearch

升级系统后安装 Oracle Java 7,既然 Elasticsearch 官方推荐使用 Oracle JDK 7 就不要尝试 JDK 8 和 OpenJDK 了:

$ sudo apt-get update
$ sudo apt-get upgrade

$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update

$ sudo apt-get install oracle-java7-installer

加入 Elasticsearch 官方源后安装 elasticsearch:

$ wget -O - http://packages.elasticsearch.org/GPG-KEY-elasticsearch | apt-key add -
$ sudo echo "deb http://packages.elasticsearch.org/elasticsearch/1.1/debian stable main" >> /etc/apt/sources.list

$ sudo apt-get update
$ sudo apt-get install elasticsearch

加入到系统启动文件并启动 elasticsearch 服务,用 curl 测试一下安装是否成功:

$ sudo update-rc.d elasticsearch defaults 95 1

$ sudo /etc/init.d/elasticsearch start

$ curl -X GET 'http://localhost:9200'
{"status" : 200,"name" : "Fer-de-Lance","version" : {"number" : "1.1.1","build_hash" : "f1585f096d3f3985e73456debdc1a0745f512bbc","build_timestamp" : "2014-04-16T14:27:12Z","build_snapshot" : false,"lucene_version" : "4.7"
  },"tagline" : "You Know, for Search"
}

Elasticsearch 的集群和数据管理界面 Marvel 非常赞,可惜只对开发环境免费,如果这个工具也免费了就无敌了,安装很简单,安装完成后重启服务访问 http://192.168.2.172:9200/_plugin/marvel/ 就可以看到界面了:

$ sudo /usr/share/elasticsearch/bin/plugin -i elasticsearch/marvel/latest

$ sudo /etc/init.d/elasticsearch restart
 * Stopping Elasticsearch Server                                           [ OK ]
 * Starting Elasticsearch Server                                           [ OK ]

Elasticsearch Marvel

安装 Python 客户端驱动

和 MongoDB 一样,我们一般用程序和 Elasticsearch 交互,Elasticsearch 也支持多种语言的客户端驱动,这里仅安装 Python 驱动,其他语言可以参考官方文档。

$ sudo apt-get install python-pip
$ sudo pip install elasticsearch

写个简单程序把 gene_info.txt 的数据导入到 Elasticsearch:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import os, os.path, sys, re
import csv, time, string
from datetime import datetime
from elasticsearch import Elasticsearch

def import_to_db():
    data = csv.reader(open('gene_info.txt', 'rb'), delimiter='\t')
    data.next()

    es = Elasticsearch()
    for row in data:
        doc = {
            'tax_id': row[0],'GeneID': row[1],'Symbol': row[2],'LocusTag': row[3],'Synonyms': row[4],'dbXrefs': row[5],'chromosome': row[6],'map_location': row[7],'description': row[8],'type_of_gene': row[9],'Symbol_from_nomenclature_authority': row[10],'Full_name_from_nomenclature_authority': row[11],'Nomenclature_status': row[12],'Other_designations': row[13],'Modification_date': row[14]
        }
        res = es.index(index="gene", doc_type='gene_info', body=doc)

def main():
    import_to_db()

if __name__ == "__main__":
    main()

Kibana 是一个功能强大的数据显示客户端,通过插件方式和 Elasticsearch 集成在一起,安装很容易,下载解压就可以了,然后重启 Elasticsearch 服务访问 http://192.168.2.172:9200/_plugin/kibana/ 就能看到界面:

$ wget https://download.elasticsearch.org/kibana/kibana/kibana-3.0.1.tar.gz
$ tar zxvf kibana-3.0.1.tar.gz
$ sudo mv kibana-3.0.1 /usr/share/elasticsearch/plugins/_site
$ sudo /etc/init.d/elasticsearch restart

Elasticsearch Kibana

从面试角度谈360和阿里的测试文化

$
0
0
从面试角度谈360和阿里的测试文化
        最近一年一直都在招聘测试开发,尤其是年后,在传统的跳槽高峰推动下,面试的时间基本上占了工作的一半。当然这其中也包含了很多吐槽和抱怨,对HR的抱怨,对候选人的抱怨,对各种奇葩的吐槽。通过朋友和猎头的推荐,自己也体验了一些其他公司的面试,一个公司的面试风格和内容,某种程度上代表着这个公司的文化,虽然自己工作的5年里各种行业公司的面试和被面试都经历过,但是这里只谈一下360和阿里的测试文化,因为这两个公司的测试文化自己相对了解一些,而且它们也具有代表性。

        首先需要阐述一下自己的观点:

                1、尽管自己在360干了两年,面试和被面试很多次,也承认这样的测试文化有独到的地方,但是比较以前的经历和其他公司,我并不推荐360的测试风格;当然这里并不谈论这个公司,只谈它的测试文化;

                2、自己去阿里,还是继续在360待着,都是自己的选择,并不能说明这两个公司如何;

                3、这里是纯粹的讨论测试和测试开发的范畴,并不讨论其他的东西,当然包括薪资——这些都是Open的,圈里打听一下都了解;

        我习惯于不定期的去外面参加一些面试,虽然大部分情况自己并不打算换工作,一方面提醒自己跟紧点,别失去活力,一方面参考一下自己在行业中的水平,还有就是接接地气,看看别人都是怎么面试的。就我的了解,这两个公司中,360以前的面试流程并不是很正规,我指的是和阿里相比。这种情况现在已经慢慢在好转,毕竟360不可能以之前管理几百人的方法去管理现在的几千人,但是这种流程也有自己的好处,对于看得上的候选人,可以很快定下来,周期很短,也可以留住一些人才。阿里这类公司招聘的流程相对较长,甚至有些人以为面试没有结果,所以接受了其他的offer,反正财大气粗,并不会在乎一两个人的去留。360测试的面试流程,相对比较简单,一般3轮面试就差不多了,有时候会有电话提前交流一下,大部分情况下几轮面试可以合在一起,候选人一趟就可以了;阿里的话一般来说候选人一趟是不行的,而且有些负责人在北京,一些在杭州,怎么样也需要候选人跑两次,算上一开始的电话面试,基本上是5轮。360一般是项目缺人就会有人头,而且直接招人就要上手干活,从这点上说,社招的时候360很少为储备人才考虑,从QA人员项目中借调就能看出来,还有就是360的校招跟社招很难联系到一起——校招并不是很完善,基本上是跟着BAT走的。测试在社招上,个人认为很难实现突破——虽然360挖人很下本,但是测试这块并不像开发和产品,毕竟不是创收型的职位,即便挖到了,在当下也很难做出东西。所以在测试文化上,360跟阿里比,并没有传承和积累,也没有一个长久的规划,这样的环境适合个人英雄的凸显,但是对整体测试团队的建设来说,很难良性的发展,改革起来也很难。

        举一个简单的例子,我知道一个360校招过来的毕业生,来公司以后被分到一个小组做手工测试,组长的价值观就是用例写的多,执行到加班为止的就给高绩效,后来这个学生实在忍受不了这样的组长,觉得组长不光不懂技术,而且还不愿意新的尝试和提高,他说他们组长说的最多的一句话就是,如果怎么怎么样,那么只能说360不适合他。这个毕业生后来去了阿里,工资翻倍,现在干的有声有色。这个组现在的组员基本上全是女生,乖乖女,让干什么就干什么的那种,基本上不懂技术,更不要说写代码了。360这边,一般QA招人的要求就是工作年限多,态度好,肯吃苦,能加班,技术方面是次要的,QA的面试官也不一定懂技术,因为公司本来就不是技术主导的。

        举这个例子并不是说360的测试团队多么差,这样的团队灵活性和凝聚力都是很高的,只是作为一个想要崛起和发展的公司,不能只顾着扛枪打仗,保障也要有力。我和360的开发聊的时候,他们管开发和测试开发叫工程师,管QA叫QA——360的测试团队里,测试开发和手工测试是分开的,QA全是手工测试,分布在项目组里。对于开发这么去对待QA,我表示理解——开发做了一个新功能让QA去测试,要求测出了问题帮忙去定位,测试问开发如何定位,开发说跟踪一下堆栈,测试说不懂,开发讲了测试还是不懂,最终的结果就是开发让测试随便去点点算了。我身边大多数QA都是这样的,我不否认所有人,大多数QA是这样的。我不认为QA的能力差或者智商低,但是他们大多数人的水平,我个人认为确实还不如一些公司的外包人员高。

        在360我有过一段很痛苦的经历,也可以说是一个适应的过程。360的测试组中,很少主动提出去自动化做测试,这需要从上往下去推动。Ok,领导给了任务去做自动化,但是QA人员没有测试用例,测试开发人员需要追着QA去收集自动化的需求,最终就是没有需求——既没有用例,对具体产品特征也说不清楚,这让测试开发很难下手。没办法,只能自己研究产品,自己写架构写工具,自己找测试机;到环境和架构起来了,又纠结起来,自动化用例怎么写?QA不会代码,那么就留下一些脚本的接口,python或者lua的,抱歉,QA还是不会,那么改成xml写自动化用例,但是QA还是觉得不行。最终的QA的反馈是,如果让他们自己写自动化用例,他们情愿自己手动点击到加班。那段时间是我在360最痛苦的时候,当时的辞职信就在邮件里,随时准备发给领导。在我工作过的公司里,一般都没有QA(或者外包),测试组只有测试开发(SDET),大家在这方面都是有相同的意识去和开发对接,做自动化的准备和设计。我并不是说不需要手工测试,手工测试当然需要,但是需要像在敏捷团队中的手工测试一样,而不是像客户验收的这种测试,我并不觉得这是大部分QA人员工作的方式和水平,这跟开发一样,搞内存的和搞字符串拼接的,就不是一个水平。在阿里面试的过程中,还有和一些阿里的同事交流的时候,完全没有这方面的顾虑,大家有相同的意识,交流起来很轻松,这就是测试团队文化上的差别,大的文化下,即便员工没有晋升的机会,但是能够做自己想做的事情,能够更有效的沟通,能够发挥自己的真正价值,也是很开心的;也就是我说的那种,给我一个合理的平台,竞争输了我也情愿。这就是我不推荐360测试的原因,我不否定它的优点,我只是说说我看到的缺点。

        笔试和面试题方面,两个公司的方式也和他们的文化有直接关系。360一般更看重候选人能不能上手干活——这并没有什么贬低的意思,360整体的思路都是这样,产品这几年也都是什么都抓一把,每个团队各行其是,产品经理相互之间斗来斗去的,除了过时的安全以外,还没有一个属于自己的生态圈,这样的环境下当然不愿意投资去培养或者储备全能技术人才,很多新开发写的代码一锅粥一样,安卓开发里不懂虚拟机不懂汇编的大有人在。这和公司的结果导向分不开的,不管长久的看是否结果最优,但是短期是肯定要最好的结果。这方面确实花了不少钱挖人和做奖励机制。相比来说,对测试方面的关心远远不够,毕竟不是产出团队。测试的笔试和面试上,完全看面试官的人品了,有些QA的面试官自己本身就不懂技术,还要偷学几招去喷别人。平心而论,哪个候选人能没有缺陷呢?我不懂那些难为别人的面试官是什么心态,但是我相信一个好的面试官不会用一两道自己知道答案的题去为难别人,从而达到自己内心的平衡。但是总的来说,360这边的招聘,更看重上手的工作能力和个人价值的体现,但是不同组和不同面试官会有所不同,就我个人而言,我希望候选人的综合能力强一些,主要是学历能力,够聪明,沟通良好,如果不幸笔试遇到我出的题或者遇到我面试,也许一个题都答不对,但是我会让候选人通过。

        手工测试的职位我不太了解,但是测试开发基本上跟开发是差不多的,很多时候对基础知识和广度的要求,比开发要高,不过基本上就是数据结构,算法,环境配置,网络技术等。

        阿里的面试,相对来说更成熟一些,更强调候选人的综合能力。一般来说会先有一个电话的沟通,大概了解一些候选人整体的技术情况和经验;接下来的一面会以技术为主,相关技术和开源的技术一般都会问到,聊之前的项目实现,设计等等,一般都会当场写一些代码,也会较深的技术的讨论,这轮通常都是职位的直属leader面;接下来的一轮一般都是职位所在的项目负责人,纯技术的东西会少一些,更多的是项目上的东西,包括不同问题的解决方案等等,这时候业务相关的东西会多一些,有没有笔试的话,主要看是不是视频面试;接下来一般还有一轮交叉的综合面试,大体上跟上一轮差不多,可能内容也会覆盖到前面两轮,但是不会面试特别精细的东西,有没有笔试依旧看是不是视频了。

        阿里的这几轮面试我还算顺利,也没什么准备,就是临时发挥。电话面的时候我正好在医院帮别人看孩子,孩子太闹也没办法调整思路,基本上说说以前的项目,聊了一些底层api,简单聊了一些技术,最后问了一些东西的实现,用到的函数忘的差不多了,只好简单说了一下思路,面试完了就把这事忘了;后来HR来通知面试,那天正好休假回来,就直接过去了,主要聊的就是GUI自动化原理,浏览器模型,插件原理等等,简单的写了一些多线程同步异步和文件数据读取处理的代码,聊的挺开心的,也学到了不少;然后没有休息直接下一轮面试,问了一些业务的东西,还有一些项目的解决方案,视频的,所以也没写代码,这两轮基本上从1点半一直到5点;终面是在半个月之后,基本上也是为了一些GUI的东西,问了一些项目的情况和自己换工作的理由等等。

        上面就是我了解的阿里和360测试的文化和面试的一些东西,再深入的东西就不能说了。基本上360的测试文化并不适合测试开发的长久规划和成长,但是机会多一些,属于“造英雄”的,对于技术一般但是推动力强的人是个好的选择,当然会叫的孩子有奶吃,另外360是一个从上到下的管理方式,所以一定要和领导处好关系,在360过得好不好完全取决于你跟哪个领导;阿里的测试基本上属于国内公司里最好的了,个人规划和发展都很好,比较适合于想在测试领域深入发展的人,另外一些技术很牛的测试人才,如果不愿意管人或者管项目,阿里也是一个很好的选择。

        最后,送出两条面试的技巧:

        一个是面试的时候要适当的表现自己,对于只有一轮的面试(比如挖人),一般该怎么表现就怎么表现,发挥到最好就可以;但是多轮面试的时候,一般第一轮都是技术,而且都是直属领导,这时候不能表现的太好,因为有些面试官不希望候选人太牛,但是又不能表现的不好,不然定级会低;后续的几轮可以放开了发挥;当然这只是技巧,并不一定都适用,很多第一轮的面试官还是不错的;

        第二个就是扬长避短,自己不会或者不熟悉的东西尽量别写到简历上,或者尽量别提;我的简历上基本没有写过linux和性能测试,面试官问我对linux是不是熟悉的时候,我一般都直接说不熟悉,只做过半年linux的开发,只会用常用命令跟vim,其他像redis什么的配置都是随用随查;问我性能测试的时候,我就说没做过性能测试,只帮助QA写过一些并发和性能测试的工具。

        基本上这个话题就能谈这么多了,算是吐槽也算是总结吧。完全个人的感受和看法,对两个公司没有针对性。

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


ITeye推荐



前后端分离的思考与实践(五)

$
0
0

基于前后端分离的多终端适配

前言

近年来各站点基于 Web 的多终端适配进行得如火如荼,行业间也发展出依赖各种技术的解决方案。有如基于浏览器原生 CSS3 Media Query 的响应式设计、基于云端智能重排的「云适配」方案等。本文则主要探讨在前后端分离基础下的多终端适配方案。

关于前后端分离

关于前后端分离的方案,在 《前后端分离的思考与实践(一)》中有非常清晰的解释。我们在服务端接口和浏览器之间引入 NodeJS 作为渲染层,因为 NodeJS 层彻底与数据抽离,同时无需关心大量的业务逻辑,所以十分适合在这一层进行多终端的适配工作。

UA 探测

进行多终端适配首先要解决的是 UA 探测问题,对于一个过来的请求,我们需要知道这个设备的类型才能针对对它输出对应的内容。现在市面上已经有非常成熟的兼容大量设备的 User Agent 特征库和探测工具, 这里有 Mozilla 整理的一个列表。其中,既有运行在浏览器端的,也有运行在服务端代码层的,甚至有些工具提供了 Nginx/Apache 的模块,负责解析每个请求的 UA 信息。

实际上我们推荐最后一种方式。基于前后端分离的方案决定了 UA 探测只能运行在服务器端,但如果把探测的代码和特征库耦合在业务代码里并不是一个足够友好的方案。我们把这个行为再往前挪,挂在 Nginx/Apache 上,它们负责解析每个请求的 UA 信息,再通过如 HTTP Header 的方式传递给业务代码。

这样做有几点好处:

  1. 我们的代码里面无需再去关注 UA 如何解析,直接从上层取出解析后的信息即可。
  2. 如果在同一台服务器上有多个应用,则能够共同使用同一个 Nginx 解析后的 UA 信息,节省了不同应用间的解析损耗。

http://gtms03.alicdn.com/tps/i3/T1s8L7FFNcXXbvv3MO-968-509.png

来自天猫分享的基于 Nginx 的 UA 探测方案

淘宝的 Tengine Web 服务器也提供了类似的模块 ngx_http_user_agent_module

值得一提的是,选用 UA 探测工具时必须要考虑特征库的可维护性,因为市面上新增的设备类型越来越多,每个设备都会有独立的 User Agent,所以该特征库必须提供良好的更新和维护策略,以适应不断变化的设备。

建立在 MVC 模式中的几种适配方案

取得 UA 信息后,我们就要考虑如果根据指定的 UA 进行终端适配了。即使在 NodeJS 层,虽然没有了大部分的业务逻辑,但我们依然把内部区分为 Model / Controller / View 三个模型。

http://gtms04.alicdn.com/tps/i4/T1oGP_FIlbXXc1sab4-200-220.png

我们先利用上面的图,去解析一些已有的多终端适配方案。

建立在 Controller 上的适配方案

http://gtms03.alicdn.com/tps/i3/T1YkZcFMhXXXa36h6P-507-400.png

这种方案应该是最简单粗暴的处理方法。通过路由(Router)将相同的 URL 统一传递到同一个控制层(Controller)。控制层再通过 UA 信息将数据和模型(Model)逻辑派发到对应的展现(View)进行渲染,渲染层则按预先的约定提供了适配几个终端的模板。

这种方案的好处是,保持了数据和控制层的统一性,业务逻辑只需处理一次遍可以应用在所有终端上。但这种场景只适合如展示型页面等低交互型的应用,一旦业务比较复杂,各个终端的 Controller 可能有各自的处理逻辑,如果还是共用一个 Controller ,会导致 Controller 非常的臃肿而且难以维护,这无疑是一个错误的选择。

建立在 Router 上的适配方案

为了解决上面遇到的问题,我们可以在 Router 上就将设备区分,针对不同的终端分发到不同的 Controller 上:

http://gtms03.alicdn.com/tps/i3/T1UQr7FK8cXXXKYi.n-531-348.png

这也是最常见的方案之一,大多表现在针对不同终端使用各自独立的一套应用。如 PC 淘宝首页和 WAP 版的淘宝首页,不同设备访问 www.taobao.com ,服务器会通过 Router 的控制,重定向到 WAP 版的淘宝首页或者 PC 版的淘宝首页,它们各自是完全独立的两套应用。

但这种方案无疑带来了数据和部分逻辑无法共用的问题,各种终端之间无法分享同一份数据和业务逻辑,产生大量重复性工作,效率低下。

为了缓解这个问题,有人提出了优化后的方案:依然是在同一套应用里面,各个数据来源抽象成各个 Model,提供给不同终端的 Controller 组合使用:

http://gtms01.alicdn.com/tps/i1/T1pR_fFGxaXXXGaK2H-685-520.png

这个方案解决了前面数据无法共用的问题。在 Controller 上各个终端还是相互独立,但能共同使用同一批数据源,至少在数据上无需再针对终端类型开发独立的接口了。

以上两种基于 Router 的方案,由于 Controller 的独立,各个终端可以为自己的页面实现不同的交互逻辑,保证了各终端自身足够的灵活度,这也是为什么大部分应用采用这种方案的主要原因。

建立在 View 层的适配方案

这是淘宝下单页面使用的方案,不过区别是下单页将整体的渲染层放在了浏览器端,而不是 NodeJS 层。不过无论是浏览器还是 NodeJS,整体设计思路还是一致的:

http://gtms02.alicdn.com/tps/i2/T18IvnFMtXXXc.mb7O-1327-494.png

在这个方案里面,Router、Controller 和 Model 都无需关注设备信息,终端类型的判断完全交给展现层来处理。图中主要的模块是「View Factory」,Model 和 Controller 将数据和渲染逻辑传递过来之后,通过 View Factory 根据设备信息和其它状态(不仅仅是 UA 信息、也可以是网络环境、用户地区等等)从一堆预设好的组件(View Component)中抓取特定的组件,再组合成最终的页面。

这种方案有几个优势:

  1. 上层无需关注设备信息(UA),多终端的视频还是交由和最终展现最大关系的 View 层来处理;
  2. 不仅仅是多终端适配,除了 UA 信息,各个 View Component 还可以根据用户状态决定自身输出何种模版,如低网速下默认隐藏图片、指定地区输出活动 Banner。
  3. 每个 View Component 的不同模版间可以自行决定是否使用同一份数据、业务逻辑,提供十分灵活的实现方式。

但明显的是,这个方案也是最复杂的,尤其是要考虑一些富交互的应用场景时,Router 和 Controller 也许无法保持这么纯粹。特别对于一些整体性比较强的业务,本身无法被拆分成组件,这种方案也许并不适用;而且对于一些简单的业务,使用这种架构可能不是最佳的选择。

总结

以上几种方案,都各自体现在 MVC 模型中的一个或多个部分,在业务上如果一个方案不满足需求,更可以采取多个方案同时采用的方式。或是可以理解为,业务上的复杂度和交互属性决定了该产品更适合采用哪种多终端适配方案。

对比基于浏览器的响应式设计方案,因为绝大部分终端探测和渲染逻辑迁移到了服务端,所以在 NodeJS 层进行适配无疑带来了更好的性能和用户体验;另外,相对于一些所谓的「云适配」方案带来的转换质量问题,在基于前后端分离的「定制式」方案中也不会存在。前后端分离的适配方案在这些方面有着天然优势。

最后,为了适应更灵活的强大的适配需求,基于前后端分离的适配方案将会面临更多挑战!

【附】相关文章列表

  1. 《前后端分离的思考与实践(一)》
  2. 《前后端分离的思考与实践(二)》
  3. 《前后端分离的思考与实践(三)》
  4. 《前后端分离的思考与实践(四)》

华为内部的Web安全原则

$
0
0
Web安全原则 1.认证模块必须采用防暴力破解机制,例如:验证码或者多次连续尝试登录失败后锁定帐号或IP。 说明:如采用多次连续尝试登录失败后锁定帐号或IP的方式,需支持连续登录失败锁定策略的“允许连续失败的次数”可配置,支持在锁定时间超时后自动解锁。 2.对于每一个需要授权访问的页面或servlet的请求都必须核实用户的会话标识是否合法、用户是否被授权执行这个操作,以防止URL越权。 说明:防止用户通过直接输入URL,进行URL越权,请求并执行一些页面或servlet;建议通过过滤器实现。 3. 登录过程中,往服务器端传递用户名和口令时,必须采用HTTPS安全协议(也就是带服务器端证书的SSL)。只提供本机接入、登录,做设备管理使用的场景暂时不要求。 说明:如果在客户端和服务器间传递如帐号、口令等敏感数据,必须使用带服务器端证书的SSL。由于SSL对服务端的CPU资源消耗很大,实施时必须考虑服务器的承受能力。 4. 对用户的最终认证处理过程必须放到服务器进行。 5. 用户产生的数据必须在服务端进行校验;数据在输出到客户端前必须先进行HTML编码,以防止执行恶意代码、跨站脚本攻击。对于不可信的数据,输出到客户端前必须先进行 HTML 编码。 6. 使用主流Web安全扫描工具扫描Web服务器和Web应用,不存在“高”级别的漏洞。 7. 非嵌入式产品的Web应用,应使用预编译语句PreparedStatement代替直接的语句执行Statement,以防止SQL注入。 数据库安全 外购数据库、开源数据库、华为自研数据库都应进行安全配置,保证不出现安全漏洞。 1.数据库口令禁止使用数据库厂商的缺省口令,且口令复杂度需满足“口令安全要求”。数据库若存在多个默认帐号,须将不使用的帐号禁用或删除。 2. 使用单独的操作系统帐号来运行数据库;数据库中的敏感文件(如:Oracle数据库的init.ora、listener.ora等)需要严格控制访问权限,只能被数据库进程运行帐户和DBA帐户读写;对数据库帐户授予的权限进行严格清晰的划分,所有数据库帐户只能具备执行其任务的最小权限;对于有监听器功能的数据库(如Oracle的listener.ora)需要设置监听器密码或者设置为本地操作系统验证。 3. 使用主流或华为指定的系统扫描软件进行安全扫描,不存在“高”级别的漏洞。 敏感数据保护 系统对敏感数据的存储、传输和处理需保证数据安全,并遵从适用国家和地区的法律和法规要求。 敏感数据定义:包括但不限于口令、银行账号、个人数据(单独使用该数据或者结合其他信息可以识别某个活着的自然人的数据,包括:最终用户姓名、帐号、主叫和被叫号码、通信记录、话单、通信时间、定位数据等)。 1. 口令不允许明文存储在系统中,应该加密保护。在不需要还原口令的场景,必须使用不可逆算法加密。对银行账号等敏感数据的访问要有认证、授权和加密机制。口令文件必须设置访问权限控制,普通用户不能读取或拷贝加密的内容。如果帐户文件/数据中含有口令又必须所有用户可访问,则需将帐户文件/数据与口令文件/数据分开。 注:对于业界第三方主流软硬件(如操作系统、数据库、Web容器)自身提供的口令功能,不受本条限制。 2. 在非信任网络之间进行敏感数据(包括口令,银行帐号,批量个人数据等)的传输须采用安全传输通道或者加密后传输,有标准协议规定除外。 3.禁止使用私有加密算法。 说明: 1) 对称加密算法建议使用:AES192及以上强度; 2) 密钥交换算法建议使用:DH1024; 3) 数字签名算法建议使用:DSA1024、ECDSA192; 4) 非对称算法建议使用:RSA2048、ECC192; 5) HASH(哈希)算法建议使用:SHA256及以上强度; 6) HMAC(基于哈希的消息验证码)算法建议使用:HMAC-SHA256; 4. 用于敏感数据传输加密的密钥,不能硬编码在代码中。 在敏感数据的安全传输上,优先使用业界的标准安全协议(如SSH v2/TLS1.0/SSL3.0/IPSec/SFTP/HTTPS等),并确保密钥可配置;如果是由产品自身实现安全传输过程,则优先使用Diffie-Hellman密钥交换算法,如果使用预置共享密钥等其他方法,也必须保证该密钥可配置和可替换。 5. 禁止在日志、话单等文件中记录口令、银行账号、通信内容等敏感数据; 6. 尽量避免在日志、话单中记录个人数据,如果必须记录个人数据,则所有数据必须进行结构化存储或适合于进行匿名化提取; 1)尽量避免在日志中记录个人数据,如果必须记录,在个人数据之前或之后加统一的标记,以区别于其他非个人数据。 2)尽量避免在话单中记录个人数据,如果必须记录,则话单必须进行结构化存储,字段间必须由统一的分隔符分开,每行的字段按列严格对应。 7. 有个人数据导出功能的产品发布时必须同时提供对个人数据进行过滤或匿名化处理和功能或工具; 8. 严格限制导出功能的权限,对导出功能的使用必须有日志记录。 9. 涉及个人数据的采集/处理的功能须提供安全保护机制(如认证、权限控制、日志记录等),并通过产品资料向客户公开。 10. 在正常业务流程和标准协议之外,禁止出于故障定位目的进行用户精确位置信息定位。如需处理用户精确位置数据,应有华为的明确需求,并在方案设计时,给予用户随时撤回同意的机会。 口令安全策略管理 1. 设置口令时,默认检测口令复杂度,口令至少满足如下要求: 1) 口令长度至少6个字符(特权用户至少8个字符); 2) 口令必须包含如下至少两种字符的组合: -至少一个小写字母; -至少一个大写字母; -至少一个数字; -至少一个特殊字符:`~!@#$%^&*()-_=+\|[{}];:’”,<.>/?  和空格 3) 口令不能和帐号或者帐号的逆序相同; 若设置的口令不符合上述规则,必须进行警告。 2. 系统必须提供锁定用户的机制。可选择如下两种方式之一: 方式一:当重复输入错误口令次数(默认3次,次数系统可以设置)超过系统限制时,系统要锁定该用户。 [...]

Socket的速率控制

$
0
0

(一)、目标

做一个以精确速率向外输出数据的数据源,要完成这个目标,最基础的是:

1、找到一种精确的计时器,在精确的时间范围内控制数据源以指定的速度向外发送数据。

2、通过对套接字选项和线程优先级的设置减少网络因素对发送速度造成的影响,从而提高发送精度,保证数据的实际发送量尽可能的达到指定的理论发送量。

     针对第一个要求,通过寻找到一种时间精度达到微秒级的精确计数器来保证,在硬件支持的情况下可以通过WindowsAPI获取时钟频率以及震荡次数,通过在事件两端分别调用函数得到震荡次数的差值并结合时钟频率可以计算出精确的时间间隔,通过指定的传输速度和精确的延时可以计算出需要发送的数据量。对于第二个要求通过设置数据源所在线程的优先级,以及对套接字选项的设置来减小协议本身对数据传输的过多控制而造成的时延,从而使实际的数据发送量尽可能的接近理论值。

(一)、设置线程优先级

首先在主函数中创建线程函数DWORDWINAPI ThreadProc(LPVOIDlpParam),在线程函数中实现数据源的功能,线程创建成功后对线程优先级进行设置,windows下对线程优先级进行设置的API函数为BOOLWINAPI SetThreadPriority( __in HANDLE hThread, __in intnPriority),其中nPriority定义了线程的优先级。


线程优先级

标志

优先级值

1

IDLE(最低)

THREAD_PRIORITY_IDLE

如果进程优先级为realtime则调整为16,其他情况为1

2

LOWEST(低)

THREAD_PRIORITY_LOWEST

-2(在原有基础上-2)

3

BELOW(低于标准)

THREAD_PRIORITY_BELOW_NORAL

-1(在原有基础上-1)

4

NORMAL(标准)

THREAD_PRIORITY_NORMAL

不变(取进程优先级)

5

ABOVE(高于标准)

THREAD_PRIORITY_ABOVE_NORMAL

+1(在原有基础上+1)

6

HIGHEST(高)

THREAD_PRIORITY_HIGHEST

+2(在原有基础上+2)

7

CRITICAL(最高)

THREAD_PRIORITY_TIME_CRITICAL

如果进程优先级为realtime则调整为31,其他情况为15

将线程优先级设置为最高级,即THREAD_PRIORITY_TIME_CRITICAL,排除本地其他进程的干扰,使得操作系统能够优先调度。

(二)、使用高精度计数器

要实现精确的发送速度,需要有精确的时间控制,在一般情况下使用以下两种方式就满足要求了。一是用SetTimer函数建立一个定时器后,在程序中通过处理有定时器发送到线程的消息队列中的WM_TIMER消息,而得到定时的效果。二是利用GetTickCount函数可以返回计算机启动后的时间,通过两次调用GetTickCount函数,然后控制他们的差值来取得定时的效果。但是以上两种方法都是毫秒级的,在Windows内部有一个高精度运行计时器(high-resolution performance counter),利用它可以获得高精度的定时间隔,其精度可以达到微秒级,其精度与CPU的时钟频率有关。利用API函数QueryPerformanceFrequency能够得到这个定时器的频率。利用API函数QueryPerformanceCounter能够得到定时器的当前值,根据要延时的时间和定时器的频率,能够算出定时器要经过的周期数,循环指定的周期数,就达到了高精度定时的目的。以下是这个API函数的原型:

  • BOOLQueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);

作用:返回硬件支持的高精度计数器的频率。

返回值:如果硬件支持高精度计数器则返回频率值,若硬件不支持返回零。

  • BOOLQueryPerformanceCounter(LARGE_INTEGER*lpPerformanceCount);

    作用:得到高精度计时器的值。

    返回值:如果安装的硬件支持高精度计时器则函数返回计数器的值,如果硬件不支持参数返回零。


采用这种方法的步骤如下:

  1. 首先调用QueryPerformanceFrequency函数取得高精度运行计数器的频率f,单位是每秒多少次(n/s),此数一般很大,在实验主机上此频率值为2241054。

  2. 在循环之外先调用一次QueryPerformanceCounter,得到计数器的值n1,在send之前调用循环调用QueryPerformanceCounter,得到计数器的值n2,两次数值的差值通过计数器的频率f换算成时间间隔,t=(n2-n1)/f。当t达到指定的大小后跳出循环,并将此时的n2赋给n1,重新进入循环计时,send发送的数据大小根据指定的速度与定时时间确定。

这种方法存在一个致命的问题,就是忙等,循环过程会占满CPU资源。因此需要调整一下算法,循环过程引入休眠机制,降低CPU占用,但是休眠机制由于操作系统调度的不确定性,会造成循环结束后的实际时间和预设时间会有差别。没有关系,我们在循环结束后调用QueryPerformanceCounter,重新计算本次和上次循环的精确时间差,运用这个时间来计算send发送的数据大小,这样保证输出的数据始终恒定。


(三)设置套接字选项

SO_SNDBUF:通过设置该选项可以改变发送缓冲区的大小,根据应用需求,设置合理的缓冲区大小能够有效提高数据发送的效率。

TCP_NODELAY:在默认情况下,Windows协议栈发送数据采用Nagle算法,这样做虽然提高了网络的吞吐量,但是实时性却降低了,如果设置了TCP_NODELAY选项,就会禁用Nagle算法,应用程序调用send发送的数据包会被立即投送到网络,而没有延迟。考虑到程序中需要做到精确发送的要求,所以将TCP_NODELAY选项设置为TRUE。

SO_DONTROUTE:通过设置该选项,可以绕过路由表中的网关所在的表项,设置了该套接字选项的socket可以将数据包不经网关发送,而是发往直接相连的主机。该套接字选项的合法的值是整数形式的布尔标志值,这里设为TRUE。

在Windows下,对套接字选项进行设置的API函数为

intsetsockopt(

__in SOCKET s,

__in int level,

__in int optname,

__in const char* optval,

__in int optlen

);

获取当先套接字选项值的API函数为

intgetsockopt(

__in SOCKET s,

__in int level,

__in int optname,

__out char* optval,

__inout int* optlen

);

通过以上两个函数可以查看并重新设置套接字选项的值。


当指定传输速度为64kb/s时,速度的波动控制在了0.002kb/s-0.02kb/s。

当指定传输速度为100kb/s时,速度的波动在0.001kb/s-0.02kb/s以内。

当指定传输速度为1000kb/s时,速度的波动控制在了0.015kb/s-0.2kb/s之内。


思考与改进:

    Socket的通信,数据从发送方到接收方,影响速率的因素很多,主要包括以下几个方面:

1、操作系统调度。

2、其他网络进程对网络带宽资源的争抢。

3、协议本身,如果是TCP协议,则速率控制受到TCP协议流量控制和拥塞控制机制,链路层流量控制机制的影响。

4、协议包在发送和接收途中受到其他网络数据的挤占及丢失等。


   我们只能在发送端的应用层以尽可能精确的速率向传输层送速率,然而数据经过传输层、IP层、链路层到物理层最终到网络上,不可控的因素太多。如果能够在网卡驱动层检测实际的进程数据流量,然后根据网卡流量来反馈控制数据投送量,将更为精确地实现速率的控制,具体实现需要进一步研究。



 
作者:songwater 发表于2014-5-16 21:48:11 原文链接
阅读:75 评论:0 查看评论

移动基于Percona XTRADB Cluster的大数据解决方案

$
0
0
移动基于Percona XTRADB Cluster的大数据解决方案
    
    一、移动的去IOE之旅
    

       最近因为“棱镜门”事件的曝光,引起了国家对信息安全问题的注意,各大行业也开展起来去“IOE”的行动。对移动而言, 一方面是对信息安全的担心,另一方面是对降低成本的考量,对开源体系架构的引入也成为一种现实的方案。

       在互联网行业,MySQL的使用成为主流,但随着Oracle对Sun的收购,MySQL的控制权落入Oracle手中,对MySQL可能闭源的风险也成为业界的共识。 由此,产生了各种MySQL的分支。本文主要对其中的PerconaXtraDB作为数据库方案进行了分析。


     二、 Percona STRADB Cluster分析

Percona XtraDBCluster是MySQL高可用性和可扩展性的解决方案.

Percona XtraDBCluster提供的特性有:

1.同步复制,事务要么在所有节点提交或不提交。

2.多主复制,可以在任意节点进行写操作。

3.在从服务器上并行应用事件,真正意义上的并行复制。

4.节点自动配置。

5.数据一致性,不再是异步复制。

Percona XtraDBCluster完全兼容MySQL和Percona Server,表现在:

1.数据的兼容性

2.应用程序的兼容性:无需更改应用程序

集群特点:

Ø 集群是有节点组成的,推荐配置至少3个节点,但是也可以运行在2个节点上。

Ø 每个节点都是普通的mysql/percona服务器,可以将现有的数据库服务器组成集群,反之,也可以将集群拆分成单独的服务器。

Ø 每个节点都包含完整的数据副本。

优点如下:

1.当执行一个查询时,在本地节点上执行。因为所有数据都在本地,无需远程访问。

2.无需集中管理。可以在任何时间点失去任何节点,但是集群将照常工作。

3.良好的读负载扩展,任意节点都可以查询。

缺点如下:

1.加入新节点,开销大。需要复制完整的数据。

2.不能有效的解决写缩放问题,所有的写操作都将发生在所有节点上。

3.有多少个节点就有多少重复的数据。

架构图如下:

Percona XTRADB Cluster架构图

Percona XtraDB Cluster与MySQL Replication区别在于:

分布式系统的CAP理论:

C—一致性,所有节点的数据一致;

A—可用性,一个或多个节点失效,不影响服务请求;

P—分区容忍性,节点间的连接失效,仍然可以处理请求;

任何一个分布式系统,需要满足这三个中的两个。

 

MySQLReplication: 可用性和分区容忍性;

Percona XtraDBCluster: 一致性和可用性。

因此MySQL Replication并不保证数据的一致性,而Percona XtraDB Cluster提供数据一致性。

 

Percona XtraDBCluster组件:

Percona XtraDB Cluster基于XtraDB的PerconaServer以及包含写复制集补丁。使用Galera 2.xlibrary,事务型应用下的通用的多主同步复制插件。

 

Galera 2.x新特性有:

1.IST(IncrementalState Transfer)增量状态传输。对于WAN特别有用。

2.RSU(RollingSchema Update)旋转更新架构。不会阻止对表进行操作。


     三、基于Percona STRADB Cluster的系统架构
      munion_sysarch
      扩展性架构
  

垂直分片:

->多个Percona XTRADB Cluster;
->Spring配置多个数据源;

水平分片:

->多个PerconaXTRADB Cluster;
->Guzz实现数据水平切片和ORM;

    LVS+Keepalived扩展:
采用三层交换机的等价路由技术最多可配置8台LVS服务器,每台都是Master,LVS配置成DR模式,消除了LVS瓶颈。

     五、总结
     我们准备在移动某互联网项目中使用以上方案,经测算在5000万PV、10000并发的情况下,需要1,828,571tpmC 的服务器一台。按8台LVS服务器,每台转发100个Percona XTRADB集群,每个集群8台服务器计算(集群内每个节点数据相同),相当于8X100=800台服务器。每台服务器tpmC按1,807,347,支持5000万PV计算,即800X5000万=4000000万PV。足够满足该项目的需求了。
     
     以上方案还需在后续的持续运营中进行验证。请大家批评斧正!
     
作者:china_world 发表于2014-5-16 16:38:04 原文链接
阅读:89 评论:0 查看评论

http协议:Web前端-HTTP Cache-control/浏览器缓存(转)

$
0
0

HTTP协议分别在 1.0 / 1.1 两个时代推出了 Expires / Cache-control 两种cache策略,这里我们无需了解全部的细节,无需记住整个RFC内容,但是当我们需要使用HTTP cache策略时,我们需要注意以下细节:
Expires 是HTTP 1.0 那个时代的东西了,目前来看,可以不使用了,因为HTTP 1.0 的user agent占有率在 0.1% 以下(我们主要面向的web浏览器均默认使用HTTP 1.1),Cache-control 是 HTTP 1.1 的新特性,也是我们主要做文章使用cache策略的工具.
Cache策略:
#1 保鲜期only
这个是最最基础的一种策略,只需要在响应头中设定:
Cache-control: max-age=[secs]
[secs]是cache在客户端存活的秒数,例如 Cache-control: max-age=1800 表明cache的时间是半小时,只使用这样一个声明就可以使浏览器能够将这个HTTP响应的内容写入临时目录做cache.
这里是简要过程:

I(1)浏览器第一次请求资源http://test.qq.com/test.cgi
(2)查询临时文件目录发现无cache存储,遂发出请求到web server
(3)web server响应资源,并设定Cache-control:max-age=300
(4)浏览器收到响应将资源呈献给用户的同时,在临时文件目录以"http://test.qq.com/test.cgi"为key缓存这个响应

---5分钟内---
II(1)浏览器再一次请求资源http://test.qq.com/test.cgi
(2)查询临时文件目录发现存在cache存储,检查保鲜期max-age,还未过期,则直接读取之,响应给用户

---5分钟后---
III(1)浏览器再一次请求资源http://test.qq.com/test.cgi
(2)查询临时文件目录发现存在cache存储,检查保鲜期max-age,已经过期,则发请求到web server
#2 保鲜期 + 最后修改时间验证
这里的要素是,在给出保鲜期的同时,给出一个资源的验证方式:
Last-Modified: [UTC time]
[UTC time]标示这个响应资源的最后修改时间,例如 Last-Modified: Mon, 06 Jul 2009 09:21:48 GMT
这个响应头只有配合Cache-control的时候才有实际价值,只是声明校验资源的方式,并不能影响资源的保鲜期时长

利用资源的可校验性,我们可以实现在cache的资源超过保鲜期浏览器再次请求时的304响应,令浏览器再次使用之前的cache

这里是简要过程:
I(1)同#1中I (1)
(2)同#1中I (2)
(3)web server响应资源,并设定
Cache-control:max-age=300
Last-Modified: Mon, 06 Jul 2009 09:21:48 GMT
(4)同#1中I (4)

---5分钟内---
(同#1中II)

<iframe id="aswift_1" style="margin: 0px; padding: 0px; border-width: 0px; vertical-align: baseline; left: 0px; position: absolute; top: 0px;" name="aswift_1" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" width="468" height="60"></iframe>

---5分钟后---
III(1)浏览器再一次请求资源http://test.qq.com/test.cgi
(2) 查询临时文件目录发现存在cache存储,检查保鲜期max-age,已经过期发现资源具有Last-Modified声明,则为请求带上头 If-Modified-Since: Mon, 06 Jul 2009 09:21:48 GMT 发送请求到web server
(3)web server收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对,若最后修改时间较新,说明资源又被改动过,则响应整片资源内容,HTTP 200 (需要整块内容写为包体).若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体),告知浏览器继续使用所保存的cache,(这里当然也可以根据自己的需要决定是200还是304,我们的CGI毕竟是一种原始的实现)

#3 保鲜期 + 自定义标识验证
这里的要素是,在给出保鲜期的同时,给出另一种资源的验证方式:
ETag: [custom flag]
[custom flag]标示这个响应资源的由开发者自己确定的签名验证标识,例如 ETag: "abcdefg",这个响应头只有配合Cache-control的时候才有实际价值,是声明校验资源的方式

ETag的使用为我们实现304响应提供了更多的灵活性,我们可以抛开必须将验证转化成时间格式的限制

这里是简要过程:
I(1)同#1中I (1)
(2)同#1中I (2)
(3)web server响应资源,并设定
Cache-control:max-age=300
ETag: "abcdefg"
(4)同#1中I (4)

---5分钟内---
(同#1中II)

---5分钟后---
III(1)浏览器再一次请求资源http://test.qq.com/test.cgi
(2)查询临时文件目录发现存在cache存储,检查保鲜期max-age,已经过期发现资源具有ETag声明,则为请求带上头 If-None-Match: "abcdefg",发送请求到web server
(3)web server收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,Etag可以是一个版本号,可以是短时间戳,可以是资源校验和(强烈不推荐使用),或者干脆是一个常量(可以干脆拿来做容错)
If-None-Match发来的串与我们的自有值比对,根据我们自己的任何策略算法,可以自由决定如何返回浏览器,304或200
这里有一个使用ETag来做容错的例子(应用列表目前在使用):
(1)我们的每次正常返回都是200
Cache-control: max-age=1800
ETag: "anything"
这里anything是个常量,我们只用来告诉浏览器,cache过期要发带If-None-Match的请求过来
(2) 这样来自客户端的一大部分请求基本上都会带上If-None-Match头,我们的CGI据此可以知道这个请求的客户端是否有cache,此时如果 CGI联系server失败,那么可以直接返回304,驱使客户端使用上一次cache的正确结果,且更新保鲜期max-age为300秒,这样我们实现 了一个基于HTTP cache的容错,如果我们的资源还能实现一套时间戳存储的话,那么我们可以在正常情况下也实现校验后的304,从而节省流量

这里还有一个比较惨的教训,国内www上都没有文献记载,全球业界也只有一点文献可以找到:
IE6 在资源有gzip压缩同时有ETag头时,cache后再次发请求不会带If-None-Match头!!!



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


ITeye推荐




Java SE 6 故障排除指南 – 4、系统崩溃故障排除

$
0
0

崩溃或致命错误导致进程异常终止。有各种可能的理由导致崩溃。例如,崩溃可能是由于HotSpot VM、系统库、Java SE 库或API、程序本地代码、甚至操作系统里的 bug。极端因素如操作系统资源耗尽也可以导致崩溃。

因 HotSpot VM 或 Java SE库代码导致的崩溃是罕见的。本章提供如何检查崩溃的建议。有时候可以变通崩溃直到导致崩溃的源被诊断和修复(也就是可以避开崩溃)。

通常,崩溃的第一步是定位致命错误日志。这是HotSpot VM生成的文本文件。附录C-致命错误日志 解释了如何定位文件和文件的详细描述。

4.1 崩溃样本

本节展示一些样本来说明错误日志是如何用于启发崩溃原因的。

4.1 测定哪里发生崩溃

错误日志头显示了有问题的帧。见 C.3 格式头。

如果顶层帧是本地帧且不是操作系统本地帧,这表明问题可能发生在本地库,而不是在JVM里。解决崩溃的第一步是研究本地库发生崩溃的源。有三个选择,取决于本地库的源。

如果本地库是由你的程序提供,研究你的本地库的源代码。选项 -Xcheck:jni可以帮助查找本地 bug。见 B.2.1 -Xcheck:jni选项。

如果你的程序使用的本地库是由其他供应商提供,报告bug,提供致命错误日志信息。

4.1.2 在本地代码里崩溃

如果致命错误日志表明崩溃是在本地库,那么可能有bug在本地代码或JNI库代码。崩溃当然也可以被其他因素导致,但分析库和任何core文件或崩溃转储是个好的开始点。例如,考虑下嘛的致命错误日志头:

# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  SIGSEGV (0xb) at pc=0x417789d7, pid=21139, tid=1024
#
# Java VM: Java HotSpot(TM) Server VM (6-beta2-b63 mixed mode)
# Problematic frame:
# C  [libApplication.so+0x9d7]

在这种情况下线程在 libApplication.so里执行时发生 SIGSEGV

有时候本地库里的bug显示它自己是在JVM代码里崩溃。考虑下面的崩溃,线程 JavaThread_thread_in_vm状态(意味着它正在执行 JVM 代码)失败:

# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x08083d77, pid=3700, tid=2896
#
# Java VM: Java HotSpot(TM) Client VM (1.5-internal mixed mode)
# Problematic frame:
# V  [jvm.dll+0x83d77]

---------------  T H R E A D  ---------------

Current thread (0x00036960):  JavaThread "main" [_thread_in_vm, id=2896]
 :
Stack: [0x00040000,0x00080000),  sp=0x0007f9f8,  free space=254k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [jvm.dll+0x83d77]
C  [App.dll+0x1047]          <========= C/native frame
j  Test.foo()V+0
j  Test.main([Ljava/lang/String;)V+0
v  ~StubRoutines::call_stub
V  [jvm.dll+0x80f13]
V  [jvm.dll+0xd3842]
V  [jvm.dll+0x80de4]
V  [jvm.dll+0x87cd2]
C  [java.exe+0x14c0]
C  [java.exe+0x64cd]
C  [kernel32.dll+0x214c7]
 :

在这种情况下,栈轨迹显示了 App.dll里的本地例程调用到了VM里(可能用JNI)。

如果你在本地程序库遇到崩溃,如果可以,你可能能够关联本地调试器到core文件或崩溃转储。取决于操作系统,本地调试器是 dbx, gdb 或 windbg。

另一个方法是运行带 -Xcheck:jni选项运行,这个选项不保证找出JNI代码里的所有问题,但它可以帮助识别问题issue的显著数字。

如果发生崩溃的本地库是JRE的一部分,那么可能是你遇到了一个库或API bug。

4.1.3 由于栈溢出崩溃

Java语言代码里的栈溢出通常将导致线程抛出 java.lang.StackOverflowError。另一方面, C and C++ write past the end of the stack and provoke a stack overflow。这个致命错误将导致进程终止。

在HotSpot实现里,Java方法与C/C++本地代码共享栈帧,也就是用户本地代码和VM自身。Java方法生成代码来检查,距栈结束有个固定距离的可得栈空间,这样可以调用本地代码而不会超出栈空间。这个朝向栈结束的距离叫作 “阴影页,Shadow Pages”。阴影页的大小在 3-20个(内存)页之间,取决于平台。这个距离是可调的,这样程序的本地代码需要多于默认距离的本地代码可以增加阴影页的大小。增加阴影页的选项是 -XX:StackShadowPages= nn大于平台的默认栈阴影页。

如果程序碰到段错误且没有core文件、或致命错误日志文件、或 在Windows 上的 STACK_OVERFLOW_ERROR、或 消息 “ An irrecoverable stack overflow has occurred ”,这表明 StackShadowPages的值被超过了,需要更多空间。

如果你增加 StackShadowPages的值,你可能也需要增加线程默认栈大小,用 -Xss参数。增加线程默认栈大小可能需要减少被创建的线程,所以小心选择线程栈的大小。不同平台的栈大小从 256k - 1024k不等。

下面是来自致命错误日志的一个段错误,在Windows系统上,线程在本地代码里激起一个栈溢出:

# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x10001011, pid=296, tid=2940
#
# Java VM: Java HotSpot(TM) Client VM (1.6-internal mixed mode, sharing)
# Problematic frame:
# C  [App.dll+0x1011]
#

---------------  T H R E A D  ---------------

Current thread (0x000367c0):  JavaThread "main" [_thread_in_native, id=2940]
:
Stack: [0x00040000,0x00080000),  sp=0x00041000,  free space=4k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [App.dll+0x1011]
C  [App.dll+0x1020]
C  [App.dll+0x1020]
:
C  [App.dll+0x1020]
C  [App.dll+0x1020]
...<more frames>...

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  Test.foo()V+0
j  Test.main([Ljava/lang/String;)V+0
v  ~StubRoutines::call_stub

注意上面输出的下面信息:

  • 异常是 EXCEPTION_STACK_OVERFLOW
  • 线程的状态是 _thread_in_native,这意味着线程在执行本地或JNI代码。
  • 栈信息里的空闲空间只有 4k(Windows 系统上单个页面)。另外栈指针sp是 0x00041000 ,非常接近于栈结束 0x00040000。
  • 本地帧输出显示,这种情况下问题是本地递归函数。
  • 输出标记 ...<more frames>..表明了存在额外的帧,但没有输出。输出限制在 100 帧。

4.1.4 HotSpot 编译线程里崩溃

如果致命错误日志输出显示 Current thread是命名为 CompilerThread0, CompilerThread1AdapterCompilerJavaThread,这很可能是你碰到一个编译器bug。在这种情况下,你可能需要临时绕过问题,通过切换编译器或排除编译激起崩溃的方法。

4.1.5 在已编译代码里崩溃

如果崩溃发生在已编译代码里,可能是你遇到一个编译器bug,导致生成不正确的代码。你可以识别已编译代码里的崩溃,如果有问题的帧标记了编码 J(意味着一个已编译Java帧)。下面是这类崩溃的示例:

# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  SIGSEGV (0xb) at pc=0x0000002a99eb0c10, pid=6106, tid=278546
#
# Java VM: Java HotSpot(TM) 64-Bit Server VM (1.6.0-beta-b51 mixed mode)
# Problematic frame:
# J  org.foobar.Scanner.body()V
#
:
Stack: [0x0000002aea560000,0x0000002aea660000),  sp=0x0000002aea65ddf0,
  free space=1015k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
J  org.foobar.Scanner.body()V

[error occurred during error reporting, step 120, id 0xb]

注意完整的线程栈不可得。输出行 error occurred during error reporting意味着在获取栈轨迹时出现错误(这个例子里可能是栈腐化corruption)。

可能需要临时绕过问题,通过切换编译器(如用HotSpot Client VM替代HotSpot Server VM,或相反)或排除编译激起崩溃的方法。在这个特定例子里,可能不能切换编译器从64-bit Server VM 切换到 32-bit Client VM。(Client VM 没有64位?)

4.1.6 VMThread 里崩溃

如果致命日志输出显示 Current threadVMThread,查找 THREAD节里包含 VM_Operation的行。VMThread 是HotSpot VM里的特殊线程。它在VM里执行特殊任务,如垃圾回收(GC)。 VM_Operation暗示是垃圾回收操作,那么你可能遇到了堆腐化的问题。

崩溃也可能是个GC问题,但它同样也可以是其他,使堆里的对象引用处于不一致或不正确状态。在这种情况下,收集尽可能多的环境信息,尝试可能的变通方案。如果问题是GC有关的,你可能可以临时绕过问题,通过改变GC配置。

4.2 找出变通方案

如果关键程序发生崩溃,且崩溃表现出是由于HotSpot VM里的bug导致,那么它值得快速找出一个临时变通方案。本节的目标是启发可能的变通方案。

注意:即使本节的变通方案成功消除了崩溃,变通方案也不是问题的修复,仅仅是临时解决方案。 Submit a support call or bug report with the original configuration that emonstrated the issue.

4.2.1 HotSpot 编译器线程或已编译代码里崩溃

如果致命错误日志表明崩溃发生在编译器线程,可能是你遇到了一个编译器bug。类似地,如果崩溃发生在已编译代码里,可能是编译器生成了不正确代码。

对于 HotSpot Client VM,编译器线程以 CompilerThread0出现在错误日志里。 HotSpot Server VM 有多个编译器线程,在错误日志文件里表现为 CompilerThread0, CompilerThread1, AdapterThread

下面是遇到编译器bug的错误日志片段,bug在Java SE 5.0开发中修复。日志文件显示使用了 HotSpot Server VM,崩溃在 CompilerThread1里发生。另外,日志文件显示了 Current CompileTask是在编译 java.lang.Thread.setPriority方法。

# An unexpected error has been detected by HotSpot Virtual Machine:
#
:
# Java VM: Java HotSpot(TM) Server VM (1.5-internal-debug mixed mode)
:
---------------  T H R E A D  ---------------

Current thread (0x001e9350): JavaThread "CompilerThread1" daemon [_thread_in_vm, id=20]

Stack: [0xb2500000,0xb2580000),  sp=0xb257e500,  free space=505k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0xc3b13c]
:

Current CompileTask:
opto: 11      java.lang.Thread.setPriority(I)V (53 bytes)

---------------  P R O C E S S  ---------------

Java Threads: ( => current thread )
  0x00229930 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=21]
=>0x001e9350 JavaThread "CompilerThread1" daemon [_thread_in_vm, id=20]
 :

这种情况有两个潜在的变通方案:

  • 暴力方法:修改配置,这样程序以 -client选项来指定 HotSpot Client VM 来运行。
  • 假设bug只发生在编译 setPriority方法,那么从排除编译此方法。

第一种方法(使用 -client选项)对有些环境可能是微不足道的。对于其它,可能是困难的,如果配置是复杂的或配置VM的命令行不可访问。通常,从 HotSpot Server VM 切换到 HotSpot Client VM 会降低程序的峰值性能。取决于环境,这可能是可接受的,直到问题被诊断和修复。

第二种方法要求在程序的工作目录下创建文件 .hotspot_compiler,文件示例:

exclude    java/lang/Thread    setPriority

通常这个文件的格式是 exclude CLASS METHOD,CLASS 是全路径类名,METHOD是方法的名字,构造器方法指定为 <init>,静态初始化器指定为 <clinit>

注意.hotspot_compiler文件不是受支持的。

一旦程序重新启动,编译器将不再尝试编译 .hotspot_compiler文件里列出的任何方法。

为了核实HotSpot VM正确定位和处理了 .hotspot_compiler文件,在运行时查找下面的日志信息。注意文件名是用句点分隔,而不是斜线。

### Excluding compile:    java.lang.Thread::setPriority

4.2.2 垃圾回收时崩溃

如果崩溃在垃圾回收时发生,那么致命错误日志报告正在进行一个 VM_Operation。为了讨论的目的,假设最主要的并发GC( -XX:+UseConcMarkSweep)未使用。 VM_Operation显示在日志的 THREAD区域,表明下面的情境之一:

  • Generation collection for allocation
  • Full generation collection
  • Parallel gc failed allocation
  • Parallel gc failed permanent allocation
  • Parallel gc system gc

日志里报告的当前线程最可能是 VMThread。这是特殊线程用于执行 HotSpot VM 里的特定任务。下面的致命错误日志片段显示了串行垃圾收集器崩溃的一个举例:

---------------  T H R E A D  ---------------

Current thread (0x002cb720):  VMThread [id=3252]

siginfo: ExceptionCode=0xc0000005, reading address 0x00000000

Registers:
EAX=0x0000000a, EBX=0x00000001, ECX=0x00289530, EDX=0x00000000
ESP=0x02aefc2c, EBP=0x02aefc44, ESI=0x00289530, EDI=0x00289530
EIP=0x0806d17a, EFLAGS=0x00010246

Top of Stack: (sp=0x02aefc2c)
0x02aefc2c:   00289530 081641e8 00000001 0806e4b8
0x02aefc3c:   00000001 00000000 02aefc9c 0806e4c5
0x02aefc4c:   081641e8 081641c8 00000001 00289530
0x02aefc5c:   00000000 00000000 00000001 00000001
0x02aefc6c:   00000000 00000000 00000000 08072a9e
0x02aefc7c:   00000000 00000000 00000000 00035378
0x02aefc8c:   00035378 00280d88 00280d88 147fee00
0x02aefc9c:   02aefce8 0806e0f5 00000001 00289530
Instructions: (pc=0x0806d17a)
0x0806d16a:   15 08 83 3d c0 be 15 08 05 53 56 57 8b f1 75 0f
0x0806d17a:   0f be 05 00 00 00 00 83 c0 05 a3 c0 be 15 08 8b 

Stack: [0x02ab0000,0x02af0000),  sp=0x02aefc2c,  free space=255k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [jvm.dll+0x6d17a]
V  [jvm.dll+0x6e4c5]
V  [jvm.dll+0x6e0f5]
V  [jvm.dll+0x71771]
V  [jvm.dll+0xfd1d3]
V  [jvm.dll+0x6cd99]
V  [jvm.dll+0x504bf]
V  [jvm.dll+0x6cf4b]
V  [jvm.dll+0x1175d5]
V  [jvm.dll+0x1170a0]
V  [jvm.dll+0x11728f]
V  [jvm.dll+0x116fd5]
C  [MSVCRT.dll+0x27fb8]
C  [kernel32.dll+0x1d33b]

VM_Operation (0x0373f71c): generation collection for allocation, mode:
 safepoint, requested by thread 0x02db7108

注意:在垃圾回收时发生崩溃不一定表示垃圾收集实现有bug。它也可以表明编译器或运行时bug或其他问题。

如果你在垃圾收集时得到一个重复的崩溃,可以尝试下面的临时解决方案:

  • 切换 GC 配置。例如,如果你使用串行收集器,尝试使用吞吐量优先收集器,或相反。
  • 如果你使用 HotSpot Server VM,尝试 HotSpot Client VM。

如果你不确定在使用的垃圾收集器是哪个,可以用 jmap 实用程序从core 文件获取堆信息,如果core文件可得。通常,如果GC配置没有在命令行指定,在Windows上将使用串行收集器。在Solaris OS和Linux上,取决于机器配置。如果机器有至少2GB内存和至少2个处理器,将使用吞吐量收集器(并行GC)。对于小一点的机器,默认使用串行收集器。 -XX:+UseSerialGC指定使用串行收集器, -XX:+UseParallelGC选项选择 吞吐量收集器。

4.2.3 类数据共享,Class data sharing

这个东西在HotSpot VM里限制较多,TODO。

4.3 Microsoft Visual C++ Version Considerations

TODO

每个程序员都应该知道的一些访问时延值

$
0
0

继续翻老文章,首先是从Jeff Dean的一篇PPT演示文稿中看到了这一页,觉得对这些数值形成概念还是挺有用的,就转过来了,经常拿出来看一下,最终能做到熟记于心,并能在理论分析和程序设计时用上。

numbers_everyone_should_know

尽管是大神给出的数据,也还是想考证一下数字的出处,于是又在搜索引擎中找了一下。找到了对这个数据比较好的总结和归纳: 链接。还有一处: 链接2。也附一下传说中的大神的介绍: Jeff Dean。坊间关于Jeff的传说也是非常多了,诸如,Jeff这个月写的代码量下降了,是因为他换了一幅新的键盘。

黄海波嫖娼为何没有引来骂声一片?

$
0
0

记者今天获悉,著名男演员黄海波因嫖娼被北京警方行政拘留。据了解,昨天晚上八点,北京警方获得线索称,在本市某酒店有人从事卖淫嫖娼活动。随后民警出动赶往现场,将黄海波和一女子当场抓获。经审讯,黄海波对嫖娼一事供认不讳。(5月16日《京华时报》)

知名演员黄海波嫖娼被抓的消息一传出,便引来人们惊讶声一片。对于黄海波,在公众的印象就是一个居家好男人。这一印象,自然来源于其所扮演的角色。无论是《咱们结婚吧》还是《媳妇的美好时代》,黄海波都给人留下了深刻的印象,以至于一些人将黄海波称之为“国民老公”。

正因为这一形象在人们心目中形成了固定认知,所以当人们一得知黄海波嫖娼的消息后,其惊讶也就可想而知。正如文章的出轨一样,由于“好男人”的形象已经被固定,一旦这个好男人做出了有违好男人之事,人们也就必然为之震惊,亦会感到有些不可思议!

这种形象的颠覆,其实还是源于人们喜欢将银屏形象和生活中人物结合在一起的缘故。事实上,银屏中的人物和现实中的人物往往是两码事。一个在银屏上扮演正义者,并不代表其在生活中就一定是个正义者;反之一个在银屏上扮演的是恶人,在生活却不一定是恶人,还会是一个大善人。可见,将银屏人物和现实人物结合一起的观念还需我们及时纠正过来。

而此次黄海波的嫖娼事件,人们的表现尽管是惊讶,但与之前对文章的一致谴责则是大不相同。毕竟,文章那是出轨,并且还是在自己妻子怀孕期间出轨;可黄海波没有妻子,也没有女朋友,就根本不存在文章那般情况。

一个没有妻子没有女友的男人去酒店嫖娼了,当然不是什么出轨,其所作所为若从“饮食男女”的角度出发则是实属正常。因此,有些网友还直赞黄海波是“真男人”,不玩潜规则。要知道,和黄海波想玩潜规则的女生可是大有人在。黄海波不玩那一套,间接证明了黄海波不是那种玩弄女性之人。

通过网友们的议论,我们亦看出网友们对于黄海波嫖娼事件是很理性的。人们并没有表现出对文章那般的一致谴责,而是有着足够的宽容和理解!不单是男网友,便是女网友也是如此。可见人们对此类事情已经不再像过去那样,只要一出现便是“义愤填膺”,便是站在某种道德的高度给予对方以“痛击”。

人们的宽容和理解,不仅是基于人性的本能需求,也是基于黄海波的个人情况而言。一个无妻无女友的正常男人,做出此等事情并不是不可原谅的。不过,黄海波嫖娼行为亦不值得我们人为去拔高,至于对其大加赞扬更是有些荒唐。因为黄海波是公众人物,而作为一名公众人物就应该遵纪守法,就应该以健康良好的形象示人。否则,不仅有损其个人形象,其所带来的负面效应也会影响到整个社会和他人。


  青春就应该这样绽放   游戏测试:三国时期谁是你最好的兄弟!!   你不得不信的星座秘密

浅谈Linux的内存管理机制(转)

$
0
0

一 物理内存和虚拟内存

我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念。
物理内存就是系统硬件提供的内存大小,是真正的内存,相对于物理内存,在linux下还有一个虚拟内存的概念,虚拟内存就是为了满足物理内存的不足而提出的策略,它是利用磁盘空间虚拟出的一块逻辑内存,用作虚拟内存的磁盘空间被称为交换空间(Swap Space)。
作为物理内存的扩展,linux会在物理内存不足时,使用交换分区的虚拟内存,更详细的说,就是内核会将暂时不用的内存块信息写到交换空间,这样以来,物理内存得到了释放,这块内存就可以用于其它目的,当需要用到原始的内容时,这些信息会被重新从交换空间读入物理内存。
linux的内存管理采取的是分页存取机制,为了保证物理内存能得到充分的利用,内核会在适当的时候将物理内存中不经常使用的数据块自动交换到虚拟内存中,而将经常使用的信息保留到物理内存。
要深入了解linux内存运行机制,需要知道下面提到的几个方面:
首先,Linux系统会不时的进行页面交换操作,以保持尽可能多的空闲物理内存,即使并没有什么事情需要内存,Linux也会交换出暂时不用的内存页面。这可以避免等待交换所需的时间。
其次,linux进行页面交换是有条件的,不是所有页面在不用时都交换到虚拟内存,linux内核根据”最近最经常使用“算法,仅仅将一些不经常使用的页面文件交换到虚拟内存,有时我们会看到这么一个现象:linux物理内存还有很多,但是交换空间也使用了很多。其实,这并不奇怪,例如,一个占用很大内存的进程运行时,需要耗费很多内存资源,此时就会有一些不常用页面文件被交换到虚拟内存中,但后来这个占用很多内存资源的进程结束并释放了很多内存时,刚才被交换出去的页面文件并不会自动的交换进物理内存,除非有这个必要,那么此刻系统物理内存就会空闲很多,同时交换空间也在被使用,就出现了刚才所说的现象了。关于这点,不用担心什么,只要知道是怎么一回事就可以了。
最后,交换空间的页面在使用时会首先被交换到物理内存,如果此时没有足够的物理内存来容纳这些页面,它们又会被马上交换出去,如此以来,虚拟内存中可能没有足够空间来存储这些交换页面,最终会导致linux出现假死机、服务异常等问题,linux虽然可以在一段时间内自行恢复,但是恢复后的系统已经基本不可用了。
因此,合理规划和设计linux内存的使用,是非常重要的.

二 内存的监控
作为一名linux系统管理员,监控内存的使用状态是非常重要的,通过监控有助于了解内存的使用状态,比如内存占用是否正常,内存是否紧缺等等,监控内存最常使用的命令有free、top等,下面是某个系统free的输出:
[haixigov@WEBServer ~]$ free
total used free shared buffers cached
Mem: 16402432 16360492 41940 0 465404 12714880
-/+ buffers/cache: 3180208 13222224
Swap: 8193108 264 8192844
我们解释下输出结果中每个选项的含义:
首先是第一行:
 total:物理内存的总大小。
 used:已经使用的物理内存多小。
 free:空闲的物理内存值。
 shared:多个进程共享的内存值。
 buffers/cached:磁盘缓存的大小。
第二行Mem:代表物理内存使用情况。
第三行(-/+ buffers/cached):代表磁盘缓存使用状态。
第四行:Swap表示交换空间内存使用状态。
free命令输出的内存状态,可以通过两个角度来查看:一个是从内核的角度来看,一个是从应用层的角度来看的。

1.从内核的角度来查看内存的状态
就是内核目前可以直接分配到,不需要额外的操作,即为上面free命令输出中第二行Mem项的值,可以看出,此系统物理内存有16G,空闲的内存只有41940K,也就是40M多一点,我们来做一个这样的计算:
16402432-16360492=41940
其实就是总的物理内存减去已经使用的物理内存得到的就是空闲的物理内存大小,注意这里的可用内存值41940并不包含处于buffers和cached状态的内存大小。
如果你认为这个系统空闲内存太小,那你就错了,实际上,内核完全控制着内存的使用情况,linux会在需要内存的时候,或在系统运行逐步推进时,将buffers和cached状态的内存变为free状态的内存,以供系统使用。

2.从应用层的角度来看系统内存的使用状态
也就是linux上运行的应用程序可以使用的内存大小,即free命令第三行“(-/+ buffers/cached)”的输出,可以看到,此系统已经使用的内存才3180208K,而空闲的内存达到13222224K,继续做这样一个计算:
41940+(465404+12714880)=13222224
通过这个等式可知,应用程序可用的物理内存值是Mem项的free值加上buffers和cached值之和,也就是说,这个free值是包括buffers和cached项大小的,
对于应用程序来说,buffers/cached占有的内存是可用的,因为buffers/cached是为了提高文件读取的性能,当应用程序需要用到内存的时候,buffers/cached会很快地被回收,以供应用程序使用。

3.buffers与cached的异同
 在 Linux 操作系统中,当应用程序需要读取文件中的数据时,操作系统先分配一些内存,将数据从磁盘读入到这些内存中,然后再将数据分发给应用程序;当需要往文件中写数据时,操作系统先分配内存接收用户数据,然后再将数据从内存写到磁盘上。然而,如果有大量数据需要从磁盘读取到内存或者由内存写入磁盘时,系统的读写性能就变得非常低下,因为无论是从磁盘读数据,还是写数据到磁盘,都是一个很消耗时间和资源的过程,在这种情况下,linux引入了buffers和cached机制。
buffers与cached都是内存操作,用来保存系统曾经打开过的文件以及文件属性信息,这样当操作系统需要读取某些文件时,会首先在buffers与cached内存区查找,如果找到,直接读出传送给应用程序,如果没有找到需要数据,才从磁盘读取,这就是操作系统的缓存机制,通过缓存,大大提高了操作系统的性能。但buffers与cached缓冲的内容却是不同的。
buffers是用来缓冲块设备做的,它只记录文件系统的元数据(metadata)以及 tracking in-flight pages,而cached是用来给文件做缓冲。更通俗一点说:buffers主要用来存放目录里面有什么内容,文件的属性以及权限等等。而cached直接用来记忆我们打开过的文件和程序。
为了验证我们的结论是否正确,可以通过vi打开一个非常大的文件,看看cached的变化,然后再次vi这个文件,感觉一下两次打开的速度有何异同,是不是第二次打开的速度明显快于第一次呢?
接着执行下面的命令:
find /* -name *.conf
看看buffers的值是否变化,然后重复执行find命令,看看两次显示速度有何不同。
Linux操作系统的内存运行原理,很大程度上是根据服务器的需求来设计的,例如系统的缓冲机制会把经常使用到的文件和数据缓存在cached中,linux总是在力求缓存更多的数据和信息,这样再次需要这些数据时可以直接从内存中取,而不需要有一个漫长的磁盘操作,这种设计思路提高了系统的整体性能。

三 交换空间swap的使用
虽然现在的内存已经变得非常廉价,但是swap仍然有很大的使用价值,合理的规划和使用swap分区,对系统稳定运行至关重要。Linux下可以使用文件系统中的一个常规文件或者一个独立分区作为交换空间使用。同时linux允许使用多个交换分区或者交换文件。

1.创建swap交换空间
创建交换空间所需的交换文件是一个普通的文件,但是,创建交换文件与创建普通文件不同,必须通过dd命令来完成,同时这个文件必须位于本地硬盘上,不能在网络文件系统(NFS)上创建swap交换文件。例如:
[root@localhost ~]# dd if=/dev/zero of=/data/swapfile bs=1024 count=65536
65536+0 records in
65536+0 records out
这样就创建一个有连续空间的交换文件,大小为60M左右,关于dd命令做简单的讲述:
if=输入文件,或者设备名称。
of=输出文件或者设备名称。
ibs=bytes 表示一次读入bytes 个字节(即一个块大小为 bytes 个字节)。
obs=bytes 表示一次写bytes 个字节(即一个块大小为 bytes 个字节)。
bs=bytes,同时设置读写块的大小,以bytes为单位,此参数可代替 ibs 和 obs。
count=blocks 仅拷贝blocks个块。
skip=blocks 表示从输入文件开头跳过 blocks 个块后再开始复制。
seek=blocks表示从输出文件开头跳过 blocks 个块后再开始复制。(通常只有当输出文件是磁盘或磁带时才有效)
这里的输入设备/dev/zero代表一个输出永远为0的设备文件,使用它作输入可以得到全为空的文件。

2.激活和使用swap
首先通过mkswap命令指定作为交换空间的设备或者文件:
[root@localhost ~]#mkswap /data/swapfile
Setting up swapspace version 1, size = 67104 kB
[root@localhost backup]# free
total used free shared buffers cached
Mem: 2066632 1998188 68444 0 26160 1588044
-/+ buffers/cache: 383984 1682648
Swap: 4088500 101036 3987464
从上面输出可知,我们指定了一个67104 kB的交换空间,而此时新建的交换空间还未被使用,下面简单介绍下mkswap命令,mkswap的一般使用格式为:
mkswap [参数] [设备名称或文件][交换区大小]
参数:
-c:建立交换区前,先检查是否有损坏的区块。
-v0:建立旧式交换区,此为预设值。
-v1:建立新式交换区。
交换区大小:指定交换区的大小,单位为1024字节。
设置交换分区后,接着通过swapon命令激活swap:
[root@localhost ~]#/usr/sbin/swapon /data/swapfile
[root@localhost backup]# free
total used free shared buffers cached
Mem: 2066632 1997668 68964 0 27404 1588880
-/+ buffers/cache: 381384 1685248
Swap: 4154028 100976 4053052
通过free命令可以看出,swap大小已经由4088500k变为4154028k,相差的值是60M左右,刚好等于我们增加的一个交换文件大小,这说明新增的交换分区已经可以使用了,但是如果linux重启,那么新增的swap空间将变得不可用,因此需要在/etc/fstab中添加自动加载设置:
/data/swapfile none swap sw 0 0
如此以来,linux在重启后就可以实现自动加载swap分区了。其实linux在启动过程中会执行“swapon -a”命令,此命令会加载列在/etc/fstab中的所有交换空间。

3.移除swap
通过swapoff即可移除一个交换空间
[root@localhost ~]#/usr/sbin/swapoff /data/swapfile
其实也可以通过“swapoff -a”移除在/etc/fstab中定义的所有交换空间,这里的“swapoff -a”与上面提到的“swapon -a”对应。执行“swapoff -a”后,free命令输出如下:
[root@localhost backup]# free
total used free shared buffers cached
Mem: 2066632 2048724 17908 0 30352 1642748
-/+ buffers/cache: 375624 1691008
Swap: 0 0 0

本文出自 “ 技术成就梦想” 博客,请务必保留此出处 http://ixdba.blog.51cto.com/2895551/541355



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


ITeye推荐



8个网络安全开源框架

$
0
0

能帮助构建、操作安全系统的开源项目多到数不清,尤其是随着对工具的安全监控和事件反应的要求不断加强的情况下,开源安全软件不得不提高自己的性能。

下面为你介绍10款开源的安全软件。

1. Bro

Bro 是一款很强大的框架,用于网络分析和安全监控,它和常见的IDS有所不同。与通用的网络传输分析工具相比它侧重于网络安全监控和提供一个完整的平台化工具。 主页| 下载

2. Moloch

moloch是一个开源的、大型的IPv4 PCAP,用于索引和收集数据库系统。Moloch目的并不是替换IDS引擎,而是它们一起工作,以标准PCAP的格式来存储和索引所有网络流量,提供快速访问。 主页

3. OSSEC

OSSEC是一个完整的平台用来监控和控制用户系统。它将HIDS、log monitoring 和 SIM/SIEM 以一种简单的、强大的、开源的方式混合在一起。 主页| 下载

4. MIDAS

MIDAS是一个用来开发Mac入侵检测分析系统的框架,主要是基于 Etsy 和 Facebook 安全团队之间的工作和协作讨论而创建出来的。这个资源库提供一个模块化框架和一些协助工具,同时还有一个案例模块用来检测修改常见的OS X的持久性机制。 主页

5. OSXAuditor

OSXAuditor是一个免费的Mac OS X计算机取证工具,这个工具显示分析内核扩展、用户下载的文件等等,然后是提取用户的隔离文件、访问历史等等,最后就可以确认文件的可信度。 主页

6. Cuckoo

Cuckoo 是开源的、动态恶意软件分析系统里的领导者,它能发现并收集系统里的恶意程序,然后将这些恶意软件粉碎。 主页|下载

7. Brakeman

Brakeman是一个静态的分析工具,它可以检测Ruby on Rails应用程序的安全漏洞。 主页| 下载

8. MozDef (The Mozilla Defense Platform) 

MozDef 的设计灵感来自于网络攻击者所使用的大量的工具,像metasploit、armitage、lair、dradis 和其它的程序套件的确是用来帮助攻击者进行协调用的,分享情报并实时的调整攻击目标。但是防御者通常被限制访问Wikis、售票系统和手动操作跟踪附加在安全信息事件管理(SIEM)系统上的数据库。MozDef (The Mozilla Defense Platform) 正在寻找自动处理安全事件处理程序,并实时调用事件处理程序。 主页| 下载

原文来自: CSDN

查找(二)简单清晰的B树、Trie树详解

$
0
0


查找(二)

 

散列表


散列表是普通数组概念的推广。由于对普通数组可以直接寻址,使得能在O(1)时间内访问数组中的任意位置。在散列表中,不是直接把关键字作为数组的下标,而是根据关键字计算出相应的下标。

使用散列的查找算法分为两步。第一步是用 散列函数将被查找的键转化为数组的一个索引。

我们需要面对两个或多个键都会散列到相同的索引值的情况。因此,第二步就是一个 处理碰撞冲突的过程,由两种经典解决碰撞的方法:拉链法和线性探测法。

 

散列表是算法在时间和空间上作出权衡的经典例子。

如果没有内存限制,我们可以直接将键作为(可能是一个超大的)数组的索引,那么所有查找操作只需要访问内存一次即可完成。但这种情况不会经常出现,因此当键很多时需要的内存太大。

另一方面,如果没有时间限制,我们可以使用无序数组并进行顺序查找,这样就只需要很少的内存。而散列表则使用了适度的空间和时间并在这两个极端之间找到了一种平衡。

 

●散列函数


我们面对的第一个问题就是散列函数的计算,这个过程会将键转化为数组的索引。我们要找的散列函数应该易于计算并且能够均匀分布所有的键。

散列函数和键的类型有关,对于每种类型的键我们都需要一个与之对应的散列函数。

 

正整数

将整数散列最常用的方法就是 除留余数法。我们选择大小为 素数M的数组,对于任意正整数k,计算k除以M的余数。(如果M不是素数,我们可能无法利用键中包含的所有信息,这可能导致我们无法均匀地散列值。)

 

浮点数

将键表示为二进制数,然后再使用除留余数法。(让浮点数的各个位都起作用)(Java就是这么做的)

 

字符串

除留余数法也可以处理较长的键,例如字符串,我们只需将它们当做大整数即可。即相当于 将字符串当做一个N位的R进制值,将它除以M并取余

 

·····软缓存

如果散列值的计算很耗时,那么我们或许可以将每个键的散列值缓存起来,即在每个键中使用一个hash变量来保存它的hashCode()返回值。

 

 

●基于拉链法的散列表


一个散列函数能够将键转化为数组索引。散列算法的第二步是碰撞处理,也就是处理两个或多个键的散列值相同的情况。

拉链法:将大小为M的数组中的每个元素指向一条链表,链表中的每个结点都存储了散列值为该元素的索引的键值对。

查找分两步:首先根据散列值找到对应的链表,然后沿着链表顺序查找相应的键。

 


拉链法在实际情况中很有用,因为每条链表确实都大约含有N/M个键值对。

基于拉链法的散列表的实现简单。在键的顺序并不重要的应用中,它可能是最快的(也是使用最广泛的)符号表实现。

 

●基于线性探测法的散列表


实现散列表的另一种方式就是用大小为M的数组保存N个键值对,其中M>N。我们需要依靠数组中的空位解决碰撞冲突。基于这种策略的所有方法被统称为开放地址散列表。

开放地址散列表中最简单的方法叫做 线性探测法:当碰撞发生时,我们直接检查散列表中的下一个位置(将索引值加1),如果不同则继续查找,直到找到该键或遇到一个空元素。

 

(开放地址类的散列表的核心思想是:与其将内存用作链表,不如将它们作为在散列表的空元素。这些空元素可以作为查找结束的标志。)

 

特点:散列最主要的目的在于均匀地将键散布开来,因此在计算散列后键的顺序信息就丢失了,如果你需要快速找到最大或最小的键,或是查找某个范围内的键,散列表都不是合适的选择。

 

【应用举例】

海量处理

给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?

答:

可以估计每个文件安的大小为5G×64=320G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。

  分而治之/hash映射

遍历文件a,对每个url求取,然后根据所取得的值将url分别存储到1000个小文件(记为,这里漏写个了a1)中。这样每个小文件的大约为300M。遍历文件b,采取和a相同的方式将url分别存储到1000小文件中(记为)。这样处理后,所有可能相同的url都在对应的小文件()中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。

  hash_set统计

求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。

 

(此题来源于v_July_v的博客)

 

 

B树(多向平衡查找树)


B-树是对2-3树数据结构的扩展。它支持对保存在磁盘或者网络上的符号表进行外部查找,这些文件可能比我们以前考虑的输入要大的多(以前的输入能够保存在内存中)。

(B树和B+树是实现数据库的数据结构,一般程序员用不到它。)

 

和2-3树一样,我们限制了每个结点中能够含有的“键-链接”对的上下数量界限:一个M阶的B-树,每个结点 最多含有M-1对键-链接(假设M足够小,使得每个M向结点都能够存放在一个页中), 最少含有M/2对键-链接,但也不能少于2对。

(B树是用于存储海量数据的,一般其一个结点就占用磁盘一个块的大小。)

 

【注】以下B树部分参考自July的博客,尤其是插入及删除示图,为了省力直接Copy自July。


B树中的结点存放的是键-值对。图中红色方块即为键对应值的指针。

B树中的每个结点根据实际情况可以包含大量的关键字信息和分支(当然是不能超过磁盘块的大小,根据磁盘驱动(diskdrives)的不同,一般块的大小在1k~4k左右);这样树的深度降低了,这就意味着查找一个元素只要很少结点从外存磁盘中读入内存,很快访问到要查找的数据。

 

查找


假如每个盘块可以正好存放一个B树的结点(正好存放2个文件名)。那么一个BTNODE结点就代表一个盘块,而子树指针就是存放另外一个盘块的地址。

下面,咱们来模拟下查找文件29的过程:

1.  根据根结点指针找到文件目录的根磁盘块1,将其中的信息导入内存。【磁盘IO操作1次】   

2.  此时内存中有两个文件名17、35和三个存储其他磁盘页面地址的数据。根据算法我们发现:17<29<35,因此我们找到指针p2。

3.  根据p2指针,我们定位到磁盘块3,并将其中的信息导入内存。【磁盘IO操作 2次】   

4.  此时内存中有两个文件名26,30和三个存储其他磁盘页面地址的数据。根据算法我们发现:26<29<30,因此我们找到指针p2。

5.  根据p2指针,我们定位到磁盘块8,并将其中的信息导入内存。【磁盘IO操作 3次】   

6.  此时内存中有两个文件名28,29。根据算法我们查找到文件名29,并定位了该文件内存的磁盘地址。分析上面的过程,发现需要3 3次磁盘IO操作和次磁盘IO操作和3次内存查找 次内存查找操作。关于内存中的文件名查找,由于是一个有序表结构,可以利用折半查找提高效率。至于IO操作是影响整个B树查找效率的决定因素。

 

插入


想想2-3树的插入。2-3树结点的最大容量是2个元素,故当插入操作造成超出容量之后,就得分裂。同样m-阶B树规定的结点的最大容量是m-1个元素,故当插入操作造成超出容量之后也得分裂,其分裂成两个结点每个结点分m/2个元素。(副作用是在其父结点中要插入一个中间元素,用于分隔这两结点。和2-3树一样,再向父结点插入一个元素也可能会造成父结点的分裂,逐级向上操作,直到不再造成分裂为止。)

向某结点中插入一个元素使其分裂,可能会造成连锁反应,使其之上的结点也可能造成分裂。

 

总结:在B树中插入关键码key的思路:

对高度为h的m阶B树,新结点一般是插在第h层。通过检索可以确定关键码应插入的结点位置。然后分两种情况讨论:

1、  若该结点中关键码个数小于m-1,则直接插入即可。

2、  若该结点中关键码个数等于m-1,则将引起结点的分裂。以中间关键码为界将结点一分为二,产生一个新结点,并把中间关键码插入到父结点(h-1层)中

重复上述工作,最坏情况一直分裂到根结点,建立一个新的根结点,整个B树增加一层。

 

【例】

1、下面咱们通过一个实例来逐步讲解下。插入以下字符字母到一棵空的B 树中(非根结点 关键字数小了(小于2个)就合并,大了(超过4个)就分裂):C N G A H E K Q M F W L T Z D P R X Y S,首先,结点空间足够,4个字母插入相同的结点中,如下图:



2、当咱们试着插入H时,结点发现空间不够,以致将其分裂成2个结点,移动中间元素G上移到新的根结点中,在实现过程中,咱们把A和C留在当前结点中,而H和N放置新的其右邻居结点中。如下图:



3、当咱们插入E,K,Q时,不需要任何分裂操作




4、插入M需要一次分裂,注意M恰好是中间关键字元素,以致向上移到父节点中



5、插入F,W,L,T不需要任何分裂操作



6、插入Z时,最右的叶子结点空间满了,需要进行分裂操作,中间元素T上移到父节点中,注意通过上移中间元素,树最终还是保持平衡,分裂结果的结点存在2个关键字元素。



7、插入D时,导致最左边的叶子结点被分裂,D恰好也是中间元素,上移到父节点中,然后字母P,R,X,Y陆续插入不需要任何分裂操作(别忘了,树中至多5个孩子)。


8、最后,当插入S时,含有N,P,Q,R的结点需要分裂,把中间元素Q上移到父节点中,但是情况来了,父节点中空间已经满了,所以也要进行分裂,将父节点中的中间元素M上移到新形成的根结点中,注意以前在父节点中的第三个指针在修改后包括D和G节点中。这样具体插入操作的完成,下面介绍删除操作,删除操作相对于插入操作要考虑的情况多点。



删除(delete)操作


首先查找B树中需删除的元素,如果该元素在B树中存在,则将该元素在其结点中进行删除,如果删除该元素后,首先判断该元素是否有左右孩子结点,如果有,则上移孩子结点中的某相近元素(“左孩子最右边的节点”或“右孩子最左边的节点”)到父节点中,然后是移动之后的情况;如果没有,直接删除后,移动之后的情况。

删除元素,移动相应元素之后,如果某结点中元素数目(即关键字数)小于 ceil(m/2)-1,则需要看其某相邻兄弟结点是否丰满(结点中元素个数大于ceil(m/2)-1)( 还记得第一节中关于B树的第5个特性中的c点么?: c)除根结点之外的结点(包括叶子结点)的关键字的个数n必须满足: (ceil(m / 2)-1)<= n <=m-1。m表示最多含有m个孩子,n表示关键字数。在本小节中举的一颗B树的示例中,关键字数n满足:2<=n<=4),如果丰满,则向父节点借一个元素来满足条件;如果其相邻兄弟都刚脱贫,即借了之后其结点数目小于ceil(m/2)-1,则该结点与其相邻的某一兄弟结点进行“合并”成一个结点,以此来满足条件。那咱们通过下面实例来详细了解吧。

以上述插入操作构造的一棵5阶B树(树中最多含有m(m=5)个孩子,因此关键字数最小为 ceil(m/ 2)-1=2。还是这句话, 关键字数小了(小于2个)就合并,大了(超过4个)就分裂)为例,依次删除H,T,R,E。

 

1、首先删除元素H,当然首先查找H,H在一个叶子结点中,且该叶子结点元素数目3大于最小元素数目ceil(m/2)-1=2,则操作很简单,咱们只需要移动K至原来H的位置,移动L至K的位置(也就是结点中删除元素后面的元素向前移动)



2、下一步,删除T,因为T没有在叶子结点中,而是在中间结点中找到,咱们发现他的继承者W(字母升序的下个元素),将W上移到T的位置,然后将原包含W的孩子结点中的W进行删除,这里恰好删除W后,该孩子结点中元素个数大于2,无需进行合并操作。


3、下一步删除R,R在叶子结点中,但是该结点中元素数目为2,删除导致只有1个元素,已经小于最小元素数目ceil(5/2)-1=2,而由前面我们已经知道:如果其某个相邻兄弟结点中比较丰满(元素个数大于ceil(5/2)-1=2),则可以向父结点借一个元素,然后将最丰满的相邻兄弟结点中上移最后或最前一个元素到父节点中(有没有看到红黑树中左旋操作的影子?),在这个实例中,右相邻兄弟结点中比较丰满(3个元素大于2),所以先向父节点借一个元素W下移到该叶子结点中,代替原来S的位置,S前移;然后X在相邻右兄弟结点中上移到父结点中,最后在相邻右兄弟结点中删除X,后面元素前移。



4、最后一步删除E, 删除后会导致很多问题,因为E所在的结点数目刚好达标,刚好满足最小元素个数(ceil(5/2)-1=2),而相邻的兄弟结点也是同样的情况,删除一个元素都不能满足条件,所以需要该节点与某相邻兄弟结点进行合并操作;首先移动父结点中的元素(该元素在两个需要合并的两个结点元素之间)下移到其子结点中,然后将这两个结点进行合并成一个结点。所以在该实例中,咱们首先将父节点中的元素D下移到已经删除E而只有F的结点中,然后将含有D和F的结点和含有A,C的相邻兄弟结点进行合并成一个结点。



5、也许你认为这样删除操作已经结束了,其实不然,在看看上图,对于这种特殊情况,你立即会发现父节点只包含一个元素G,没达标(因为非根节点包括叶子结点的关键字数n必须满足于2=<n<=4,而此处的n=1),这是不能够接受的。如果这个问题结点的相邻兄弟比较丰满,则可以向父结点借一个元素。假设这时右兄弟结点(含有Q,X)有一个以上的元素(Q右边还有元素),然后咱们将M下移到元素很少的子结点中,将Q上移到M的位置,这时,Q的左子树将变成M的右子树,也就是含有N,P结点被依附在M的右指针上。所以在这个实例中,咱们没有办法去借一个元素,只能与兄弟结点进行合并成一个结点,而根结点中的唯一元素M下移到子结点,这样,树的高度减少一层。




为了进一步详细讨论删除的情况, 再举另外一个实例

这里是一棵不同的5序B树,那咱们试着删除C



于是将删除元素C的右子结点中的D元素上移到C的位置,但是出现上移元素后,只有一个元素的结点的情况。

又因为含有E的结点,其相邻兄弟结点才刚脱贫(最少元素个数为2),不可能向父节点借元素,所以只能进行合并操作,于是这里将含有A,B的左兄弟结点和含有E的结点进行合并成一个结点。



这样又出现只含有一个元素F结点的情况,这时,其相邻的兄弟结点是丰满的(元素个数为3>最小元素个数2),这样就可以想父结点借元素了,把父结点中的J下移到该结点中,相应的如果结点中J后有元素则前移,然后相邻兄弟结点中的第一个元素(或者最后一个元素)上移到父节点中,后面的元素(或者前面的元素)前移(或者后移);注意含有K,L的结点以前依附在M的左边,现在变为依附在J的右边。这样每个结点都满足B树结构性质。



从以上操作可看出:除根结点之外的结点(包括叶子结点)的关键字的个数n满足:(ceil(m / 2)-1)<= n <= m-1,即2<=n<=4。这也佐证了咱们之前的观点。删除操作完。



 

(我思:)

(1、       关于B树中指针的表示。指针就是线索,是为了指示你找到目标。在内存中用内存的线性地址表示,在磁盘上,用磁盘的柱面和磁道号表示。

(2、       B树也是一种文件组织形式。它与OS文件系统的区别是,文件系统是面向磁盘上各种应用的文件的,所有文件的索引都被组织在一个系统文件表中。这样,一个相关应用的文件之间就没有体现有序性,我们对某组相关的文件进行查找,效率就会较低。  而B树是专门对某组相关的文件进行组织,使其之间相对有序,提高查找效率。 --尤其是对于需要频繁查找访问文件的操作。

例如: 对10亿个有序数,其分布在1000个文件中。普通的查找(类2分查找),和构造一个B树,普通的二分查找不仅需要多次访问文件,且其通过OS的文件系统通过文件名来访问文件,这样效率低——OS需要在整张系统文件表中通过文件名查找文件。  而B树,其是多叉树,树的深度比二分树要小很多,需要查找的文件比二分查找需要的少。且其通过自己建立的B树来索引文件(每次查找文件都通过该B树得到文件在磁盘上的位置)。B树是独立于OS的文件系统的,它中的每个文件都有相应的磁盘位置,而不仅是文件名。

 

 

B+树


B+ tree:是应文件系统所需而产生的一种B-tree的变形树。

一棵m阶的B+树和m阶的B树的异同点在于:

1、有n棵子树的结点中含有n-1 个关键字; (与B 树n棵子树有n-1个关键字 保持一致,)

2、所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且 叶子结点本身依关键字的大小自小而大的顺序链接。 (而B 树的叶子节点并没有包括全部需要查找的信息)

3、所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终结点也包含需要查找键的有效信息),即非终端结点中只包含键,而不是键-值对。要想查找到某个键的值得到相应的叶子结点中找。

 


 

【应用举例】

1、为什么说B+-tree比B 树更适合实际应用中操作系统的文件索引和数据库索引?

数据库索引采用B+树的主要原因是 B树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。正是为了解决这个问题,B+树应运而生。

B+树只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作(或者说效率太低)。

2、B+-tree的应用: VSAM(虚拟存储存取法)文件

 

B树与B+树


走进搜索引擎的作者梁斌老师针对B树、B+树给出了他的意见(来源于July):

“B+树还有一个最大的好处,方便扫库,B树必须用中序遍历的方法按序扫库,而B+树直接从叶子结点挨个扫一遍就完了,B+树支持range-query非常方便,而B树不支持。这是数据库选用B+树的最主要原因。

比如要查 5-10之间的,B+树一把到5这个标记,再一把到10,然后串起来就行了,B树就非常麻烦。B树的好处,就是成功查询特别有利,因为树的高度总体要比B+树矮。不成功的情况下,B树也比B+树稍稍占一点点便宜。B树比如你的例子中查,17的话,一把就得到结果了。

有很多基于频率的搜索是选用B树,越频繁query的结点越往根上走,前提是需要对query做统计,而且要对key做一些变化。

另外B树也好B+树也好,根或者上面几层因为被反复query,所以这几块基本都在内存中,不会出现读磁盘IO,一般已启动的时候,就会主动换入内存。”

"mysql 底层存储是用B+树实现的,因为在内存中B+树是没有优势的,但是一到磁盘,B+树的威力就出来了"。

 

 

我应该使用符号表的哪种实现

对于典型的应用程序,应该在散列表和二叉查找树之间进行选择。

相对于二叉查找树,散列表的优点在于代码更简单,且查找时间最优(常数级别)。二叉查找树相对于散列表的优点在于抽象结构更简单(不需要设计散列函数),红黑树可以保证最坏情况下的性能且它能够支持的操作更多(如排名、选择和范围查找)。

大多数程序员的第一选择都是散列表,在其他因素更重要时才会选择红黑树。(”第一选择”的例外:当键都是长字符串时,我们可以构造出比红黑树更灵活而又比散列表更高效的数据结构 Trie树)

 

 

 

=================================================字符串的查找============================================

单词查找树(Trie树)


单词查找树的英文单词trie来自于E.Fredkin在1960年玩的一个文字游戏,因为这个数据结构的作用是取出(retrieval)数据,但发音为try是为了避免与tree相混淆。

 

基本性质

每个结点都含有R条链接,其中R为字母表的大小。(单词查找树一般都含有大量的空链接,因此在绘制一颗单词查找树时一般会忽略空链接。)

 

树中的每个结点中不是包含一个或几个关键字,而是只含有 组成关键字的符号。例如,若关键字是数值,则结点中只包含一个数位;若关键字是单词,则结点中只包含一个字母字符。我们将每个键所关联的值保存在该键的最后一个字母所对应的结点中。

(这种树会给某种类型关键字的表的查找带来方便。)

 

假设有如下关键字的集合

{ CAI、CAO、LI、LAN、CHA、CHANG、WEN、CHAO、YUN、YANG、LONG、WANG、ZHAO、LIU、WU、CHEN  }



若以树的多重链表来表示Trie树,则树的每个结点中应含有d个指针域。

若从Trie树中某个结点到叶子结点的路径上每个结点都只有一个孩子,则可将该路径上所有结点 压缩成一个“叶子结点”,且在该叶子结点中存储关键字及指向记录的指针等信息。

 



在Trie树中有两种结点:

分支结点:含有d个指针域和一个 指示该结点中非空指针域的个数的整数域。(分支结点所表示的字符是由其指向子树指针的索引位置决定的)

叶子结点:含有 关键字域和指向 记录的指针域。

 

typedef structTrieNode

{

    NodeKind kind ;

    union {

        struct {KeyType K;  Record *infoptr} lf ;   //叶子结点

        struct {TrieNode *ptr[27];  int num} bh ; //分支结点

    } ;

} TrieNode,*TrieTree ;

 

查找

 

在Trie树上进行查找的过程为:从根结点出发,沿给定值相应的指针逐层向下,直至叶子结点,若叶子结点中的关键字和给定值相等,则查找成功。若分支结点中和给定值相应的指针为空,或叶结点中的关键字和给定值不相等,则查找不成功。

 

分割


查找操作的时间依赖于树的深度。

我们可以对关键字集选择一种合适的分割,以缩减Trie树的深度。

例如:先按首字符不同分成多个子集之后,然后按最后一个字符不同分割每个子集,再按第二个字符……,前后交叉分割。

如下图:在该树上,除两个叶子结点在第四层上外,其余叶子结点均在第三层上。

若分割的合适,则可使每个叶子结点中只含有少数几个同义词。



插入和删除

在Trie树上易于进行插入和删除,只是需要相应地增加和删除一些分支结点。

把沿途分支结点中相应的指针域置空,再把其分支结点中的num-1,然后删除叶子结点。当分支结点中num域的值减为1时,便可删除。

 

 

【应用举例】

寻找热门查询,300万个查询字符串中统计最热门的10个查询。

 





作者:yang_yulei 发表于2014-5-18 0:05:30 原文链接
阅读:1 评论:0 查看评论

查找(一)史上最简单清晰的红黑树讲解

$
0
0

查找(一)



我们使用 符号表这个词来描述一张抽象的表格,我们会将信息( )存储在其中,然后按照指定的 来搜索并获取这些信息。键和值的具体意义取决于不同的应用。

符号表中可能会保存很多键和很多信息,因此实现一张高效的符号表也是一项很有挑战性的任务。

我们会用三种经典的数据类型来实现高效的符号表: 二叉查找数红黑树散列表


二分查找


我们使用有序数组存储键,经典的二分查找能够根据数组的索引大大减少每次查找所需的比较次数。

在查找时,我们先将被查找的键和子数组的中间键比较。如果被查找的键小于中间键,我们就在左子数组中继续查找,如果大于我们就在右子数组中继续查找,否则中间键就是我们要找的键。

 

一般情况下二分查找都比顺序查找快的多,它也是众多实际应用程序的最佳选择。对于一个静态表(不允许插入)来说,将其在初始化时就排序是值得的。

 

当然,二分查找也不适合很多应用。现代应用需要 同时能够支持高效的查找和插入两种操作的符号表实现。也就是说, 我们需要在构造庞大的符号表的同时能够任意插入(也许还有删除)键值对,同时也要能够完成查找操作

 

要支持高效的插入操作,我们似乎需要一种链式结构。当单链接的链表是无法使用二分查找的,因为二分查找的高效来自于能够快速通过索引取得任何子数组的中间元素。为了将二分查找的效率和链表的灵活性结合起来,我们需要更加复杂的数据结构。

能够同时拥有两者的就是 二叉查找树

 

二叉查找树


一颗二叉查找树( BST)是一颗二叉树,其中每个节点都含有一个可比较的键(以及相关联的值)且 每个结点的键都大于其左子树中的任意结点的键而小于右子树的任意结点的键

 

一颗二叉查找树代表了一组键(及其相应的值)的集合,而同一个集合可以用多颗不同的二叉查找树表示。

如果我们将一颗二叉查找树的所有键 投影到一条直线上,保证一个结点的左子树中的键出现在它的右边,右子树中的键出现在它的右边,那么我们一定可以得到一条有序的键列。

 



查找


在二叉查找树中查找一个键的递归算法:

如果树是空的,则查找未命中。如果被查找的键和根结点的键相等,查找命中。否则我们就在适当的子树中继续查找。如果被查找的键较小就选择左子树,较大就选择右子树。

在二叉查找树中, 随着我们不断向下查找,当前结点所表示的子树的大小也在减小(理想情况下是减半)

 

插入


查找代码几乎和二分查找的一样简单,这种 简洁性是二叉查找树的重要特性之一。而二叉查找树的另一个更重要的特性就是 插入的实现难度和查找差不多

当查找一个不存在于树中的结点并结束于一条空链接时,我们需要做的就是将链接指向一个含有被查找的键的新结点。如果被查找的键小于根结点的键,我们会继续在左子树中插入该键,否则在右子树中插入该键。

 

分析


使用二叉查找树的算法的运行时间取决于 树的形状,而树的形状又取决于键被插入的先后顺序。

在最好的情况下,一颗含有N个结点的树是完全平衡的,每条空链接和根结点的距离都为~lgN。在最坏的情况下,搜索路径上可能有N个结点。但在一般情况下树的形状和最好情况更接近。



我们假设键的插入顺序是随机的。对这个模型的分析而言, 二叉查找树和快速排序几乎就是“双胞胎”。树的根结点就是快速排序中的第一个切分元素(左侧的键都比它小,右侧的键都比它大),而这对于所有的子树同样适用,这和快速排序中对于子数组的递归排序完全对应。

【在由N个随机键构造的二叉查找树中,查找命中平均所需的比较次数为~2lgN。 N越大这个公式越准确】

 

平衡查找树


在一颗含有N个结点的树中,我们希望树高为~lgN,这样我们就能保证所有查找都能在~lgN此比较内结束,就和二分查找一样。不幸的是,在动态插入中保证树的完美平衡的代价太高了。我们放松对完美平衡的要求,使符号表API中所有操作均能够在对数时间内完成。

 

2-3查找树


为了保证查找树的平衡性,我们需要一些灵活性,因此在这里我们允许树中的一个结点保存多个键。

2-结点:含有一个键(及值)和两条链接,左链接指向的2-3树中的键都小于该结点,右链接指向的2-3树中的键都大于该结点。

3-结点:含有两个键(及值)和三条链接,左链接指向的2-3树中的键都小于该结点,中链接指向的2-3树中的键都位于该结点的两个键之间,右链接指向的2-3树中的键都大于该结点。

(2-3指的是2叉-3叉的意思)




一颗完美平衡的2-3查找树中的所有空链接到根结点的距离都是相同的。

 

查找


要判断一个键是否在树中,我们先将它和根结点中的键比较。如果它和其中的任何一个相等,查找命中。否则我们就根据比较的结果找到指向相应区间的链接,并在其指向的子树中递归地继续查找。如果这是个空链接,查找未命中。

 

插入


要在2-3树中插入一个新结点,我们可以和二叉查找树一样先进行一次未命中的查找,然后把新结点挂在树的底部。但这样的话树无法保持完美平衡性。我们使用2-3树的主要原因就在于它能够在插入之后继续保持平衡。

如果未命中的查找结束于一个2-结点,我们只要把这个2-结点替换为一个3-结点,将要插入的键保存在其中即可。 如果未命中的查找结束于一个3-结点,事情就要麻烦一些。

 

热身


先考虑最简单的例子:只有一个3-结点的树,向其插入一个新键。

这棵树唯一的结点中已经没有可插入的空间了。我们又不能把新键插在其空结点上(破坏了完美平衡)。为了将新键插入,我们先临时将新键存入该结点中,使之成为一个4-结点。创建一个4-结点很方便,因为很容易将它转换为一颗由3个2-结点组成的2-3树(如图所示),这棵树既是一颗含有3个结点的二叉查找树,同时也是一颗完美平衡的2-3树,其中所有空链接到根结点的距离都相等。


 

向一个父结点为2-结点的3-结点中插入新键

假设未命中的查找结束于一个3-结点,而它的父结点是一个2-结点。在这种情况下我们需要在维持树的完美平衡的前提下为新键腾出空间。

我们先像刚才一样构造一个临时的4-结点并将其分解,但此时我们不会为 中键创建一个新结点,而是 将其移动至原来的父结点中。(如图所示)


这次转换也并不影响(完美平衡的)2-3树的主要性质。树仍然是有序的,因为中键被移动到父结点中去了,树仍然是完美平衡的,插入后所有的空链接到根结点的距离仍然相同。

 

向一个父结点为3-结点的3-结点中插入新键

假设未命中的查找结束于一个3-结点,而它的父结点是一个3-结点。

我们再次和刚才一样构造一个临时的4-结点并分解它,然后将它的中键插入它的父结点中。但父结点也是一个3-结点,因此我们再用这个中键构造一个新的临时4-结点,然后在这个结点上进行相同的变换,即分解这个父结点并将它的中键插入到它的父结点中去。

我们就这样 一直向上不断分解临时的4-结点并将中键插入更高的父结点,直至遇到一个2-结点并将它替换为一个不需要继续分解的3-结点,或者是到达3-结点的根。


总结

先找插入结点,若结点有空(即2-结点),则直接插入。如结点没空(即3-结点),则插入使其临时容纳这个元素,然后分裂此结点,把中间元素移到其父结点中。对父结点亦如此处理。(中键一直往上移,直到找到空位,在此过程中没有空位就先搞个临时的,再分裂。)

 

 

★2-3树插入算法的根本在于这些变换都是局部的:除了相关的结点和链接之外不必修改或者检查树的其他部分。每次变换中,变更的链接数量不会超过一个很小的常数。所有局部变换都不会影响整棵树的有序性和平衡性。

 

{你确定理解了2-3树的插入过程了吗? 如果你理解了,那么你也就基本理解了红黑树的插入}

 

构造


和标准的二叉查找树由上向下生长不同,2-3树的生长是由下向上的。



优点


2-3树在最坏情况下仍有较好的性能。每个操作中处理每个结点的时间都不会超过一个很小的常数,且这两个操作都只会访问一条路径上的结点,所以 任何查找或者插入的成本都肯定不会超过对数级别

完美平衡的2-3树要平展的多。例如,含有10亿个结点的一颗2-3树的高度仅在19到30之间。我们最多只需要访问30个结点就能在10亿个键中进行任意查找和插入操作。

 

缺点


我们需要维护两种不同类型的结点,查找和插入操作的实现需要大量的代码,而且它们所产生的额外开销可能会使算法比标准的二叉查找树更慢。

平衡一棵树的初衷是为了消除最坏情况,但我们希望这种保障所需的代码能够越少越好。

 


红黑二叉查找树


【前言:本文所讨论的红黑树之目的在于使读者能更简单清晰地了解红黑树的构造,使读者能在纸上清晰快速地画出红黑树,而不是为了写出红黑树的实现代码。

若是要在代码级理解红黑树,则势必需要记住其复杂的插入和旋转的各种情况,我认为那只有助于增加大家对红黑树的恐惧,实际面试和工作中几乎不会遇到需要自己动手实现红黑树的情况(很多语言的标准库中就有红黑树的实现)。  若对于红黑树的C代码实现有兴趣的,可移步至July的博客。】

 

理解红黑树一句话就够了:红黑树就是用红链接表示3-结点的2-3树。那么红黑树的插入、构造就可转化为2-3树的问题,即: 在脑中用2-3树来操作,得到结果,再把结果中的3-结点转化为红链接即可。而2-3树的插入,前面已有详细图文,实际也很简单:有空则插,没空硬插,再分裂。  这样,我们就不用记那么复杂且让人头疼的红黑树插入旋转的各种情况了。只要清楚2-3树的插入方式即可。  下面图文详细演示。)

 

红黑树的本质

红黑树是对2-3查找树的改进,它能用一种统一的方式完成所有变换。

 

替换3-结点


★红黑树背后的思想是用标准的二叉查找树(完全由2-结点构成)和一些额外的信息(替换3-结点)来表示2-3树。

我们将树中的链接分为两种类型:红链接将两个2-结点连接起来构成一个3-结点,黑链接则是2-3树中的普通链接。确切地说,我们将3-结点表示为由一条 左斜的红色链接相连的两个2-结点。

这种表示法的一个优点是,我们无需修改就可以直接使用标准二叉查找树的get()方法。对于任意的2-3树,只要对结点进行转换,我们都可以立即派生出一颗对应的二叉查找树。我们将用这种方式表示2-3树的二叉查找树称为红黑树。


红黑树的另一种定义是满足下列条件的二叉查找树:

⑴红链接均为左链接。

⑵没有任何一个结点同时和两条红链接相连。

⑶该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同。

 

如果我们将一颗红黑树中的红链接画平,那么所有的空链接到根结点的距离都将是相同的。如果我们将由红链接相连的结点合并,得到的就是一颗2-3树。

相反,如果将一颗2-3树中的3-结点画作由红色左链接相连的两个2-结点,那么不会存在能够和两条红链接相连的结点,且树必然是完美平衡的。


 

无论我们用何种方式去定义它们,红黑树都既是二叉查找树,也是2-3树。

(2-3树的深度很小,平衡性好,效率高,但是其有两种不同的结点,实际代码实现比较复杂。而红黑树用红链接表示2-3树中另类的3-结点,统一了树中的结点类型,使代码实现简单化,又不破坏其高效性。)

 

颜色表示

因为每个结点都只会有一条指向自己的链接(从它的父结点指向它),我们将链接的颜色保存在表示结点的Node数据类型的布尔变量color中(若指向它的链接是红色的,那么该变量为true,黑色则为false)。

当我们提到一个结点颜色时,我们指的是指向该结点的链接的颜色。

 

旋转


在我们实现的某些操作中可能会出现红色右链接或者两条连续的红链接,但在操作完成前这些情况都会被小心地旋转并修复。

(我们在这里不讨论旋转的几种情况,把红黑树看做2-3树,自然可以得到正确的旋转后结果)

 

插入


在插入时我们可以使用旋转操作帮助我们保证2-3树和红黑树之间的一一对应关系,因为旋转操作可以保持红黑树的两个重要性质:有序性和完美平衡性。

 

热身


向2-结点中插入新键

(向红黑树中插入操作时,想想2-3树的插入操作。红黑树与2-3树在本质上是相同的,只是它们对3结点的表示不同。

向一个只含有一个2-结点的2-3树中插入新键后,2-结点变为3-结点。我们再把这个3-结点转化为红结点即可)


向一颗双键树(即一个3-结点)中插入新键

(向红黑树中插入操作时,想想2-3树的插入操作。你把红黑树当做2-3树来处理插入,一切都变得简单了)

(向2-3树中的一个3-结点插入新键,这个3结点临时成为4-结点,然后分裂成3个2结点)



★一颗红黑树的构造全过程


平衡二叉树(AVL树)


定义:平衡二叉树(Balance Binary Tree)又称AVL树。它或者是一颗空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。

若将二叉树上结点的平衡因子BF(BalanceFactor)定义为该结点的左子树深度减去它的右子树深度,则 平衡因子的绝对值大于1

 

其旋转操作 用2-3树的分裂来类比想象。




下半部分——散列表、B树、B+树、Trie树。





作者:yang_yulei 发表于2014-5-18 0:05:19 原文链接
阅读:5 评论:0 查看评论

Android屏幕大小适配问题解决

$
0
0


一、一些基本概念

1、长度(真实长度):英寸、inch

2、分辨率:density 每英寸像素数  dpi(密度)

3、像素:px

4、dip的公式:px /dip=dpi/160  所以 dip 类似于英寸、长度(dp=dip,sp类似于dip)  dip=160*inch

dip= 160/dpi * px

当dip一定时,dpi 越大,px就越大

5、广义分辨率=长px*宽px

二、常用规律

1、平时我们说 手机的分辨率是 320*480的,其实的这里的分辨率是相对分辨率

意思是:水平方向上的像素数是320,垂直方向上像素数是480,

分辨率是160(默认是160,意思是每英寸像素数160)

那么水平方向:320 /160=2英寸

垂直方向:480/160=3英寸

于是乎 屏幕对角线 是根号下4*9=3.6(这就是常说的3.6英寸屏幕)


2、说一个手机的屏幕参数有三个:长宽像素之积(相对分辨率)、真实分辨率(dpi)、对角线长度


3、模拟器的分辨率都是160,所以像素越大,屏越大


4、l、m、h 三个文件夹是按 真是分辨率dpi 来对应找文件的。


5、有三种方案解决屏幕适配

(1)按像素比      y/开发时用的屏幕像素=x/用户设备像素

(2)按长度        用dip(假设屏幕尺寸基本不变)

(3)按密度        放在l、m、h文件夹(假设屏幕尺寸基本不变,dpi越大 px越大)

6、如果手机是hdpi,但hdpi里没有东西,l里有东西,程序就会去l里找图片并且把它按比例放大。


7、最全的办法:单独适配

屏幕分辨率:1024x600
density:1(160)
文件夹:values-mdpi-1024x600

屏幕分辨率:1024x600
density:1.5(240)
文件夹:values-hdpi-683x400  由1024/1.5  600/1.5得到,需要四舍五入。

屏幕分辨率:800x480
density:1(160)
文件夹:values-mdpi-800x480

屏幕分辨率:800x480
density:1.5(240)
文件夹:values-hdpi-533x320  由800/1.5  480/1.5得到,需要四舍五入。

以此类推
一般情况下需要创建出values 、values-mdpi 、 values-hdpi文件夹,以备在一些没有规定的尺寸屏幕上找不到资源的情况。


8、我的原则,能用拉伸图片的就拉伸、能用相对布局的就用相对布局、能用代码计算宽度就代码计算。


让美工出一份  1080*1920 放到XXH

布局时按照:宽:320dip 高 480 dip


9、主流机型参数:

比例
0.75
1
1.5
2
3
最常见的像素长宽
240
320
480
640*960
960*1440
dpi
~120
~160
~240
~320
~480
文件夹
L
M
H
XH
XXH
主流机



720*1280(红米、s3、note2)
720*1280(米2)1080*1920(米3、note3、s4)


1dp
=1px

1dp
=2px
1dp
=2.5px
主流机



note2、红米、s3 米2、米3、s4、note3


ldpi Resources for low-density (ldpi) screens (~120dpi).
mdpi Resources for medium-density (mdpi) screens (~160dpi). (This is the baseline density.)
hdpi Resources for high-density (hdpi) screens (~240dpi).
xhdpi Resources for extra high-density (xhdpi) screens (~320dpi).


----------------------------------------------------------------

红米:


屏幕分辨率为:720 * 1280

绝对宽度:720pixels

绝对高度:1280pixels

逻辑密度:2.0

X 维 :294.96774像素每英尺

Y 维 :285.193像素每英尺


res/drawable下的文件会做失真压缩

res/drawable-nodpi下的文件不做任何处理


=================================

10、获取屏幕像素的方法

String str = "";

        DisplayMetrics dm = new DisplayMetrics();

        dm = this.getApplicationContext().getResources().getDisplayMetrics();

        int screenWidth = dm.widthPixels;

        int screenHeight = dm.heightPixels;

        float density = dm.density;

        float xdpi = dm.xdpi;

        float ydpi = dm.ydpi;

        str += "屏幕分辨率为:" + dm.widthPixels + " * " + dm.heightPixels + "\n";

        str += "绝对宽度:" + String.valueOf(screenWidth) + "pixels\n";

        str += "绝对高度:" + String.valueOf(screenHeight)

                + "pixels\n";

        str += "逻辑密度:" + String.valueOf(density)

                + "\n";

        str += "X 维 :" + String.valueOf(xdpi) + "像素每英尺\n";

        str += "Y 维 :" + String.valueOf(ydpi) + "像素每英尺\n";

        L.i(str);


作者:u014077888 发表于2014-5-17 22:58:48 原文链接
阅读:54 评论:0 查看评论

JAVA多线程和并发基础面试问答

$
0
0

原文链接   译文连接 作者:Pankaj   译者郑旭东   校对:方腾飞

多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题。( 校对注:非常赞同这个观点

Java多线程面试问题

1. 进程和线程之间有什么不同?

一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了不同的类和程序的单一进程。线程可以被称为轻量级进程。线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源。

2. 多线程编程的好处是什么?

在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。多个线程共享堆内存(heap memory),因此创建多个线程去执行一些任务会比创建多个进程更好。举个例子,Servlets比CGI更好,是因为Servlets支持多线程而CGI不支持。

3. 用户线程和守护线程有什么区别?

当我们在Java程序中创建一个线程,它就被称为用户线程。一个守护线程是在后台执行并且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序并且退出。一个守护线程创建的子线程依然是守护线程。

4. 我们如何创建一个线程?

有两种创建线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创建一个Thread对象;二是直接继承Thread类。若想了解更多可以阅读这篇关于如何在 Java中创建线程的文章。

5. 有哪些不同的线程生命周期?

当我们在Java程序中新建一个线程时,它的状态是 New。当我们调用线程的start()方法时,状态被改变为 Runnable。线程调度器会为 Runnable线程池中的线程分配CPU时间并且讲它们的状态改变为 Running。其他的线程状态还有 Waiting,Blocked 和 Dead。读这篇文章可以了解更多关于 线程生命周期的知识。

6. 可以直接调用Thread类的run()方法么?

当然可以,但是如果我们调用了Thread的run()方法,它的行为就会和普通的方法一样,为了在新的线程中执行我们的代码,必须使用Thread.start()方法。

7. 如何让正在运行的线程暂停一段时间?

我们可以使用Thread类的Sleep()方法让线程暂停一段时间。需要注意的是,这并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为 Runnable,并且根据线程调度,它将得到执行。

8. 你对线程优先级的理解是什么?

每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,但这依赖于线程调度的实现,这个实现是和操作系统相关的(OS dependent)。我们可以定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程优先级是一个int变量(从1-10),1代表最低优先级,10代表最高优先级。

9. 什么是线程调度器(Thread Scheduler)和时间分片(Time Slicing)?

线程调度器是一个操作系统服务,它负责为 Runnable状态的线程分配CPU时间。一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。时间分片是指将可用的CPU时间分配给可用的 Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。线程调度并不受到Java虚拟机控制,所以由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优先级)。

10. 在多线程中,什么是上下文切换(context-switching)?

上下文切换是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。上下文切换是多任务操作系统和多线程环境的基本特征。

11. 你如何确保main()方法所在的线程是Java程序最后结束的线程?

我们可以使用Thread类的joint()方法来确保所有程序创建的线程在main()方法退出前结束。这里有一篇文章关于 Thread类的joint()方法

12.线程之间是如何通信的?

当线程间是可以共享资源时,线程间通信是协调它们的重要的手段。Object类中wait()\notify()\notifyAll()方法可以用于线程间通信关于资源的锁的状态。点击 这里有更多关于线程wait, notify和notifyAll.

13.为什么线程通信的方法wait(), notify()和notifyAll()被定义在Object类里?

Java的每个对象中都有一个锁(monitor,也可以成为监视器) 并且wait(),notify()等方法用于等待对象的锁或者通知其他线程对象的监视器可用。在Java的线程中并没有可供任何对象使用的锁和同步器。这就是为什么这些方法是Object类的一部分,这样Java的每一个类都有用于线程间通信的基本方法

14. 为什么wait(), notify()和notifyAll()必须在同步方法或者同步块中被调用?

当一个线程需要调用对象的wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的notify()方法。同样的,当一个线程需要调用对象的notify()方法时,它会释放这个对象的锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用。

15. 为什么Thread类的sleep()和yield()方法是静态的?

Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。所以在其他处于等待状态的线程上调用这些方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。

16.如何确保线程安全?

在Java中可以有很多方法来保证线程安全——同步,使用原子类(atomic concurrent classes),实现并发锁,使用volatile关键字,使用不变类和线程安全类。在 线程安全教程中,你可以学到更多。

17. volatile关键字在Java中有什么作用?

当我们使用volatile关键字去修饰变量的时候,所以线程都会直接读取该变量并且不缓存它。这就确保了线程读取到的变量是同内存中是一致的。

18. 同步方法和同步块,哪个是更好的选择?

同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。

19.如何创建守护线程?

使用Thread类的setDaemon(true)方法可以将线程设置为守护线程,需要注意的是,需要在调用start()方法前调用这个方法,否则会抛出IllegalThreadStateException异常。

20. 什么是ThreadLocal?

ThreadLocal用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。

每个线程都会拥有他们自己的Thread变量,它们可以使用get()\set()方法去获取他们的默认值或者在线程内部改变他们的值。ThreadLocal实例通常是希望它们同线程状态关联起来是private static属性。在 ThreadLocal例子这篇文章中你可以看到一个关于ThreadLocal的小程序。

21. 什么是Thread Group?为什么建议使用它?

ThreadGroup是一个类,它的目的是提供关于线程组的信息。

ThreadGroup API比较薄弱,它并没有比Thread提供了更多的功能。它有两个主要的功能:一是获取线程组中处于活跃状态线程的列表;二是设置为线程设置未捕获异常处理器(ncaught exception handler)。但在Java 1.5中Thread类也添加了 setUncaughtExceptionHandler(UncaughtExceptionHandler eh) 方法,所以ThreadGroup是已经过时的,不建议继续使用。

 
 
1
2
3
4
5
6
t1.setUncaughtExceptionHandler(newUncaughtExceptionHandler(){
@Override
publicvoiduncaughtException(Threadt,Throwablee){
System.out.println("exception occured:"+e.getMessage());
}
});

 

22. 什么是Java线程转储(Thread Dump),如何得到它?

线程转储是一个JVM活动线程的列表,它对于分析系统瓶颈和死锁非常有用。有很多方法可以获取线程转储——使用Profiler,Kill -3命令,jstack工具等等。我更喜欢jstack工具,因为它容易使用并且是JDK自带的。由于它是一个基于终端的工具,所以我们可以编写一些脚本去定时的产生线程转储以待分析。读这篇文档可以了解更多关于 产生线程转储的知识。

23. 什么是死锁(Deadlock)?如何分析和避免死锁?

死锁是指两个以上的线程永远阻塞的情况,这种情况产生至少需要两个以上的线程和两个以上的资源。

分析死锁,我们需要查看Java应用程序的线程转储。我们需要找出那些状态为BLOCKED的线程和他们等待的资源。每个资源都有一个唯一的id,用这个id我们可以找出哪些线程已经拥有了它的对象锁。

避免嵌套锁,只在需要的地方使用锁和避免无限期等待是避免死锁的通常办法,阅读这篇文章去学习 如何分析死锁

24. 什么是Java Timer类?如何创建一个有特定时间间隔的任务?

java.util.Timer是一个工具类,可以用于安排一个线程在未来的某个特定时间执行。Timer类可以用安排一次性任务或者周期任务。

java.util.TimerTask是一个实现了Runnable接口的抽象类,我们需要去继承这个类来创建我们自己的定时任务并使用Timer去安排它的执行。

这里有关于 java Timer的例子

25. 什么是线程池?如何创建一个Java线程池?

一个线程池管理了一组工作线程,同时它还包括了一个用于放置等待执行的任务的队列。

java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池。 线程池例子展现了如何创建和使用线程池,或者阅读 ScheduledThreadPoolExecutor例子,了解如何创建一个周期任务。

Java并发面试问题

1. 什么是原子操作?在Java Concurrency API中有哪些原子类(atomic classes)?

原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。

int++并不是一个原子操作,所以当一个线程读取它的值并加1时,另外一个线程有可能会读到之前的值,这就会引发错误。

为了解决这个问题,必须保证增加操作是原子的,在JDK1.5之前我们可以使用同步技术来做到这一点。到JDK1.5,java.util.concurrent.atomic包提供了int和long类型的装类,它们可以自动的保证对于他们的操作是原子的并且不需要使用同步。可以阅读这篇文章来了解 Java的atomic类

2. Java Concurrency API中的Lock接口(Lock interface)是什么?对比同步它有什么优势?

Lock接口比同步方法和同步块提供了更具扩展性的锁操作。他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的条件对象。

它的优势有:

  • 可以使锁更公平
  • 可以使线程在等待锁的时候响应中断
  • 可以让线程尝试获取锁,并在无法获取锁的时候立即返回或者等待一段时间
  • 可以在不同的范围,以不同的顺序获取和释放锁

阅读更多 关于锁的例子

3. 什么是Executors框架?

Executor框架同java.util.concurrent.Executor 接口在Java 5中被引入。Executor框架是一个根据一组执行策略调用,调度,执行和控制的异步任务的框架。

无限制的创建线程会引起应用程序内存溢出。所以创建一个线程池是个更好的的解决方案,因为可以限制线程的数量并且可以回收再利用这些线程。利用Executors框架可以非常方便的创建一个线程池,阅读这篇文章可以了解 如何使用Executor框架创建一个线程池

4. 什么是阻塞队列?如何使用阻塞队列来实现生产者-消费者模型?

java.util.concurrent.BlockingQueue的特性是:当队列是空的时,从队列中获取或删除元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。

阻塞队列不接受空值,当你尝试向队列中添加空值的时候,它会抛出NullPointerException。

阻塞队列的实现都是线程安全的,所有的查询方法都是原子的并且使用了内部锁或者其他形式的并发控制。

BlockingQueue 接口是java collections框架的一部分,它主要用于实现生产者-消费者问题。

阅读这篇文章了解 如何使用阻塞队列实现生产者-消费者问题。

5. 什么是Callable和Future?

Java 5在concurrency包中引入了java.util.concurrent.Callable 接口,它和Runnable接口很相似,但它可以返回一个对象或者抛出一个异常。

Callable接口使用泛型去定义它的返回类型。Executors类提供了一些有用的方法去在线程池中执行Callable内的任务。由于Callable任务是并行的,我们必须等待它返回的结果。java.util.concurrent.Future对象为我们解决了这个问题。在线程池提交Callable任务后返回了一个Future对象,使用它我们可以知道Callable任务的状态和得到Callable返回的执行结果。Future提供了get()方法让我们可以等待Callable结束并获取它的执行结果。

阅读这篇文章了解更多 关于Callable,Future的例子

6. 什么是FutureTask?

FutureTask是Future的一个基础实现,我们可以将它同Executors使用处理异步任务。通常我们不需要使用FutureTask类,单当我们打算重写Future接口的一些方法并保持原来基础的实现是,它就变得非常有用。我们可以仅仅继承于它并重写我们需要的方法。阅读 Java FutureTask例子,学习如何使用它。

7.什么是并发容器的实现?

Java集合类都是快速失败的,这就意味着当集合被改变且一个线程在使用迭代器遍历集合的时候,迭代器的next()方法将抛出ConcurrentModificationException异常。

并发容器支持并发的遍历和并发的更新。

主要的类有ConcurrentHashMap, CopyOnWriteArrayList 和CopyOnWriteArraySet,阅读这篇文章了解 如何避免ConcurrentModificationException

8. Executors类是什么?

Executors为Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable类提供了一些工具方法。

Executors可以用于方便的创建线程池。



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


ITeye推荐



Linux服务器性能评估

$
0
0

一、影响Linux服务器性能的因素
1. 操作系统级

  • CPU
  • 内存
  • 磁盘I/O带宽
  • 网络I/O带宽

2. 程序应用级

二、系统性能评估标准
影响性能因素

影响性能因素评判标准
糟糕
CPUuser% + sys%< 70%user% + sys%= 85%user% + sys% >=90%
内存Swap In(si)=0

 

Swap Out(so)=0

Per CPU with 10 page/sMore Swap In & Swap Out
磁盘iowait % < 20%iowait % =35%iowait % >= 50%

 

其中:
%user:表示CPU处在用户模式下的时间百分比。
%sys:表示CPU处在系统模式下的时间百分比。
%iowait:表示CPU等待输入输出完成时间的百分比。
swap in:即si,表示虚拟内存的页导入,即从SWAP DISK交换到RAM
swap out:即so,表示虚拟内存的页导出,即从RAM交换到SWAP DISK。

三、系统性能分析工具

1.常用系统命令
Vmstat、sar、iostat、netstat、free、ps、top等

2.常用组合方式
• 用vmstat、sar、iostat检测是否是CPU瓶颈
• 用free、vmstat检测是否是内存瓶颈
• 用iostat检测是否是磁盘I/O瓶颈
• 用netstat检测是否是网络带宽瓶颈

四、Linux性能评估与优化

1. 系统整体性能评估(uptime命令)

[root@server ~]# uptime
16:38:00 up 118 days, 3:01, 5 users, load average: 1.22, 1.02, 0.91
这里需要注意的是:load average这个输出值,这三个值的大小一般不能大于系统CPU的个数,例如,本输出中系统有8个CPU,如果load average的三个值长期大于8时,说明CPU很繁忙,负载很高,可能会影响系统性能,但是偶尔大于8时,倒不用担心,一般不会影响系统性能。相反,如果load average的输出值小于CPU的个数,则表示CPU还有空闲的时间片,比如本例中的输出,CPU是非常空闲的。

2. CPU性能评估

(1)利用vmstat命令监控系统CPU
该命令可以显示关于系统各种资源之间相关性能的简要信息,这里我们主要用它来看CPU一个负载情况。
下面是vmstat命令在某个系统的输出结果:

[root@node1 ~]# vmstat 2 3
procs ———–memory———- —swap– —–io—- –system– —–cpu——
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 162240 8304 67032 0 0 13 21 1007 23 0 1 98 0 0
0 0 0 162240 8304 67032 0 0 1 0 1010 20 0 1 100 0 0
0 0 0 162240 8304 67032 0 0 1 1 1009 18 0 1 99 0 0

  • Procs

r列表示运行和等待cpu时间片的进程数,这个值如果长期大于系统CPU的个数,说明CPU不足,需要增加CPU。
b列表示在等待资源的进程数,比如正在等待I/O、或者内存交换等。

  • Cpu

us列显示了用户进程消耗的CPU 时间百分比。us的值比较高时,说明用户进程消耗的cpu时间多,但是如果长期大于50%,就需要考虑优化程序或算法。
sy列显示了内核进程消耗的CPU时间百分比。Sy的值较高时,说明内核消耗的CPU资源很多。
根据经验,us+sy的参考值为80%,如果us+sy大于 80%说明可能存在CPU资源不足。

(2)利用sar命令监控系统CPU

sar功能很强大,可以对系统的每个方面进行单独的统计,但是使用sar命令会增加系统开销,不过这些开销是可以评估的,对系统的统计结果不会有很大影响。
下面是sar命令对某个系统的CPU统计输出:
[root@webserver ~]# sar -u 3 5
Linux 2.6.9-42.ELsmp (webserver) 11/28/2008 _i686_ (8 CPU)
11:41:24 AM CPU %user %nice %system %iowait %steal %idle
11:41:27 AM all 0.88 0.00 0.29 0.00 0.00 98.83
11:41:30 AM all 0.13 0.00 0.17 0.21 0.00 99.50
11:41:33 AM all 0.04 0.00 0.04 0.00 0.00 99.92
11:41:36 AM all 90.08 0.00 0.13 0.16 0.00 9.63
11:41:39 AM all 0.38 0.00 0.17 0.04 0.00 99.41
Average: all 0.34 0.00 0.16 0.05 0.00 99.45

对上面每项的输出解释如下:

  • %user列显示了用户进程消耗的CPU 时间百分比。
  • %nice列显示了运行正常进程所消耗的CPU 时间百分比。
  • %system列显示了系统进程消耗的CPU时间百分比。
  • %iowait列显示了IO等待所占用的CPU时间百分比
  • %steal列显示了在内存相对紧张的环境下pagein强制对不同的页面进行的steal操作 。
  • %idle列显示了CPU处在空闲状态的时间百分比。

问题
1.你是否遇到过系统CPU整体利用率不高,而应用缓慢的现象?
在一个多CPU的系统中,如果程序使用了单线程,会出现这么一个现象,CPU的整体使用率不高,但是系统应用却响应缓慢,这可能是由于程序使用单线程的原因,单线程只使用一个CPU,导致这个CPU占用率为100%,无法处理其它请求,而其它的CPU却闲置,这就导致了整体CPU使用率不高,而应用缓慢现象的发生。

3. 内存性能评估
(1)利用free指令监控内存
free是监控linux内存使用状况最常用的指令,看下面的一个输出:
[root@webserver ~]# free -m
total used free shared buffers cached
Mem: 8111 7185 926 0 243 6299
-/+ buffers/cache: 643 7468
Swap: 8189 0 8189
一般有这样一个经验公式:应用程序可用内存/系统物理内存>70%时,表示系统内存资源非常充足,不影响系统性能,应用程序可用内存/系统物理内存<20%时,表示系统内存资源紧缺,需要增加系统内存,20%<应用程序可用内存/系统物理内存<70%时,表示系统内存资源基本能满足应用需求,暂时不影响系统性能。

(2)利用vmstat命令监控内存

[root@node1 ~]# vmstat 2 3
procs ———–memory———- —swap– —–io—- –system– —–cpu——
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 162240 8304 67032 0 0 13 21 1007 23 0 1 98 0 0
0 0 0 162240 8304 67032 0 0 1 0 1010 20 0 1 100 0 0
0 0 0 162240 8304 67032 0 0 1 1 1009 18 0 1 99 0 0

  • memory

swpd列表示切换到内存交换区的内存数量(以k为单位)。如果swpd的值不为0,或者比较大,只要si、so的值长期为0,这种情况下一般不用担心,不会影响系统性能。
free列表示当前空闲的物理内存数量(以k为单位)
buff列表示buffers cache的内存数量,一般对块设备的读写才需要缓冲。
cache列表示page cached的内存数量,一般作为文件系统cached,频繁访问的文件都会被cached,如果cache值较大,说明cached的文件数较多,如果此时IO中bi比较小,说明文件系统效率比较好。

  • swap

si列表示由磁盘调入内存,也就是内存进入内存交换区的数量。
so列表示由内存调入磁盘,也就是内存交换区进入内存的数量。
一般情况下,si、so的值都为0,如果si、so的值长期不为0,则表示系统内存不足。需要增加系统内存。

4.磁盘I/O性能评估
(1)磁盘存储基础

  • 熟悉RAID存储方式,可以根据应用的不同,选择不同的RAID方式。
  • 尽可能用内存的读写代替直接磁盘I/O,使频繁访问的文件或数据放入内存中进行操作处理,因为内存读写操作比直接磁盘读写的效率要高千倍。
  • 将经常进行读写的文件与长期不变的文件独立出来,分别放置到不同的磁盘设备上。
  • 对于写操作频繁的数据,可以考虑使用裸设备代替文件系统。

使用裸设备的优点有:

  • 数据可以直接读写,不需要经过操作系统级的缓存,节省了内存资源,避免了内存资源争用。
  • 避免了文件系统级的维护开销,比如文件系统需要维护超级块、I-node等。
  • 避免了操作系统的cache预读功能,减少了I/O请求。

使用裸设备的缺点是:

  • 数据管理、空间管理不灵活,需要很专业的人来操作。

(2)利用iostat评估磁盘性能
[root@webserver ~]# iostat -d 2 3
Linux 2.6.9-42.ELsmp (webserver) 12/01/2008 _i686_ (8 CPU)

Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
sda 1.87 2.58 114.12 6479462 286537372

Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
sda 0.00 0.00 0.00 0 0

Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
sda 1.00 0.00 12.00 0 24
对上面每项的输出解释如下:

  • Blk_read/s表示每秒读取的数据块数。
  • Blk_wrtn/s表示每秒写入的数据块数。
  • Blk_read表示读取的所有块数。
  • Blk_wrtn表示写入的所有块数。
  1. 可以通过Blk_read/s和Blk_wrtn/s的值对磁盘的读写性能有一个基本的了解,如果Blk_wrtn/s值很大,表示磁盘的写操作很频繁,可以考虑优化磁盘或者优化程序,如果Blk_read/s值很大,表示磁盘直接读取操作很多,可以将读取的数据放入内存中进行操作。
  2. 对于这两个选项的值没有一个固定的大小,根据系统应用的不同,会有不同的值,但是有一个规则还是可以遵循的:长期的、超大的数据读写,肯定是不正常的,这种情况一定会影响系统性能。

(3)利用sar评估磁盘性能
通过“sar –d”组合,可以对系统的磁盘IO做一个基本的统计,请看下面的一个输出:
[root@webserver ~]# sar -d 2 3
Linux 2.6.9-42.ELsmp (webserver) 11/30/2008 _i686_ (8 CPU)

11:09:33 PM DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
11:09:35 PM dev8-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

11:09:35 PM DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
11:09:37 PM dev8-0 1.00 0.00 12.00 12.00 0.00 0.00 0.00 0.00

11:09:37 PM DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
11:09:39 PM dev8-0 1.99 0.00 47.76 24.00 0.00 0.50 0.25 0.05

Average: DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
Average: dev8-0 1.00 0.00 19.97 20.00 0.00 0.33 0.17 0.02
需要关注的几个参数含义:

  • await表示平均每次设备I/O操作的等待时间(以毫秒为单位)。
  • svctm表示平均每次设备I/O操作的服务时间(以毫秒为单位)。
  • %util表示一秒中有百分之几的时间用于I/O操作。

对以磁盘IO性能,一般有如下评判标准:
正常情况下svctm应该是小于await值的,而svctm的大小和磁盘性能有关,CPU、内存的负荷也会对svctm值造成影响,过多的请求也会间接的导致svctm值的增加。
await值的大小一般取决与svctm的值和I/O队列长度以及I/O请求模式,如果svctm的值与await很接近,表示几乎没有I/O等待,磁盘性能很好,如果await的值远高于svctm的值,则表示I/O队列等待太长,系统上运行的应用程序将变慢,此时可以通过更换更快的硬盘来解决问题。
%util项的值也是衡量磁盘I/O的一个重要指标,如果%util接近100%,表示磁盘产生的I/O请求太多,I/O系统已经满负荷的在工作,该磁盘可能存在瓶颈。长期下去,势必影响系统的性能,可以通过优化程序或者通过更换更高、更快的磁盘来解决此问题。

5. 网络性能评估

(1)通过ping命令检测网络的连通性
(2)通过netstat –i组合检测网络接口状况
(3)通过netstat –r组合检测系统的路由表信息
(4)通过sar –n组合显示系统的网络运行状态

 

转载自 http://www.itlearner.com/article/4553



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


ITeye推荐



手机终端网页调试程序使用说明

$
0
0

 

(按照本文最后总结部分所述进行操作可以快速抓住要领,学会使用)

一、调试核心程序weinre简介

Weinre是一种远程调试工具,即可以在电脑上调试手机上的页面。调试界面和webkit内核浏览器(chrome、safari等)调试界面很相似,熟悉webkit内核浏览器的开发人员很容易上手使用。

二、weinre的下载、安装及使用

1、Weinre的下载地址如下:(需要java环境支持)

http://pan.baidu.com/netdisk/singlepublic?fid=902768_1583634639(weinre.jar)

2、下载weinre.jar后,将其放在你自己选择的目录。这里为方便说明,放在D盘根目录下面

3、打开cmd,定位到weinre.jar所在目录,这里是D盘

4、输入java -jar weinre.jar --httpPort 8088 --boundHost -all-返回如下信息,则weinre成功开启

5、打开 http://localhost:8088,则打开weinre主页

6、打开 http://localhost:8088/client/#anonymous则进入调试监控页面,这个页面就是我们需要用到的远程调试页面(熟悉chrome等浏览器的开发人员很容易上手)

至此,weinre部分介绍完毕。更多关于weinre的参考资料如下:

http://www.iinterest.net/2012/02/08/debugging-mobile-web-applications-with-the-weinre/

http://blog.csdn.net/dojotoolkit/article/details/6280924

三、连接weinre入口程序cheat.js介绍

为方便用户使用weinre调试程序,本人制作weinre入口程序cheat.js,方便开发人员进行远程调试。

Cheat.js下载地址:

https://tc-svn.tencent.com/isd/isd_snsapp_rep/web_proj/trunk/build/snsapp/debug/cheat.js(需要svn权限)或者

http://pan.baidu.com/netdisk/singlepublic?fid=903571_3362738424(网盘下载)

 

四、入口程序cheat.js使用说明

1、下载的js文件加载到需要进行远程调试的手机页面。

比如: http://qzs.qq.com/snsapp/app/bee/mobile/index.html?uin=462938286&mod=subscribe该程序需要进行远程调试,则在head头部嵌入cheat.js文件

2、激活远程调试入口界面

激活方式:请在间隔不超过2秒的时间内,分别依次在屏幕上3手指触摸,4手指触摸,5手指触摸

按上述激活方式操作后,可以成功看到如下界面:

 

在输入框输入weinre所在服务器的IP地址即可开启手机页面与weinre的连接。

然后在PC端输入http://[weinre所在服务器的IP地址]:8088/client/#anonymous即可远程调试

五、总结

1、weinre服务成功开启(也就是完成到第二大步,第四小步的工作)

2、cheat.js加载到需要调试的手机页面中

3、在该手机页面中操作激活秘籍(3手触摸,4手触摸,5手触摸)

4、在激活入口页面输入weinre所在服务器的IP地址

5、在PC端输入http://[weinre所在服务器的IP地址]:8088/client/#anonymous

 

 

 

 

 

第二种方法

 一、首先确保你的电脑已经搭建好了java环境。即:安装了jdk。这里就不多说了,在网上搜个jdk,一步步安装及ok。

  二、 安装及运行Weinre

    1、下载: http://ishare.iask.sina.com.cn/f/23607399.html  ,这里直接下载jar包,下载好之后放在一个文件夹里就行不需任何处理。
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

     2、运行cmd,在weinre所在文件夹的 地址栏输入代码:java -jar weinre.jar --httpPort 8081 --boundHost -all-  (如下图):

 

     3、回车后会出现相应信息(注意:在调试过程中不要关闭cmd):

 

     4、打开本地浏览器,(使用webkit内核浏览器(chrome、safari))访问 http://localhost:8081/,不出意外的话可以看到weinre的基本信息。

 

     5、上图中的"debug client user interface"是weinre的Debug客户端,点击进入后可以看到目前还没有被测试的目标网页。

 

  三、 添加Debug Target

    为了让需要调试的页面被weinre检测到,需要添加Debug Target,有两种方法:
     1、Target Script 

           该方法需要在调试的页面中增加一个 js

<script src="http://192.168.0.106:8081/target/target-script-min.js#anonymous" type="text/javascript">
</script>

    添加后在移动设备中访问该页面即可,如果调试的页面比较少可以使用这个方法,如果多的话推荐第二种方法

     2、Target Bookmarklet
    该方法是将一段js保存到移动设备的书签中,可以在 http://localhost:8081/ 找到这段js:

javascript:(function(e){e.setAttribute("src","http://localhost:8081/target/target-script-min.js#anonymous");document.getElementsByTagName("body")[0].appendChild(e);})(document.createElement("script"));void(0);

    我将这段js保存到名为Debug书签中,然后使用移动设备访问我想要调试的页面,比如说 http://iinterest.net,最后点击Debug书签就OK了。

   四、真机调试。

    1、 移动设备须有有 wifi无线连接,且和电脑在同一网段,(确保本机安装了 服务器,可以到网上搜 xamppwamp,)把要调试的 页面放在服务器中相应的文件夹中,我安装的是xampp,所以放在htdocs目录下。在手机的页面中打开本页面的地址,如:http://192.168.0.102/index.html 。回到http://localhost:8081页面,点击“debug client user interface:”链接进入weinre的Debug界面,如果成功添加了Debug Target,这里可以看到它。

 

  2、接下来我们就可用自己熟悉的方式调试页面了,并且调试结果会实时显示在移动设备上

 

 

MAC系统更为简单,不用命令行,直接运行app即可启动weinre,接下来的步骤和windows一样。

 

 

 

 

 

 





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


ITeye推荐



hbase client访问的超时时间、重试次数、重试间隔时间的配置

$
0
0

   超时时间、重试次数、重试时间间隔的配置也比较重要,因为默认的配置的值都较大,如果出现hbase集群或者RegionServer以及ZK关掉,则对应用程序是灾难性的,超时和重新等会迅速占满web容器的链接,导致web容器停止服务,关于socket的超时时间,有两种:1:建立连接的超时时间;2:读数据的超时时间。

可以配置如下几个参数:

1. hbase.rpc.timeout:rpc的超时时间,默认60s,不建议修改,避免影响正常的业务,在线上环境刚开始配置的是3秒,运行半天后发现了大量的timeout error,原因是有一个region出现了如下问题阻塞了写操作:“Blocking updates … memstore size 434.3m is >= than blocking 256.0m size”可见不能太低。

2. ipc.socket.timeout:socket建立链接的超时时间,应该小于或者等于rpc的超时时间,默认为20s

3. hbase.client.retries.number:重试次数,默认为14,可配置为3

4. hbase.client.pause:重试的休眠时间,默认为1s,可减少,比如100ms

5. zookeeper.recovery.retry:zk的重试次数,可调整为3次,zk不轻易挂,且如果hbase集群出问题了,每次重试均会对zk进行重试操作,zk的重试总次数是:hbase.client.retries.number * zookeeper.recovery.retry,并且每次重试的休眠时间均会呈2的指数级增长,每次访问hbase均会重试,在一次hbase操作中如果涉及多次zk访问,则如果zk不可用,则会出现很多次的zk重试,非常浪费时间。

6. zookeeper.recovery.retry.intervalmill:zk重试的休眠时间,默认为1s,可减少,比如:200ms

7. hbase.regionserver.lease.period:scan查询时每次与server交互的超时时间,默认为60s,可不调整。

 

 

 



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


ITeye推荐



Viewing all 15843 articles
Browse latest View live


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