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

Hive运行架构及配置部署

$
0
0

Hive 运行架构

由Facebook开源,最初用于解决海量结构化的日志数据统计问题:ETL工具;

   构建于Hadoop的HDFS和MapReduce智商,用于管理和查询结构化/非结构化数据的数据仓库;

   设计目的是让SQL技能良好,但Java技能较弱的分析师可以查询海量数据:

           使用HQL作为查询接口;

           使用HDFS作为存储底层;

           使用MapReduce作为执行层;

   2008年facebook把Hive项目贡献给Apache;

1、Hive的缺点

       Hive的HQL表达能力有限:有些复杂运算用HQL不易表达;

       Hive效率低:Hive自动生成MR作业,通常不够智能;HQL调优困难,粒度较粗;可控性差;

2、Hive系统架构

      1) 元数据存储(Metastore):Hive的数据由两部分组成:数据文件和元数据;

               元数据存储,Derby只能用于一个Hive连接,一般存储在MySQL。

       2)驱动(Driver):编译器、优化器、执行器;

               用户通过下面的接口提交Hive给Driver,由Driver进行HQL语句解析,此时从Metastore中获取表的信息,先生成逻辑计划,再生成物理计划,再由Executor生成Job交给Hadoop运行,然后由Driver将结果返回给用户。

               编译器(Hive的核心):1,语义解析器(ParseDriver),将查询字符串转换成解析树表达式;2,语法解析器(SemanticAnalyzer),将解析树转换成基于语句块的内部查询表达式;3,逻辑计划生成器(Logical Plan Generator),将内部查询表达式转换为逻辑计划,这些计划由逻辑操作树组成,操作符是Hive的最小处理单元,每个操作符处理代表一道HDFS操作或者是MR作业;4,查询计划生成器(QueryPlan Generator),将逻辑计划转化成物理计划(MR Job)。

               优化器:优化器是一个演化组件,当前它的规则是:列修剪,谓词下压。

               执行器:编译器将操作树切分成一个Job链(DAG),执行器会顺序执行其中所有的Job;如果Task链不存在依赖关系,可以采用并发执行的方式进行Job的执行。

       3)接口:CLI(Common LineInterface)、HWI(Hive WebInterface)、ThriftServer;

               CLI:为命令行工具,默认服务。bin/hive或bin/hive--service cli;

               HWI:为Web接口,可以用过浏览器访问Hive,默认端口9999,启动方式为bin/hive --service hwi;

               ThriftServer:通过Thrift对外提供服务,默认端口是10000,启动方式为bin/hive --service hiveserver;

 

       4)其他服务(bin/hive --service -help):metastore(bin/hive --service metastore)、hiveserver2(bin/hive --service hiveserver2),HiveServer2是HieServer改进版本,它提供给新的ThriftAPI来处理JDBC或者ODBC客户端,进行Kerberos身份验证,多个客户端并发,HS2还提供了新的CLI:BeeLine,是Hive 0.11引入的新的交互式CLI,基于SQLLine,可以作为Hive JDBC Client 端访问HievServer2,启动一个beeline就是维护了一个session.

       5)Hadoop:用HDFS进行存储,用MapReduce进行计算;

               表中的一个Partition对应表下的一个子目录,每一个Bucket对应一个文件;Hive的默认数据仓库目录是/user/hive/warehouse,在hive-site.xml中由hive.metastore.warehouse.dir项定义;

3、Hive的运行过程

       由客户端提供查询语句,提交给Hive,Hive再交给Driver处理(1,Compiler先编译,编译时要从Metastore中获取元数据信息,生成逻辑计划;2,生成物理计划;3,由Driver进行优化;4,Executor执行时对物理计划再进行分解成Job,并将这些Job提交给MR的JobTracker运行,提交Job的同时,还需要提取元数据信息关联具体的数据,这些元数据信息送到NN),JT拆分成各个Task进行计算,并将结果返回或写入HDFS。

4、Hive的数据模型

       Database

       Table

       Partition

       Bucket

       File(文件格式:TextFile,RCFile,),

       数据类型,Numeric(Tinyint,Smallint,Bigint,Float,Double,Decimal(Hive 0.13.0可以自定义精度进行扩展)),Date/Time(TIMESTAMP,DATE(0.12.0支持)),String(STRING,VARCHAR(0.12),CHAR(0.13)),Misc(BOLLEAN,BINARY),Complex(ARRAY、MAP、STRUCT、UNIONTYPE)

 

 



 Hive 0.13的配置与部署

使用版本:Hive 0.13.1

1、Metastore(MySQL作为元数据存储,安装于Master1节点,前提是MySQL的访问权限需要设置好。)

1)解压

       tar zxf apache-hive-0.13.1-bin.tar.gz

       mv apache-hive-0.13.1-bin /usr/hive013

       vim /etc/profile加入

         export HIVE_HOME=/usr/hive013

         PATH=$HIVE_HOME/bin:$PATH

         source /etc/profile

2)配置hive-env.sh文件(启动时用到的环境文件)

       HADOOP_HOME=/usr/hadoop-2.2.0   

3)复制hive-default.xml -> hive-site.xml文件(配置文件)

<property>

  <name>javax.jdo.option.ConnectionURL</name>

 <!--<value>jdbc:derby:;databaseName=metastore_db;create=true</value>-->

 <value>jdbc:mysql://master1:3306/metastore?=createDatabaseIfNotExist=true</value>

  <description>JDBC connect string for a JDBCmetastore</description>

</property> 

 

<property>

  <name>javax.jdo.option.ConnectionDriverName</name>

 <!--<value>org.apache.derby.jdbc.EmbeddedDriver</value>-->

  <value>com.mysql.jdbc.Driver</value>

  <description>Driver class name for a JDBCmetastore</description>

</property>

 

<property>

  <name>javax.jdo.option.ConnectionUserName</name>

  <value>hive</value>

  <description>username to use against metastoredatabase</description>

</property>

<property>

  <name>javax.jdo.option.ConnectionPassword</name>

  <value>hive</value>

  <description>password to use against metastoredatabase</description>

</property>

4)拷贝jdbc文件到hive的lib中

     下载jdbc的jar文件http://pan.baidu.com/s/1zYi5o

      cp mysql-connector-java-5.1.26-bin.jar$HIVE_HOME/lib/

2、HiveClient,在Slave1上安装Hive客户端,同样设置好HIVE_HOME的环境变量

1)将Hive013从Master1拷贝到Slave1

     scp -r /usr/hive013  root@slave1:/usr/

2)配置Metastore的连接信息

 <property>

  <name>hive.metastore.uris</name>

  <value>thrift://master1:9083</value>

  <description>Thrift URI for the remote metastore.Used by metastore client to connect to remote metastore.</description>

</property>

3)启动metastore服务

     前台服务:hive--service metastore  CTRL+C关闭

     后台服务:nohup hive--service metastore > metastore.log 2>&1 &    jobs查看后台任务    kill %id关闭后台任务

4)测试链接Metastore

      由于设置好HIVE_HOME环境变量,直接在Shell中输入hive即可打开HiveCLI

5)拷贝至OPC节点,可以达到多个客户端访问metastore

作者:harryxujiao 发表于2014-8-12 17:28:27 原文链接
阅读:58 评论:0 查看评论

分布式日志收集收集系统:Flume

$
0
0

Flume是一个分布式、可靠、和高可用的海量日志采集、聚合和传输的系统。支持在系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种数据接受方(可定制)的能力。Flume 初始的发行版本目前被统称为 Flume OG(original generation),属于 cloudera。但随着 Flume 功能的扩展,Flume OG 代码工程臃肿、核心组件设计不合理、核心配置不标准等缺点暴露出来,尤其是在 Flume OG 的最后一个发行版本 0.94.0 中,日志传输不稳定的现象尤为严重。为了解决这些问题,2011 年 10 月 22 号,cloudera 完成了 Flume-728,对 Flume 进行了里程碑式的改动:重构核心组件、核心配置以及代码架构,重构后的版本统称为 Flume NG(next generation);改动的另一原因是将 Flume 纳入 apache 旗下,cloudera Flume 改名为 Apache Flume。IBM 的这篇文章: Flume NG:Flume 发展史上的第一次革命,从基本组件以及用户体验的角度阐述 Flume OG 到 Flume NG 发生的革命性变化。

一、Flume OG

Flume OG的设计目标:

  1. 可靠性:当节点出现故障时,日志能够被传送到其他节点上而不会丢失。Flume提供了三种级别的可靠性保障,从强到弱依次分别为:end-to-end(收到数据agent首先将event写到磁盘上,当数据传送成功后,再删除;如果数据发送失败,可以重新发送。),Store on failure(这也是scribe采用的策略,当数据接收方crash时,将数据写到本地,待恢复后,继续发送),Best effort(数据发送到接收方后,不会进行确认)。
  2. 可扩展性:Flume采用了三层架构,分别为agent,collector和storage,每一层均可以水平扩展。其中,所有agent和collector由master统一管理,这使得系统容易监控和维护,且master允许有多个(使用ZooKeeper进行管理和负载均衡),这就避免了单点故障问题。
  3. 可管理性:所有agent和Collector由master统一管理,这使得系统便于维护。多master情况,Flume利用ZooKeeper和gossip,保证动态配置数据的一致性。用户可以在master上查看各个数据源或者数据流执行情况,且可以对各个数据源配置和动态加载。Flume提供了web 和shell script command两种形式对数据流进行管理。
  4. 功能可扩展性:用户可以根据需要添加自己的agent,collector或者storage。此外,Flume自带了很多组件,包括各种agent(file,syslog等),collector和storage(file,HDFS等)。

Flume OG的架构:

flume-og

在Flume中,最重要的抽象是data flow(数据流),data flow描述了数据从产生,传输、处理并最终写入目标的一条路径。

flume-1

  • 对于agent数据流配置就是从哪得到数据,把数据发送到哪个collector。
  • 对于collector是接收agent发过来的数据,把数据发送到指定的目标机器上。

Flume框架对hadoop和zookeeper的依赖只是在jar包上,并不要求flume启动时必须将hadoop和zookeeper服务也启动。

如前面提到的,Flume采用了分层架构:分别为Agent,Collector和Storage。Agent用于采集数据,Agent是Flume中产生数据流的地方。同时,Agent会将产生的数据流传输到Collector。Collector用于对数据进行聚合,往往会产生一个更大的流,然后传输到Storage。其中,Agent和Collector均由两部分组成:source和sink,source是数据来源,sink是数据去向。Flume使用两个组件:Master和Node,Node根据在Master shell或web中动态配置,决定其是作为Agent还是Collector。

1、Agent

Agent的作用是将数据源的数据发送给collector。Flume自带了很多直接可用的数据源(source),如:

  • text(“filename”):将文件filename作为数据源,按行发送
  • tail(“filename”):探测filename新产生的数据,按行发送出去
  • fsyslogTcp(5140):监听TCP的5140端口,并且接收到的数据发送出去
  • tailDir(“dirname”[, fileregex=".*"[, startFromEnd=false[, recurseDepth=0]]]):监听目录中的文件末尾,使用正则去选定需要监听的文件(不包含目录),recurseDepth为递归监听其下子目录的深度

更多可参见这位朋友的整理: http://www.cnblogs.com/zhangmiao-chp/archive/2011/05/18/2050465.html

同时提供了很多sink,如:

  • console[("format")] :直接将将数据显示在consolr上
  • text(“txtfile”):将数据写到文件txtfile中
  • dfs(“dfsfile”):将数据写到HDFS上的dfsfile文件中
  • syslogTcp(“host”,port):将数据通过TCP传递给host节点
  • agentSink[("machine"[,port])]:等价于agentE2ESink,如果省略,machine参数,默认使用flume.collector.event.host与flume.collector.event.port作为默认collecotr
  • agentDFOSink[("machine" [,port])]:本地热备agent,agent发现collector节点故障后,不断检查collector的存活状态以便重新发送event,在此间产生的数据将缓存到本地磁盘中
  • agentBESink[("machine"[,port])]:不负责的agent,如果collector故障,将不做任何处理,它发送的数据也将被直接丢弃
  • agentE2EChain:指定多个collector提高可用性。 当向主collector发送event失效后,转向第二个collector发送,当所有的collector失败后,它会非常执着的再来一遍

更多可参见这位朋友的整理: http://www.cnblogs.com/zhangmiao-chp/archive/2011/05/18/2050472.html

2、Collector

Collector的作用是将多个Agent的数据汇总后,加载到Storage中。它的source和sink与agent类似。

数据源(source),如:

  • collectorSource[(port)]:Collector source,监听端口汇聚数据
  •  autoCollectorSource:通过master协调物理节点自动汇聚数据
  • logicalSource:逻辑source,由master分配端口并监听rpcSink

sink,如:

  • collectorSink( “fsdir”,”fsfileprefix”,rollmillis):collectorSink,数据通过collector汇聚之后发送到hdfs, fsdir 是hdfs目录,fsfileprefix为文件前缀码
  • customdfs(“hdfspath”[, "format"]):自定义格式dfs

3、Storage

storage是存储系统,可以是一个普通file,也可以是HDFS,HIVE,HBase,分布式存储等。

4、Master

Master是管理协调Agent和Collector的配置等信息,是flume集群的控制器。

二、Flume NG

对于Flume OG ,可以说他是一个分布式日志收集系统,有Mater概念,依赖于Zookeeper,Agent用于采集数据,Agent是Flume中产生数据流的地方,同时,Agent会将产生的数据流传输到Collector。对应的,collector用于对数据进行聚合,往往会产生一个更大的流。而对于Flume NG,它摒弃了Master和zookeeper,collector也没有了,web配置台也没有了,只剩下source,sink和channel,此时一个Agent的概念包括source、channel和sink,完全由一个分布式系统变成了传输工具。不同机器之间的数据传输不再是OG那样由agent->collector,而是由一个Agent端的sink流向另一个agent的source。

Flume NG中的核心概念:

  • Client:生产数据,运行在一个独立的线程。
  • Source:从Client收集数据,传递给Channel。可以接收外部源发送过来的数据。不同的 source,可以接受不同的数据格式。比如有目录池(spooling directory)数据源,可以监控指定文件夹中的新文件变化,如果目录中有文件产生,就会立刻读取其内容。
  • Channel:是一个存储地,接收source的输出,直到有sink消费掉channel中的数据。Channel中的数据直到进入到下一个channel中或者进入终端才会被删除。当sink写入失败后,可以自动重启,不会造成数据丢失,因此很可靠。
  • Sink:会消费channel中的数据,然后送给外部源或者其他source。如数据可以写入到HDFS或者HBase中。
  • Agent:使用JVM 运行Flume。每台机器运行一个agent,但是可以在一个agent中包含多个sources和sinks。
  • Events:Flume NG传输的数据的基本单位是event,如果是文本文件,通常是一行记录,这也是事务的基本单位。

Flume NG相对于Flume OG的主要变化:

  • sources和sinks 使用channels 进行链接
  • 两个主要channel:in-memory channel,非持久性支持,速度快; JDBC-based channel 持久性支持。
  • 不再区分逻辑和物理node,所有物理节点统称为agents,每个agents 都能运行0个或多个sources 和sinks
  • 不再需要master节点和对zookeeper的依赖,配置文件简单化。
  • 插件化,一部分面对用户,工具或系统开发人员。
  • 使用Thrift、Avro Flume sources 可以从flume0.9.4 发送 events 到flume 1.x

Flume OG节点组成图:

flume-og-1

Flume NG节点组成图:

flume-ng-1

对应于 OG 的特点,FLUM NG 的特点是:

  • NG 只有一种角色的节点:代理节点(agent)。
  • 没有 collector、master 节点。这是核心组件最核心的变化。
  • 去除了 physical nodes、logical nodes 的概念和相关内容。
  • agent 节点的组成也发生了变化。

Flume NG 以agent为最小的独立运行单位。一个agent就是一个JVM。单agent由Source、Sink和Channel三大组件构成。

Flume的数据流由事件(Event)贯穿始终。事件是Flume的基本数据单位,它携带日志数据(字节数组形式)并且携带有头信息,这些Event由Agent外部的Source,比如上图中的Web Server生成。当Source捕获事件后会进行特定的格式化,然后Source会把事件推入(单个或多个)Channel中。可以把Channel看作是一个缓冲区,它将保存事件直到Sink处理完该事件。Sink负责持久化日志或者把事件推向另一个Source。值得注意的是,Flume提供了大量内置的Source、Channel和Sink类型。不同类型的Source、Channel和Sink可以自由组合。组合方式基于用户设置的配置文件,非常灵活。比如:Channel可以把事件暂存在内存里,也可以持久化到本地硬盘上。Sink可以把日志写入HDFS, HBase,甚至是另外一个Source等等。Flume支持用户建立多级流,也就是说,多个agent可以协同工作,并且支持Fan-in、Fan-out、Contextual Routing、Backup Routes。如下图:

flume-ng-2

Flume 允许多个 agent 连在一起,形成前后相连的多级跳:

flume-ng-3

1、 source

Flume 支持 Avro,log4j,syslog 和 http post(body为json格式)。可以让应用程序同已有的Source直接打交道,如AvroSource,SyslogTcpSource。也可以 写一个 Source,以 IPC 或 RPC 的方式接入自己的应用,Avro和 Thrift 都可以(分别有 NettyAvroRpcClient 和 ThriftRpcClient 实现了 RpcClient接口),其中 Avro 是默认的 RPC 协议。具体代码级别的 Client 端数据接入,可以参考官方手册。对现有程序改动最小的使用方式是使用是直接读取程序原来记录的日志文件,基本可以实现无缝接入,不需要对现有程序进行任何改动。 对于直接读取文件 Source,有两种方式:

  • ExecSource: 以运行 Linux 命令的方式,持续的输出最新的数据,如 tail -F 文件名 指令,在这种方式下,取的文件名必须是指定的。 ExecSource 可以实现对日志的实时收集,但是存在Flume不运行或者指令执行出错时,将无法收集到日志数据,无法保证日志数据的完整性。
  •  SpoolSource: 监测配置的目录下新增的文件,并将文件中的数据读取出来。需要注意两点:拷贝到 spool 目录下的文件不可以再打开编辑;spool 目录下不可包含相应的子目录。SpoolSource 虽然无法实现实时的收集数据,但是可以使用以分钟的方式分割文件,趋近于实时。如果应用无法实现以分钟切割日志文件的话, 可以两种收集方式结合使用。在实际使用的过程中,可以结合 log4j 使用,使用 log4j的时候,将 log4j 的文件分割机制设为1分钟一次,将文件拷贝到spool的监控目录。log4j 有一个 TimeRolling 的插件,可以把 log4j 分割文件到 spool 目录。基本实现了实时的监控。Flume 在传完文件之后,将会修改文件的后缀,变为 .COMPLETED(后缀也可以在配置文件中灵活指定)

2、Channel

当前有几个 channel 可供选择,分别是 Memory Channel, JDBC Channel , File Channel,Psuedo Transaction Channel。比较常见的是前三种 channel。

  • MemoryChannel 可以实现高速的吞吐,但是无法保证数据的完整性。
  • MemoryRecoverChannel 在官方文档的建议上已经建义使用FileChannel来替换。
  • FileChannel保证数据的完整性与一致性。在具体配置FileChannel时,建议FileChannel设置的目录和程序日志文件保存的目录设成不同的磁盘,以便提高效率。

File Channel 是一个持久化的隧道(channel),它持久化所有的事件,并将其存储到磁盘中。因此,即使 Java 虚拟机当掉,或者操作系统崩溃或重启,再或者事件没有在管道中成功地传递到下一个代理(agent),这一切都不会造成数据丢失。Memory Channel 是一个不稳定的隧道,其原因是由于它在内存中存储所有事件。如果 java 进程死掉,任何存储在内存的事件将会丢失。另外,内存的空间收到 RAM大小的限制,而 File Channel 这方面是它的优势,只要磁盘空间足够,它就可以将所有事件数据存储到磁盘上。

3、sink

Sink在设置存储数据时,可以向文件系统、数据库、hadoop存数据,在日志数据较少时,可以将数据存储在文件系中,并且设定一定的时间间隔保存数据。在日志数据较多时,可以将相应的日志数据存储到Hadoop中,便于日后进行相应的数据分析。更多sink的内容可以参考 官方手册

从整体上讲,NG 在核心组件上进行了大规模的调整,核心组件的数目由 7 删减到 4。由于 Flume 的使用涉及到众多因素,如 avro、thrift、hdfs、jdbc、zookeeper 等,而这些组件和 Flume 的整合都需要关联到所有组件。所以核心组件的改革对整个 Flume 的使用影响深远:

  • 大大降低了对用户的要求,如不再依赖 zookeeper,用户无需去搭建 zookeeper 集群
  • 用户也不再纠结于 OG 中的模糊概念(尤其是 physical nodes、logical nodes,agent、collector)
  • 有利于 Flume 和其他技术、hadoop 周边组件的整合,比如在 NG 版本中,Flume 轻松实现了和 jdbc、hbase 的集成
  • 将 OG 版本中复杂、大规模、不稳定的标签移除,Flume 实现了向灵活、轻便的转变,而且在功能上更加强大、可扩展性更高

参考链接:

分布式流式处理框架:Storm

$
0
0

Storm是一个免费开源、分布式、高容错的实时计算系统。它与其他大数据解决方案的不同之处在于它的处理方式。Hadoop 在本质上是一个批处理系统,数据被引入 Hadoop 文件系统 (HDFS) 并分发到各个节点进行处理。当处理完成时,结果数据返回到 HDFS 供始发者使用。Hadoop的高吞吐,海量数据处理的能力使得人们可以方便地处理海量数据。但是,Hadoop的缺点也和它的优点同样鲜明——延迟大,响应缓慢,运维复杂。Storm就是为了弥补Hadoop的实时性为目标而被创造出来。Storm 支持创建拓扑结构来转换没有终点的数据流。不同于 Hadoop 作业,这些转换从不停止,它们会持续处理到达的数据。Storm经常用于在实时分析、在线机器学习、持续计算、分布式远程调用和ETL等领域。Storm的部署管理非常简单,而且,在同类的流式计算工具,Storm的性能也是非常出众的。

Strom的优点:

  • 简单的编程模型。类似于MapReduce降低了并行批处理复杂性,Storm降低了进行实时处理的复杂性。
  • 可以使用各种编程语言。你可以在Storm之上使用各种编程语言。默认支持Clojure、Java、Ruby和Python。要增加对其他语言的支持,只需实现一个简单的Storm通信协议即可。
  • 容错性。Storm会管理工作进程和节点的故障。模块都是无状态的,随时宕机重启。由于是分布式,一个节点挂了不能影响系统的正常运行。
  • 水平扩展。计算是在多个线程、进程和服务器之间并行进行的。
  • 可靠的消息处理。Storm保证每个消息至少能得到一次完整处理。任务失败时,它会负责从消息源重试消息。
  • 快速。系统的设计保证了消息能得到快速的处理,使用ZeroMQ(新的消息机制使用netty代替ZeroMQ)作为其底层消息队列。
  • 本地模式。Storm有一个“本地模式”,可以在处理过程中完全模拟Storm集群。这让你可以快速进行开发和单元测试。

Storm的组成:

在介绍Storm前我们先来看下它与Hadoop的对比:

hadoop-storm

Storm主要分为两种组件Nimbus和Supervisor。这两种组件都是快速失败的,没有状态。任务状态和心跳信息等都保存在Zookeeper上的,提交的代码资源都在本地机器的硬盘上。Storm中的一些概念:

  • Nimbus:负责资源分配和任务调度。集群里面发送代码,分配工作给机器,并且监控状态。全局只有一个。
  • Supervisor:负责接受nimbus分配的任务,启动和停止属于自己管理的worker进程。会监听分配给它那台机器的工作,根据需要启动/关闭工作进程Worker。每一个要运行Storm的机器上都要部署一个,并且,按照机器的配置设定上面分配的槽位数。
  • Worker:运行具体处理组件逻辑的进程。
  • Task:worker中每一个spout/bolt的线程称为一个task. 在storm0.8之后,task不再与物理线程对应,同一个spout/bolt的task可能会共享一个物理线程,该线程称为executor。
  • Zookeeper:Storm重点依赖的外部资源。Nimbus和Supervisor甚至实际运行的Worker都是把心跳保存在Zookeeper上的。Nimbus也是根据Zookeerper上的心跳和任务运行状况,进行调度和任务分配的。
  • Topology:storm中运行的一个实时应用程序,因为各个组件间的消息流动形成逻辑上的一个拓扑结构。Topology处理的最小的消息单位是一个Tuple,也就是一个任意对象的数组。Topology由Spout和Bolt构成。
  • Spout:在一个topology中产生源数据流的组件。通常情况下spout会从外部数据源(如Message Queue、RDBMS、NoSQL、Realtime Log)中读取数据,然后转换为topology内部的源数据。Spout是一个主动的角色,其接口中有个nextTuple()函数,storm框架会不停地调用此函数,用户只要在其中生成源数据即可。
  • Bolt:在一个topology中接受数据然后执行处理的组件。Bolt可以执行过滤、函数操作、合并、写数据库等任何操作。Bolt是一个被动的角色,其接口中有个execute(Tuple input)函数,在接受到消息后会调用此函数,用户可以在其中执行自己想要的操作。
  • Tuple:一次消息传递的基本单元。本来应该是一个key-value的map,但是由于各个组件间传递的tuple的字段名称已经事先定义好,所以tuple中只要按序填入各个value就行了,所以就是一个value list。
  • Stream:源源不断传递的tuple就组成了stream。是Storm中对数据进行的抽象,它是时间上无界的tuple元组序列。在Topology中,Spout是Stream的源头,负责为Topology从特定数据源发射Stream;Bolt可以接收任意多个Stream作为输入,然后进行数据的加工处理过程,如果需要,Bolt还可以发射出新的Stream给下级Bolt进行处理。
  • Stream Grouping即消息的partition方法。目前Storm中提供了以下7种Stream Grouping策略:Shuffle Grouping、Fields Grouping、All Grouping、Global Grouping、Non Grouping、Direct Grouping、Local or shuffle grouping,具体策略可以参考 这里

下图描述了Nimbus、Supervisor、Worker、Task、Zookeeper这几个角色之间的关系:

storm-1

在Storm中,一个实时应用的计算任务被打包作为Topology发布,这同Hadoop的MapReduce任务相似。但是有一点不同的是:在Hadoop中,MapReduce任务最终会执行完成后结束;而在Storm中,Topology任务一旦提交后永远不会结束,除非你显示去停止任务。计算任务Topology是由不同的Spouts和Bolts,通过数据流(Stream)连接起来的图。下面是一个Topology的结构示意图:

Topology

Topology中每一个计算组件(Spout和Bolt)都有一个并行执行度,在创建Topology时可以进行指定,Storm会在集群内分配对应并行度个数的线程来同时执行这一组件。既然对于一个Spout或Bolt,都会有多个task线程来运行,那么如何在两个组件(Spout和Bolt)之间发送tuple元组呢?Storm提供了若干种数据流分发(Stream Grouping)策略用来解决这一问题。在Topology定义时,需要为每个Bolt指定接收什么样的Stream作为其输入(注:Spout并不需要接收Stream,只会发射Stream)。

下图是Topology的提交流程图:

storm-2

Storm 的一个最有趣的地方是它注重容错和管理。Storm 实现了有保障的消息处理,所以每个元组都会通过该拓扑结构进行全面处理;如果发现一个元组还未处理,它会自动从喷嘴处重放。Storm 还实现了任务级的故障检测,在一个任务发生故障时,消息会自动重新分配以快速重新开始处理。Storm 包含比 Hadoop 更智能的处理管理,流程会由监管员来进行管理,以确保资源得到充分使用。

下图是Storm的数据交互图。可以看出两个模块Nimbus和Supervisor之间没有直接交互。状态都是保存在Zookeeper上。Worker之间通过ZeroMQ(新的消息机制使用netty代替ZeroMQ)传送数据。

storm-3

Storm 使用 ZeroMQ 传送消息,这就消除了中间的排队过程,使得消息能够直接在任务自身之间流动。在消息的背后,是一种用于序列化和反序列化 Storm 的原语类型的自动化且高效的机制。

Storm的应用:

Storm被广泛应用于实时分析,在线机器学习,持续计算、分布式远程调用等领域。如果,业务场景中需要低延迟的响应,希望在秒级或者毫秒级完成分析、并得到响应,而且希望能够随着数据量的增大而拓展。那就可以考虑使用Storm。Storm的适用场景:

  • 流数据处理。Storm可以用来处理源源不断流进来的消息,处理之后将结果写入到某个存储中去。
  • 分布式rpc。由于storm的处理组件是分布式的,而且处理延迟极低,所以可以作为一个通用的分布式rpc框架来使用。

来看一些实际的应用:

  • 一淘- 实时分析系统pora:实时分析用户的属性,并反馈给搜索引擎。最初,用户属性分析是通过每天在云梯上定时运行的MR job来完成的。为了满足实时性的要求,希望能够实时分析用户的行为日志,将最新的用户属性反馈给搜索引擎,能够为用户展现最贴近其当前需求的结果。
  • 携程- 网站性能监控:实时分析系统监控携程网的网站性能。利用HTML5提供的performance标准获得可用的指标,并记录日志。Storm集群实时分析日志和入库。使用DRPC聚合成报表,通过历史数据对比等判断规则,触发预警事件。

参考资料:

Android这一仗还没打完 果冻壳诺基亚X2国内上市

$
0
0

微软的政策还没贯彻到底,诺基亚得把手中的牌全面亮出去,就像是今天正式登陆中国的诺基亚X2双卡双待手机。对诺基亚X系列熟识的同学对于这款手机身在微软阵营却采用Android系统的本质应该相当了解了,这个系列的手机即便有着AOSP的内里,服务却是一水儿的微软特色,包括必应搜索、OneDrive存储、MixRadio音乐等。

Android这一仗还没打完 果冻壳诺基亚X2国内上市

这次推广的诺基亚X2除了同样秉持微软风格,在配置上相比一代的X也有了些升级,包括1.2GHz高通骁龙200双核处理器、1GB RAM内存、支持自动对焦的500万像素摄像头、携有闪光灯、前置VGA规格摄像头,这些相比诺基亚X确实好了不少,即便整体仍在入门级水准上。国行版的诺基亚X2在硬件方面的升级亮点则在4.3英寸的全贴合ClearBlack悦幕显示屏,前面板的质感也因此好了不少;此外可更换的彩壳采用“果冻双层”设计,据说也是为了配合炎炎夏日,这种“果冻”后壳有“冰爽”的意思。

最早一批低配诺基亚X遭人诟病的最大问题在系统使用的流畅性上,诺基亚X2期望通过硬件升级来解决这样的尴尬,但更重要的是诺基亚X软件平台2.0版本也随同上线了。针对诺基亚版的Android系统,2.0平台的更新内容主要包括基于AOSP的果冻豆4.3系统,新功能如长按“返回”按键可开启多任务操作屏幕,并可点击下方关闭按键结束某个任务——这一点实际在前一阵面向Nokia X初代系列产品的系统更新中也出现过,虽然后者的系统执行效率相比前者更低一些;另外还有一些小功能升级,像是拍照界面中,模式、拍摄和相册按钮被统一为拍摄按键,通知中心加入双SIM切换入口等,新加入的WP风格应用列表等。

Android这一仗还没打完 果冻壳诺基亚X2国内上市

诺基亚X2双卡双待手机从今天起就可以在诺基亚天猫官方旗舰店预订了,据说前2000名预约的用户能够获赠移动电源和内存卡。有诺基亚情节的同学还是可以关注这款Android设备,毕竟这样的产品在微软和诺基亚的历史上可能都并不多见:诺基亚X2后壳有绿橙黑白四种颜色可选,国内初上市的价格为799元。话说诺基亚XL 4G版刚开卖不久,诺基亚X2就又开始卖了,各位渴求诺基亚Android手机的小伙伴有否因此获得满足,还是不知如何下手...

如何安全地关闭MySQL实例

$
0
0

   本文分析了mysqld进程关闭的过程,以及如何安全、缓和地关闭MySQL实例,对这个过程不甚清楚的同学可以参考下。

关闭过程:

  • 1、发起shutdown,发出  SIGTERM信号

  • 2、有必要的话,新建一个关闭线程(shutdown thread)

  •    如果是客户端发起的关闭,则会新建一个专用的关闭线程

       如果是直接收到 SIGTERM 信号进行关闭的话,专门负责信号处理的线程就会负责关闭工作,或者新建一个独立的线程负责这个事

       当无法创建独立的关闭线程时(例如内存不足),MySQL Server会发出类似下面的告警信息:

       Error: Can’t create thread to kill server

  • 3、MySQL Server不再响应新的连接请求

  •    关闭TCP/IP网络监听,关闭Unix Socket等渠道

  • 4、逐渐关闭当前的连接、事务

  •    空闲连接,将立刻被终止;

       当前还有事务、SQL活动的连接,会将其标识为 killed,并定期检查其状态,以便下次检查时将其关闭;(参考 KILL 语法)

       当前有活跃事务的,该事物会被回滚,如果该事务中还修改了非事务表,则已经修改的数据无法回滚,可能只会完成部分变更;

       如果是Master/Slave复制场景里的Master,则对复制线程的处理过程和普通线程也是一样的;

       如果是Master/Slave复制场景里的Slave,则会依次关闭IO、SQL线程,如果这2个线程当前是活跃的,则也会加上 killed 标识,然后再关闭;

       Slave服务器上,SQL线程是允许直接停止当前的SQL操作的(为了避免复制问题),然后再关闭该线程;

       在MySQl 5.0.80及以前的版本里,如果SQL线程当时正好执行一个事务到中间,该事务会回滚;从5.0.81开始,则会等待所有的操作结束,除非用户发起KILL操作。

       当Slave的SQL线程对非事务表执行操作时被强制 KILL了,可能会导致Master、Slave数据不一致;

  • 5、MySQL Server进程关闭所有线程,关闭所有存储引擎;

  •    刷新所有表cache,关闭所有打开的表;

       每个存储引擎各自负责相关的关闭操作,例如MyISAM会刷新所有等待写入的操作;InnoDB会将buffer pool刷新到磁盘中(从MySQL 5.0.5开始,如果innodb_fast_shutdown不设置为 2 的话),把当前的LSN记录到表空间中,然后关闭所有的内部线程。

  • 6、MySQL Server进程退出

  • 关于KILL指令

       从5.0开始,KILL 支持指定  CONNECTION | QUERY两种可选项:

  • KILL CONNECTION和原来的一样,停止回滚事务,关闭该线程连接,释放相关资源;

  • KILL QUERY则只停止线程当前提交执行的操作,其他的保持不变;

  •    提交KILL操作后,该线程上会设置一个特殊的 kill标记位。通常需要一段时间后才能真正关闭线程,因为kill标记位只在特定的情况下才检查:

  • 1、执行SELECT查询时,在ORDER BY或GROUP BY循环中,每次读完一些行记录块后会检查 kill标记位,如果发现存在,该语句会终止;

  • 2、执行ALTER TABLE时,在从原始表中每读取一些行记录块后会检查 kill 标记位,如果发现存在,该语句会终止,删除临时表;

  • 3、执行UPDATE和DELETE时,每读取一些行记录块并且更新或删除后会检查 kill 标记位,如果发现存在,该语句会终止,回滚事务,若是在非事务表上的操作,则已发生变更的数据不会回滚;

  • 4、GET_LOCK() 函数返回NULL;

  • 5、INSERT DELAY线程会迅速内存中的新增记录,然后终止;

  • 6、如果当前线程持有表级锁,则会释放,并终止;

  • 7、如果线程的写操作调用在等待释放磁盘空间,则会直接抛出“磁盘空间满”错误,然后终止;

  • 8、当MyISAM表在执行REPAIR TABLE 或 OPTIMIZE TABLE 时被 KILL的话,会导致该表损坏不可用,指导再次修复完成。

  • 安全关闭MySQL几点建议

       想要安全关闭 mysqld 服务进程,建议按照下面的步骤来进行:

  • 0、用具有SUPER、ALL等最高权限的账号连接MySQL,最好是用 unix socket 方式连接;

  • 1、在5.0及以上版本,设置innodb_fast_shutdown = 1,允许快速关闭InnoDB(不进行full purge、insert buffer merge),如果是为了升级或者降级MySQL版本,则不要设置;

  • 2、设置innodb_max_dirty_pages_pct = 0,让InnoDB把所有脏页都刷新到磁盘中去;

  • 3、设置max_connections和max_user_connections为1,也就最后除了自己当前的连接外,不允许再有新的连接创建;

  • 4、关闭所有不活跃的线程,也就是状态为Sleep  且 Time 大于 1 的线程ID;

  • 5、执行 SHOW PROCESSLIST  确认是否还有活跃的线程,尤其是会产生表锁的线程,例如有大数据集的SELECT,或者大范围的UPDATE,或者执行DDL,都是要特别谨慎的;

  • 6、执行 SHOW ENGINE INNODB STATUS 确认History list length的值较低(一般要低于500),也就是未PURGE的事务很少,并且确认Log sequence number、Log flushed up to、Last checkpoint at三个状态的值一样,也就是所有的LSN都已经做过检查点了;

  • 7、然后执行FLUSH LOCKAL TABLES 操作,刷新所有 table cache,关闭已打开的表(LOCAL的作用是该操作不记录BINLOG);

  • 8、如果是SLAVE服务器,最好是先关闭 IO_THREAD,等待所有RELAY LOG都应用完后,再关闭 SQL_THREAD,避免 SQL_THREAD 在执行大事务被终止,耐心待其全部应用完毕,如果非要强制关闭的话,最好也等待大事务结束后再关闭SQL_THREAD;

  • 9、最后再执行 mysqladmin shutdown。

  • 10、紧急情况下,可以设置innodb_fast_shutdown = 1,然后直接执行 mysqladmin shutdown 即可,甚至直接在操作系统层调用 kill 或者 kill -9 杀掉 mysqld 进程(在innodb_flush_log_at_trx_commit = 0 的时候可能会丢失部分事务),不过mysqld进程再次启动时,会进行CRASH RECOVERY工作,需要有所权衡。

  •    嗦那么多,其实正常情况下执行 mysqladmin shutdown 就够了,如果发生阻塞,再参考上面的内容进行分析和解决吧,哈哈:)

抱歉猜想失败,您看看下面的文章有用吗?

MySQL大数据量主库如何部署从库

$
0
0

我们在部署MySQL Replication从库时,通常是一开始就做好一个从库,然后随着业务的变化,数据也逐渐复制到从服务器。

但是,如果我们想对一个已经上线较久,有这大数据量的数据库部署复制从库时,应该怎么处理比较合适呢?

本文以我近期所做Zabbix数据库部署MySQL Replication从库为例,向大家呈现一种新的复制部署方式。由于Zabbix历史数据非常多, 在转TokuDB之前的InnoDB引擎时,已经接近700G,转成TokuDB后,还有300多G,而且主要集中在trends_uint、history_uint等几个大表上。做一次全量备份后再恢复耗时太久,怕对主库写入影响太大,因此才有了本文的分享。

我大概分为几个步骤来做Zabbix数据迁移的:

1、初始化一个空的Zabbix库

2、启动复制,但设置忽略几个常见错误(这几个错误代码对应具体含义请自行查询手册)
#忽略不重要的错误,极端情况下,甚至可以直接忽略全部错误,例如
#slave-skip-errors=all
slave-skip-errors=1032,1053,1062

3、将大多数小表正常备份导出,在SLAVE服务器上导入恢复。在这里,正常导出即可,无需特别指定 --master-data 选项

4、逐一导出备份剩下的几个大表。在备份大表时,还可以分批次并发导出,方便并发导入,使用mysqldump的"-w"参数,然后在SLAVE上导入恢复(可以打开后面的参考文章链接)

5、全部导入完成后,等待复制没有延迟了,关闭忽略错误选项,重启,正式对外提供服务

上述几个步骤完成后,可能还有个别不一致的数据,不过会在后期逐渐被覆盖掉,或者被当做过期历史数据删除掉。

本案例的步骤并不适用于全部场景,主要适用于:

不要求数据高一致性,且数据量相对较大,尤其是单表较大的情况,就像本次的Zabbix数据一样。

参考文章:

迁移Zabbix数据库到TokuDB

[MySQL FAQ]系列— mysqldump加-w参数备份

猜您喜欢

百度是如何使用hadoop的

$
0
0

标签:   hadoop   百度

   百度作为全球最大的中文搜索引擎公司,提供基于搜索引擎的各种产品,几乎覆盖了中文网络世界中所有的搜索需求,因此,百度对海量数据处理的要求是比较高的, 要在线下对数据进行分析,还要在规定的时间内处理完并反馈到平台上。百度在互联网领域的平台需求要通过性能较好的云平台进行处理了,Hadoop就是很好 的选择。在百度,Hadoop主要应用于以下几个方面:

   

   日志的存储和统计;      

   网页数据的分析和挖掘;      

   商业分析,如用户的行为和广告关注度等;      

   在线数据的反馈,及时得到在线广告的点击情况;      

   用户网页的聚类,分析用户的推荐度及用户之间的关联度。

   MapReduce主要是一种思想,不能解决所有领域内与计算有关的问题,百度的研究人员认为比较好的模型应该如下图:

    map reduce

   HDFS 实现共享存储,一些计算使用MapReduce解决,一些计算使用MPI解决,而还有一些计算需要通过两者来共同处理。因为MapReduce适合处理数 据很大且适合划分的数据,所以在处理这类数据时就可以用MapReduce做一些过滤,得到基本的向量矩阵,然后通过MPI进一步处理后返回结果,只有整 合技术才能更好地解决问题。

   百度现在拥有3个Hadoop集群,总规模在700台机器左右,其中有100多台新机器和600多台要淘汰的机器(它们的计算能力相当于200多台新机器),不过其规模还在不断的增加中。现在每天运行的MapReduce任务在3000个左右,处理数据约120TB/天。

   百度为了更好地用Hadoop进行数据处理,在以下几个方面做了改进和调整:

   (1)调整MapReduce策略

   限制作业处于运行状态的任务数;      

   调整预测执行策略,控制预测执行量,一些任务不需要预测执行;      

   根据节点内存状况进行调度;      

   平衡中间结果输出,通过压缩处理减少I/O负担。

   (2)改进HDFS的效率和功能

   权限控制,在PB级数据量的集群上数据应该是共享的,这样分析起来比较容易,但是需要对权限进行限制;      

   让分区与节点独立,这样,一个分区坏掉后节点上的其他分区还可以正常使用;      

   修改DSClient选取块副本位置的策略,增加功能使DFSClient选取块时跳过出错的DataNode;      

   解决VFS(Virtual File System)的POSIX(Portable Operating System Interface of Unix)兼容性问题。

   (3)修改Speculative的执行策略

   采用速率倒数替代速率,防止数据分布不均时经常不能启动预测执行情况的发生;      

   增加任务时必须达到某个百分比后才能启动预测执行的限制,解决reduce运行等待map数据的时间问题;      

   只有一个map或reduce时,可以直接启动预测执行。

   (4)对资源使用进行控制

   对应用物理内存进行控制。如果内存使用过多会导致操作系统跳过一些任务,百度通过修改Linux内核对进程使用的物理内存进行独立的限制,超过阈值可以终止进程。分组调度计算资源,实现存储共享、计算独立,在Hadoop中运行的进程是不可抢占的。在大块文件系统中,X86平台下一个页的大小是4KB。如果页较小,管理的数据就会很多,会增加数据操作的代价并影响计算效率,因此需要增加页的大小。

   百度在使用Hadoop时也遇到了一些问题,主要有:

  •    MapReduce的效率问题:比如,如何在shuffle效率方面减少I/O次数以提高并行效率;如何在排序效率方面设置排序为可配置的,因为排序过程会浪费很多的计算资源,而一些情况下是不需要排序的。

  •    HDFS的效率和可靠性问题:如何提高随机访问效率,以及数据写入的实时性问题,如果Hadoop每写一条日志就在HDFS上存储一次,效率会很低。

  •    内存使 用的问题:reducer端的shuffle会频繁地使用内存,这里采用类似Linux的buddy system来解决,保证Hadoop用最小的开销达到最高的利用率;当Java 进程内容使用内存较多时,可以调整垃圾回收(GC)策略;有时存在大量的内存复制现象,这会消耗大量CPU资源,同时还会导致内存使用峰值极高,这时需要 减少内存的复制。

  •    作业调度的问题:如何限制任务的map和reduce计算单元的数量,以确保重要计算可以有足够的计算单元;如何对TaskTracker进行分组控制,以限制作业执行的机器,同时还可以在用户提交任务时确定执行的分组并对分组进行认证。

  •    性能提 升的问题:UserLogs cleanup在每次task结束的时候都要查看一下日志,以决定是否清除,这会占用一定的任务资源,可以通过将清理线程从子Java进程移到 TaskTracker来解决;子Java进程会对文本行进行切割而map和reduce进程则会重新切割,这将造成重复处理,这时需要关掉Java进程 的切割功能;在排序的时候也可以实现并行排序来提升性能;实现对数据的异步读写也可以提升性能。

  •    健壮性 的问题:需要对mapper和reducer程序的内存消耗进行限制,这就要修改Linux内核,增加其限制进程的物理内存的功能;也可以通过多个map 程序共享一块内存,以一定的代价减少对物理内存的使用;还可以将DataNode和TaskTracker的UGI配置为普通用户并设置账号密码;或者让 DataNode和TaskTracker分账号启动,确保HDFS数据的安全性,防止Tracker操作DataNode中的内容;在不能保证用户的每 个程序都很健壮的情况下,有时需要将进程终止掉,但要保证父进程终止后子进程也被终止。

  •    Streaming 局限性的问题:比如,只能处理文本数据,mapper和reducer按照文本行的协议通信,无法对二进制的数据进行简单处理。为了解决这个问题,百度人 员新写了一个类Bistreaming(Binary Streaming),这里的子Java进程mapper和reducer按照(KeyLen,Key,ValLen,Value)的方式通信,用户可以 按照这个协议编写程序。

  •    用户认证的问题:这个问题的解决办法是让用户名、密码、所属组都在NameNode和Job Tracker上集中维护,用户连接时需要提供用户名和密码,从而保证数据的安全性。

  •    百度下一步的工作重点可能主要会涉及以下内容:

  •    内存方面,降低NameNode的内存使用并研究JVM的内存管理;

  •    调度方面,改进任务可以被抢占的情况,同时开发出自己的基于Capacity的作业调度器,让等待作业队列具有优先级且队列中的作业可以设置Capacity,并可以支持TaskTracker分组;

  •    压缩算 法,选择较好的方法提高压缩比、减少存储容量,同时选取高效率的算法以进行shuffle数据的压缩和解压;对mapper程序和reducer程序使用 的资源进行控制,防止过度消耗资源导致机器死机。以前是通过修改Linux内核来进行控制的,现在考虑通过在Linux中引入cgroup来对 mapper和reducer使用的资源进行控制;将DataNode的并发数据读写方式由多线程改为select方式,以支持大规模并发读写和 Hypertable的应用。

  •    百度同时也在使用Hypertable,它是以Google发布的BigTable为基础的开源分布式数据存储系统,百度将它作为分析用户行为的平台,同时在元数据集中化、内存占用优化、集群安全停机、故障自动恢复等方面做了一些改进。

       via: 中国大数据


您可能还对下面的文章感兴趣:

  1. 央视批百度批错了么? [2011-08-24 14:04:10]
  2. 在百度的第一年 [2011-07-05 23:13:44]
  3. 百度日本-四面楚歌 [2011-02-20 23:35:19]
  4. 百度这个公司 [2010-11-22 21:17:57]
  5. 我们需要什么样的网站数据 [2010-11-03 23:50:24]
  6. 读书笔记-壹百度:百度十年千倍的29条法则 [2010-10-31 23:22:30]
  7. 实名制是核心问题吗? [2010-10-07 22:22:42]
  8. 百度的框,请移动一下 [2010-09-09 22:03:53]
  9. 手机产品设计方向 [2010-08-17 10:17:56]
  10. 集中暴创新项目,各大互联网公司都有 [2010-03-24 23:31:55]
  11. php实现百度音乐采集下载 [2009-12-20 12:51:35]
  12. 整合搜索,阿拉丁,云计算,以及框计算 [2009-11-04 09:23:54]

Linux系统巡检常用命令

$
0
0

标签:   巡检   检查

   Linux系统需要定期巡检,以检查服务器软硬件使用情况,相当于对人的体检,确保可以及时发现问题、解决问题,降低损失,常用的巡检命令如下:

   # uname -a # 查看内核/操作系统/CPU信息

   # head -n 1 /etc/issue # 查看操作系统版本

   # cat /proc/cpuinfo # 查看CPU信息

   # hostname # 查看计算机名

   # lspci -tv # 列出所有PCI设备

   # lsusb -tv # 列出所有USB设备

   # lsmod # 列出加载的内核模块

   # env # 查看环境变量

   # free -m # 查看内存使用量和交换区使用量

   # df -h # 查看各分区使用情况

   # du -sh < 目录名> # 查看指定目录的大小

   # grep MemTotal /proc/meminfo # 查看内存总量

   # grep MemFree /proc/meminfo # 查看空闲内存量

   # uptime # 查看系统运行时间、用户数、负载

   # cat /proc/loadavg # 查看系统负载

   # mount | column -t # 查看挂接的分区状态

   # fdisk -l # 查看所有分区

   # swapon -s # 查看所有交换分区

   # hdparm -i /dev/hda # 查看磁盘参数(仅适用于IDE设备)

   # dmesg | grep IDE # 查看启动时IDE设备检测状况

   # ifconfig # 查看所有网络接口的属性

   # iptables -L # 查看防火墙设置

   # route -n # 查看路由表

   # netstat -lntp # 查看所有监听端口

   # netstat -antp # 查看所有已经建立的连接

   # netstat -s # 查看网络统计信息

   # ps -ef # 查看所有进程

   # top # 实时显示进程状态

   # w # 查看活动用户

   # id < 用户名> # 查看指定用户信息

   # last # 查看用户登录日志

   # cut -d: -f1 /etc/passwd # 查看系统所有用户

   # cut -d: -f1 /etc/group # 查看系统所有组

   # crontab -l # 查看所有用户的定时任务

您可能还对下面的文章感兴趣:

  1. InnoDB线程并发检查机制 [2010-02-25 09:29:49]
  2. InnoDB线程并发检查机制 [2009-11-18 09:26:50]

Tomcat 的三种(bio,nio.apr) 高级 Connector 运行模式

$
0
0

tomcat的运行模式有3种.修改他们的运行模式.3种模式的运行是否成功,可以看他的启动控制台,或者启动日志.或者登录他们的默认页面http://localhost:8080/查看其中的服务器状态。 

1)bio 

默认的模式,性能非常低下,没有经过任何优化处理和支持. 

2)nio 

利用java的异步io护理技术,no blocking IO技术. 

想运行在该模式下,直接修改server.xml里的Connector节点,修改protocol为 

<Connector port="80" protocol="org.apache.coyote.http11.Http11NioProtocol" 
	connectionTimeout="20000" 
	URIEncoding="UTF-8" 
	useBodyEncodingForURI="true" 
	enableLookups="false" 
	redirectPort="8443" /> 

启动后,就可以生效。 

3)apr 

安装起来最困难,但是从操作系统级别来解决异步的IO问题,大幅度的提高性能. 

必须要安装apr和native,直接启动就支持apr。下面的修改纯属多余,仅供大家扩充知识,但仍然需要安装apr和native 

如nio修改模式,修改protocol为org.apache.coyote.http11.Http11AprProtocol

 

 

Tomcat的四种基于HTTP协议的Connector性能比较
<Connector port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol"                           connectionTimeout="20000" redirectPort="8443"/>
<Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000"
               redirectPort="8443"/>
<Connector executor="tomcatThreadPool"
               port="8081" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
<Connector executor="tomcatThreadPool"
               port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol"
               connectionTimeout="20000"
               redirectPort="8443" />

我们姑且把上面四种Connector按照顺序命名为 NIO, HTTP, POOL, NIOP

为了不让其他因素影响测试结果,我们只对一个很简单的jsp页面进行测试,这个页面仅仅是输出一个Hello World。假设地址是 http://tomcat1/test.jsp

我们依次对四种Connector进行测试,测试的客户端在另外一台机器上用ab命令来完成,测试命令为: ab -c 900 -n 2000 http://tomcat1/test.jsp ,最终的测试结果如下表所示(单位:平均每秒处理的请求数):



NIO HTTP POOL NIOP
281 65 208 365
666 66 110 398
692 65 66 263
256 63 94 459
440 67 145 363

由这五组数据不难看出,HTTP的性能是很稳定,但是也是最差的,而这种方式就是Tomcat的默认配置。NIO方式波动很大,但没有低于280 的,NIOP是在NIO的基础上加入线程池,可能是程序处理更复杂了,因此性能不见得比NIO强;而POOL方式则波动很大,测试期间和HTTP方式一样,不时有停滞。

由于linux的内核默认限制了最大打开文件数目是1024,因此此次并发数控制在900。

尽管这一个结果在实际的网站中因为各方面因素导致,可能差别没这么大,例如受限于数据库的性能等等的问题。但对我们在部署网站应用时还是具有参考价值的。



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


ITeye推荐



七个让你意想不到的Android系统应用领域

$
0
0

108813837

 

谷歌的Android Wear操作系统将Android从用户的智能手机带到了手腕上。而未来谷歌绝不希望Android仅仅局限于移动平台和可穿戴设备领域。虽然在今年的I/O大会上谷歌已经宣布希望能够在更多的地方应用Android系统,不过现在实际上我们已经可以在一些非常意想不到的地方见到Android系统的身影。

对于智能手表、相机这样的领域,预装Android系统早已不是什么新鲜事。今天我们就为大家来找找那些你绝对想不到,但是同样可以见到Android系统身影的领域,其中一些甚至让人觉得非常“荒谬”。

Android在厨房

108813848

 

用谷歌搜索一下,你就会看到各种各样内置Android系统的厨房电器或电子设备,并且数量还相当庞大。

之前松下开发出了一款基于Android系统和云服务的微波炉,它可以自动搜索食谱及解冻食品;另外还有一款Android系统电饭煲,可以通过RFID与Android应用程序直接交换食谱。在去年拉斯维加斯的CES消费电子展上,三星展示了一款型号为T9000的智能冰箱,在这台冰箱的门上镶嵌了一款搭载Android系统的触摸显示屏,不仅拥有普通平板电脑的功能,同时还可以直接通过Android系统对冰箱的温度和功能进行控制。

虽然智能电器是不错的注意,可以帮助我们节省不少在厨房中的时间,但是目前这类产品的价格都非常高,并且好多还没有量产,因此在厨房中的Android系统电器还需要一段时间发展。

汽车中的另一种Android

108813859

 

Android Auto将会成为下一代连接智能手机与汽车系统的桥梁,它可以直接将Android系统手机与汽车的控制系统同步,让普通的汽车瞬间变成“智能汽车”。

不过在谷歌正式宣布Android Auto之前,在2014年款的起亚Soul车型的仪表盘上就已经开始使用Android系统。不过看上去它与我们在平板电脑或智能手机上已经熟悉的Android系统并不怎么一样,但这也许就是谷歌为什么如此急于推出Android Auto来统一汽车Android操作的界面。

Android打印机

108813868

 

一段时间之前,惠普曾经推出了Photosmart eStation c150多功能打印机,这款打印机上配备了3.45英寸的Android系统显示屏,可以直接运行应用程序、游戏及用来扫描文件。

奇怪的是,这台打印机的Android系统似乎没有对打印功能本身有任何促进作用,看上去就像是单纯的在打印机上镶嵌了一台平板电脑而已。另外,宝丽来在今年年初也喜欢不推出了同样内置Android系统的拍立得相机。

不过无论如何,Android KitKat系统的加入暂时并没有人让打印机或拍立得本身的功能有任何加强,因此如何更好的开发系统,还需要厂商们继续努力。

看看镜子里的Android

108813872

 

日本Seraku公司曾经展示了一种名叫Smart Basin的智能浴室镜,可以作为完整的信息终端。通过内置的应用程序可以检测每日浴室的用水量、管理用户的体重及嵌入式的信息展示。

其实这种智能镜子就是将一台大号的平板电脑,只不过将屏幕做成了镜面的形式。但无论是哪种方式,Android系统的加入都能够让我们使用上更有趣的“智能浴室”。

“Android Touch”

108813875

 

有好几个朋友都问我为什么有一款像iPod那样搭载Android系统的音乐播放器呢?虽然iPod已经有很长时间的历史,但是现在仍然还有比较大的市场。

像Creative和Archos等公司其实都推出过预装Android系统的音乐播放器,,甚至三星也推出过旗下Galaxy系列的产品。而最近索尼则推出了全新的Walkman系列NWS ZX-1顶级音乐播放器,可以播放192kHz的高品质FLAC音乐文件,并且运行了Android 4.1操作系统。

Android上太空

108813878

 

也许到目前为止最酷、也是最有价值的Android应用就是在NASA的SmartSPHERES项目了。谷歌的Project Tango产品目前是非常热门的概念,而未来它也可以帮助宇航员在太空中完成一些任务。

拍照分享一步完成

108813880

 

正如之前所提到的,Android系统对于卡片式数码相机来说是很好的选择,因为它可以运行许多中照片编辑软件。而三星已经率先推出了Galaxy Camera系列智能相机,配备了1630万像素摄像头及OIS光学稳定系统,还有4.8英寸显示屏,支持Wi-Fi连接,预装了Android 4.3果冻豆系统。而作为智能相机,Galaxy Camera可以安装诸如Instagram和VSCO Cam这样的知名照片软件,并且可以直接分享到社交网站上。在这一点上,要比传统的相机方便了许多。

七个让你意想不到的Android系统应用领域,首发于 极客范 - GeekFan.net

影响服务器高性能的四大杀手

$
0
0

本文将与你分享我多年来在服务器开发方面的一些经验。对于这里所说的服务器,更精确的定义应该是每秒处理大量离散消息或者请求的服务程序,网络服务器更符合这种情况,但并非所有的网络程序都是严格意义上的服务器。使用“高性能请求处理程序”是一个很糟糕的标题,为了叙述起来简单,下面将简称为“服务器”。

本文不会涉及到多任务应用程序,在单个程序里同时处理多个任务现在已经很常见。比如你的浏览器可能就在做一些并行处理,但是这类并行程序设计没有多大挑战性。 真正的挑战出现在服务器的架构设计对性能产生制约时,如何通过改善架构来提升系统性能。对于在拥有上G内存和G赫兹CPU上运行的浏览器来说,通过DSL线路进行多个并发下载任务不会有如此的挑战性。这里,应用的焦点不在于通过吸管小口吮吸,而是如何通过水龙头大口畅饮,这里麻烦是如何解决在硬件性能的制约.(作者的意思应该是怎么通过网络硬件的改善来增大流量)

一些人可能会对我的某些观点和建议发出置疑,或者自认为有更好的方法, 这是无法避免的。在本文中我不想扮演上帝的角色;这里所谈论的是我自己的一些经验,这些经验对我来说, 不仅在提高服务器性能上有效,而且在降低调试困难度和增加系统的可扩展性上也有作用。但是对某些人的系统可能会有所不同。如果有其它更适合于你的方法,那实在是很不错. 但是值得注意的是,对本文中所提出的每一条建议的其它一些可替代方案,我经过实验得出的结论都是悲观的。你自己的小聪明在这些实验中或许有更好的表现,但是如果因此怂恿我在这里建议读者这么做,可能会引起无辜读者的反感。你并不想惹怒读者,对吧?

本文的其余部分将主要说明影响服务器性能的四大杀手:

1) 数据拷贝(Data Copies)

2) 环境切换(Context Switches)

3) 内存分配(Memory allocation)

4) 锁竞争(Lock contention)

在文章结尾部分还会提出其它一些比较重要的因素,但是上面的四点是主要因素。如果服务器在处理大部分请求时能够做到没有数据拷贝,没有环境切换,没有内存分配,没有锁竞争,那么我敢保证你的服务器的性能一定很出色。

数据拷贝(Data Copies)

本节会有点短,因为大多数人在数据拷贝上吸取过教训。几乎每个人都知道产生数据拷贝是不对的,这点是显而易见的,在你的职业生涯中,你很早就会见识过它;而且遇到过这个问题,因为10年前就有人开始说这个词。对我来说确实如此。现今,几乎每个大学课程和几乎所有how-to文档中都提到了它。甚至在某些商业宣传册中,"零拷贝" 都是个流行用语。

尽管数据拷贝的坏处显而易见,但是还是会有人忽视它。因为产生数据拷贝的代码常常隐藏很深且带有伪装,你知道你所调用的库或驱动的代码会进行数据拷贝吗?答案往往超出想象。猜猜“程序I/O” 在计算机上到底指什么?哈希函数是伪装的数据拷贝的例子,它有带拷贝的内存访问消耗和更多的计算。曾经指出哈希算法作为一种有效的“拷贝+”似乎能够被避免,但据我所知,有一些非常聪明的人说过要做到这一点是相当困难的。如果想真正去除数据拷贝,不管是因为影响了服务器性能,还是想在黑客大会上展示"零复制”技术,你必须自己跟踪可能发生数据拷贝的所有地方,而不是轻信宣传。

有一种可以避免数据拷贝的方法是使用buffer的描述符(或者buffer chains的描述符)来取代直接使用buffer指针,每个buffer描述符应该由以下元素组成:

l 一个指向buffer的指针和整个buffer的长度

l 一个指向buffer中真实数据的指针和真实数据的长度,或者长度的偏移

l 以双向链表的形式提供指向其它buffer的指针

l 一个引用计数

现在,代码可以简单的在相应的描述符上增加引用计数来代替内存中数据的拷贝。这种做法在某些条件下表现的相当好,包括在典型的网络协议栈的操作上,但有些情况下这做法也令人很头大。一般来说,在buffer chains的开头和结尾增加buffer很容易,对整个buffer增加引用计数,以及对buffer chains的即刻释放也很容易。在chains的中间增加buffer,一块一块的释放buffer,或者对部分buffer增加引用技术则比较困难。而分割,组合chains会让人立马崩溃。

我不建议在任何情况下都使用这种技术,因为当你想在链上搜索你想要的一个块时,就不得不遍历一遍描述符链,这甚至比数据拷贝更糟糕。最适用这种技术地方是在程序中大的数据块上,这些大数据块应该按照上面所说的那样独立的分配描述符,以避免发生拷贝,也能避免影响服务器其它部分的工作.(大数据块拷贝很消耗CPU,会影响其它并发线程的运行)。

关于数据拷贝最后要指出的是:在避免数据拷贝时不要走极端。我看到过太多的代码为了避免数据拷贝,最后结果反而比拷贝数据更糟糕,比如产生环境切换或者一个大的I/O请求被分解了。数据拷贝是昂贵的,但是在避免它时,是收益递减的(意思是做过头了,效果反而不好)。为了除去最后少量的数据拷贝而改变代码,继而让代码复杂度翻番,不如把时间花在其它方面。

上下文切换(Context Switches)

相对于数据拷贝影响的明显,非常多的人会忽视了上下文切换对性能的影响。在我的经验里,比起数据拷贝,上下文切换是让高负载应用彻底完蛋的真正杀手。系统更多的时间都花费在线程切换上,而不是花在真正做有用工作的线程上。令人惊奇的是,(和数据拷贝相比)在同一个水平上,导致上下文切换原因总是更常见。 引起环境切换的第一个原因往往是活跃线程数比CPU个数多。随着活跃线程数相对于CPU个数的增加,上下文切换的次数也在增加,如果你够幸运,这种增长是线性的,但更常见是指数增长。这个简单的事实解释了为什么每个连接对应一个单独线程的多线程设计模式的可伸缩性更差。 对于一个可伸缩性的系统来说,限制活跃线程数少于或等于CPU个数是更有实际意义的方案。曾经这种方案的一个变种是只使用一个活跃线程,虽然这种方案避免了环境争用,同时也避免了锁,但它不能有效利用多CPU在增加总吞吐量上的价值,因此除非程序无CPU限制(non-CPU-bound),(通常是网络I/O限制 network-I/O-bound),应该继续使用更实际的方案。

一个有适量线程的程序首先要考虑的事情是规划出如何创建一个线程去管理多连接。这通常意味着前置一个select/poll,异步I/O,信号或者完成端口,而后台使用一个事件驱动的程序框架。关于哪种前置API是最好的有很多争论。 Dan Kegel的 C10K问题在这个领域是一篇不错的论文。个人认为,select/poll和信号通常是一种丑陋的方案,因此我更倾向于使用AIO或者完成端口,但是实际上它并不会好太多。也许除了select(),它们都还不错。所以不要花太多精力去探索前置系统最外层内部到底发生了什么。

对于最简单的多线程事件驱动服务器的概念模型, 其内部有一个请求缓存队列,客户端请求被一个或者多个监听线程获取后放到队列里,然后一个或者多个工作线程从队列里面取出请求并处理。从概念上来说,这是一个很好的模型,有很多用这种方式来实现他们的代码。这会产生什么问题吗? 引起环境切换的第二个原因是把对请求的处理从一个线程转移到另一个线程。有些人甚至把对请求的回应又切换回最初的线程去做,这真是雪上加霜,因为每一个请求至少引起了2次环境切换。把一个请求从监听线程转换到成工作线程,又转换回监听线程的过程中,使用一种“平滑”的方法来避免环境切换是非常重要的。此时,是否把连接请求分配到多个线程,或者让所有线程依次作为监听线程来服务每个连接请求,反而不重要了。

即使在将来,也不可能有办法知道在服务器中同一时刻会有多少激活线程.毕竟,每时每刻都可能有请求从任意连接发送过来,一些进行特殊任务的“后台”线程也会在任意时刻被唤醒。 那么如果你不知道当前有多少线程是激活的,又怎么能够限制激活线程的数量呢?根据我的经验,最简单同时也是最有效的方法之一是:用一个老式的带计数的信号量,每一个线程执行的时候就先持有信号量。如果信号量已经到了最大值,那些处于监听模式的线程被唤醒的时候可能会有一次额外的环境切换,(监听线程被唤醒是因为有连接请求到来, 此时监听线程持有信号量时发现信号量已满,所以即刻休眠), 接着它就会被阻塞在这个信号量上,一旦所有监听模式的线程都这样阻塞住了,那么它们就不会再竞争资源了,直到其中一个线程释放信号量,这样环境切换对系统的影响就可以忽略不计。更主要的是,这种方法使大部分时间处于休眠状态的线程避免在激活线程数中占用一个位置,这种方式比其它的替代方案更优雅。

一旦处理请求的过程被分成两个阶段(监听和工作),那么更进一步,这些处理过程在将来被分成更多的阶段(更多的线程)就是很自然的事了。最简单的情况是一个完整的请求先完成第一步,然后是第二步 (比如回应)。然而实际会更复杂:一个阶段可能产生出两个不同执行路径,也可能只是简单的生成一个应答(例如返回一个缓存的值)。由此每个阶段都需要知道下一步该如何做,根据阶段分发函数的返回值有三种可能的做法:

l 请求需要被传递到另外一个阶段(返回一个描述符或者指针)

l 请求已经完成(返回ok)

l 请求被阻塞(返回"请求阻塞")。这和前面的情况一样,阻塞到直到别的线程释放资源

应该注意到在这种模式下,对阶段的排队是在一个线程内完成的,而不是经由两个线程中完成。这样避免不断把请求放在下一阶段的队列里,紧接着又从该队列取出这个请求来执行。这种经由很多活动队列和锁的阶段很没必要。

这种把一个复杂的任务分解成多个较小的互相协作的部分的方式,看起来很熟悉,这是因为这种做法确实很老了。我的方法,源于CAR在1978年发明的"通信序列化进程"( Communicating Sequential Processes CSP),它的基础可以上溯到1963时的Per Brinch Hansen and Matthew Conway--在我出生之前!然而,当Hoare创造出CSP这个术语的时候,他说的“进程”是抽象数学意义上的进程,而且,这个CSP术语中的进程和操作系统中同名的那个进程并没有关系。依我看来,这种在操作系统提供的单个线程之内,实现类似多线程一样协同并发工作的CSP的方法,在可扩展性方面让很多人头疼。

一个实际的例子是,Matt Welsh的 SEDA,这个例子表明分段执行的(stage-execution)思想朝着一个比较合理的方向发展。SEDA是一个很好的“server Aarchitecture done right”的例子,值得把它的特性评论一下:

1. SEDA的批处理倾向于强调一个阶段处理多个请求,而我的方式倾向于强调一个请求分成多个阶段处理。

2. 在我看来SEDA的一个重大缺陷是给每个阶段申请一个独立的在加载响应阶段只进行线程“后台”重分配的线程池。结果,原因1和原因2引起的环境切换仍然很多。

3. 在纯技术的研究项目中,在Java中使用SEDA是有用的,然而在实际应用场合,我觉得这种方法很少被选择。

内存分配(Memory Allocator)

申请和释放内存是应用程序中最常见的操作, 因此发明了许多聪明的技巧使得内存的申请效率更高。然而再聪明的方法也不能弥补这种事实:在很多场合中,一般的内存分配方法非常没有效率。所以为了减少向系统申请内存,我有三个建议。

建议一是使用预分配。我们都知道由于使用静态分配而导致对程序的功能加上人为限制是一种糟糕的设计。但是还是有许多其它很不错的预分配方案。通常认为,通过系统一次性分配内存要比分开几次分配要好,即使这样做在程序中浪费了某些内存。如果能够确定在程序中会有几项内存使用,在程序启动时预分配就是一个合理的选择。 即使不能确定,在开始时为请求句柄预分配可能需要的所有内存也比在每次需要一点的时候才分配要好。通过系统一次性连续分配多项内存还能极大减少错误处理代码。在内存比较紧张时,预分配可能不是一个好的选择,但是除非面对最极端的系统环境,否则预分配都是一个稳赚不赔的选择。

建议二是使用一个内存释放分配的lookaside list(监视列表或者后备列表)。基本的概念是把最近释放的对象放到链表里而不是真的释放它,当不久再次需要该对象时,直接从链表上取下来用,不用通过系统来分配。使用lookaside list的一个额外好处是可以避免复杂对象的初始化和清理.

通常,让lookaside list不受限制的增长,即使在程序空闲时也不释放占用的对象是个糟糕的想法。在避免引入复杂的锁或竞争情况下,不定期的“清扫"非活跃对象是很有必要的。一个比较妥当的办法是,让lookaside list由两个可以独立锁定的链表组成:一个"新链"和一个"旧链".使用时优先从"新"链分配,然后最后才依靠"旧"链。对象总是在"新"链上被释放。清除线程则按如下规则运行:

1. 锁住两个链

2. 保存旧链的头结点

3. 把前一个新链挂到旧链的前头

4. 解锁

5. 在空闲时通过第二步保存的头结点开始释放旧链的所有对象。

使用了这种方式的系统中,对象只有在真的没用时才会释放,释放至少延时一个清除间隔期(指清除线程的运行间隔),但同常不会超过两个间隔期。清除线程不会和普通线程发生锁竞争。理论上来说,同样的方法也可以应用到请求的多个阶段,但目前我还没有发现有这么用的。

使用lookaside lists有一个问题是,保持分配对象需要一个链表指针(链表结点),这可能会增加内存的使用。但是即使有这种情况,使用它带来的好处也能够远远弥补这些额外内存的花销。

第三条建议与我们还没有讨论的锁有关系。先抛开它不说。即使使用lookaside list,内存分配时的锁竞争也常常是最大的开销。 解决方法是使用线程私有的lookasid list, 这样就可以避免多个线程之间的竞争。更进一步,每个处理器一个链会更好,但这样只有在非抢先式线程环境下才有用。基于极端考虑,私有lookaside list甚至可以和一个共用的链工作结合起来使用。

锁竞争(Lock Contention)

高效率的锁是非常难规划的, 以至于我把它称作卡律布狄斯和斯库拉(参见附录)。一方面, 锁的简单化(粗粒度锁)会导致并行处理的串行化,因而降低了并发的效率和系统可伸缩性; 另一方面, 锁的复杂化(细粒度锁)在空间占用上和操作时的时间消耗上都可能产生对性能的侵蚀。偏向于粗粒度锁会有死锁发生,而偏向于细粒度锁则会产生竞争。在这两者之间,有一个狭小的路径通向正确性和高效率,但是路在哪里?

由于锁倾向于对程序逻辑产生束缚,所以如果要在不影响程序正常工作的基础上规划出锁方案基本是不可能的。这也就是人们为什么憎恨锁,并且为自己设计的不可扩展的单线程方案找借口了。

几乎我们每个系统中锁的设计都始于一个"锁住一切的超级大锁",并寄希望于它不会影响性能,当希望落空时(几乎是必然), 大锁被分成多个小锁,然后我们继续祷告(性能不会受影响),接着,是重复上面的整个过程(许多小锁被分成更小的锁), 直到性能达到可接受的程度。通常,上面过程的每次重复都回增加大于20%-50%的复杂性和锁负荷,并减少5%-10%的锁竞争。最终结果是取得了适中的效率,但是实际效率的降低是不可避免的。设计者开始抓狂:"我已经按照书上的指导设计了细粒度锁,为什么系统性能还是很糟糕?"

在我的经验里,上面的方法从基础上来说就不正确。设想把解决方案当成一座山,优秀的方案表示山顶,糟糕的方案表示山谷。上面始于"超级锁"的解决方案就好像被形形色色的山谷,凹沟,小山头和死胡同挡在了山峰之外的登山者一样,是一个典型的糟糕爬山法;从这样一个地方开始登顶,还不如下山更容易一些。那么登顶正确的方法是什么?

首要的事情是为你程序中的锁形成一张图表,有两个轴:

l 图表的纵轴表示代码。如果你正在应用剔出了分支的阶段架构(指前面说的为请求划分阶段),你可能已经有这样一张划分图了,就像很多人见过的OSI七层网络协议架构图一样。

l 图表的水平轴表示数据集。在请求的每个阶段都应该有属于该阶段需要的数据集。

现在,你有了一张网格图,图上每个单元格表示一个特定阶段需要的特定数据集。 下面是应该遵守的最重要的规则:两个请求不应该产生竞争,除非它们在同一个阶段需要同样的数据集。如果你严格遵守这个规则,那么你已经成功了一半。

一旦你定义出了上面那个网格图,在你的系统中的每种类型的锁就都可以被标识出来了。你的下一个目标是确保这些标识出来的锁尽可能在两个轴之间均匀的分布,这部分工作是和具体应用相关的。你得像个钻石切割工一样,根据你对程序的了解,找出请求阶段和数据集之间的自然“纹理线”。有时候它们很容易发现,有时候又很难找出来,此时需要不断回顾来发现它。在程序设计时,把代码分隔成不同阶段是很复杂的事情,我也没有好的建议,但是对于数据集的定义,有一些建议给你:

l 如果你能对请求按顺序编号,或者能对请求进行哈希,或者能把请求和事物ID关联起来,那么根据这些编号或者ID就能对数据更好的进行分隔。

l 有时,基于数据集的资源最大化利用,把请求动态的分配给数据,相对于依据请求的固有属性来分配会更有优势。就好像现代CPU的多个整数运算单元知道把请求分离一样。

l 确定每个阶段指定的数据集是不一样的是非常有用的,以便保证一个阶段争夺的数据在另外阶段不会争夺。

如果你在纵向和横向上把“锁空间(这里实际指锁的分布)" 分隔了,并且确保了锁均匀分布在网格上,那么恭喜你获得了一个好方案。现在你处在了一个好的登山点,打个比喻,你面有了一条通向顶峰的缓坡,但你还没有到山顶。现在是时候对锁竞争进行统计,看看该如何改进了。以不同的方式分隔阶段和数据集,然后统计锁竞争,直到获得一个满意的分隔。当你做到这个程度的时候,那么无限风景将呈现在你脚下。

其他方面

我已经阐述完了影响性能的四个主要方面。然而还有一些比较重要的方面需要说一说, 大多数都可归结于你的平台或系统环境:

l 你的存储子系统在大数据读写和小数据读写,随即读写和顺序读写方面是如何进行?在预读和延迟写入方面做得怎样?

l 你使用的网络协议效率如何?是否可以通过修改参数改善性能?是否有类似于TCP_CORK, MSG_PUSH,Nagle-toggling算法的手段来避免小消息产生?

l 你的系统是否支持Scatter-Gather I/O(例如readv/writev)? 使用这些能够改善性能,也能避免使用缓冲链(见第一节数据拷贝的相关叙述)带来的麻烦。(说明:在dma传输数据的过程中,要求源物理地址和目标物理地址必须是连续的。但在有的计算机体系中,如IA,连续的存储器地址在物理上不一定是连续的,则dma传输要分成多次完成。如果传输完一块物理连续的数据后发起一次中断,同时主机进行下一块物理连续的传输,则这种方式即为block dma方式。scatter/gather方式则不同,它是用一个链表描述物理不连续的存储器,然后把链表首地址告诉dma master。dma master传输完一块物理连续的数据后,就不用再发中断了,而是根据链表传输下一块物理连续的数据,最后发起一次中断。很显然 scatter/gather方式比block dma方式效率高)

l 你的系统的页大小是多少?高速缓存大小是多少?向这些大小边界进行对起是否有用?系统调用和上下文切换花的代价是多少?

l 你是否知道锁原语的饥饿现象?你的事件机制有没有"惊群"问题?你的唤醒/睡眠机制是否有这样糟糕的行为: 当X唤醒了Y, 环境立刻切换到了Y,但是X还有没完成的工作?

我在这里考虑的了很多方面,相信你也考虑过。在特定情况下,应用这里提到的某些方面可能没有价值,但能考虑这些因素的影响还是有用的。如果在系统手册中,你没有找到这些方面的说明,那么就去努力找出答案。写一个测试程序来找出答案;不管怎样,写这样的测试代码都是很好的技巧锻炼。如果你写的代码在多个平台上都运行过,那么把这些相关的代码抽象为一个平台相关的库,将来在某个支持这里提到的某些功能的平台上,你就赢得了先机。

对你的代码,“知其所以然”, 弄明白其中高级的操作, 以及在不同条件下的花销.这不同于传统的性能分析, 不是关于具体的实现,而是关乎设计. 低级别的优化永远是蹩脚设计的最后救命稻草.

 

本文翻译自: http://pl.atyp.us/content/tech/servers.html

译文转载自: http://blog.csdn.net/zhoudaxia/article/details/14223755  原文标题《高性能服务器架构》


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

20个设计模式和软件设计面试问题

$
0
0

不管是参加Java面试还是C#面试, 设计模式和软件设计都是任何编程面试中的必问问题。实际上,编程能力和设计技巧是对彼此很好的补充。一个好的程序员通常都是一个好的软件设计人员。他们知道怎么把一个问题分割成一段段代码或者软件设计,但这些能力和技巧并不能凭空而来。你需要持续做大型、小型系统的设计和编码,并且不断从错误中学习。从 面向对象设计原则开始是一个不错的选择。好吧,这篇文章是关于一些在各种面试中频繁问到的一些设计问题。考虑到针对性和困难度,我把它们分成两个种类,分别针对初学者和有经验的人。

对中高级别的设计模式面试问题

这是一些和设计模式还有软件设计都相关的问题。这些问题需要一些思考和经验来回答。在大多数情况下,面试官并不是需要一个确切的答案,而是希望听到你的想法,你对这个问题是怎么考虑的,你能不能想通这个问题,能不能挖掘一些没有告诉你的潜在信息。在解决一个问题时你应该考虑什么等等可以使你经验大涨。总的来说,这些设计问题会让你动起脑来。有时面试官也会让你写代码,所以还是准备一下吧。如果你知道编程和设计技巧中的概念,例子和程序,你应该可以在这些问题中有突出的表现。

1. 举出一个例子,在这种情况你会更倾向于使用抽象类,而不是接口?

这是很常用但又是很难回答的设计面试问题。接口和抽象类都遵循”面向接口而不是实现编码”设计原则,它可以增加代码的灵活性,可以适应不断变化的需求。下面有几个点可以帮助你回答这个问题:

  1. 在Java中,你只能继承一个类,但可以实现多个接口。所以一旦你继承了一个类,你就失去了继承其他类的机会了。
  2. 接口通常被用来表示附属描述或行为如: Runnable、Clonable、Serializable等等,因此当你使用抽象类来表示行为时,你的类就不能同时是 RunnableClonable(译者注:这里的意思是指如果把 Runnable等实现为抽象类的情况),因为在Java中你不能继承两个类,但当你使用接口时,你的类就可以同时拥有多个不同的行为。
  3. 在一些对时间要求比较高的应用中,倾向于使用抽象类,它会比接口稍快一点。
  4. 如果希望把一系列行为都规范在类继承层次内,并且可以更好地在同一个地方进行编码,那么抽象类是一个更好的选择。有时,接口和抽象类可以一起使用,接口中定义函数,而在抽象类中定义默认的实现。

希望了解更多关于Java接口的,可以看我的文章 Java接口需要知道的10件事

2. 设计一个贩卖机,可以接收不同的货币,出售不同的产品?

这是一个开放设计问题,你可以作为练习,尝试着写出设计文档、代码和JUnit测试而不是只是解决这个问题,看看它花了你多少时间得到解决方案和得到需要的原形。理想情况下,这个问题应该可以在3个小时内解决,至少应该得到一个可以运行的版本。

3. 你有一个 Smartphone类,可以派生如 IPhone、AndroidPhone、WindowsMobilePhone

它还可以是一些有着品牌的手机名称,你会怎么设计这个类系统呢。

这是另外一个设计模式练习,你可以应用你的面向对象设计技巧来得到一个设计,这个设计需要足够灵活能够支持未来产品的扩展,足够稳定能够支持在现有模型进行修改。

4. 在Java中,什么时候用重载,什么时候用重写?

对有经验的Java设计师来说,这是一个相当简单的问题。如果你看到一个类的不同实现有着不同的方式来做同一件事,那么就应该用重写(overriding),而重载(overloading)是用不同的输入做同一件事。在Java中,重载的方法签名不同,而重写并不是。

5. 设计一个ATM机?

我们所有人都使用 ATM(自动柜员机)。想想你会怎么设计一个ATM?就设计金融系统来说,必须知道它们应该在任何情况下都能够如期工作。不管是断电还是其他情况,ATM应该保持 正确的状态(事务) , 想想 加锁(locking)、事务(transaction)、错误条件(error condition)、边界条件(boundary condition)等等。尽管你不能想到具体的设计,但如果你可以指出非功能性需求,提出一些问题,想到关于边界条件,这些都会是很好的一步。

6. 你正在写一些类提供市场数据,你知道你可以不定时切换不同的厂商如Reuters、wombat或者直接的批发商, 你会如何设计你的市场数据系统。

这是一个非常有趣的设计面试问题,并且真的在一家大的投资银行问到过,如果你是用Java编码的话这是一个相当平常的场景。最主要的一点是你要有一个 MarketData接口,它会有调用端需要的方法如: getBid()、getPrice()、getLevel()等等,而 MarketData应该由一个 MarketDataProvider通过 依赖注入(dependency injection)组成。因此,当你修改你的 MarketData提供器( MarketDataProvider)时,调用端不会受影响,因为它们是通过 MarketData接口或类的方法来访问的。

7. 在Java中,为什么不允许从静态方法中访问非静态变量?

你在Java中不能从静态上下文访问非静态数据只是因为非静态变量是跟具体的对象实例关联的,而静态的却没有和任何实例关联。你可以看我的文章 为什么在静态上下文中不能访问非静态变量查看详细的讨论。

8. 在Java中设计一个并发规则的pipeline?

并发编程并发设计这些天很火,它可以充分利用现在不断提升的高级处理器的处理能力,而Java成为一个多线程语言也从这种情况获益良多。设计一个并发系统需要记住的最关键的点是 线程安全,不可变性,本地变量和避免使用static或者 类变量(instance variables)。你只需要想着每一类都可以同时被多个线程同时执行,所以最好的做法就是每一个线程都处理自己的数据 ,不跟其他数据交互,并且运行时只需要最小的同步保证。这个问题可以涉及到从最初的讨论到完整的类和接口编码,但只要你记住并发中最重要的点和问题如, 竞争条件(race condition)死锁(deadlock)、内存交互问题(memory interference)、原子性、 ThreadLocal变量等,你都可以回答它。

给初学者的设计模式面试问题

这些软件设计和设计模式问题大多在初学者层次时被问起,目的只是了解一下候选人(应聘者)对设计模式知道多少,如, 设计模式是什么或者 一个特定的设计模式做什么 ?这些问题通过简单地记忆概念就可以回答,但就信息和知识而言还是有价值的。

1. 什么是设计模式?你是否在你的代码里面使用过任何设计模式?

设计模式是世界上各种各样程序员用来解决特定设计问题的尝试和测试的方法。设计模式是代码可用性的延伸。

2. 你可以说出几个在JDK库中使用的设计模式吗?

装饰器设计模式(Decorator design pattern)被用于多个Java IO类中。单例模式(Singleton pattern)用于 RuntimeCalendar和其他的一些类中。工厂模式(Factory pattern)被用于各种不可变的类如Boolean,像 Boolean.valueOf,观察者模式(Observer pattern)被用于Swing和很多的事件监听中。

3. Java中什么是单例设计模式?用Java写出线程安全的单例

单例模式重点在于在整个系统上共享一些创建时较耗资源的对象。整个应用中只维护一个特定类实例,它被所有组件共同使用。 Java.lang.Runtime是单例模式的经典例子。你可以在我的文章 Java单例模式的10个问题看到更多的问题和讨论。从Java 5开始你可以使用 枚举(enum)来实现线程安全的单例。

4. 使用工厂模式最主要的好处是什么?你在哪里使用?

工厂模式的最大好处是增加了创建对象时的封装层次。如果 你使用工厂来创建对象,之后你可以使用更高级和更高性能的实现来替换原始的产品实现或类,这不需要在调用层做任何修改。可以看我的文章 工厂模式得更详细的解释和和了解更多的好处。

5. 在Java中,什么叫观察者设计模式(observer design pattern)

观察者模式是基于对象的状态变化和观察者的通讯,以便他们作出相应的操作。简单的例子就是一个天气系统,当天气变化时必须在展示给公众的视图中进行反映。这个视图对象是一个主体,而不同的视图是观察者。可以在 这篇文章中看到Java观察者模式的完整例子。

6. 举一个用Java实现的装饰模式(decorator design pattern)?它是作用于对象层次还是类层次?

装饰模式增加强了单个对象的能力。Java IO到处都使用了装饰模式,经典的例子就是Buffered系列类如 BufferedReaderBufferedWriter,它们增强了 ReaderWriter对象,以实现提升性能的Buffer层次的读取和写入。可以看 这篇文章了解更多。

7. 什么是MVC设计模式?举一个MVC设计模式的例子?

8, Java中什么是表示层设计模式(FrontController design pattern)?举一个使用表示层设计模式(front controller pattern)的例子?

9. 什么是责任链模式(Chain of Responsibility)?

10. 什么是适配器模式?举用Java实现适配器模式的例子?

这些留给你自己做练习,作为面试准备,试着去找出这些设计模式的答案。

这些是我在很多面试中都看到的设计模式问题,当然,在 google面试和各种各样的公司如Amzone、Microsoft等等还有很多重要的专业软件设计问题。如果你遇到一些有趣的值得分享的设计问题,不妨分享出来。

相关文章

远程视频监控之驱动篇(LED)

$
0
0

转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/38515205        


       之前一直在考虑该不该写这篇,因为我之前在博客里有写过LED的驱动,但是没有详细的讲解。后来本着叫大家都能看懂驱动的想法,我还是决定要写一下。我想通过LED的驱动,让不了解驱动的小伙伴,能够有一个感性的认识。

一.代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/device.h>
#include <mach/gpio.h>

static struct class *wvm_led_class;
static  int major;

volatile unsigned long *gpbcon = NULL;
volatile unsigned long *gpbdat = NULL;


static int wvm_led_drv_open(struct inode *inode, struct file *file)
{
	/*
	 * LED1,LED2,LED4对应GPB5、GPB6、GPB7、GPB8
	 */
	/* 配置GPB5,6,7,8为输出 */
	*gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2)));
	*gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));
	return 0;
}

static int wvm_led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	int val,ret;

	ret=copy_from_user(&val, buf, count); //	copy_to_user();
	if (ret)
	return -EAGAIN;

	if (val == 1)
	{
		// 点灯
		*gpbdat &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8));
	}
	else
	{
		// 灭灯
		*gpbdat |= (1<<5) | (1<<6) | (1<<7) | (1<<8);
	}
	return 0;
}

static struct file_operations wvm_led_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   wvm_led_drv_open,     
    .write  =	wvm_led_drv_write,	   
};

static int wvm_led_drv_init(void)  //入口函数(做初始化,创建设备等工作)
{
	major = register_chrdev(0, "wvm_led_drv", &wvm_led_drv_fops); // 注册, 告诉内核
	if(major < 0)
        {
          printk(  " wvm_led_drv register falid!/n");
          return major;
        }
	wvm_led_class = class_create(THIS_MODULE, "wvm_led");
	if(IS_ERR(wvm_led_class))
        {
          printk( " wvm_led_drv register class falid!/n");
          return -1;
        }
        device_create(wvm_led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */

	gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16);
	gpbdat = gpbcon + 1;

	return 0;
}

static void wvm_led_drv_exit(void)  //出口函数(做卸载和销毁工作)
{
	unregister_chrdev(major, "wvm_led_drv"); // 卸载
	device_destroy(wvm_led_class, MKDEV(major, 0));
	class_destroy(wvm_led_class);
	iounmap(gpbcon);
}

module_init(wvm_led_drv_init);  //定义入口函数
module_exit(wvm_led_drv_exit);  //定义出口函数

MODULE_LICENSE("GPL");


二.驱动结构

     正所谓麻雀虽小五脏俱全,它包括了一个驱动的基本功能。下面我写一个类似于模板的东西给大家。

//头文件

...

//定义一些变量和结构体等

...

//各种操作函数

xx_open()

{

.....

}

xx_close()

{

.....

}

xx_ioctl()

{

.....

}

...

//file_operations结构体

static struct file_operations XXX_drv_fops = {
        .owner   =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
        .open     =  XXX_open,           //后面的名字要与操作函数一致
        .close    =  XXX_close,   

        .ioctl       =  XXX_ioctl, 
  
};

//入口函数

static int XXX_init(void)

{

主要做创建设备等初始化工作,参照前面驱动(要判断返回值)。

}

//出口函数

static voidXXX_exit(void)

{

主要卸载创建的设备,做一些清理工作,参考前面的驱动去写

}

//入口、出口、证书的声明

module_init(XXX_init);
module_exit(XXX_exit);
MODULE_LICENSE("GPL");


三.应用测试

     应用就是简单的测试一下开灯和关灯

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* led_test on    开灯
  * led_test off  关灯
  */
int main(int argc, char **argv)
{
	int fd;
	int val = 1;
	fd = open("/dev/led", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	if (argc != 2)
	{
		printf("Usage :\n");
		printf("%s <on|off>\n", argv[0]);
		return 0;
	}

	if (strcmp(argv[1], "on") == 0)
	{
		val  = 1;
	}
	else
	{
		val = 0;
	}
	write(fd, &val, 4);
	return 0;
}


看到这或许有的小伙伴们已经买白了驱动怎么写,但是灯怎么亮的呢?

        应用程序:write()----->驱动程序:write()

        细心的小伙伴已经注意到驱动中的write()中有ret=copy_from_user(&val, buf, count);

        然后就可以开始执行亮灯和灭灯了

两步搞定亮灭灯:

        由于一个引脚可能允许有不同的功能,所以引脚的寄存器分为两类,一类为控制寄存器,一类数据寄存器

要操作某个引脚先设置控制寄存器(配置为某种功能),然后设置数据寄存器(实现功能)

    *gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2)));  //清零
    *gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));     //配置为输出引脚


        // 点灯
        *gpbdat &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8));

        // 灭灯
        *gpbdat |= (1<<5) | (1<<6) | (1<<7) | (1<<8);


这里面要注意一点,在以前单片机写程序的的时候我们可以直接操作物理地址,但是现在驱动要操作虚拟地址。所以我们要做一个映射

gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16);  //物理地址0x56000010 ,映射长度16字节

gpbdat = gpbcon + 1;


作者:u013584315 发表于2014-8-13 18:08:58 原文链接
阅读:7 评论:0 查看评论

springmvc框架配置

$
0
0


web.xml

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
			xmlns="http://java.sun.com/xml/ns/j2ee" 
			xmlns:javaee="http://java.sun.com/xml/ns/javaee" 
			xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"><!-- 加载所有的配置文件 --><context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring/spring-context.xml</param-value></context-param><!-- 配置Spring监听 --><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener> <!-- servlet  spring-servlet --><servlet><servlet-name>spring</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring/spring-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup>   </servlet><servlet-mapping><servlet-name>spring</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping>  <welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list></web-app>

spring-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>   <beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-3.2.xsd
	http://www.springframework.org/schema/mvc
	http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"><!-- 启动SpringMVC的注解功能,它会自动注册HandlerMapping、HandlerAdapter、ExceptionResolver的相-->    <mvc:annotation-driven /><!-- 注解扫描包 --><context:component-scan base-package="com.cms.action" /><!-- 完成请求和注解POJO的映射 --><bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/><!-- 静态资源(js/image)的访问 --><mvc:resources location="/js/" mapping="/js/**"/><!-- 对模型视图名称的解析,即在模型视图名称添加前后缀  WEB-INF/jsp/-->     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/"></property><property name="suffix" value=".jsp"></property></bean></beans>  

action

package com.cms.action;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("account")    //
public class AccountAction {
	@RequestMapping("/login.do")
	public String login(){		
		return "login";  //jsp下面的login.jsp页面
	}
}

index.jsp 请求:

<form action="/account/login.do">



























作者:u011518709 发表于2014-8-13 17:26:37 原文链接
阅读:42 评论:0 查看评论

Linux下gdb调试

$
0
0

关于gdb的其他客套话不多说,直接进入正题。


一、gdb基本命令列表

命令解释简写
file装入想要调试的可执行文件
list列出产生执行文件源代码的一部分l
next执行一行源代码但不进入函数内部n
step执行一行源代码而且进入函数内部s
run执行当前被调试的程序r
continue继续执行程序c
quit终止gdbq
print输出当前指定变量的值p
break在代码里设置断点b
info break查看设置断点的信息ib
delete删除设置的断点d
watch监视一个变量的值,一旦值有变化程序停住wa
helpgdb中的帮助命令h


二、gdb命令运用举例

1.新建一个源文件vi yrp.cc,源代码如下:

#include<iostream>
using namespace std;
void swap(int &a,int &b)
{
        int tmp;
        tmp=a;
        a=b;
        b=tmp;
}
int main()
{
        int i,j;
        cout<<endl<<"Input two int number:"<<endl;
        cin>>i>>j;
        cout<<"Before swap(),i="<<i<<" j="<<j<<endl;
        swap(i,j);
        cout<<"After swap(),i="<<i<<" j="<<j<<endl<<endl;
        return 0;
}


2.生成可执行文件 g++ -g -o yrp yrp.cc 注意必须使用-g参数,编译会加入调试信息,否则无法调试执行文件.



3.启动调试 gdb yrp



4.查看源文件 list 1,(由第一行开始)回车重复上一次指令



5.设置调试断点 break 16,在第16行设置断点, info break查看断点信息(简写ib)



6.调试运行输入 run



7.单步调试 step,进入函数内部



8.查看变量 print 举例print b



9.查看函数堆栈 bt,退出函数 finish



10.继续运行直到下一个断点或主函数结束 continue



11.退出调试 quit





结束!






作者:ssdut2013 发表于2014-8-13 17:14:52 原文链接
阅读:60 评论:0 查看评论

Linux下Java线程详细监控和其dump的分析使用----分析Java性能瓶颈

$
0
0
这里对linux下、sun(oracle) JDK的线程资源占用问题的查找步骤做一个小结;
linux环境下,当发现java进程占用CPU资源很高,且又要想更进一步查出哪一个java线程占用了CPU资源时,按照以下步骤进行查找:

(一):通过【 top  -p 12377 -H】 查看java进程的有哪些线程的运行情况;
      和通过【jstack 12377 > stack.log】生成Java线程的dump详细信息;

    1. 先用top命令找出占用资源厉害的java进程id,如图:# top
    2. 如上图所示,java的进程id为'52554',接下来用top命令单独对这个进程中的所有线程作监视:
  1. 1top  -p 52554 -H

    #  top视图里面里面可以通过快捷键依次b ,x高亮显示top的列找出需要的线程,默认CPU排序,Shift+< ,Shift+>可以左右移动高亮排序的列;
    如图:(这时就看出来哪个java线程CPU高,哪个线程内存用的多)

  2. 如上图所示,linux下,所有的java内部线程,其实都对应了一个进程id,也就是说,linux上的sun jvm将java程序中的线程映射为了操作系统进程;我们看到,占用CPU资源最高的那个进程id是'15417',这个进程id对应java线程信息中的'nid'('n' stands for 'native');
  3. (1)要想找到到底是哪段具体的代码占用了如此多的资源,先使用jstack打出当前栈信息到一个文件里, 比如stack.log:
    1
    jstack 52554 > stack.log
    然后使用'jtgrep'脚本把这个进程号为'9757'的java线程在stack.log中抓出来:
    1jtgrep 9757 stack.log

    其中,'jtgrep'是自己随便写的一个shell脚本:

    1#!/bin/sh
    3nid=`python -c "print hex($1)"`
    4grep  -i $nid $2

    道理很简单,就是 把'9757'转换成16进制后,直接grep stack.log;可以看到,被grep出的那个线程的nid=0x3c39,正好是15417的16进制表示。

(2) 通过(windows程序-->计算器),选择程序员计算器将进程ID转换成16进制 到dump里面的nid 就可以搜索到
"http-nio-8080-exec-25" daemon prio=10 tid=0x00007f69686b4800 nid=0x1ce5 waiting on condition [0x00007f698e7cf000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x0000000777063ec8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(Unknown Source)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(Unknown Source)
        at java.util.concurrent.LinkedBlockingQueue.take(Unknown Source)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
        at java.util.concurrent.ThreadPoolExecutor.getTask(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)

(二)第二种通过 Java visualMv结合jconsole.exe  工具即可查看如图所示;(第一种方式可能更准确一些)






三:在Java Visualvm工具里面安装JTA插件,分析线程dump文件,注意,正常阶段的dump文件与非正常时期的Dump文件进行比较更容易分析出问题:
(1)下载:https://java.net/projects/tda/downloads/directory/visualvm
  (2)安装与使用:
(3)使用:


四:直接通过tda-bin-2.2\bin\tda.sh 来分析导出ThreadDump文件;(在没有JMX监控的情况下手动查看threadDump信息)
       下载地址:https://java.net/projects/tda/downloads/directory/visualvm 
 





























作者:tianya846 发表于2014-8-13 16:21:16 原文链接
阅读:64 评论:1 查看评论

Hadoop 2.5.0新特性和改进

$
0
0

Apache Hadoop 2.5.0是一个在2.x.y发布线上的一个小版本,建立在之前稳定的发布版本2.4.1之上。主要特性和改进:

1. Common

a) 使用HTTP代理服务器时认证改进。当通过代理服务器使用WebHDFS时这是非常有用的。

b) 增加了一个新的Hadoop指标监控sink,允许直接写到Graphite。

c) Hadoop文件系统兼容相关的规范工作。

2. HDFS

a) 支持 POSIX风格的扩展文件系统。更多细节查看Extended Attributes in HDFS文档。

b) 支持离线image浏览,客户端现在可以通过WebHDFS的API浏览一个fsimage。

c) NFS网关得到大量可支持性的改进和bug修复。Hadoop portmapper不在需要运行网关,网关现在可以拒绝没有权限的端口的连接。

d) SecondaryNameNode, JournalNode, and DataNode 的web UI已经使用HTML5和JS美化。

3. YARN

a) YARN的REST API现在支持写/修改操作。用户可以用REST API提交和杀死应用程序。

b) 时间线存储到YARN,用来存储一个应用通用的和特殊的信息,支持Kerberos认证。

c) 公平调度器支持动态分层用户队列,运行时,用户队列在任一指定的父队列中被动态的创建。

作者:chen517611641 发表于2014-8-13 16:03:12 原文链接
阅读:254 评论:0 查看评论

SpringAOP拦截Controller,Service实现日志管理(自定义注解的方式)

$
0
0

         从业近二,三年了,第一次写博客,平时做做脚手架或者架构一些基础框架然后给大家使用或者自己总结翻译一些文档。虽然是第一次但是我还是要拿Spring开刀。希望张开涛,涛兄看到的时候不要喷我,给我一点指导。

         首先我们为什么需要做日志管理,在现实的上线中我们经常会遇到系统出现异常或者问题。这个时候就马上打开CRT或者SSH连上服务器拿日子来分析。受网络的各种限制。于是我们就想为什么不能直接在管理后台查看报错的信息呢。于是日志管理就出现了。

         其次个人觉得做日志管理最好的是Aop,有的人也喜欢用拦截器。都可以,在此我重点介绍我的实现方式。

         Aop有的人说拦截不到Controller。有的人说想拦AnnotationMethodHandlerAdapter截到Controller必须得拦截org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter。

首先Aop可以拦截到Controller的,这个是毋容置疑的其次须拦截AnnotationMethodHandlerAdapter也不是必须的。最起码我没有验证成功过这个。我的Spring版本是4.0.3。

         Aop之所以有的人说拦截不到Controller是因为Controller被jdk代理了。我们只要把它交给cglib代理就可以了。

第一步定义两个注解:

 

Java代码 复制代码
  1. package com.annotation;  
  2.   
  3. import java.lang.annotation.*;  
  4.   
  5. /** 
  6.  *自定义注解 拦截Controller 
  7.  */  
  8.   
  9. @Target({ElementType.PARAMETER, ElementType.METHOD})  
  10. @Retention(RetentionPolicy.RUNTIME)  
  11. @Documented  
  12. public  @interface SystemControllerLog {  
  13.   
  14.     String description()  default "";  
  15.   
  16.   
  17. }  
  18.   
  19. package com.annotation;  
  20.   
  21. import java.lang.annotation.*;  
  22.   
  23. /** 
  24.  *自定义注解 拦截service 
  25.  */  
  26.   
  27. @Target({ElementType.PARAMETER, ElementType.METHOD})  
  28. @Retention(RetentionPolicy.RUNTIME)  
  29. @Documented  
  30. public  @interface SystemServiceLog {  
  31.   
  32.     String description()  default "";  
  33.   
  34.   
  35. }  

第二步创建一个切点类:

 

 

Java代码 复制代码
  1. package com.annotation;  
  2.   
  3. import com.model.Log;  
  4. import com.model.User;  
  5. import com.service.LogService;  
  6. import com.util.DateUtil;  
  7. import com.util.JSONUtil;  
  8. import com.util.SpringContextHolder;  
  9. import com.util.WebConstants;  
  10. import org.aspectj.lang.JoinPoint;  
  11. import org.aspectj.lang.annotation.*;  
  12. import org.slf4j.Logger;  
  13. import org.slf4j.LoggerFactory;  
  14. import org.springframework.stereotype.Component;  
  15. import org.springframework.web.context.request.RequestContextHolder;  
  16. import org.springframework.web.context.request.ServletRequestAttributes;  
  17. import javax.annotation.Resource;  
  18. import javax.servlet.http.HttpServletRequest;  
  19. import javax.servlet.http.HttpSession;  
  20. import java.lang.reflect.Method;  
  21.   
  22. /** 
  23.  * 切点类 
  24.  * @author tiangai 
  25.  * @since 2014-08-05 Pm 20:35 
  26.  * @version 1.0 
  27.  */  
  28. @Aspect  
  29. @Component  
  30. public  class SystemLogAspect {  
  31.     //注入Service用于把日志保存数据库  
  32.     @Resource  
  33.      private LogService logService;  
  34.     //本地异常日志记录对象  
  35.      private  static  final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class);  
  36.   
  37.     //Service层切点  
  38.     @Pointcut("@annotation(com.annotation.SystemServiceLog)")  
  39.      public  void serviceAspect() {  
  40.     }  
  41.   
  42.     //Controller层切点  
  43.     @Pointcut("@annotation(com.annotation.SystemControllerLog)")  
  44.      public  void controllerAspect() {  
  45.     }  
  46.   
  47.     /** 
  48.      * 前置通知 用于拦截Controller层记录用户的操作 
  49.      * 
  50.      * @param joinPoint 切点 
  51.      */  
  52.     @Before("controllerAspect()")  
  53.      public  void doBefore(JoinPoint joinPoint) {  
  54.   
  55.         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();  
  56.         HttpSession session = request.getSession();  
  57.         //读取session中的用户  
  58.         User user = (User) session.getAttribute(WebConstants.CURRENT_USER);  
  59.         //请求的IP  
  60.         String ip = request.getRemoteAddr();  
  61.          try {  
  62.             //*========控制台输出=========*//  
  63.             System.out.println("=====前置通知开始=====");  
  64.             System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));  
  65.             System.out.println("方法描述:" + getControllerMethodDescription(joinPoint));  
  66.             System.out.println("请求人:" + user.getName());  
  67.             System.out.println("请求IP:" + ip);  
  68.             //*========数据库日志=========*//  
  69.             Log log = SpringContextHolder.getBean("logxx");  
  70.             log.setDescription(getControllerMethodDescription(joinPoint));  
  71.             log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));  
  72.             log.setType("0");  
  73.             log.setRequestIp(ip);  
  74.             log.setExceptionCode( null);  
  75.             log.setExceptionDetail( null);  
  76.             log.setParams( null);  
  77.             log.setCreateBy(user);  
  78.             log.setCreateDate(DateUtil.getCurrentDate());  
  79.             //保存数据库  
  80.             logService.add(log);  
  81.             System.out.println("=====前置通知结束=====");  
  82.         }  catch (Exception e) {  
  83.             //记录本地异常日志  
  84.             logger.error("==前置通知异常==");  
  85.             logger.error("异常信息:{}", e.getMessage());  
  86.         }  
  87.     }  
  88.   
  89.     /** 
  90.      * 异常通知 用于拦截service层记录异常日志 
  91.      * 
  92.      * @param joinPoint 
  93.      * @param e 
  94.      */  
  95.     @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")  
  96.      public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {  
  97.         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();  
  98.         HttpSession session = request.getSession();  
  99.         //读取session中的用户  
  100.         User user = (User) session.getAttribute(WebConstants.CURRENT_USER);  
  101.         //获取请求ip  
  102.         String ip = request.getRemoteAddr();  
  103.         //获取用户请求方法的参数并序列化为JSON格式字符串  
  104.         String params = "";  
  105.          if (joinPoint.getArgs() !=  null && joinPoint.getArgs().length > 0) {  
  106.              for ( int i = 0; i < joinPoint.getArgs().length; i++) {  
  107.                 params += JSONUtil.toJsonString(joinPoint.getArgs()[i]) + ";";  
  108.             }  
  109.         }  
  110.          try {  
  111.               /*========控制台输出=========*/  
  112.             System.out.println("=====异常通知开始=====");  
  113.             System.out.println("异常代码:" + e.getClass().getName());  
  114.             System.out.println("异常信息:" + e.getMessage());  
  115.             System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));  
  116.             System.out.println("方法描述:" + getServiceMthodDescription(joinPoint));  
  117.             System.out.println("请求人:" + user.getName());  
  118.             System.out.println("请求IP:" + ip);  
  119.             System.out.println("请求参数:" + params);  
  120.                /*==========数据库日志=========*/  
  121.             Log log = SpringContextHolder.getBean("logxx");  
  122.             log.setDescription(getServiceMthodDescription(joinPoint));  
  123.             log.setExceptionCode(e.getClass().getName());  
  124.             log.setType("1");  
  125.             log.setExceptionDetail(e.getMessage());  
  126.             log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));  
  127.             log.setParams(params);  
  128.             log.setCreateBy(user);  
  129.             log.setCreateDate(DateUtil.getCurrentDate());  
  130.             log.setRequestIp(ip);  
  131.             //保存数据库  
  132.             logService.add(log);  
  133.             System.out.println("=====异常通知结束=====");  
  134.         }  catch (Exception ex) {  
  135.             //记录本地异常日志  
  136.             logger.error("==异常通知异常==");  
  137.             logger.error("异常信息:{}", ex.getMessage());  
  138.         }  
  139.          /*==========记录本地异常日志==========*/  
  140.         logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);  
  141.   
  142.     }  
  143.   
  144.   
  145.     /** 
  146.      * 获取注解中对方法的描述信息 用于service层注解 
  147.      * 
  148.      * @param joinPoint 切点 
  149.      * @return 方法描述 
  150.      * @throws Exception 
  151.      */  
  152.      public  static String getServiceMthodDescription(JoinPoint joinPoint)  
  153.              throws Exception {  
  154.         String targetName = joinPoint.getTarget().getClass().getName();  
  155.         String methodName = joinPoint.getSignature().getName();  
  156.         Object[] arguments = joinPoint.getArgs();  
  157.         Class targetClass = Class.forName(targetName);  
  158.         Method[] methods = targetClass.getMethods();  
  159.         String description = "";  
  160.          for (Method method : methods) {  
  161.              if (method.getName().equals(methodName)) {  
  162.                 Class[] clazzs = method.getParameterTypes();  
  163.                  if (clazzs.length == arguments.length) {  
  164.                     description = method.getAnnotation(SystemServiceLog. class).description();  
  165.                      break;  
  166.                 }  
  167.             }  
  168.         }  
  169.          return description;  
  170.     }  
  171.   
  172.     /** 
  173.      * 获取注解中对方法的描述信息 用于Controller层注解 
  174.      * 
  175.      * @param joinPoint 切点 
  176.      * @return 方法描述 
  177.      * @throws Exception 
  178.      */  
  179.      public  static String getControllerMethodDescription(JoinPoint joinPoint)  throws Exception {  
  180.         String targetName = joinPoint.getTarget().getClass().getName();  
  181.         String methodName = joinPoint.getSignature().getName();  
  182.         Object[] arguments = joinPoint.getArgs();  
  183.         Class targetClass = Class.forName(targetName);  
  184.         Method[] methods = targetClass.getMethods();  
  185.         String description = "";  
  186.          for (Method method : methods) {  
  187.              if (method.getName().equals(methodName)) {  
  188.                 Class[] clazzs = method.getParameterTypes();  
  189.                  if (clazzs.length == arguments.length) {  
  190.                     description = method.getAnnotation(SystemControllerLog. class).description();  
  191.                      break;  
  192.                 }  
  193.             }  
  194.         }  
  195.          return description;  
  196.     }  
  197. }  

 第三步把Controller的代理权交给cglib

 

在实例化ApplicationContext的时候需要加上

 

Xml代码 复制代码
  1. <!-- 启动对@AspectJ注解的支持 -->  
  2. <aop:aspectj-autoproxy/>  

 在调用Controller的时候AOP发挥作用所以在SpringMVC的配置文件里加上

 

 

Xml代码 复制代码
  1. <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller- >  
  2. <aop:aspectj-autoproxy proxy-target-class="true" />  

 第四步使用

 

Controller层的使用

 

Java代码 复制代码
  1. /** 
  2.     * 删除用户 
  3.     * 
  4.     * @param criteria 条件 
  5.     * @param id       id 
  6.     * @param model    模型 
  7.     * @return 数据列表 
  8.     */  
  9.    @RequestMapping(value = "/delete")  
  10.    //此处为记录AOP拦截Controller记录用户操作  
  11.    @SystemControllerLog(description = "删除用户")  
  12.     public String del(Criteria criteria, String id, Model model, HttpSession session) {  
  13.         try {  
  14.            User user = (User) session.getAttribute(WebConstants.CURRENT_USER);  
  15.             if ( null != user) {  
  16.                 if (user.getId().equals(id)) {  
  17.                    msg = "您不可以删除自己!";  
  18.                    criteria = userService.selectByCriteriaPagination(criteria);  
  19.                }  else {  
  20.                    //删除数据并查询出数据  
  21.                    criteria = userService.delete(id, criteria);  
  22.                    msg = "删除成功!";  
  23.                }  
  24.            }  
  25.        }  catch (Exception e) {  
  26.            msg = "删除失败!";  
  27.        }  finally {  
  28.            model.addAttribute("msg", msg);  
  29.            model.addAttribute("criteria", criteria);  
  30.        }  
  31.        //跳转列表页  
  32.         return "user/list";  
  33.    }  

 Service层的使用

 

 

Java代码 复制代码
  1. /** 
  2.     * 按照分页查询 
  3.     * @param criteria 
  4.     * @return 
  5.     */  
  6.    //此处为AOP拦截Service记录异常信息。方法不需要加try-catch  
  7.    @SystemServiceLog(description = "查询用户")  
  8.     public Criteria<User> selectByCriteriaPagination(Criteria<User> criteria)  
  9.    {  
  10.        criteria.getList().get(0).getAccount();  
  11.        //查询总数  
  12.         long total=userMapper.countByCriteria(criteria);  
  13.        //设置总数  
  14.        criteria.setRowCount(total);  
  15.        criteria.setList(userMapper.selectByCriteriaPagination(criteria));  
  16.         return  criteria;  
  17.    }  

效果图

 

用户操作:



 异常



 初次写博客,写的不好敬请见谅。

下一篇将讲解微信6大消息接口的封装及开源我的封装敬请关注



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


ITeye推荐



一次优化引发的血案

$
0
0

前些天一个Nginx+PHP项目上线后遭遇了性能问题,于是打算练练手,因为代码并不是我亲自写的,所以决定从系统层面入手看看能否做一些粗线条的优化。

首先,我发现服务的Backlog设置过小,这点可以通过ss命令查询Send-Q来确认:

shell> ss -ln
Recv-Q Send-Q    Local Address:Port      Peer Address:Port
0      511                   *:80                   *:*
0      128           127.0.0.1:9000                 *:*

明显看出,Nginx的Backlog是511;PHP的Backlog是128,在高并发时易成为瓶颈。关于TCP队列的详细介绍,推荐阅读「 TCP queue 的一些问题」,此外,大家有兴趣的可以关注一下在Linux中 全连接半连接队列长度是如何计算的。

其次,我发现服务的进程数设置过少,Nginx的进程数好说,通过 worker_processes指令控制,按照CPU个数设置就行了,如果版本够的话,可以直接设置成auto。 PHP的进程数设置多少合适,并没有一个固定的答案,如果内存充足的话,我一般选择静态模式,并设置进程数为1024个,当然不能片面的以为进程数越多越好,不然调度会成问题。

关于PHP进程数的权衡,我建议大家阅读如下资料:

按照如上的分析,我在测试环境实施时,一切都非常顺利,不过在正式环境实施时,彻底被吓尿了:首先优化PHP,一切正常;接着优化Nginx,结果服务宕机,赶紧回滚了Nginx的优化,服务依然没有起死回生。无奈放出大招:重启服务器,尼玛好了!

转瞬之间经历了莫名其妙的大悲大喜,真让人无法承受,好在重启服务器之后一切都正常了,可以相对从容的查找问题的原因,其实错误日志里已经留下了线索:

setuid(99) failed (11: Resource temporarily unavailable)

原来出现了资源不足!确认一下到底是哪个用户:

shell> grep -w 99 /etc/passwd
nobody:x:99:99:Nobody:/:/sbin/nologin

恰好Nginx和PHP的子进程都是通过nobody用户启动的,看来问题就出在这里了,可是为什么测试环境正常,正式环境异常呢?原来差异出现在操作系统的版本上:虽然操作系统都是CentOS,但测试环境的版本是5.5,正式环境的版本是6.2,最要命的是新版的操作系统里多了一个限制用户进程数的配置文件,缺省设置是1024:

shell> cat /etc/security/limits.d/90-nproc.conf
# Default limit for number of user's processes to prevent
# accidental fork bombs.
# See rhbz #432903 for reasoning.

*          soft    nproc     1024

也就是说,nobody用户最多只能启动1024个进程。案例中,先优化的PHP,由于启动的进程数较多,一下子就花光了所有的资源配额,接着优化Nginx时,失败无法避免。

不过为什么重启服务器后一切看起来都正常了呢?这是启动顺序造成的:

shell> ls /etc/rc3.d/ | grep -E 'nginx|php'
S55nginx
S84php-fpm

也就是说,重启服务器后,Nginx先于PHP启动,由于Nginx的进程数较少,所以启动成功,接着PHP启动时,虽然依然会触发限制阈值,但大部分进程都能够启动成功,只有少部分进程启动失败,所以从表象上看,我们认为成功了。

如果这次优化引发的血案让你意犹未尽,可以继续阅读 ulimit限制之nproc问题

postgresql wal日志参数浅析

$
0
0
之前一直没有认真的学习pg的wal日志相关的参数,发现对这一部分有些不清楚,结合文档又梳理了一遍,浅浅的记录一下,因为wal日志非常重要,并且对性能影响很大,在生产库上要小心调整。

1.fsync
fsync :控制wal日志刷新是否开启刷新到磁盘,此参数控制wal_sync_method参数的刷新方法,如果fsync为off,则wal_sync_method的方法是没有意义的,
如果没开启这个参数,则可能由于wal日志块没有刷新到磁盘永久存储而导致故障发生后实例出现块折断(oracle称其为block curruption)

2.synchronous_commit:
synchronous_commit:同步提交参数, 控制事务提交后返回客户端是否成功的策略,可选值为:on, remote_write, local, and off.当为on时还要看是否有同步备库,因此为on时表现如下:
2.1 为on且没有开启同步备库的时候,会当wal日志真正刷新到磁盘永久存储后才会返回客户端事务已提交成功,
2.2 当为on且开启了同步备库的时候(设置了synchronous_standby_names),必须要等事务日志刷新到本地磁盘,并且还要等远程备库也提交到磁盘才能返回客户端已经提交.

off:写到缓存中就会向客户端返回提交成功,但也不是一直不刷到磁盘,延迟写入磁盘,延迟的时间为最大3倍的wal_writer_delay参数的(默认200ms)的时间,所有如果即使关闭synchronous_commit,也只会造成最多600ms的事务丢失,此事务甚至包括已经提交的事务(会丢数据),但数据库确可以安全启动,不会发生块折断,只是丢失了部分数据,但对高并发的小事务系统来说,性能来说提升较大。
remote_write:当事务提交时,不仅要把wal刷新到磁盘,还需要等wal日志发送到备库操作系统(但不用等备库刷新到磁盘),因此如果备库此时发生实例中断不会有数据丢失,因为数据还在操作系统上,而如果操作系统故障,则此部分wal日志还没有来得及写入磁盘就会丢失,备库启动后还需要想主库索取wal日志。
local:当事务提交时,仅写入本地磁盘即可返回客户端事务提交成功,而不管是否有同步备库

另外需要注意的是,如果没有设置同步备库,则 on/remote_write/local都是一样的,仅等待事务刷新到本地磁盘.
而且此参数还可以局部设置,当有临时批量任务时可以这样设置:
 SET LOCAL synchronous_commit TO OFF;
 这样局部事务可向备库异步的方式同步,而其他重要的事务以同步的方式向备库同步。

3.wal_sync_method 
wal_sync_method :wal日志刷新方法,可选值为open_datasync/fdatasync/fsync/fsync_writethrough/open_sync 
linux系统默认为fdatasync,以open开头的在某些系统上不支持

4.full_page_writes
full_page_writes: 是否开启全页写入,此参数是为了防止块折断的一种策略,关于块折断,每种数据库都会遇到这样的问题,起因是这样的:linux操作系统文件系统一个块一般是4k,而数据库则一般是一个块8k,当数据库的脏块刷新到磁盘上时,由于底层是两个块组成的,比如刷第一个操作系统块到磁盘上了,而当刷第二个操作系统块的时候发生了停电等突然停机事故,则就发生了块折断(数据块是否折断是根据块的checksum值来检查的),为了避免这种事故,pg采用了这样的机制:
当checkpoint后的一个块第一次变脏后就要整块写入到wal日志中,后续继续修改此块则只把修改的信息写入wal日志中,如果在此过程中发生了停电,则实例启动后会从checkpoint检查点,之后开始进行实例恢复,如果有块折断,则在全页写入的块为基础进行恢复,最后覆盖磁盘上的折断块,所以当每次checkpoint后如果数据有修改都会进行全页写入,因此控制checkpoint的
间隔是否重要,如果checkpoint_segments设置太小就会造成频繁的checkpoint,进而导致写入了过多的全页.mysql为了防止块折断采用了double write,oracle采用了redo+undo机制,其中undo记录了前镜像,而redo则既记录了修改数据又记录了undo块。

5.wal_buffers
wal_buffers :wal缓冲区,默认为-1,大小为1/32的shared_buffer,最小不少于64k,最大不大于一个wal_segment(默认16M大小),一般保持默认即可,因为过了wal_writer_delay(默认200ms)总会刷新清空此缓存,设置太大了也用不上.

6.wal_writer_delay
wal_writer_delay:前面已经说过,这有点类似oracle和mysql的1s定时写日志策略,每隔这么长时间就会刷wal日志缓冲区的数据,然后sleep,到点后再刷,如此循环往复.

7.commit_delay
commit_delay :提交的延迟时间,如果设置了此参数,则会commit后延迟一段时间再进行提交,此机制可以合并其他事务进而一起进行组提交,不过合并的事务数是有限制的,要至少有commit_siblings参数个事务等待提交的时候才会延迟,所有当有大量事务的时候会延迟,而如果事务很稀少就不会再被延迟了.

8.commit_siblings
commit_siblings :组提交个数的最少个数,此参数上面已经进行说明

Viewing all 15843 articles
Browse latest View live


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