频道栏目
IT货架 > > 正文
(转)Quartz 在 Spring 中怎么动态配置时间 DBI 数据库模块辨析:Perl DBI 数据库通讯模块规范,工作原理和实例 mesos集群装配
网友分享于:Jun 12, 2018 10:47:58 PM    来源: IT货架   
(转)Quartz 在 Spring 中如何动态配置时间
  在项目中有一个需求,需要灵活配置调度任务时间,并能自由启动或停止调度。 
有关调度的实现我就第一就想到了Quartz这个开源调度组件,因为很多项目使用过,Spring结合Quartz静态配置调度任务时间,非常easy。比如:每天凌晨几点定时运行一个程序,这只要在工程中的spring配置文件中配置好spring整合quartz的几个属性就好。 

Spring配置文件 
引用

<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
<property name="targetObject" ref="simpleService" />  
<property name="targetMethod" value="test" />  
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">  
<property name="jobDetail" ref="jobDetail" />  
<property name="cronExpression" value="0 0/50 * ? * * *" />  
</bean>  
<bean  id="schedulerTrigger" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
<property name="triggers">  
<list>
<ref bean="cronTrigger"/>       
</list>  
</property>  
</bean>  


这种配置就是对quartz的一种简单的使用了,调度任务会在spring启动的时候加载到内存中,按照cronTrigger中定义的 cronExpression定义的时间按时触发调度任务。但是这是quartz使用“内存”方式的一种配置,也比较常见,当然对于不使用spring的项目,也可以单独整合quartz。方法也比较简单,可以从quartz的doc中找到配置方式,或者看一下《Quartz Job Scheduling Framework 》。  

但是对于想持久化调度任务的状态,并且灵活调整调度时间的方式来说,上面的内存方式就不能满足要求了,正如本文开始我遇到的情况,需要采用数据库方式集成 Quartz,这部分集成其实在《Quartz Job Scheduling Framework 》中也有较为详细的介绍,当然doc文档中也有,但是缺乏和spring集成的实例。  

一、需要构建Quartz数据库表,建表脚本在Quartz发行包的docs\dbTables目录,里面有各种数据库建表脚本,我采用的Quartz 1.6.5版本,总共12张表,不同版本,表个数可能不同。我用mysql数据库,执行了Quartz发行包的docs\dbTables\tables_mysql_innodb.sql建表。  

二、建立java project,完成后目录如下  
 

三、配置数据库连接池  
配置jdbc.properties文件  
引用

jdbc.driverClassName=com.mysql.jdbc.Driver  
jdbc.url=jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true  
jdbc.username=root  
jdbc.password=kfs  
cpool.checkoutTimeout=5000  
cpool.minPoolSize=10  
cpool.maxPoolSize=25  
cpool.maxIdleTime=7200  
cpool.acquireIncrement=5  
cpool.autoCommitOnClose=true  


配置applicationContext.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:aop="http://www.springframework.org/schema/aop"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xmlns:context="http://www.springframework.org/schema/context"  
     xmlns:jee="http://www.springframework.org/schema/jee"  
    xsi:schemaLocation="  
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd  
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd  
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
     http://www.springframework.org/schema/jee  
       http://www.springframework.org/schema/jee/spring-jee-2.5.xsd"  >  
   
   <context:component-scan base-package="com.sundoctor"/>  

<!-- 属性文件读入 -->  
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
<property name="locations">  
<list>  
<value>classpath:jdbc.properties</value>  
</list>  
</property>  
</bean>  

<!-- 数据源定义,使用c3p0 连接池 -->  
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="initialPoolSize" value="${cpool.minPoolSize}"/>
<property name="minPoolSize" value="${cpool.minPoolSize}" />
<property name="maxPoolSize" value="${cpool.maxPoolSize}" />
<property name="acquireIncrement" value="${cpool.acquireIncrement}" />  
    <property name="maxIdleTime" value="${cpool.maxIdleTime}"/>    
</bean>

</beans>  

这里只是配置了数据连接池,我使用c3p0 连接池,还没有涉及到Quartx有关配置,下面且听我慢慢道来。  

四、实现动态定时任务  
  什么是动态定时任务:是由客户制定生成的,服务端只知道该去执行什么任务,但任务的定时是不确定的(是由客户制定)。  
这样总不能修改配置文件每定制个定时任务就增加一个trigger吧,即便允许客户修改配置文件,但总需要重新启动web服务啊,研究了下Quartz在Spring中的动态定时,发现  
引用

<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean" >  
         <property name="jobDetail" ref="schedulerJobDetail"/>  
         <property name="cronExpression">  
             <value>0/10 * * * * ?</value>  
         </property>  

cronExpression是关键,如果可以动态设置 cronExpression的值,就可以顺利解决问题了。这样我们就不能直接使用org.springframework.scheduling.quartz.CronTriggerBean,需要自己实现一个动态调度服务类,在其中构建CronTrigger或SimpleTrigger,动态配置时间。  
动态调度服务接口:  
Java代码   
  1. package com.sundoctor.quartz.service;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import org.quartz.CronExpression;  
  6.   
  7. public interface SchedulerService {  
  8.     /** 
  9.      * 根据 Quartz Cron Expression 调试任务 
  10.      * @param cronExpression  Quartz Cron 表达式,如 "0/10 * * ? * * *"等 
  11.      */  
  12.     void schedule(String cronExpression);  
  13.       
  14.     /** 
  15.      * 根据 Quartz Cron Expression 调试任务 
  16.      * @param name  Quartz CronTrigger名称 
  17.      * @param cronExpression Quartz Cron 表达式,如 "0/10 * * ? * * *"等 
  18.      */  
  19.     void schedule(String name,String cronExpression);  
  20.       
  21.     /** 
  22.      * 根据 Quartz Cron Expression 调试任务 
  23.      * @param cronExpression Quartz CronExpression 
  24.      */  
  25.     void schedule(CronExpression cronExpression);  
  26.       
  27.     /** 
  28.      * 根据 Quartz Cron Expression 调试任务 
  29.      * @param name Quartz CronTrigger名称 
  30.      * @param cronExpression Quartz CronExpression 
  31.      */  
  32.     void schedule(String name,CronExpression cronExpression);  
  33.       
  34.     /** 
  35.      * 在startTime时执行调试一次 
  36.      * @param startTime 调度开始时间 
  37.      */  
  38.     void schedule(Date startTime);    
  39.       
  40.     /** 
  41.      * 在startTime时执行调试一次 
  42.      * @param name Quartz SimpleTrigger 名称 
  43.      * @param startTime 调度开始时间 
  44.      */  
  45.     void schedule(String name,Date startTime);  
  46.       
  47.     /** 
  48.      * 在startTime时执行调试,endTime结束执行调度 
  49.      * @param startTime 调度开始时间 
  50.      * @param endTime 调度结束时间 
  51.      */  
  52.     void schedule(Date startTime,Date endTime);   
  53.       
  54.     /** 
  55.      * 在startTime时执行调试,endTime结束执行调度 
  56.      * @param name Quartz SimpleTrigger 名称 
  57.      * @param startTime 调度开始时间 
  58.      * @param endTime 调度结束时间 
  59.      */  
  60.     void schedule(String name,Date startTime,Date endTime);  
  61.       
  62.     /** 
  63.      * 在startTime时执行调试,endTime结束执行调度,重复执行repeatCount次 
  64.      * @param startTime 调度开始时间 
  65.      * @param endTime 调度结束时间 
  66.      * @param repeatCount 重复执行次数 
  67.      */  
  68.     void schedule(Date startTime,Date endTime,int repeatCount);   
  69.       
  70.     /** 
  71.      * 在startTime时执行调试,endTime结束执行调度,重复执行repeatCount次 
  72.      * @param name Quartz SimpleTrigger 名称 
  73.      * @param startTime 调度开始时间 
  74.      * @param endTime 调度结束时间 
  75.      * @param repeatCount 重复执行次数 
  76.      */  
  77.     void schedule(String name,Date startTime,Date endTime,int repeatCount);  
  78.       
  79.     /** 
  80.      * 在startTime时执行调试,endTime结束执行调度,重复执行repeatCount次,每隔repeatInterval秒执行一次 
  81.      * @param startTime 调度开始时间 
  82.      * @param endTime 调度结束时间 
  83.      * @param repeatCount 重复执行次数 
  84.      * @param repeatInterval 执行时间隔间 
  85.      */  
  86.     void schedule(Date startTime,Date endTime,int repeatCount,long repeatInterval) ;  
  87.       
  88.     /** 
  89.      * 在startTime时执行调试,endTime结束执行调度,重复执行repeatCount次,每隔repeatInterval秒执行一次 
  90.      * @param name Quartz SimpleTrigger 名称 
  91.      * @param startTime 调度开始时间 
  92.      * @param endTime 调度结束时间 
  93.      * @param repeatCount 重复执行次数 
  94.      * @param repeatInterval 执行时间隔间 
  95.      */  
  96.     void schedule(String name,Date startTime,Date endTime,int repeatCount,long repeatInterval);  
  97. }  


动态调度服务实现类:  
Java代码   
  1. package com.sundoctor.quartz.service;  
  2.   
  3. import java.text.ParseException;  
  4. import java.util.Date;  
  5. import java.util.UUID;  
  6.   
  7. import org.quartz.CronExpression;  
  8. import org.quartz.CronTrigger;  
  9. import org.quartz.JobDetail;  
  10. import org.quartz.Scheduler;  
  11. import org.quartz.SchedulerException;  
  12. import org.quartz.SimpleTrigger;  
  13. import org.springframework.beans.factory.annotation.Autowired;  
  14. import org.springframework.beans.factory.annotation.Qualifier;  
  15. import org.springframework.stereotype.Service;  
  16.   
  17. @Service("schedulerService")  
  18. public class SchedulerServiceImpl implements SchedulerService {  
  19.   
  20.     private Scheduler scheduler;  
  21.     private JobDetail jobDetail;  
  22.   
  23.     @Autowired  
  24.     public void setJobDetail(@Qualifier("jobDetail") JobDetail jobDetail) {  
  25.         this.jobDetail = jobDetail;  
  26.     }  
  27.   
  28.     @Autowired  
  29.     public void setScheduler(@Qualifier("quartzScheduler") Scheduler scheduler) {  
  30.         this.scheduler = scheduler;  
  31.     }  
  32.   
  33.     @Override  
  34.     public void schedule(String cronExpression) {  
  35.         schedule(null, cronExpression);  
  36.     }  
  37.   
  38.     @Override  
  39.     public void schedule(String name, String cronExpression) {  
  40.         try {  
  41.             schedule(name, new CronExpression(cronExpression));  
  42.         } catch (ParseException e) {  
  43.             throw new RuntimeException(e);  
  44.         }  
  45.     }  
  46.   
  47.     @Override  
  48.     public void schedule(CronExpression cronExpression) {  
  49.         schedule(null, cronExpression);  
  50.     }  
  51.   
  52.     @Override  
  53.     public void schedule(String name, CronExpression cronExpression) {  
  54.         if (name == null || name.trim().equals("")) {  
  55.             name = UUID.randomUUID().toString();  
  56.         }  
  57.   
  58.         try {  
  59.             scheduler.addJob(jobDetail, true);  
  60.   
  61.             CronTrigger cronTrigger = new CronTrigger(name, Scheduler.DEFAULT_GROUP, jobDetail.getName(),  
  62.                     Scheduler.DEFAULT_GROUP);  
  63.             cronTrigger.setCronExpression(cronExpression);  
  64.             scheduler.scheduleJob(cronTrigger);  
  65. DBI 数据库模块辨析:Perl DBI 数据库通讯模块规范,工作原理和实例

    DBI 数据库模块剖析:Perl DBI 数据库通讯模块规范,工作原理和实例

    原文:http://tech.ddvip.com/2010-06/1275904179154934.html

         本文详细地介绍了 Perl 语言中用于和数据库通讯的 DBI 模块。以细腻的笔法和生动地示例给读者讲述了 DBI 模块的主要组成部分,结构和供用户编程时调用的方法。同时,本文也涉及了一些 DBI 模块中的高级主题,如对存储过程的调用,对并发事务的处理和数据库及执行语句句柄属性的设置和元数据的处理。除此之外,本文还对 DBD 模块的接口给予了入门级的介绍,希望可以对读者在专研第三方数据库的 DBD 模块时可以有所帮助。在本文的最后,通过定义了一套通用的 DBI 模块测试接口,对三种主流数据库 DB2,Sybase 和 MySql 进行测试,通过比较测试结果,得出了 DBI 模块在这三种数据库上的异同点和支持上的盲点。

      DBI 数据库模块剖析

      为了和数据库进行通讯,Perl 的社区开发出了统一数据库通讯接口模块:DBI。DBI 作为 Perl 语言中和数据库进行通讯的标准接口,它定义了一系列的方法,变量和常量,成功地提供一个和具体数据库平台无关的数据库持久层。

      DBI 模块的体系结构

      概述

      整个 DBI 模块的结构可以被分成两个主要的部分:DBI 模块本身和实现与具体数据库平台通讯的驱动模块。DBI 模块用于定义提供给 Perl 开发者使用的编程接口,和对不同数据库驱动模块的调用方法。具体的数据库通讯驱动模块的实现和特定的数据库平台有关,并且负责和具体数据通讯的实际操作。

      DBI 模块中的三种句柄

      在 DBI 模块的定义中,用户可以创建三种不同类型的句柄。

      驱动模块句柄(Driver Handle):驱动模块句柄代表一个在内存中加载的驱动,它在 DBI 模块加载其对应的驱动模块被创建。它和实际的驱动模块之间是一一对应的关系。驱动模块句柄提供了两个主要的方法是 data_sources() 和 connect()。

      数据库句柄(Database Handles):数据库句柄是程序员使用 DBI 模块和后台数据库进行通讯的第一步,它包含了一个到特定数据库的某个独立的连接。表一给出了对于常见数据库的连接字符串:

      执行语句句柄(Statement Handles):执行语句句柄在 DBI 规范中被定义为和数据库进行交互和操作的接口。这些句柄包装了一条条 SQL 语句,并将它们交送给后台数据库执行。一个使用执行语句句柄执行 SQL 语句的例子见程序一。

    表 1. 常见数据库的连接字符串

     

    数据库 连接字符串(示例)
    DBD::mysql “DBI:mysql:database=$dbname;host=$hostname;port=$port”;
    DBD::Oracle “dbi:Oracle:$dbname”; “dbi:Oracle:host=$hostname;sid=$sid”;
    DBD::DB2 “dbi:DB2:$dbname”;
    DBD::Sybase “dbi:Sybase:host=$hostname;database=$dbname;port=$port”;

     

    清单 1. 使用 DBI 模块连接数据库并执行 SQL 语句

     $dbh = DBI->connect ($connection_string, $userid, $passwd); 
     $sth = $dbh->prepare (“SELECT * FROM tbl”); 
     $sth->execute(); 
     while (@row = $sth->fetchrow_array()) { 
     print “Each record: @row \n”; 
     } 
     $sth->finish(); 
     $dbh->disconnect(); 

      建立和释放连接

      使用 DBI 模块提供的 connect()方法创建一个数据库句柄是,程序员必须提供一个数据源用于指定要连接的数据库。DBI 模块的规范要求数据源的名字并须以 dbi: 开头,然后接上数据库通讯驱动模块的名字并且以‘ : ’结尾,比如‘ dbi:db2: ’。和 connect()方法相对应,DBI 模块中还定义了一个 disconnect()方法。

      通过调用 DBI->available_drivers()方法,程序员可以得到已安装在当前机器上的所有数据库通讯驱动模块的列表。接着用驱动模块名作为参数,调用 DBI->data_sources()方法,可以得到对应此驱动模块的所有数据源的列表,程序二给出了一个这样的例子 .

    清单 2. 得到所有支持驱动模块的所有数据源

     my @drivers = DBI->available_drivers(); 
     die “No dirvers defined! \n” unless @drivers; 
     foreach my $driver (@drivers) { 
     print “Driver: $driver \n”; 
     my @data_sources = DBI->data_sources ($driver) { 
     foreach my $data_source (@data_sources) { 
      print “\t Data Source: $data_source \n”; 
     } 
     print “\n”; 
     } 

      错误处理

      在 DBI 模块中提供了两种错误处理的方法。第一种方法依靠程序员手工检测被调用方法的返回值;第二种方法通过 DBI 模块对错误进行自动检测,这种方法类似于程序的异常处理机制。对于手工错误处理,可以通过将“PrintError”和“RaiseError”两个变量设定为 0 来激活。在默认情况下,“PrintError”参数是被激活的。

      方法一,在调用 DBI 的 connect()函数的参数中设置:

     %attr = (PrintError => 0, RaiseError=>0); 
     my $dbh = DBI->connect (“dbi:Oracle:testdb”, “username”, “password”, \%attr); 

      方法二,在数据库句柄中直接设置:

     $dbh->{PrintError} = 0; $dbh->{RaiseError} = 0; 

      对于自动错误检测,DBI 提供了两种不同级别的错误处理方法。用于句柄的“PrintError”参数在被设置为 1 的时候,DBI 模块会调用 warn()函数进行错误处理。它会将错误信息打印到屏幕,但是并不会中止进程。而用于句柄的“RaiseError”参数在被设置为 1 的时候,DBI 模块会调用 die()函数,并且中止进程。这两个不同级别的错误处理方法可以在 DBI 模块的任何一个有效的句柄中被激活。

      除了错误处理手段以外,DBI 模块还提供了对错误信息进行诊断的方法。这些方法可以对于任何一个有效的句柄进行使用,它们的返回值包括错误号和错误信息。

     $rv = $h->err(); $str = $h->errstr(); $str = $h->state(); 

      $h->err()方法返回一个由底层数据库生成的错误号;$h->errstr()方法则返回一个由底层数据库生成的错误信息描述;$h->state()方法返回一个 5 位的 SQLSTATE 错误字符串。除了上面的三种方法会返回错误信息供排错之外,在 DBI 模块级别,$DBI::err,$DBI::errstr,$DBI::state 会返回和上述函数同样的值。一个利用 DBI 模块内置错误处理方法的例子见程序三。

    清单 3. 利用 DBI 模块的内置错误处理方法

     while (1) { 
     my $dbh; 
     # disable automatic error handle 
     until { 
     $dbh = DBI->connect ($connection_string, $userid, $username); 
     $dbh->{PrintError} = 0; 
     $dbh->{RaiseError} = 0; 
     warn “Unable to connect: $DBI::errstr. sleep for 5 minutes. \n”; 
     sleep (300); 
     } 
     # enable automatic error handle 
     eval { 
     $dbh->{RaiseError} = 1; 
     my $sth = $dbh->prepare (“SELECT foo, bar from tbl”); 
     while (1) { 
     $sth->execute(); 
     while ( my @row = $sth->fetchrow_array()) { 
     print “Row: @row \n”; 
     } 
     sleep 60; 
     } 
     } 
     warn “Monitoring aborted by error: $@\n” if $@; 
     sleep 5; 
     } 

      操作数据库

      执行简单查询

      应用程序与数据库之间最常见的操作是从数据库中查询并且提取数据。在标准的 SQL 语句规范中,这一过程是使用关键字 SELECT 的。

      一个通过 DBI 执行的标准简单查询包括以下四个阶段:

      准备阶段(Perpare SQL statement):

      通过调用 prepare()方法,准备阶段解析 SQL 语句,对 SQL 语句进行验证,并且返回一个执行语句句柄,这个句柄代表将在数据库内被执行的这条 SQL 语句。

      执行阶段(Execute select statement):

      通过调用 execute()方法,执行阶段执行 SQL 语句,查询数据库,并且以被查询的数据填充 Perl 的数据结构。但是,在这一阶段中,你的 Perl 应用程序并未真正地访问到被查询的数据。

      数据抽取阶段(fetching date):

      第三阶段被称为数据抽取阶段,在这一阶段实际的数据从数据库中被抽取出来,通过调用 fetch()方法族的一组方法。数据抽取阶段从数据库得到查询所得的数据,以每一条数据为单位,注入 Perl 的数据结构。DBI 提供了多种方法对数据进行抽取,可以将被抽取的数据用一个列表,一个指向数组的引用,或是一个指向哈希表的引用的方式提供给应用程序。而且每条记录中字段的顺序也是由 SQL 语句中指定的顺序所决定。一个包含三种方法的例子见程序四。

      完成阶段(finishing date fetch):

      最后一个阶段被称为完成阶段,这个阶段主要释放资源,并且清理相关数据结构中保存的历史信息,通过显式地调用 finish()方法来完成。当一个执行语句句柄(statement handler)被成功执行后,它的状态会被标记成为活跃。你可以通过访问执行语句句柄的 Active 属性来访问它。在用户执行 fetch()方法,从数据库中抽取了最后一列数据之后,数据库驱动自动关闭数据库中正在进行地和这个执行语句句柄有关的工作,并且重置 Active 属性为不活跃状态。这一切工作都是在读取了最后一列数据之后被自动触发,在大多数情况下,用户并不需要额外地关心这一过程中后台所作的工作。某些额外情况,需要应用程序主动调用 finish()方法释放资源。一个典型的例子就是:当数据库占用了数目可观的磁盘空间存储临时文件,存放查询结果,而应用程序又不需要保存所有的查询结果。比如执行一条形如 “SELECT EMP_DEP, count(*) FROM EMP GROUP BY EMP_DEP ORDER BY count(*) DESC”的 SQL 语句,在应用程序只需要部分统计结果的情况,应该显示调用 finish()方法,释放被申请和占用的机器资源。

    清单 4. 三种不同的数据抽取方法

     $sth->execute(); 
     # fetch data by an array 
     while ( @row = $sth->fetchrow_array() ) { 
     print “Column1: $row[0] \t Column2: $row[1]. \n”; 
     } 
     # fetch data by a reference pointing to array 
     while ( $array_ref = $sth->fetchrow_arrayref() ) { 
     print “Column1: $array_ref->[0] \t Column2: $array_ref->[1]. \n”; 
     } 
     # fetch data by a reference pointing to hash table 
     while ( $hash_ref = $sth->fetchrow_hashref() ) { 
     print “Column1: $hash_ref->{column1} \t Column2: $hash_ref->{column2}. \n”; 
     } 

      执行非查询语句

      在数据库的常用 DML 语句中,除了 SELECT 语句之外,还有 INSERT,DELETE,UPDATE 三种,我们统称这三种语句为非查询语句。与查询语句 SELECT 不同,它们只是改变了数据库中的部分纪录,而不会返回一个记录集给应用程序。所以,相对对于查询语句中的 prepare-execute-fetch-deallocate 序列来说,非查询语句不需要数据抽取阶段,同时也可以将 prepare 和 execute 阶段用一个 do()方法来完成。一个调用 do()方法的例子如下 :

     $affected_row_number = $dbh->do (“DELETE FROM tbl WHERE foo = ‘ bar ’”); 

      DBI 模块提供了一个 do()方法用以简化工作,取代了原先需要被调用的 prepare()和 execute()方法。实际上,DBI 模块中的 do()方法只是简单地包含了 prepare()和 execute()方法。这种方法在使用添写方法生成 SQL 执行语句的时候和分别使用 prepare()方法和 execute()方法没有任何性能上面的区别,但是如果使用了参数绑定的 SQL 执行语句生成方法,两者在性能上将会有显著地差别。因为使用 do()方法的话,以 INSERT 操作为例,对于每条被插入的记录,数据库必须解析每条插入语句,并且生成执行计划。然而使用 prepare()方法的话,可以在 prepare()方法中使用占位符,而使得所有的插入语句可以共用一个执行计划,达到了提升效率的目地,具体的例子见程序六。

      参数绑定

      在准备阶段,有一个重要的概念就是数据绑定,与之相关的有三个术语:占位符(placeholder),参数(parameter)和绑定(binding),他们都是用来根据程序上下文动态地生成 SQL 语句。将变量放入 SQL 语句的方法有两种:第一种通过添写方法生成 SQL 语句(interpolated SQL statement creation),该方法将变量直接通过字符串和 SQL 语句的其它部分连接起来,生成可以被执行的 SQL 语句。程序五展示了一个利用 Perl 字符串技术生成的一个 SQL 执行语句。

    清单 5. 使用 Perl 字符串技术动态生成 SQL 执行语句的例子

     foreach $table_name ( qw (table1, table2, table3) ) { 
     $sth = $dbh->prepare(“SELECT count(*) FROM $table_name”); 
     $sth->execute(); 
     } 

      第二种方法在 SQL 语句中使用占位符,并且通过 bind_param()方法将变量与之绑定,生成 SQL 语句。无论何时,bind_param()方法必须在 execute()方法之前被调用,否则被绑定的参数无法填入 SQL 语句中,而对 SQL 语句的调用也注定会失败。采用绑定方法是成 SQL 语句(Bind value SQL statement creation)的一个典型的例子见程序六。

    清单 6. 使用占位符动态生成 SQL 执行语句的例子

     $sth = $dbh->prepare (“SELECT foo, bar FROM table WHERE foo = ? AND bar = ?”); 
     $sth->bind_param (1, “FOO”); 
     $sth->bind_param (2, “BAR”); 
     $sth->execute(); 

      第一种方法使用 Perl 的字符串处理函数,生成一条完整的 SQL 语句,并将它送往后台数据库;而绑定方法则不同,它将含有占位符的 SQL 语句和绑定值分开传送给数据,在数据后台处理数据绑定,然后执行绑定后的 SQL 语句。两种不同的处理方法将会带来性能上的巨大差异,尤其是在有大量相似的 SQL 语句需要被处理的情况下。主流的大型数据库都有一种被称为“Shared SQL Cache”的部件,它存储了诸如查询语句执行计划之类的辅助数据结构,帮助数据库执行 SQL 语句。在得到一个新的处理 SQL 语句的请求时,如果 SQL 语句已经存在于“Shared SQL Cache”中,数据库就不需要重新处理这条语句,而可以重用 Cache 中存储的信息,这样就可以带来性能上的显著提升。

      调用存储过程

      存储过程运行在后台数据库上,可以有效地减少客户端和数据库之间的通讯量。在这种工作模式下,客户端不再需要将每一条需要被执行的 SQL 语句发往后台数据库,借助存储过程可以将所有要执行的 SQL 语句定义在一个存储过程内,统一执行并且返回运算结果给客户端。

      在 DBI 模块中有一个和 bind_param()方法相似的方法,叫做 bind_param_inout()方法,可以从执行语句句柄中直接返回值。这个方法最主要的应用在于调用存储过程用来接收参数和返回结果。需要注意的是,某些数据库(比如 MySql)不支持这种方法。bind_param_inout()方法通过传递引用的方式向数据库传入一个可以接受返回值的参数,一个简单的例子如下:

     $sth->bind_param_inout (1, \$bar, 50); 

      上面例子当中,bind_param_inout()方法的第三个参数是返回值的最大长度。

      一个在 DB2 数据库中调用存储过程的完整示例见程序七。

    清单 7. 调用 DB2 数据库中存储过程的例子

     create procedure proc (in foo char(6), out bar integer) 
     specific proc_example 
     dynamic result sets 0 
     modifies sql data 
     not deterministic 
     language sql 
     begin atomic 
     insert into tbl ( ‘ foo ’ , current date); 
     select count(*) into bar from tbl where foo = ‘ foo ’ ; 
     end 
     
     # start perl script to call sql procedure 
     $sth = $dbh->prepare (“CALL proc (?,?)”); 
     $sth->bind_param (1, $foo); 
     $sth->bind_param_inout (2, \$bar, 50); 
     $sth->execute(); 
     print “stored procedure returned $bar. \n”; 

      执行数据库事务

      数据库事务是一种将一组相互之间有密切关系的 SQL 语句放到一起执行的技术,它们要么都被成功执行,要么都执行失败,我们称之为“all-or-nothing”模式。一个事务从它的第一条可被执行的 SQL 语句开始,到被提交(commit)或者回滚(rollback)结束。如果一个事务被提交,那么它对数据库所作的所有修改都会被保存并且对其它并发过程可见;如果一个事务被回滚,它对数据库所作的所有修改都会被放弃。

      并不是所有的数据库软件都支持事务,但是对于所有支持事务的数据库,DBI 模块提供了统一的接口用于操作事务。尽管数据库的实现各不相同,DBI 模块提供了事务自动提交(auutomatic transcation committing)和手工事务处理(powerful manual transaction)的两种不同处理方式。如果用户在创建一个数据库句柄(database handle)时,将句柄的参数“AutoCommit”设置为 1,那么通过这个数据库句柄执行的每一条 SQL 语句的操作都会被立即提交,而不需要任何显式语句进行提交或回滚;反之,如果参数“AutoCommit”被设置为 0,则每个事物都必须以显示地调用 commit()方法或 rollback()方法来结束。如果后台数据库不支持事务处理,那么当用户试图修改“AutoCommit”参数为 0 的时候,DBI 模块将会抛出错误。在 DBI 模块中定义了 commit()方法,用来显式地提交在一个事务范围内对数据库所作的修改。该方法通过数据库句柄来调用,如下:

     $dbh->commit(); 

      如果调用 commit()方法的数据库句柄中的“AutoCommit”属性被设置为 1,那么调用 commit()方法之后会得到一个“commit ineffective with AutoCommit”的警告信息。如果后台数据库不支持事务处理,那么“AutoCommit”属性默认打开,每次调用 commit()方法之后也会得到一个相同的警告信息。与之相似的是 rollback()方法,每次调用 rollback()方法会回滚事务范围内对数据库所作的所有修改。如果“AutoCommit”设置为一或者是后台数据库不支持事务处理,同样的警告信息也将会被返回。一个调用 rollback()方法的例子如下:

     $dbh->rollback(); 

      不幸的事情是,当“AutoCommit”属性被设置为 0 的时候,显式地调用 disconnect()方法终止和数据库的连接后触发的行为无法预测。在某些数据库的版本中,在 disconnect()方法被 DBI 模块调用之前,会自动调用 commit()方法,提交所有对数据库的修改;也有某些数据库版本会调用 rollback()方法,回滚所有对数据库所作的修改。一个综合示例见程序八。

    清单 8. DBI 模块处理数据库事务的详细例子

     $dbh->connect(); 
     $dbh->{AutoCommit} = 0; 
     $dbh->{RaiseError} = 1; 
     eval { 
     load_some_data_to_database(); 
     insert_some_data_to_database(); 
     delete_some_data_from_database(); 
     $dbh->commit() 
     } 
     if ($@) { 
     $dbh->rollback(); 
     } 
     $dbh->disconnect(); 

      DBI 模块中的高级主题

      DBI 中的句柄属性

      除了与数据库句柄和执行语句句柄绑定的一组方法以外,DBI 模块也提供了一组和这些句柄有关的属性,供用户对他们的执行环境进行调优。这些句柄属性的实质是一个由“键 / 值”对组成的哈希表,可以通过和操作哈希表引用一样的方法来访问和修改这些属性。一个典型的例子见程序九。

    清单 9. 修改和显示句柄的属性

     $dbh->connect($connect_string, $userid, $passwd); 
     $dbh->{AutoCommit} = 1; 
     print “AutoCommit: $dbh->{AutoCommit} \n”; 

      当用户访问或者设置这些句柄属性时,DBI 模块自动检查用户输入的属性名称。如果用户试图引用一个位置属性时,DBI 模块将会产生一个错误。同理,如果用户试图修改一个只读属性时,DBI 模块会以同样的方式抛出一个错误。无论用户有没有在创建数据库句柄时设置了“RaiseError”属性,在检测到上述错误之后,DBI 模块都会使用 die()方法抛出错误,返回一个错误值。

      句柄属性的命名规则

      句柄属性的命名看似杂乱无章,其实微言大义,在 DBI 模块的兼容性问题发挥了重要的作用。句柄属性的大小写命名规则直接反映了是谁定义了这个属性并对其赋值,其规则有三:

      全大写命名方式(UPPER_CASE):全大写属性通常是由外部标准所定义的,比如说 ISO SQL 或者 ODBC。

      混合命名方式(Mixed_Case):此类属性的名字通常由大写字母开头,但是其中也混有小写字母。这类属性通常由 DBI 模块标准自身定义。

      全小写命名方式(lower_case):此类属性由各自的数据库驱动所定义,被称为“驱动相关”属性。

      公共句柄属性

      表二列出了部分最常见的数据库句柄(database handle)和执行语句句柄(statement handle)都支持的句柄属性。

    表 2. 常见公共句柄属性

     

    属性名 说明
    PrintError 如果此值被设置为 1,当 DBI 模块返回一个错误结果的时候,使用 warn()方法;
    RaiseError 如果此值被设置为 1,当 DBI 模块一个错误结果的时候,使用 die()方法;

     

      数据库句柄的属性和数据库元数据

      表三列出了最常见的数据库句柄所支持的句柄属性。

    表 3. 常见数据库句柄属性

     

    属性名 说明
    AutoCommit 如果设置为 1,每一条通过 DBI 模块执行的结果都会被立即提交;反之,如果设置为 0,
    所有的执行结果都要用 commit()方法显示提交或用 rollback()方法回滚;
    Name 只读属性,存储数据库的名字;

     

      数据库原数据(database metadata)被称为“描述数据的数据”,用来描述数据本身。当用户有动态生成 SQL 执行语句或者创建动态视图需求的时候,数据库元数据所提供的信息显得格外地重要。不用数据库厂商存储数据库元数据的方法和保存的数据库元数据各不相同,大多数主流数据提供了一个系统目录(system catalog),通过表和视图保存这些信息。尽管因为上述的种种差异,使得 DBI 模块提供一个统一的访问数据库元数据的接口成了一件任重道远的任务,在 DBI 模块的定义中依然提供了两个可供调用的方法,用来访问数据库元数据。

      第一个方法 $dbh->tables()返回一个包含调用它的数据库句柄(database handle)所能发现的所有表和视图的数组,见程序十。

    清单 10. 利用数据库句柄返回数据库的元数据

     my @tables = $dbh->tables(); 
      foreach my $table (@tables) { 
       print “Table Name: ” . $table . “ \n”; 
      } 

      第二个方法 $dbh->table_info()返回一个包含更多详细信息的执行语句句柄(statement handle),可以通过和访问普通执行语句句柄一样的方法,抽取其中的信息。

      执行语句句柄属性和表原数据

      同数据库句柄一样,执行语句句柄(statement handle)也有自己的句柄属性。执行语句句柄从创建它的数据库句柄继承了部分属性,并且它的大部分属性是用来表示语句的执行状态和结果的,属于只读属性。表四列出了常见的执行语句属性:

    表 4. 常见执行语句句柄属性

     

    属性名 说明
    NUM_OF_FIELDS 被 SELECT 语句返回的字段的数量;
    NAME 被 SELECT 语句返回的字段的名称;
    NULLABLE 某个字段是否可以为空;
    TYPE 某个字段的类型;
    NUM_OF_PARAMS 一个 SQL 执行语句所使用的占位符的数量;

     

      通过读取某些执行语句的句柄属性,可以得到表的原数据,示例如下:

    清单 11. 利用执行语句句柄返回表的元数据

     my $sth = $dbh->prepare (“SELECT * FROM tbl”); 
     $sth->execute(); 
     my $field_number = $sth->{NUM_OF_FIELDS}; 
     print “NUMBER OF FIELDS: $field_number \n”; 
     print “Column Name                 Type  Nullable? \n”; 
     print “--------------------------------------- ---- --------- \n”; 
     for (my $index=0; $index<$field_number; $index++) { 
     my $name = $sth->{NAME}->[$index]; 
     my $type = $sth->{TYPE}->[$index]; 
     my $nullable = (“No”, “Yes”, “Unknown”)[$sth->{NULLABLE}->[$index]]; 
     printf “%-30s %4d $s \n”, $name, $type, $nullable; 
     } 
     $sth->finish(); 

      符合 DBI 模块接口的 DBD 模块

      DBI 模块定义了一组和用户交互的接口,而 DBD 实现与数据库之间的具体接口。两者之间的关系可以类比为面向对象编程中接口和继承接口的类之间的关系。有两种不同的数据库驱动程序的实现方式,第一种是基于纯粹的 Perl 语言,不依赖任何 C 语言编译器。这种方法实现最为简单,却不被大多数的数据库所支持,比较典型的 DBD 模块例子有 DBD::File 和 DBD::CSV。另一种方法更为普遍,使用了 C 程序的帮助和数据库进行通讯,被称为 C/XS 驱动程序。

      对于纯粹 Perl 语言开发的数据库驱动而言,整个 DBD 模块的核心在一个 DBD::Driver.pm 的模块上。根据实际要连接的数据库的不同,Driver 的名字可以被替换成为 DBD::Oracle 或者 DBD::DB2。在 DBD::Driver 包中,通常包含子包,不同的子包有着不同的作用,关于它们的详细信息见表五。

    表 5. 典型的 DBD 模块的内部设计

     

    包名 作用 函数表(部分)
    DBD::Driver 提供 DBD 级别的方法 driver()
    DBD::Driver::dr 提供驱动模块句柄级别的方法 connect()
    data_sources()
    DBD::Driver::db 提供数据库句柄级别的方法 do()
    prepare()
    table_info()
    ping()
    rollback()
    commit()
    FETCH()
    STORE()
    DBD::Driver::st 提供执行语句句柄级别的方法 execute()
    bind_param()
    fetchrow_array()
    fetchrow_arrayref()
    fetchrow_hashref()

     

      基于 C/XS 驱动程序的 DBD 模块十分复杂,在这里不作介绍。

      基于 MySql 数据库的例子

      我们对 MySql 数据库进行了简单的测试。测试的结果见表六。

    表 6. MySql 数据库对 DBI 模块的测试结果

     

    测试用例 目的 问题
    test_dbh_and_sth() 测试数据库句柄和执行语句句柄;
    查询,插入和删除数据库记录;
    字段名称要全部小写;
    test_fetch_metadata_of_table() 测试获取数据标的原数据; 部分信息错误;
    test_call_sql_procedure() 测试调用 SQL 存储过程; bind_param_inout()函数无法正常工作,要得到存储过程的返回值需要特殊处理;

     

      基于 DB2 数据库的例子

      同样我们也有一个 SQL 脚本用于在 DB2 数据创建测试环境。(见附件三)测试结果见表六。

      使用 DBI 模块连接 DB2 数据库的示例见程序十二。

    表 7. DB2 数据库对 DBI 模块的测试结果

     

    测试用例 目的 问题
    test_drh() 测试驱动程序句柄; N/A
    test_dbh_and_sth() 测试数据库句柄和执行语句句柄;
    查询,插入和删除数据库记录;
    字段名称要全部大写;
    test_mixed_error_checing() 测试 DBI 模块的错误处理机制; N/A
    test_bind_parameter_to_statement() 测试执行语句后期绑定功能; N/A
    test_fetch_metadata_of_table() 测试获取数据标的原数据; 部分信息错误;
    test_call_sql_procedure() 测试调用 SQL 存储过程; N/A
    test_run_transaction() 测试通过 DBI 模块执行事务; N/A

     

    清单 12. 使用 DBI 模块连接 DB2 数据库

     sub setup_connections { 
     my $connections_reference = shift; 
     my $passwd = "xxxxxxxx"; 
     
     my $db2_connection_string = "dbi:DB2:test"; 
     my $db2_userid = "db2inst1"; 
     my %db2_connection = (); 
     $db2_connection{dbName} = "DB2"; 
     $db2_connection{connStr} = $db2_connection_string; 
     $db2_connection{userid} = $db2_userid; 
     $db2_connection{passwd} = $passwd; 
     push (@{$connections_reference}, \%db2_connection); 
     } 

      总结

      本文详细地介绍了 Perl 语言中用于和数据库通讯的 DBI 模块。以细腻的笔法和生动地示例给读者讲述了 DBI 模块的主要组成部分,结构和供用户编程时调用的方法。同时,本文也涉及了一些 DBI 模块中的高级主题,如对存储过程的调用,对并发事务的处理和数据库及执行语句句柄属性的设置和元数据的处理。除此之外,本文还对 DBD 模块的接口给予了入门级的介绍,希望可以对读者在专研第三方数据库的 DBD 模块时可以有所帮助。在本文的最后,通过定义了一套通用的 DBI 模块测试接口,对三种主流数据库 DB2,Sybase 和 MySql 进行测试,通过比较测试结果,得出了 DBI 模块在这三种数据库上的异同点和支持上的盲点。

mesos集群装配

mesos集群安装

1、说明

Mesos是一个集群资源管理的软件,利用它可以实现多种并行计算框架对于同一个集群资源的共享和利用。从而提高资源的使用效率,节约成本!本文是参考网上一位兄弟的安装,结合自己的环境,整理的安装指导:

 

2、软件准备

环境: 

有三台机器IP地址分别为: 

75,76,79

Mesos版本

mesos-0.17.0.tar.gz

三台机器系统版本: 

Red Hat Enterprise Linux Server release 6.2 (Santiago)

挂载安装光盘

mkdir /mnt/cdrom

mount /dev/cdrom /mnt/cdrom

配置yum为本地源

直接编辑/etc/yum.conf文件,增加下面的配置

[Server]

name=Yum Source

baseurl=file:///mnt/cdrom

enabled=1

gpgcheck=1

gpgkey=file:///mnt/cdrom/RPM-GPG-KEY-redhat-release

 

所需依赖软件版本: 

1、Python2.6及其以上版本均可 

2、jdk1.6以上的版本 

拷贝一个jdk安装包到本地

新建一个文件vi /etc/profile.d/java.sh

 

JAVA_HOME=/usr/java/jdk

PATH=$PATH:/$JAVA_HOME/bin

CLASSPATH=.:$JAVA_HOME/lib

JRE_HOME=$JAVA_HOME/jre

export JAVA_HOME PATH CLASSPATH JRE_HOME

 

3、ant 

4、g++ 4.1或更高版本 

rpm -ivh kernel-headers-2.6.32-220.el6.x86_64.rpm

rpm -ivh glibc-headers-2.12-1.47.el6.x86_64.rpm

rpm -ivh glibc-devel-2.12-1.47.el6.x86_64.rpm

rpm -ivh mpfr-2.4.1-6.el6.x86_64.rpm 

rpm -ivh cpp-4.4.6-3.el6.x86_64.rpm 

rpm -ivh ppl-0.10.2-11.el6.x86_64.rpm

rpm -ivh cloog-ppl-0.15.7-1.2.el6.x86_64.rpm 

rpm -ivh gcc-4.4.6-3.el6.x86_64.rpm 

rpm -ivh pcre-devel-7.8-3.1.el6.x86_64.rpm 

rpm -ivh libxml2-devel-2.7.6-4.el6.x86_64.rpm

rpm -ivh bison-2.4.1-5.el6.x86_64.rpm

 

 

rpm -ivh libstdc++-4.4.6-3.el6.x86_64.rpm

rpm -ivh libstdc++-devel-4.4.6-3.el6.x86_64.rpm

rpm -ivh gcc-c++-4.4.6-3.el6.x86_64.rpm

5、安装libtool

rpm -ivh autoconf-2.63-5.1.el6.noarch.rpm

rpm -ivh automake-1.11.1-1.2.el6.noarch.rpm

rpm -ivh libtool-2.2.6-15.5.el6.x86_64.rpm

6、安装python支持文件

rpm -ivh python-devel-2.6.6-29.el6.x86_64.rpm

7、安装libcurl

rpm -ivh libidn-devel-1.18-2.el6.x86_64.rpm

rpm -ivh libcurl-devel-7.19.7-26.el6_1.2.x86_64.rpm

8、安装sasl

rpm -ivh cyrus-sasl-devel-2.1.23-13.el6.x86_64.rpm

9、zlib

rpm -ivh zlib-devel-1.2.3-27.el6.x86_64.rpm

10、安装Python2.6或以上版本(有的话忽略此步骤) 

[root@Arion3 ~]# tar jxf Python-2.6.tar.bz2 

[root@Arion3 ~]# cd Python-2.6 

[root@Arion3 Python-2.6]# ./configure --enable-shared --prefix=/usr/local/python2.6 

../configure --with-webui --with-included-zookeeper

[root@Arion3 Python-2.6]# make 

[root@Arion3 Python-2.6]# make install 

[root@Arion3 ~]# cd /usr/bin/ 

[root@Arion3 ~]# mv pyhton python2.4 

[root@Arion3 ~]# ln -sf /usr/local/python2.6/bin/python /usr/bin/python 

[root@Arion3 ~]# vi /usr/bin/yum  

将其中#!/usr/bin/python改为!/usr/bin/python2.4 

[root@Arion3 ~]# vi /etc/ld.so.conf  

在其中追加python2.6的库路径:/usr/local/python2.6/lib/ 

[root@Arion3 ~]# ldconfig 

安装的时候注意,因为yum是用Python2.4版本写的,所以一般系统中会带有Python2.4,我们在安装Python2.6时候,不能将原来的版本卸载,否则会导致yum不能使用。 

 

3、主机配置

下面开始安装步骤

1、集群中的三台机器有如下对应关系: 

193.168.1.75 master

193.168.1.76 slave1

193.168.1.79 slave2 

2、分别修改三台机器的/etc/hosts文件,在其中追加以上内容,修改后hosts文件的内容如下: 

# Do not remove the following line, or various programs 

# that require network functionality will fail. 

10.10.42.3 Arion3 localhost.localdomain localhost//注意这里,一定要将开头的127.0.0.1换成机器本身的IP地址否则当你安装完毕,启动的时候会报HTTP通信异常!!这里一定要注意!!! 

#::1 localhost6.localdomain6 localhost6 

#10.10.42.2 newcloud.guoshi.com 

#mesos 

192.168.1.75 host75 host75

192.168.1.75 master 

192.168.1.76 slave1 

192.168.1.79 slave2 

3、配置ssh,以确保Master可以无密码登录到slave1和slave2两台机器上!配置过程比较的简单,以root用户为例来说明,其他用户也是一样的。 

在master节点上执行命令ssh-keygen -t rsa,在/root/.ssh中生成/root/.ssh/id_rsa.pub

执行cat id_rsa.pub>> authorized_keys

将master上的id_rsa.pub这个文件拷贝到slaves上,再slaves上重复执行cat id_rsa.pub>> authorized_keys

4、编译安装

(1)从镜像网站下载Mesos 0.9.0-incubating,得到压缩文件mesos-0.9.0-incubating.tar.gz。 

(2) 将压缩文件拷贝到master结点的某个目录中,使用如下命令解压文件, 得到文件夹mesos-0.9.0,进入该文件。 

[root@Arion3 ~]# tar zxf mesos-0.9.0-incubating.tar.gz 

(3)进入文件夹mesos-0.9.0后,使用configure脚本配置mesos。 

find / -type d -name python2.6

../configure --with-webui --with-included-zookeeper --prefix=/usr/local/mesos

如果没有指定prefix,默认安装在/usr/local

(4)make 

make check (执行测试用例)

(5)make install 

(6)分别在slave1和slave2两台机器上用相同的用户目录,重复上述5个步骤。 

5Mesos配置

(7)在master结点上做如下两个配置: 

编辑文件/usr/local/var/mesos/deploy/masters(如果没有可以创建),在文件中列出主结点的主机名或者IP地址。 

编辑文件 /usr/local/var/mesos/deploy/slaves(如果没有可以创建),在文件中列出从结点的主机名或者IP地址。 

我的配置如下所示: 

#masters文件的内容 

master 

 

#slaves文件的内容 

slave1 

slave2 

slave3 

 

编辑文件/usr/local/var/mesos/conf/mesos.conf(如果没有可以创建),配置主结点的日志文件目录。我的配置如下: 

log_dir=/home/ mesos/log/ 

(8)在所有从结点上做如下配置。 编辑conf文件(如果没有可以创建),配置从结点的日志文件目录、主结点URI和从结点资源。从结点的资源可以根据每个结点的CPU和内存的空闲情况进行配置,以供mesos调度使用。其中cpus后面的数字代表CPU有几个核,而mem后面的数字代表分配多少空闲内存给mesos使用,单位是MB。 

 

master=master:5050 

log_dir=/home/ mesos/log 

resources=cpus:8;mem:3200 

 

内存单位是MB

(9)启动与停止mesos集群管理器。 如果配置正确无误,就可以进入目录 /usr/local/sbin/,使用如下命令启动集群管理器: 

master节点上: 

nohup ./mesos-master --ip=193.168.1.75  --whitelist="/usr/local/var/mesos/deploy/slaves"  >/dev/null 2>&1 &

 

 

注:一定要将slaves加入到whitelist中。

slave节点上: 

nohup ./mesos-slave --master=193.168.1.75:5050 >/dev/null 2>&1 &

(10) mesos启动后,可以通过Web界面来查看集群中的资源,以及集群中正在运行的框架和任务。具体的访问方式是通过浏览器访问master结点的8080接口,即http://master:5050/。如果mesos启动正常,可以在该web界面上看到整个集群的资源总和和每一个slave结点的资源。如果有框架正在运行,还可以看到框架的信息,以及正在执行的任务信息。 

 

6、测试默认案例:

1、测试默认的framework

/opt/mesos-0.17.0/build/src

[root@host75 src]# ./test-framework  --master=193.168.1.75:5050

I0326 16:32:22.872979 16936 sched.cpp:218] No credentials provided. Attempting to register without authentication

I0326 16:32:22.873389 16936 sched.cpp:230] Detecting new master

Registered!



 

<!--EndFragment-->
如果您想提高自己的技术水平,欢迎加入本站官方1号QQ群:116537189  ,  2号QQ群:246889341,在群里结识技术精英和交流技术^_^
本站联系邮箱:155120699@qq.com     
DBI 数据库模块剖析:Perl DBI 数据库通讯模块规范,工作原理和实例

原文:http://tech.ddvip.com/2010-06/1275904179154934.html

     本文详细地介绍了 Perl 语言中用于和数据库通讯的 DBI 模块。以细腻的笔法和生动地示例给读者讲述了 DBI 模块的主要组成部分,结构和供用户编程时调用的方法。同时,本文也涉及了一些 DBI 模块中的高级主题,如对存储过程的调用,对并发事务的处理和数据库及执行语句句柄属性的设置和元数据的处理。除此之外,本文还对 DBD 模块的接口给予了入门级的介绍,希望可以对读者在专研第三方数据库的 DBD 模块时可以有所帮助。在本文的最后,通过定义了一套通用的 DBI 模块测试接口,对三种主流数据库 DB2,Sybase 和 MySql 进行测试,通过比较测试结果,得出了 DBI 模块在这三种数据库上的异同点和支持上的盲点。

  DBI 数据库模块剖析

  为了和数据库进行通讯,Perl 的社区开发出了统一数据库通讯接口模块:DBI。DBI 作为 Perl 语言中和数据库进行通讯的标准接口,它定义了一系列的方法,变量和常量,成功地提供一个和具体数据库平台无关的数据库持久层。

  DBI 模块的体系结构

  概述

  整个 DBI 模块的结构可以被分成两个主要的部分:DBI 模块本身和实现与具体数据库平台通讯的驱动模块。DBI 模块用于定义提供给 Perl 开发者使用的编程接口,和对不同数据库驱动模块的调用方法。具体的数据库通讯驱动模块的实现和特定的数据库平台有关,并且负责和具体数据通讯的实际操作。

  DBI 模块中的三种句柄

  在 DBI 模块的定义中,用户可以创建三种不同类型的句柄。

  驱动模块句柄(Driver Handle):驱动模块句柄代表一个在内存中加载的驱动,它在 DBI 模块加载其对应的驱动模块被创建。它和实际的驱动模块之间是一一对应的关系。驱动模块句柄提供了两个主要的方法是 data_sources() 和 connect()。

  数据库句柄(Database Handles):数据库句柄是程序员使用 DBI 模块和后台数据库进行通讯的第一步,它包含了一个到特定数据库的某个独立的连接。表一给出了对于常见数据库的连接字符串:

  执行语句句柄(Statement Handles):执行语句句柄在 DBI 规范中被定义为和数据库进行交互和操作的接口。这些句柄包装了一条条 SQL 语句,并将它们交送给后台数据库执行。一个使用执行语句句柄执行 SQL 语句的例子见程序一。

表 1. 常见数据库的连接字符串

 

数据库 连接字符串(示例)
DBD::mysql “DBI:mysql:database=$dbname;host=$hostname;port=$port”;
DBD::Oracle “dbi:Oracle:$dbname”; “dbi:Oracle:host=$hostname;sid=$sid”;
DBD::DB2 “dbi:DB2:$dbname”;
DBD::Sybase “dbi:Sybase:host=$hostname;database=$dbname;port=$port”;

 

清单 1. 使用 DBI 模块连接数据库并执行 SQL 语句

 $dbh = DBI->connect ($connection_string, $userid, $passwd); 
 $sth = $dbh->prepare (“SELECT * FROM tbl”); 
 $sth->execute(); 
 while (@row = $sth->fetchrow_array()) { 
 print “Each record: @row \n”; 
 } 
 $sth->finish(); 
 $dbh->disconnect(); 

  建立和释放连接

  使用 DBI 模块提供的 connect()方法创建一个数据库句柄是,程序员必须提供一个数据源用于指定要连接的数据库。DBI 模块的规范要求数据源的名字并须以 dbi: 开头,然后接上数据库通讯驱动模块的名字并且以‘ : ’结尾,比如‘ dbi:db2: ’。和 connect()方法相对应,DBI 模块中还定义了一个 disconnect()方法。

  通过调用 DBI->available_drivers()方法,程序员可以得到已安装在当前机器上的所有数据库通讯驱动模块的列表。接着用驱动模块名作为参数,调用 DBI->data_sources()方法,可以得到对应此驱动模块的所有数据源的列表,程序二给出了一个这样的例子 .

清单 2. 得到所有支持驱动模块的所有数据源

 my @drivers = DBI->available_drivers(); 
 die “No dirvers defined! \n” unless @drivers; 
 foreach my $driver (@drivers) { 
 print “Driver: $driver \n”; 
 my @data_sources = DBI->data_sources ($driver) { 
 foreach my $data_source (@data_sources) { 
  print “\t Data Source: $data_source \n”; 
 } 
 print “\n”; 
 } 

  错误处理

  在 DBI 模块中提供了两种错误处理的方法。第一种方法依靠程序员手工检测被调用方法的返回值;第二种方法通过 DBI 模块对错误进行自动检测,这种方法类似于程序的异常处理机制。对于手工错误处理,可以通过将“PrintError”和“RaiseError”两个变量设定为 0 来激活。在默认情况下,“PrintError”参数是被激活的。

  方法一,在调用 DBI 的 connect()函数的参数中设置:

 %attr = (PrintError => 0, RaiseError=>0); 
 my $dbh = DBI->connect (“dbi:Oracle:testdb”, “username”, “password”, \%attr); 

  方法二,在数据库句柄中直接设置:

 $dbh->{PrintError} = 0; $dbh->{RaiseError} = 0; 

  对于自动错误检测,DBI 提供了两种不同级别的错误处理方法。用于句柄的“PrintError”参数在被设置为 1 的时候,DBI 模块会调用 warn()函数进行错误处理。它会将错误信息打印到屏幕,但是并不会中止进程。而用于句柄的“RaiseError”参数在被设置为 1 的时候,DBI 模块会调用 die()函数,并且中止进程。这两个不同级别的错误处理方法可以在 DBI 模块的任何一个有效的句柄中被激活。

  除了错误处理手段以外,DBI 模块还提供了对错误信息进行诊断的方法。这些方法可以对于任何一个有效的句柄进行使用,它们的返回值包括错误号和错误信息。

 $rv = $h->err(); $str = $h->errstr(); $str = $h->state(); 

  $h->err()方法返回一个由底层数据库生成的错误号;$h->errstr()方法则返回一个由底层数据库生成的错误信息描述;$h->state()方法返回一个 5 位的 SQLSTATE 错误字符串。除了上面的三种方法会返回错误信息供排错之外,在 DBI 模块级别,$DBI::err,$DBI::errstr,$DBI::state 会返回和上述函数同样的值。一个利用 DBI 模块内置错误处理方法的例子见程序三。

清单 3. 利用 DBI 模块的内置错误处理方法

 while (1) { 
 my $dbh; 
 # disable automatic error handle 
 until { 
 $dbh = DBI->connect ($connection_string, $userid, $username); 
 $dbh->{PrintError} = 0; 
 $dbh->{RaiseError} = 0; 
 warn “Unable to connect: $DBI::errstr. sleep for 5 minutes. \n”; 
 sleep (300); 
 } 
 # enable automatic error handle 
 eval { 
 $dbh->{RaiseError} = 1; 
 my $sth = $dbh->prepare (“SELECT foo, bar from tbl”); 
 while (1) { 
 $sth->execute(); 
 while ( my @row = $sth->fetchrow_array()) { 
 print “Row: @row \n”; 
 } 
 sleep 60; 
 } 
 } 
 warn “Monitoring aborted by error: $@\n” if $@; 
 sleep 5; 
 } 

  操作数据库

  执行简单查询

  应用程序与数据库之间最常见的操作是从数据库中查询并且提取数据。在标准的 SQL 语句规范中,这一过程是使用关键字 SELECT 的。

  一个通过 DBI 执行的标准简单查询包括以下四个阶段:

  准备阶段(Perpare SQL statement):

  通过调用 prepare()方法,准备阶段解析 SQL 语句,对 SQL 语句进行验证,并且返回一个执行语句句柄,这个句柄代表将在数据库内被执行的这条 SQL 语句。

  执行阶段(Execute select statement):

  通过调用 execute()方法,执行阶段执行 SQL 语句,查询数据库,并且以被查询的数据填充 Perl 的数据结构。但是,在这一阶段中,你的 Perl 应用程序并未真正地访问到被查询的数据。

  数据抽取阶段(fetching date):

  第三阶段被称为数据抽取阶段,在这一阶段实际的数据从数据库中被抽取出来,通过调用 fetch()方法族的一组方法。数据抽取阶段从数据库得到查询所得的数据,以每一条数据为单位,注入 Perl 的数据结构。DBI 提供了多种方法对数据进行抽取,可以将被抽取的数据用一个列表,一个指向数组的引用,或是一个指向哈希表的引用的方式提供给应用程序。而且每条记录中字段的顺序也是由 SQL 语句中指定的顺序所决定。一个包含三种方法的例子见程序四。

  完成阶段(finishing date fetch):

  最后一个阶段被称为完成阶段,这个阶段主要释放资源,并且清理相关数据结构中保存的历史信息,通过显式地调用 finish()方法来完成。当一个执行语句句柄(statement handler)被成功执行后,它的状态会被标记成为活跃。你可以通过访问执行语句句柄的 Active 属性来访问它。在用户执行 fetch()方法,从数据库中抽取了最后一列数据之后,数据库驱动自动关闭数据库中正在进行地和这个执行语句句柄有关的工作,并且重置 Active 属性为不活跃状态。这一切工作都是在读取了最后一列数据之后被自动触发,在大多数情况下,用户并不需要额外地关心这一过程中后台所作的工作。某些额外情况,需要应用程序主动调用 finish()方法释放资源。一个典型的例子就是:当数据库占用了数目可观的磁盘空间存储临时文件,存放查询结果,而应用程序又不需要保存所有的查询结果。比如执行一条形如 “SELECT EMP_DEP, count(*) FROM EMP GROUP BY EMP_DEP ORDER BY count(*) DESC”的 SQL 语句,在应用程序只需要部分统计结果的情况,应该显示调用 finish()方法,释放被申请和占用的机器资源。

清单 4. 三种不同的数据抽取方法

 $sth->execute(); 
 # fetch data by an array 
 while ( @row = $sth->fetchrow_array() ) { 
 print “Column1: $row[0] \t Column2: $row[1]. \n”; 
 } 
 # fetch data by a reference pointing to array 
 while ( $array_ref = $sth->fetchrow_arrayref() ) { 
 print “Column1: $array_ref->[0] \t Column2: $array_ref->[1]. \n”; 
 } 
 # fetch data by a reference pointing to hash table 
 while ( $hash_ref = $sth->fetchrow_hashref() ) { 
 print “Column1: $hash_ref->{column1} \t Column2: $hash_ref->{column2}. \n”; 
 } 

  执行非查询语句

  在数据库的常用 DML 语句中,除了 SELECT 语句之外,还有 INSERT,DELETE,UPDATE 三种,我们统称这三种语句为非查询语句。与查询语句 SELECT 不同,它们只是改变了数据库中的部分纪录,而不会返回一个记录集给应用程序。所以,相对对于查询语句中的 prepare-execute-fetch-deallocate 序列来说,非查询语句不需要数据抽取阶段,同时也可以将 prepare 和 execute 阶段用一个 do()方法来完成。一个调用 do()方法的例子如下 :

 $affected_row_number = $dbh->do (“DELETE FROM tbl WHERE foo = ‘ bar ’”); 

  DBI 模块提供了一个 do()方法用以简化工作,取代了原先需要被调用的 prepare()和 execute()方法。实际上,DBI 模块中的 do()方法只是简单地包含了 prepare()和 execute()方法。这种方法在使用添写方法生成 SQL 执行语句的时候和分别使用 prepare()方法和 execute()方法没有任何性能上面的区别,但是如果使用了参数绑定的 SQL 执行语句生成方法,两者在性能上将会有显著地差别。因为使用 do()方法的话,以 INSERT 操作为例,对于每条被插入的记录,数据库必须解析每条插入语句,并且生成执行计划。然而使用 prepare()方法的话,可以在 prepare()方法中使用占位符,而使得所有的插入语句可以共用一个执行计划,达到了提升效率的目地,具体的例子见程序六。

  参数绑定

  在准备阶段,有一个重要的概念就是数据绑定,与之相关的有三个术语:占位符(placeholder),参数(parameter)和绑定(binding),他们都是用来根据程序上下文动态地生成 SQL 语句。将变量放入 SQL 语句的方法有两种:第一种通过添写方法生成 SQL 语句(interpolated SQL statement creation),该方法将变量直接通过字符串和 SQL 语句的其它部分连接起来,生成可以被执行的 SQL 语句。程序五展示了一个利用 Perl 字符串技术生成的一个 SQL 执行语句。

清单 5. 使用 Perl 字符串技术动态生成 SQL 执行语句的例子

 foreach $table_name ( qw (table1, table2, table3) ) { 
 $sth = $dbh->prepare(“SELECT count(*) FROM $table_name”); 
 $sth->execute(); 
 } 

  第二种方法在 SQL 语句中使用占位符,并且通过 bind_param()方法将变量与之绑定,生成 SQL 语句。无论何时,bind_param()方法必须在 execute()方法之前被调用,否则被绑定的参数无法填入 SQL 语句中,而对 SQL 语句的调用也注定会失败。采用绑定方法是成 SQL 语句(Bind value SQL statement creation)的一个典型的例子见程序六。

清单 6. 使用占位符动态生成 SQL 执行语句的例子

 $sth = $dbh->prepare (“SELECT foo, bar FROM table WHERE foo = ? AND bar = ?”); 
 $sth->bind_param (1, “FOO”); 
 $sth->bind_param (2, “BAR”); 
 $sth->execute(); 

  第一种方法使用 Perl 的字符串处理函数,生成一条完整的 SQL 语句,并将它送往后台数据库;而绑定方法则不同,它将含有占位符的 SQL 语句和绑定值分开传送给数据,在数据后台处理数据绑定,然后执行绑定后的 SQL 语句。两种不同的处理方法将会带来性能上的巨大差异,尤其是在有大量相似的 SQL 语句需要被处理的情况下。主流的大型数据库都有一种被称为“Shared SQL Cache”的部件,它存储了诸如查询语句执行计划之类的辅助数据结构,帮助数据库执行 SQL 语句。在得到一个新的处理 SQL 语句的请求时,如果 SQL 语句已经存在于“Shared SQL Cache”中,数据库就不需要重新处理这条语句,而可以重用 Cache 中存储的信息,这样就可以带来性能上的显著提升。

  调用存储过程

  存储过程运行在后台数据库上,可以有效地减少客户端和数据库之间的通讯量。在这种工作模式下,客户端不再需要将每一条需要被执行的 SQL 语句发往后台数据库,借助存储过程可以将所有要执行的 SQL 语句定义在一个存储过程内,统一执行并且返回运算结果给客户端。

  在 DBI 模块中有一个和 bind_param()方法相似的方法,叫做 bind_param_inout()方法,可以从执行语句句柄中直接返回值。这个方法最主要的应用在于调用存储过程用来接收参数和返回结果。需要注意的是,某些数据库(比如 MySql)不支持这种方法。bind_param_inout()方法通过传递引用的方式向数据库传入一个可以接受返回值的参数,一个简单的例子如下:

 $sth->bind_param_inout (1, \$bar, 50); 

  上面例子当中,bind_param_inout()方法的第三个参数是返回值的最大长度。

  一个在 DB2 数据库中调用存储过程的完整示例见程序七。

清单 7. 调用 DB2 数据库中存储过程的例子

 create procedure proc (in foo char(6), out bar integer) 
 specific proc_example 
 dynamic result sets 0 
 modifies sql data 
 not deterministic 
 language sql 
 begin atomic 
 insert into tbl ( ‘ foo ’ , current date); 
 select count(*) into bar from tbl where foo = ‘ foo ’ ; 
 end 
 
 # start perl script to call sql procedure 
 $sth = $dbh->prepare (“CALL proc (?,?)”); 
 $sth->bind_param (1, $foo); 
 $sth->bind_param_inout (2, \$bar, 50); 
 $sth->execute(); 
 print “stored procedure returned $bar. \n”; 

  执行数据库事务

  数据库事务是一种将一组相互之间有密切关系的 SQL 语句放到一起执行的技术,它们要么都被成功执行,要么都执行失败,我们称之为“all-or-nothing”模式。一个事务从它的第一条可被执行的 SQL 语句开始,到被提交(commit)或者回滚(rollback)结束。如果一个事务被提交,那么它对数据库所作的所有修改都会被保存并且对其它并发过程可见;如果一个事务被回滚,它对数据库所作的所有修改都会被放弃。

  并不是所有的数据库软件都支持事务,但是对于所有支持事务的数据库,DBI 模块提供了统一的接口用于操作事务。尽管数据库的实现各不相同,DBI 模块提供了事务自动提交(auutomatic transcation committing)和手工事务处理(powerful manual transaction)的两种不同处理方式。如果用户在创建一个数据库句柄(database handle)时,将句柄的参数“AutoCommit”设置为 1,那么通过这个数据库句柄执行的每一条 SQL 语句的操作都会被立即提交,而不需要任何显式语句进行提交或回滚;反之,如果参数“AutoCommit”被设置为 0,则每个事物都必须以显示地调用 commit()方法或 rollback()方法来结束。如果后台数据库不支持事务处理,那么当用户试图修改“AutoCommit”参数为 0 的时候,DBI 模块将会抛出错误。在 DBI 模块中定义了 commit()方法,用来显式地提交在一个事务范围内对数据库所作的修改。该方法通过数据库句柄来调用,如下:

 $dbh->commit(); 

  如果调用 commit()方法的数据库句柄中的“AutoCommit”属性被设置为 1,那么调用 commit()方法之后会得到一个“commit ineffective with AutoCommit”的警告信息。如果后台数据库不支持事务处理,那么“AutoCommit”属性默认打开,每次调用 commit()方法之后也会得到一个相同的警告信息。与之相似的是 rollback()方法,每次调用 rollback()方法会回滚事务范围内对数据库所作的所有修改。如果“AutoCommit”设置为一或者是后台数据库不支持事务处理,同样的警告信息也将会被返回。一个调用 rollback()方法的例子如下:

 $dbh->rollback(); 

  不幸的事情是,当“AutoCommit”属性被设置为 0 的时候,显式地调用 disconnect()方法终止和数据库的连接后触发的行为无法预测。在某些数据库的版本中,在 disconnect()方法被 DBI 模块调用之前,会自动调用 commit()方法,提交所有对数据库的修改;也有某些数据库版本会调用 rollback()方法,回滚所有对数据库所作的修改。一个综合示例见程序八。

清单 8. DBI 模块处理数据库事务的详细例子

 $dbh->connect(); 
 $dbh->{AutoCommit} = 0; 
 $dbh->{RaiseError} = 1; 
 eval { 
 load_some_data_to_database(); 
 insert_some_data_to_database(); 
 delete_some_data_from_database(); 
 $dbh->commit() 
 } 
 if ($@) { 
 $dbh->rollback(); 
 } 
 $dbh->disconnect(); 

  DBI 模块中的高级主题

  DBI 中的句柄属性

  除了与数据库句柄和执行语句句柄绑定的一组方法以外,DBI 模块也提供了一组和这些句柄有关的属性,供用户对他们的执行环境进行调优。这些句柄属性的实质是一个由“键 / 值”对组成的哈希表,可以通过和操作哈希表引用一样的方法来访问和修改这些属性。一个典型的例子见程序九。

清单 9. 修改和显示句柄的属性

 $dbh->connect($connect_string, $userid, $passwd); 
 $dbh->{AutoCommit} = 1; 
 print “AutoCommit: $dbh->{AutoCommit} \n”; 

  当用户访问或者设置这些句柄属性时,DBI 模块自动检查用户输入的属性名称。如果用户试图引用一个位置属性时,DBI 模块将会产生一个错误。同理,如果用户试图修改一个只读属性时,DBI 模块会以同样的方式抛出一个错误。无论用户有没有在创建数据库句柄时设置了“RaiseError”属性,在检测到上述错误之后,DBI 模块都会使用 die()方法抛出错误,返回一个错误值。

  句柄属性的命名规则

  句柄属性的命名看似杂乱无章,其实微言大义,在 DBI 模块的兼容性问题发挥了重要的作用。句柄属性的大小写命名规则直接反映了是谁定义了这个属性并对其赋值,其规则有三:

  全大写命名方式(UPPER_CASE):全大写属性通常是由外部标准所定义的,比如说 ISO SQL 或者 ODBC。

  混合命名方式(Mixed_Case):此类属性的名字通常由大写字母开头,但是其中也混有小写字母。这类属性通常由 DBI 模块标准自身定义。

  全小写命名方式(lower_case):此类属性由各自的数据库驱动所定义,被称为“驱动相关”属性。

  公共句柄属性

  表二列出了部分最常见的数据库句柄(database handle)和执行语句句柄(statement handle)都支持的句柄属性。

表 2. 常见公共句柄属性

 

属性名 说明
PrintError 如果此值被设置为 1,当 DBI 模块返回一个错误结果的时候,使用 warn()方法;
RaiseError 如果此值被设置为 1,当 DBI 模块一个错误结果的时候,使用 die()方法;

 

  数据库句柄的属性和数据库元数据

  表三列出了最常见的数据库句柄所支持的句柄属性。

表 3. 常见数据库句柄属性

 

属性名 说明
AutoCommit 如果设置为 1,每一条通过 DBI 模块执行的结果都会被立即提交;反之,如果设置为 0,
所有的执行结果都要用 commit()方法显示提交或用 rollback()方法回滚;
Name 只读属性,存储数据库的名字;

 

  数据库原数据(database metadata)被称为“描述数据的数据”,用来描述数据本身。当用户有动态生成 SQL 执行语句或者创建动态视图需求的时候,数据库元数据所提供的信息显得格外地重要。不用数据库厂商存储数据库元数据的方法和保存的数据库元数据各不相同,大多数主流数据提供了一个系统目录(system catalog),通过表和视图保存这些信息。尽管因为上述的种种差异,使得 DBI 模块提供一个统一的访问数据库元数据的接口成了一件任重道远的任务,在 DBI 模块的定义中依然提供了两个可供调用的方法,用来访问数据库元数据。

  第一个方法 $dbh->tables()返回一个包含调用它的数据库句柄(database handle)所能发现的所有表和视图的数组,见程序十。

清单 10. 利用数据库句柄返回数据库的元数据

 my @tables = $dbh->tables(); 
  foreach my $table (@tables) { 
   print “Table Name: ” . $table . “ \n”; 
  } 

  第二个方法 $dbh->table_info()返回一个包含更多详细信息的执行语句句柄(statement handle),可以通过和访问普通执行语句句柄一样的方法,抽取其中的信息。

  执行语句句柄属性和表原数据

  同数据库句柄一样,执行语句句柄(statement handle)也有自己的句柄属性。执行语句句柄从创建它的数据库句柄继承了部分属性,并且它的大部分属性是用来表示语句的执行状态和结果的,属于只读属性。表四列出了常见的执行语句属性:

表 4. 常见执行语句句柄属性

 

属性名 说明
NUM_OF_FIELDS 被 SELECT 语句返回的字段的数量;
NAME 被 SELECT 语句返回的字段的名称;
NULLABLE 某个字段是否可以为空;
TYPE 某个字段的类型;
NUM_OF_PARAMS 一个 SQL 执行语句所使用的占位符的数量;

 

  通过读取某些执行语句的句柄属性,可以得到表的原数据,示例如下:

清单 11. 利用执行语句句柄返回表的元数据

 my $sth = $dbh->prepare (“SELECT * FROM tbl”); 
 $sth->execute(); 
 my $field_number = $sth->{NUM_OF_FIELDS}; 
 print “NUMBER OF FIELDS: $field_number \n”; 
 print “Column Name                 Type  Nullable? \n”; 
 print “--------------------------------------- ---- --------- \n”; 
 for (my $index=0; $index<$field_number; $index++) { 
 my $name = $sth->{NAME}->[$index]; 
 my $type = $sth->{TYPE}->[$index]; 
 my $nullable = (“No”, “Yes”, “Unknown”)[$sth->{NULLABLE}->[$index]]; 
 printf “%-30s %4d $s \n”, $name, $type, $nullable; 
 } 
 $sth->finish(); 

  符合 DBI 模块接口的 DBD 模块

  DBI 模块定义了一组和用户交互的接口,而 DBD 实现与数据库之间的具体接口。两者之间的关系可以类比为面向对象编程中接口和继承接口的类之间的关系。有两种不同的数据库驱动程序的实现方式,第一种是基于纯粹的 Perl 语言,不依赖任何 C 语言编译器。这种方法实现最为简单,却不被大多数的数据库所支持,比较典型的 DBD 模块例子有 DBD::File 和 DBD::CSV。另一种方法更为普遍,使用了 C 程序的帮助和数据库进行通讯,被称为 C/XS 驱动程序。

  对于纯粹 Perl 语言开发的数据库驱动而言,整个 DBD 模块的核心在一个 DBD::Driver.pm 的模块上。根据实际要连接的数据库的不同,Driver 的名字可以被替换成为 DBD::Oracle 或者 DBD::DB2。在 DBD::Driver 包中,通常包含子包,不同的子包有着不同的作用,关于它们的详细信息见表五。

表 5. 典型的 DBD 模块的内部设计

 

包名 作用 函数表(部分)
DBD::Driver 提供 DBD 级别的方法 driver()
DBD::Driver::dr 提供驱动模块句柄级别的方法 connect()
data_sources()
DBD::Driver::db 提供数据库句柄级别的方法 do()
prepare()
table_info()
ping()
rollback()
commit()
FETCH()
STORE()
DBD::Driver::st 提供执行语句句柄级别的方法 execute()
bind_param()
fetchrow_array()
fetchrow_arrayref()
fetchrow_hashref()

 

  基于 C/XS 驱动程序的 DBD 模块十分复杂,在这里不作介绍。

  基于 MySql 数据库的例子

  我们对 MySql 数据库进行了简单的测试。测试的结果见表六。

表 6. MySql 数据库对 DBI 模块的测试结果

 

测试用例 目的 问题
test_dbh_and_sth() 测试数据库句柄和执行语句句柄;
查询,插入和删除数据库记录;
字段名称要全部小写;
test_fetch_metadata_of_table() 测试获取数据标的原数据; 部分信息错误;
test_call_sql_procedure() 测试调用 SQL 存储过程; bind_param_inout()函数无法正常工作,要得到存储过程的返回值需要特殊处理;

 

  基于 DB2 数据库的例子

  同样我们也有一个 SQL 脚本用于在 DB2 数据创建测试环境。(见附件三)测试结果见表六。

  使用 DBI 模块连接 DB2 数据库的示例见程序十二。

表 7. DB2 数据库对 DBI 模块的测试结果

 

测试用例 目的 问题
test_drh() 测试驱动程序句柄; N/A
test_dbh_and_sth() 测试数据库句柄和执行语句句柄;
查询,插入和删除数据库记录;
字段名称要全部大写;
test_mixed_error_checing() 测试 DBI 模块的错误处理机制; N/A
test_bind_parameter_to_statement() 测试执行语句后期绑定功能; N/A
test_fetch_metadata_of_table() 测试获取数据标的原数据; 部分信息错误;
test_call_sql_procedure() 测试调用 SQL 存储过程; N/A
test_run_transaction() 测试通过 DBI 模块执行事务; N/A

 

清单 12. 使用 DBI 模块连接 DB2 数据库

 sub setup_connections { 
 my $connections_reference = shift; 
 my $passwd = "xxxxxxxx"; 
 
 my $db2_connection_string = "dbi:DB2:test"; 
 my $db2_userid = "db2inst1"; 
 my %db2_connection = (); 
 $db2_connection{dbName} = "DB2"; 
 $db2_connection{connStr} = $db2_connection_string; 
 $db2_connection{userid} = $db2_userid; 
 $db2_connection{passwd} = $passwd; 
 push (@{$connections_reference}, \%db2_connection); 
 } 

  总结

  本文详细地介绍了 Perl 语言中用于和数据库通讯的 DBI 模块。以细腻的笔法和生动地示例给读者讲述了 DBI 模块的主要组成部分,结构和供用户编程时调用的方法。同时,本文也涉及了一些 DBI 模块中的高级主题,如对存储过程的调用,对并发事务的处理和数据库及执行语句句柄属性的设置和元数据的处理。除此之外,本文还对 DBD 模块的接口给予了入门级的介绍,希望可以对读者在专研第三方数据库的 DBD 模块时可以有所帮助。在本文的最后,通过定义了一套通用的 DBI 模块测试接口,对三种主流数据库 DB2,Sybase 和 MySql 进行测试,通过比较测试结果,得出了 DBI 模块在这三种数据库上的异同点和支持上的盲点。

mesos集群安装

1、说明

Mesos是一个集群资源管理的软件,利用它可以实现多种并行计算框架对于同一个集群资源的共享和利用。从而提高资源的使用效率,节约成本!本文是参考网上一位兄弟的安装,结合自己的环境,整理的安装指导:

 

2、软件准备

环境: 

有三台机器IP地址分别为: 

75,76,79

Mesos版本

mesos-0.17.0.tar.gz

三台机器系统版本: 

Red Hat Enterprise Linux Server release 6.2 (Santiago)

挂载安装光盘

mkdir /mnt/cdrom

mount /dev/cdrom /mnt/cdrom

配置yum为本地源

直接编辑/etc/yum.conf文件,增加下面的配置

[Server]

name=Yum Source

baseurl=file:///mnt/cdrom

enabled=1

gpgcheck=1

gpgkey=file:///mnt/cdrom/RPM-GPG-KEY-redhat-release

 

所需依赖软件版本: 

1、Python2.6及其以上版本均可 

2、jdk1.6以上的版本 

拷贝一个jdk安装包到本地

新建一个文件vi /etc/profile.d/java.sh

 

JAVA_HOME=/usr/java/jdk

PATH=$PATH:/$JAVA_HOME/bin

CLASSPATH=.:$JAVA_HOME/lib

JRE_HOME=$JAVA_HOME/jre

export JAVA_HOME PATH CLASSPATH JRE_HOME

 

3、ant 

4、g++ 4.1或更高版本 

rpm -ivh kernel-headers-2.6.32-220.el6.x86_64.rpm

rpm -ivh glibc-headers-2.12-1.47.el6.x86_64.rpm

rpm -ivh glibc-devel-2.12-1.47.el6.x86_64.rpm

rpm -ivh mpfr-2.4.1-6.el6.x86_64.rpm 

rpm -ivh cpp-4.4.6-3.el6.x86_64.rpm 

rpm -ivh ppl-0.10.2-11.el6.x86_64.rpm

rpm -ivh cloog-ppl-0.15.7-1.2.el6.x86_64.rpm 

rpm -ivh gcc-4.4.6-3.el6.x86_64.rpm 

rpm -ivh pcre-devel-7.8-3.1.el6.x86_64.rpm 

rpm -ivh libxml2-devel-2.7.6-4.el6.x86_64.rpm

rpm -ivh bison-2.4.1-5.el6.x86_64.rpm

 

 

rpm -ivh libstdc++-4.4.6-3.el6.x86_64.rpm

rpm -ivh libstdc++-devel-4.4.6-3.el6.x86_64.rpm

rpm -ivh gcc-c++-4.4.6-3.el6.x86_64.rpm

5、安装libtool

rpm -ivh autoconf-2.63-5.1.el6.noarch.rpm

rpm -ivh automake-1.11.1-1.2.el6.noarch.rpm

rpm -ivh libtool-2.2.6-15.5.el6.x86_64.rpm

6、安装python支持文件

rpm -ivh python-devel-2.6.6-29.el6.x86_64.rpm

7、安装libcurl

rpm -ivh libidn-devel-1.18-2.el6.x86_64.rpm

rpm -ivh libcurl-devel-7.19.7-26.el6_1.2.x86_64.rpm

8、安装sasl

rpm -ivh cyrus-sasl-devel-2.1.23-13.el6.x86_64.rpm

9、zlib

rpm -ivh zlib-devel-1.2.3-27.el6.x86_64.rpm

10、安装Python2.6或以上版本(有的话忽略此步骤) 

[root@Arion3 ~]# tar jxf Python-2.6.tar.bz2 

[root@Arion3 ~]# cd Python-2.6 

[root@Arion3 Python-2.6]# ./configure --enable-shared --prefix=/usr/local/python2.6 

../configure --with-webui --with-included-zookeeper

[root@Arion3 Python-2.6]# make 

[root@Arion3 Python-2.6]# make install 

[root@Arion3 ~]# cd /usr/bin/ 

[root@Arion3 ~]# mv pyhton python2.4 

[root@Arion3 ~]# ln -sf /usr/local/python2.6/bin/python /usr/bin/python 

[root@Arion3 ~]# vi /usr/bin/yum  

将其中#!/usr/bin/python改为!/usr/bin/python2.4 

[root@Arion3 ~]# vi /etc/ld.so.conf  

在其中追加python2.6的库路径:/usr/local/python2.6/lib/ 

[root@Arion3 ~]# ldconfig 

安装的时候注意,因为yum是用Python2.4版本写的,所以一般系统中会带有Python2.4,我们在安装Python2.6时候,不能将原来的版本卸载,否则会导致yum不能使用。 

 

3、主机配置

下面开始安装步骤

1、集群中的三台机器有如下对应关系: 

193.168.1.75 master

193.168.1.76 slave1

193.168.1.79 slave2 

2、分别修改三台机器的/etc/hosts文件,在其中追加以上内容,修改后hosts文件的内容如下: 

# Do not remove the following line, or various programs 

# that require network functionality will fail. 

10.10.42.3 Arion3 localhost.localdomain localhost//注意这里,一定要将开头的127.0.0.1换成机器本身的IP地址否则当你安装完毕,启动的时候会报HTTP通信异常!!这里一定要注意!!! 

#::1 localhost6.localdomain6 localhost6 

#10.10.42.2 newcloud.guoshi.com 

#mesos 

192.168.1.75 host75 host75

192.168.1.75 master 

192.168.1.76 slave1 

192.168.1.79 slave2 

3、配置ssh,以确保Master可以无密码登录到slave1和slave2两台机器上!配置过程比较的简单,以root用户为例来说明,其他用户也是一样的。 

在master节点上执行命令ssh-keygen -t rsa,在/root/.ssh中生成/root/.ssh/id_rsa.pub

执行cat id_rsa.pub>> authorized_keys

将master上的id_rsa.pub这个文件拷贝到slaves上,再slaves上重复执行cat id_rsa.pub>> authorized_keys

4、编译安装

(1)从镜像网站下载Mesos 0.9.0-incubating,得到压缩文件mesos-0.9.0-incubating.tar.gz。 

(2) 将压缩文件拷贝到master结点的某个目录中,使用如下命令解压文件, 得到文件夹mesos-0.9.0,进入该文件。 

[root@Arion3 ~]# tar zxf mesos-0.9.0-incubating.tar.gz 

(3)进入文件夹mesos-0.9.0后,使用configure脚本配置mesos。 

find / -type d -name python2.6

../configure --with-webui --with-included-zookeeper --prefix=/usr/local/mesos

如果没有指定prefix,默认安装在/usr/local

(4)make 

make check (执行测试用例)

(5)make install 

(6)分别在slave1和slave2两台机器上用相同的用户目录,重复上述5个步骤。 

5Mesos配置

(7)在master结点上做如下两个配置: 

编辑文件/usr/local/var/mesos/deploy/masters(如果没有可以创建),在文件中列出主结点的主机名或者IP地址。 

编辑文件 /usr/local/var/mesos/deploy/slaves(如果没有可以创建),在文件中列出从结点的主机名或者IP地址。 

我的配置如下所示: 

#masters文件的内容 

master 

 

#slaves文件的内容 

slave1 

slave2 

slave3 

 

编辑文件/usr/local/var/mesos/conf/mesos.conf(如果没有可以创建),配置主结点的日志文件目录。我的配置如下: 

log_dir=/home/ mesos/log/ 

(8)在所有从结点上做如下配置。 编辑conf文件(如果没有可以创建),配置从结点的日志文件目录、主结点URI和从结点资源。从结点的资源可以根据每个结点的CPU和内存的空闲情况进行配置,以供mesos调度使用。其中cpus后面的数字代表CPU有几个核,而mem后面的数字代表分配多少空闲内存给mesos使用,单位是MB。 

 

master=master:5050 

log_dir=/home/ mesos/log 

resources=cpus:8;mem:3200 

 

内存单位是MB

(9)启动与停止mesos集群管理器。 如果配置正确无误,就可以进入目录 /usr/local/sbin/,使用如下命令启动集群管理器: 

master节点上: 

nohup ./mesos-master --ip=193.168.1.75  --whitelist="/usr/local/var/mesos/deploy/slaves"  >/dev/null 2>&1 &

 

 

注:一定要将slaves加入到whitelist中。

slave节点上: 

nohup ./mesos-slave --master=193.168.1.75:5050 >/dev/null 2>&1 &

(10) mesos启动后,可以通过Web界面来查看集群中的资源,以及集群中正在运行的框架和任务。具体的访问方式是通过浏览器访问master结点的8080接口,即http://master:5050/。如果mesos启动正常,可以在该web界面上看到整个集群的资源总和和每一个slave结点的资源。如果有框架正在运行,还可以看到框架的信息,以及正在执行的任务信息。 

 

6、测试默认案例:

1、测试默认的framework

/opt/mesos-0.17.0/build/src

[root@host75 src]# ./test-framework  --master=193.168.1.75:5050

I0326 16:32:22.872979 16936 sched.cpp:218] No credentials provided. Attempting to register without authentication

I0326 16:32:22.873389 16936 sched.cpp:230] Detecting new master

Registered!



 

<!--EndFragment-->

广告服务联系QQ:1134687142 | 网站地图

版权所有: IT货架- 内容来自互联网,仅供用于技术学习,请遵循相关法律法规. 京ICP备11030978号-1