qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

数据库中索引的优缺点

一、索引的概念
  索引就是加快检索表中数据的方法。数据库的索引类似于书籍的索引。在书籍中,索引允许用户不必翻阅完整个书就能迅速地找到所需要的信息。在数据库中,索引也允许数据库程序迅速地找到表中的数据,而不必扫描整个数据库。
  二、索引的特点
  1.索引可以加快数据库的检索速度
  2.索引降低了数据库插入、修改、删除等维护任务的速度
  3.索引创建在表上,不能创建在视图上
  4.索引既可以直接创建,也可以间接创建
  5.可以在优化隐藏中,使用索引
  6.使用查询处理器执行SQL语句,在一个表上,一次只能使用一个索引
  7.其他
  三、索引的优点
  1.创建唯一性索引,保证数据库表中每一行数据的唯一性
  2.大大加快数据的检索速度,这也是创建索引的最主要的原因
  3.加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
  4.在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
  5.通过使用索引,可以在查询的过程中使用优化隐藏器,提高系统的性能。
  四、索引的缺点
  1.创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加
  2.索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大
  3.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度
  五、索引分类
  1.直接创建索引和间接创建索引
  直接创建索引: CREATE INDEX mycolumn_index ON mytable (myclumn)
  间接创建索引:定义主键约束或者唯一性键约束,可以间接创建索引
  2.普通索引和唯一性索引
  普通索引:
  CREATE INDEX mycolumn_index ON mytable (myclumn)
  唯一性索引:保证在索引列中的全部数据是唯一的,对聚簇索引和非聚簇索引都可以使用
  CREATE UNIQUE COUSTERED INDEX myclumn_cindex ON mytable(mycolumn)
  3.单个索引和复合索引
  单个索引:即非复合索引
  复合索引:又叫组合索引,在索引建立语句中同时包含多个字段名,最多16个字段
  CREATE INDEX name_index ON username(firstname,lastname)
  4.聚簇索引和非聚簇索引(聚集索引,群集索引)
  聚簇索引:物理索引,与基表的物理顺序相同,数据值的顺序总是按照顺序排列
  CREATE CLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn) WITH
  ALLOW_DUP_ROW(允许有重复记录的聚簇索引)
  非聚簇索引:
  CREATE UNCLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn)
  六、索引的使用
  1.当字段数据更新频率较低,查询使用频率较高并且存在大量重复值是建议使用聚簇索引
  2.经常同时存取多列,且每列都含有重复值可考虑建立组合索引
  3.复合索引的前导列一定好控制好,否则无法起到索引的效果。如果查询时前导列不在查询条件中则该复合索引不会被使用。前导列一定是使用最频繁的列
  4.多表操作在被实际执行前,查询优化器会根据连接条件,列出几组可能的连接方案并从中找出系统开销最小的最佳方案。连接条件要充份考虑带有索引的表、行数多的表;内外表的选择可由公式:外层表中的匹配行数*内层表中每一次查找的次数确定,乘积最小为最佳方案
  5.where子句中对列的任何操作结果都是在sql运行时逐列计算得到的,因此它不得不进行表搜索,而没有使用该列上面的索引;如果这些结果在查询编译时就能得到,那么就可以被sql优化器优化,使用索引,避免表搜索。
  例:
  select * from record where substring(card_no,1,4)=’5378’ && select * from record where card_no like ’5378%’
  任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边
  6.where条件中的’in’在逻辑上相当于’or’,所以语法分析器会将in ('0','1')转化为column='0' or column='1'来执行。我们期望它会根据每个or子句分别查找,再将结果相加,这样可以利用column上的索引;但实际上它却采用了"or策 略",即先取出满足每个or子句的行,存入临时数据库的工作表中,再建立唯一索引以去掉重复行,最后从这个临时表中计算结果。因此,实际过程没有利用 column上索引,并且完成时间还要受tempdb数据库性能的影响。in、or子句常会使用工作表,使索引失效;如果不产生大量重复值,可以考虑把子 句拆开;拆开的子句中应该包含索引
  7.要善于使用存储过程,它使sql变得更加灵活和高效

posted @ 2014-02-21 10:36 顺其自然EVO 阅读(229) | 评论 (0)编辑 收藏

Linux下TCP通信简单实例

 基于TCP(面向连接)的socket编程,分为服务器端和客户端
  服务器端的流程如下:
  (1)创建套接字(socket)
  (2)将套接字绑定到一个本地地址和端口上(bind)
  (3)将套接字设为监听模式,准备接收客户端请求(listen)
  (4)等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)
  (5)用返回的套接字和客户端进行通信(send/recv)
  (6)返回,等待另一个客户请求。
  (7)关闭套接字。
  客户端的流程如下:
  (1)创建套接字(socket)
  (2)向服务器发出连接请求(connect)
  (3)和服务器端进行通信(send/recv)
  (4)关闭套接字
  下面通过一个具体例子讲解一下具体的过程和相关的函数,环境是suse linux。
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <memory.h>
#include <unistd.h>
//#include <linux/in.h>
#include <netinet/in.h>
//#include <linux/inet_diag.h>
#include <arpa/inet.h>
#include <signal.h>
/**
关于 sockaddr  sockaddr_in  socketaddr_un说明
http://maomaozaoyue.blog.sohu.com/197538359.html
*/
#define PORT    11910   //定义通信端口
#define BACKLOG 5       //定义侦听队列长度
#define buflen  1024
void process_conn_server(int s);
void sig_pipe(int signo);
int ss,sc;  //ss为服务器socket描述符,sc为某一客户端通信socket描述符
int main(int argc,char *argv[])
{
struct sockaddr_in server_addr; //存储服务器端socket地址结构
struct sockaddr_in client_addr; //存储客户端 socket地址结构
int err;    //返回值
pid_t pid;  //分叉进行的ID
/*****************socket()***************/
ss = socket(AF_INET,SOCK_STREAM,0); //建立一个序列化的,可靠的,双向连接的的字节流
if(ss<0)
{
printf("server : server socket create error\n");
return -1;
}
//注册信号
sighandler_t ret;
ret = signal(SIGTSTP,sig_pipe);
if(SIG_ERR == ret)
{
printf("信号挂接失败\n");
return -1;
}
else
printf("信号挂接成功\n");
/******************bind()****************/
//初始化地址结构
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;           //协议族
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);   //本地地址
server_addr.sin_port = htons(PORT);
err = bind(ss,(struct sockaddr *)&server_addr,sizeof(sockaddr));
if(err<0)
{
printf("server : bind error\n");
return -1;
}
/*****************listen()***************/
err = listen(ss,BACKLOG);   //设置监听的队列大小
if(err < 0)
{
printf("server : listen error\n");
return -1;
}
/****************accept()***************/
/**
  为类方便处理,我们使用两个进程分别管理两个处理:
  1,服务器监听新的连接请求;2,以建立连接的C/S实现通信
  这两个任务分别放在两个进程中处理,为了防止失误操作
  在一个进程中关闭 侦听套接字描述符 另一进程中关闭
  客户端连接套接字描述符。注只有当所有套接字全都关闭时
  当前连接才能关闭,fork调用的时候父进程与子进程有相同的
  套接字,总共两套,两套都关闭掉才能关闭这个套接字
*/
for(;;)
{
socklen_t addrlen = sizeof(client_addr);
//accept返回客户端套接字描述符
sc = accept(ss,(struct sockaddr *)&client_addr,&addrlen);  //注,此处为了获取返回值使用 指针做参数
if(sc < 0)  //出错
{
continue;   //结束此次循环
}
else
{
printf("server : connected\n");
}
//创建一个子线程,用于与客户端通信
pid = fork();
//fork 调用说明:子进程返回 0 ;父进程返回子进程 ID
if(pid == 0)        //子进程,与客户端通信
{
close(ss);
process_conn_server(sc);
}
else
{
close(sc);
}
}
}
/**
服务器对客户端连接处理过程;先读取从客户端发送来的数据,
然后将接收到的数据的字节的个数发送到客户端
*/
//通过套接字 s 与客户端进行通信
void process_conn_server(int s)
{
ssize_t size = 0;
char buffer[buflen];  //定义数据缓冲区
for(;;)
{
//等待读
for(size = 0;size == 0 ;size = read(s,buffer,buflen));
//输出从客户端接收到的数据
printf("%s",buffer);
//结束处理
if(strcmp(buffer,"quit") == 0)
{
close(s);   //成功返回0,失败返回-1
return ;
}
sprintf(buffer,"%d bytes altogether\n",size);
write(s,buffer,strlen(buffer)+1);
}
}
void sig_pipe(int signo)
{
printf("catch a signal\n");
if(signo == SIGTSTP)
{
printf("接收到 SIGTSTP 信号\n");
int ret1 = close(ss);
int ret2 = close(sc);
int ret = ret1>ret2?ret1:ret2;
if(ret == 0)
printf("成功 : 关闭套接字\n");
else if(ret ==-1 )
printf("失败 : 未关闭套接字\n");
exit(1);
}
}
  客户端代码:
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <stdlib.h>
#include <memory.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h> //添加信号处理  防止向已断开的连接通信
/**
信号处理顺序说明:在Linux操作系统中某些状况发生时,系统会向相关进程发送信号,
信号处理方式是:1,系统首先调用用户在进程中注册的函数,2,然后调用系统的默认
响应方式,此处我们可以注册自己的信号处理函数,在连接断开时执行
*/
#define PORT    11910
#define Buflen  1024
void process_conn_client(int s);
void sig_pipe(int signo);    //用户注册的信号函数,接收的是信号值
int s;  //全局变量 , 存储套接字描述符
int main(int argc,char *argv[])
{
sockaddr_in server_addr;
int err;
sighandler_t ret;
char server_ip[50] = "";
int port = 0;
strcpy(server_ip, argv[1]);
port = atoi(argv[2]);
/********************socket()*********************/
s= socket(AF_INET,SOCK_STREAM,0);
if(s<0)
{
printf("client : create socket error\n");
return 1;
}
printf("client : socket fd = %d\n", s);
//信号处理函数  SIGINT 是当用户按一个 Ctrl-C 建时发送的信号
ret = signal(SIGTSTP,sig_pipe);
if(SIG_ERR == ret)
{
printf("信号挂接失败\n");
return -1;
}
else
printf("信号挂接成功\n") ;
/*******************connect()*********************/
//设置服务器地址结构,准备连接到服务器
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
/*将用户数入对额字符串类型的IP格式转化为整型数据*/
//inet_pton(AF_INET,argv[1],&server_addr.sin_addr.s_addr);
printf("please input server ip address : \n");
read(0,server_ip,50);
//err = inet_pton(AF_INET,server_ip,&server_addr.sin_addr.s_addr);
server_addr.sin_addr.s_addr = inet_addr(server_ip);
err = connect(s,(struct sockaddr *)&server_addr,sizeof(sockaddr));
if(err == 0)
{
printf("client : connect to server\n");
}
else
{
printf("client : connect error\n");
return -1;
}
//与服务器端进行通信
process_conn_client(s);
close(s);
}
void process_conn_client(int s)
{
ssize_t size = 0;
char buffer[Buflen];
for(;;)
{
memset(buffer,'\0',Buflen);
/*从标准输入中读取数据放到缓冲区buffer中*/
size = read(0,buffer,Buflen);   // 0,被默认的分配到标准输入  1,标准输出  2,error
if(size >  0)
{
//当向服务器发送 “quit” 命令时,服务器首先断开连接
write(s,buffer,strlen(buffer)+1);   //向服务器端写
//等待读取到数据
for(size = 0 ; size == 0 ; size = read(s,buffer,Buflen) );
write(1,buffer,strlen(buffer)+1);   //向标准输出写
}
}
}
void sig_pipe(int signo)    //传入套接字描述符
{
printf("Catch a signal\n");
if(signo == SIGTSTP)
{
printf("接收到 SIGTSTP 信号\n");
int ret = close(s);
if(ret == 0)
printf("成功 : 关闭套接字\n");
else if(ret ==-1 )
printf("失败 : 未关闭套接字\n");
exit(1);
}
}

posted @ 2014-02-20 10:57 顺其自然EVO 阅读(455) | 评论 (0)编辑 收藏

如何快速在数据库中插入数据

工作中很少用mysql插入数据,今天正好遇到,学习下:
  在toad mysql工具里面,除了像execl表格一样手动插入数据之外,最好用insert语句插入数据,怎么快速生成数据呢?
  1. 右击需要插入数据表,这里是cd_financing_income,弹出Generate SQL--->选择TO editor——>Insert statement,以今天需要用到的cd_financing_income为例:
  生成
INSERT INTO `51qian`.cd_financing_income
(service_range_id,trac_id, ic_user_id,principal,interest,  status,create_time,income_time,service_fee,order_id, bid_id,real_income_time)
VALUES ('trac_id', ic_user_id,principal,interest,  'status','create_time','income_time',service_fee,'order_id', bid_id,'real_income_time');
  2   按照需求,我们需要添加以下数据:
idservice_range_idtrac_idic_user_idprincipal,intereststatuscreate_timeincome_timeservice_feeorder_idbid_idreal_income_time
15102147M20131113000020548044105200478602014/2/13 13:082014/3/13 12:321795205485961
15103147M20131113000020548053105200529902014/2/13 13:082014/4/13 12:321987205485961
  关于id:a    可以自己添加设置id号(id号切记不能重复);也可以不设置,默认为空,就是待插入数据之后,系统自动生成id号;
  通常情况下,若添加的数据id与几张表相关联,则id号需要自己设置添加(id不能重复);
  在这里,我们让系统自动生成id号,故选择缺省;
  b    字段赋值有引号的加引号(字段名只有单引号,没有双引号),没引号的不要加;
  3  最后一个字段real_income_time为空,在这里,我们需要双击该表,打开Script,若real_income_time字段注释为DEFAULT NULL,我们就把为空的字段去掉;
  若字段注释为DEFAULT '0',我们就‘0’(表示空);
  这里的real_income_time字段注释为DEFAULT NULL,我们则直接把该字段进行缺省(字段名和字段值直接缺省);

 4    第一个括号后面表示字段名,values后面的括号是字段值,我们只需要改字段值即可(除非是缺省的值,字段名需要另外去掉);
  下面就写成:
INSERT INTO cd_financing_income(service_range_id,
trac_id,
ic_user_id,
principal,
interest,
status,
create_time,
income_time,
service_fee,
order_id,
bid_id)
VALUES (147,
'M20131113000020548044',
10520,
0,
4786,
'0',
'2014/2/13  13:08:04',
'2014/3/13  12:32:33',
1795,
'20548',
5961);
INSERT INTO cd_financing_income(service_range_id,
trac_id,
ic_user_id,
principal,
interest,
status,
create_time,
income_time,
service_fee,
order_id,
bid_id)
VALUES (147,
'M20131113000020548053',
10520,
0,
5299,
'0',
'2014/2/13  13:08:04',
'2014/4/13  12:32:33',
1987,
'20548',
5961);
  说明:1.   51qian是数据库名,若是在线上插入数据,库名不一样,需要省略;
  2       为了使插入数据清晰,不易出错,我们可以选择格式化图标format  SQL;
  -------------------------------
  在此说明下数据库的连接,因为平时测试时候需要导数据,这时候就需要用到多个库;
  目前我们连接的是51qian库,若改用test库,我们就在51qian上选择右击disconnect;
  再连接test;

posted @ 2014-02-20 10:56 顺其自然EVO 阅读(388) | 评论 (0)编辑 收藏

Java 里的thread (线程)简介

Java里 thread 就是线程的意思.
  说到线程的概念, 自然离不开另外两个词: 程序和进程.
  从最基本的程序讲起:
  一. 什么是程序(Program)
  所谓程序, 就是1个严格有序的指令集合. 程序规定了完成某一任务时,计算机所需要做的各种操作, 以及操作的顺序.
  1.1 单道程序运行环境
  所谓单道程序环境就是指, 计算机除了操作系统之外, 只允许运行1个用户程序.
  以前的DOS系统就是1个典型的单道程序运行环境.
  单道程序有如下特点:
  1. 资源的独占性: 任何时候, 内存内只有1个用户程序, 这个程序独享系统的所有资源.
  2. 执行顺序性: 内存中只执行1个程序, 各程序是按次序执行的, 即是做完1件事后, 才做另一件事.
  绝不可能执行1个程序的中途暂停, 然后去执行另1个程序.
  3.结果的再现性: 只要执行环境和初始条件相同, 重复执行1个程序, 获得的结果总是一样的.
  1.2 多道程序运行环境
  所谓多道程序运行环境是指, 计算机除了操作系统之外, 允许同时运行多个用户程序.
  当今的unix, linux都是典型的多道程序运行环境.
  多道程序有如下特点:
  1. 间断性: 也就是cpu会在多个程序之间切换执行, 例如cpu执行程序A一段时间后, 会切换去执行程序B一段时间.
  由于这个时间段很短, 所以对用户造成1个程序A和程序B同时执行的假象.
  2. 失去封闭性:  程序执行受外界影响, 也就是多个程序之间共享资源, 互相制约.
  3. 不可再现性:  重复执行时, 可能得到不同的结果. 原因就是上面的点, 执行时可能会受到内存中其他程序的影响.
  二. 什么是进程(process)
  进程这个概念是基于多道程序运行环境来讲的.
  1个进程就是程序在内存中运行的1个实例
  由于在多道程序运行环境中, 同1个程序可以同时产生两个实例在内存里运行.
  举个简单例子:  例如你可以在操作系统打开两个gvim 文本编辑器, 同时编辑两个不同的文件.
  这时执行ps -ef | grep gvim 就会见到系统中有两个名字是gvim的进程, 但是它们的进程id是不同的.
  也就是将,  gvim这个程序在内存中生成两个进程同时运行.
  在多道程序运行环境中.  引用进程这个概念来描叙程序在内存里事例, 用来区别于程序本身.
  真正深入了解进程并不简单, 进程其实算是属于操作系统这门课的范畴. 1个进程包括程序段, 数据段, 程序控制块等,  但是作为一个普通的程序猿, 没必要理解得这么深入.
  三. 什么是线程(thread).
  相对地, 我认为作为1个普通的程序猿, 必须深入了解线程这个概念.
  其实, 线程就是1个进程的执行路径.
  一般的java程序都是从启动类的main函数入口开始执行, main函数的结束而停止.   这条执行路径就是java程序的主线程.
  也就是讲:
  线程是相对于进程来讲的, 1个进程至少存在1条线程.
  而java允许多线程编程, 也就是1个进程除主线程外,  还允许存在1个或若干个其他线程, cpu会在这些线程当中间断切换执行, 给用户造成同时执行的假象.
  四. 1个单线程java程序的简单例子.
  例子如下:
package Thread_kng;
class S_thrd_1{
public void f(){
while (true){
System.out.printf("Thread main is runing!\n");
}
//System.out.printf("f() is done!\n");  //compilation fail
}
}
public class S_thread_expl{
public static void g(){
S_thrd_1 s = new S_thrd_1();
s.f();
System.out.printf("g() is done!\n");
}
}


 上面的例子中,  g() 函数调用了f() 函数,
  g()函数在最后尝试输出"g() is done!" , 但是因为f() 是一个无限循环.  所以g() 调用f()后, 根本没机会执行后面的语句!
  也就是说, 虽然g() 跟 f() 是两个不同类的函数, 但是它们是在程序中的同一个线程(主线程)内执行.
  在同一个线程内, 语句总是按程序猿编写的顺序依次执行, 一条语句未执行完时, 不会跳到后面执行其他的语句.
  五. 1个多线程java程序的简单例子.
  例子如下:
package Thread_kng;
class M_thrd_1 extends Thread{  //extends
public M_thrd_1(String name){
super(name);
}
public void run(){ //overwrite the method run() of superclass
while (true){
System.out.printf("Thread " + this.getName()+ " is runing!\n");
}
//System.out.printf("f() is done!\n");  //compilation fail
}
}
public class M_thread_expl{
public static void g(){
M_thrd_1 s = new M_thrd_1("T1");
s.start(); //start()method is extended from superclass, it will call the method run()
M_thrd_1 s2 = new M_thrd_1("T2");
s2.start();
System.out.printf("g() is done!\n");
}
  上面例子中, 类 M_thrd_1继承了线程类Thread.  并重写了run方法.
  在下面的g()函数中.
  实例化了两个类M_thrd_1的对象s, s1 的对象.
  执行了start()方法.
  start()方法继承字Thread, 它会启动1新线程, 并调用run()函数.
  而g()后面本身也在不断循环输出"THread Main is running"
  所以输出如下:
  可以 见到, 输出结果是T1,T2 和Main 交替输出在屏幕上.
  实际上, 这个程序有3个线程.
  其中1个就是主线程.
  主线程main通过实例化两个M_thrd_1的对象, 调用其start()函数开启了两个子线程.
  其中1个子线程不断输出T1
  另1个子线程不断输出T2
  但是主线程并不会等待其子线程执行完成, 会继续执行下面的代码,所以不断循环输出Main!
  所以这个例子实际上要3个线程在输出信息到屏幕了!
  六. Java 开启一条新进程的两种方式.
  java开启一条新线程, 有两种方式. 但是都要利用java的线程基类Thread.
  6.1 方式1. 创建Thread的派生类
  这个就类似上面的例子:
  具体步骤如下:
  6.1.1 线程准备: 新建1个类, 继承线程类Thread.  将业务代码写入重写后的run() 方法中.
  例如上面的例子中的M_thrd_1类
class M_thrd_1 extends Thread{  //extends
public M_thrd_1(String name){
super(name);
}
public void run(){ //overwrite the method run() of superclass
while (true){
System.out.printf("Thread " + this.getName()+ " is runing!\n");
}
//System.out.printf("f() is done!\n");  //compilation fail
}
}
  可以见到, 我们将线程要执行的业务代码写入了run() 方法.
  6.1.2 启动线程: 新建1个上述类的对象, 运行start()方法
  例如
  M_thrd_1 s = new M_thrd();
  s. start();
  其中start() 是类M_thrd_1 继承自超类Thread的.
  我们看看JDK API 对start() 方法的定义:
  void  start()
  使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
  可以见到, 调用了start() 方法后, 会开始执行改线程, 也就是在当前线程下开启一条新线程.
  而这条新线程做什么呢, 就是执行run()里面的代码.
  所以我们一般只需要把也业务的代码写入run()里面就ok了. 不要重写start()方法.
  注意, 如果直接执行s.run(); 则会在当前线程下执行run里面的代码, 并没有开启一条新线程. 区别很大的.
  6.2 方式2. 创建1个类, 实现Runnable 接口
  步骤跟上面的方式差别不大
  6.2.1 线程准备: 新建1个类, 实现接口Runnable. 将业务代码写入重写后的run() 方法中.
  例子:
class M_thrd_2 implements Runnable{  //extends
public int id = 0;
public void run(){ //overwrite the method run() of superclass
while (true){
System.out.printf("%s: Thread " + this.id+ " is runing!\n", Thread.currentThread().getName());
}
//System.out.printf("f() is done!\n");  //compilation fail
}
}
  上面的例子 M_thrd_2 就实现了接口Runnable
  并重写了接口Runnable 的抽象方法run();
  注意.  currentThread() 是类Thread的一个静态方法, 作用是获取当前语句所在的线程.

  6.2.2 启动线程: 新建1个上述类的对象s, 在新建1个类Thread的对象t, 把s作为一个参数用于t的构造方法.  并执行t.start()
  貌似比方式一复杂.
  其实非如下:
  new Thread(new M_thrd_2()).start()
  例子:
public class M_thread_expl2{
public static void g(){
M_thrd_2 s = new M_thrd_2();
s.id = 1;
Thread t1 = new Thread(s);
t1.start();
Thread t2 = new Thread(s);
s.id = 2;
t2.start();
}
}
  在g() 函数中,
  首先新建1个类M_thrd_2的 对象s.
  并利用s作为参数 建立了两个线程类Thread的对象t1 和 t2. 并启动这两个线程.
  注:
  1. 这个例子中有3个线程, 其中1个主线程(也就是g() 函数所在的线程). 开启了两个子线程, 该两个子线程都在不断循环输出信息到屏幕上.
  2. Thread(object ob) 是1个属于类Thread的构造方法,  其中的对象ob 必须实现Runnable 接口.
  3. 这个例子执行时 , 第一个t1首先会输出id = 1,    但是当第二个线程t2开始执行后, t1会输出id=2,  因为t1, 和t2都是利用同1个对象建立的.
  也就是说, 这个对象的变动会同时影响两个线程.
  输出如下:
  6.3 两种方式的区别
  1.  方式1是创建继承类Thread的派生类,  方式2是创建实现Runnable接口的类.
  2.  启动方式:  方式1是直接调用业务类的对象的start()方法, 方式2是利用业务类类的对象新建1个类Thread的对象, 并调用该对象的start()方法.
  3.  如果要启动多个线程, 通过方式1需要新建多个不同业务类的对象, 方式2 则可以通过业务1个对象 构建多个Thread类对象, 但是业务对象的变动会同时影响对应的多个线程.
  七. 关于线程的一些常用方法介绍.
  7.1 run()
  我们要吧线把要执行的业务代码写入这个方法. 上面提过了.  注意, 如果我们直接执行1个线程对象的run()方法可以合法的, 但是仍然是在当前线程内执行, 并没有开始一条新线程.
  7.2 Start()
  当1个线程or其派生类执行1个start()方法. 就开启1个新线程, 并调用该类的run()方法.
  注意: 1个线程如果已经调用过一次start(), 再调用start()则或抛异常.
  7.3 stop()
  停止1个1个线程.
  7.4 setName() 和 getName()
  设置和获得对应线程的名字.
  7.5 Thread.currentThread()
  注意这个方法是一个static方法,  而上面的都不是静态方法.
  这个方法返回当前执行语句所属的线程.
  例如1个线程对象A.    它的run() 方法里调用了 Thread.currentThread().getName() 函数.
  假如直接执行A.run()  则返回的是当前线程(而不是A线程) 的名字,  因为对象A并没有启动自己的线程.
  假如执行难过A.start(), 那么该函数就返回A启动的线程的名字了.
  八, 小结
  这篇文章只是简介, 很多重要的方法例如 sleep() , wait() 等都没有介绍, 这些是线程控制的内容. 本吊打算在另一篇博文里介绍.

posted @ 2014-02-20 10:55 顺其自然EVO 阅读(707) | 评论 (0)编辑 收藏

从一个脚本谈loadrunner的脚本初始化

 昨天一个同事请我一个问题,在下列代码中如何将
  InputStream is 这个元素只初始化一次呢?
/*
LoadRunner Java script. (Build: _build_number_)
*
* Script Description:
*
*/
import lrapi.lr;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import com.google.common.io.ByteStreams;
import com.jd.jfs.JingdongFileSystem;
public class Actions
{
File file = new File("C:/8K.file");
InputStream is =null; // 返回一个byte数组<pre code_snippet_id="145362" snippet_file_name="blog_20140107_2_8044261" name="code" class="java">         byte[] fileBytes = new byte[(int) file.length()];
// 创建一个数据来保存文件数据
JingdongFileSystem jfs =new JingdongFileSystem();
public int init() throws Throwable {
is = new FileInputStream(file);
ByteStreams.readFully(is, fileBytes);
is.close();
//jfs = new JingdongFileSystem();
return 0;
} //end of init
public int action() throws Throwable {
try {
lr.start_transaction("jfs-w");
String key = jfs.writeBytes(fileBytes);
//上传
System.out.println(key);
} catch (Exception e) {
e.printStackTrace();
}
lr.end_transaction("jfs-w", lr.AUTO);
return 0;
}//end of action
public int end() throws Throwable {
return 0;
}//end of end
}



我们知道,在loadrunner的java_vuser协议的脚本中,init方法在每个vuer初始化的时候都会被执行一次,换句话说N个用户就会被执行N次,所以上面的脚本中inputStream对象会被初始化N次。我随即做了如下修改
File file = new File("C:/8K.file");
static InputStream is =null;
// 返回一个byte数组  <span style="color: #ff0000; ">
</span>    byte[] fileBytes = new byte[(int) file.length()];
// 创建一个数据来保存文件数据
JingdongFileSystem jfs =new JingdongFileSystem();
public int init() throws Throwable {
if(is==null){
is = new FileInputStream(file);
ByteStreams.readFully(is, fileBytes);
is.close();
//jfs = new JingdongFileSystem();
//初始化
return 0;
}//end of init
理论上来说,上述代码实现了单例模式。但是这个脚本并发下无效。。。 经过和开发探讨最终换了以下的代码来处理:
static {
File file = new File("C:/8K.file");
fileBytes = new byte[(int) file.length()];
// 创建一个数据来保存文件数据
try {
InputStream is = new FileInputStream(file);
ByteStreams.readFully(is, fileBytes);
is.close();
} catch (Exception e) {
e.printStackTrace();
}
jfs = new JingdongFileSystem();
}
  静态初始化块:使用static定义,当类装载到系统时执行一次.若在静态初始化块中想初始化变量,那仅能初始化类变量,即static修饰的数据成员.
  总结一下:
  在java_vuser协议的脚本中:
  1. vuser_init模块始终不会被执行;
  2. init方法会被初始化合并发数相同的次数
  3. 如果想实现多个并发用户公用一个变量,请使用静态初始化块。

posted @ 2014-02-20 10:54 顺其自然EVO 阅读(1439) | 评论 (0)编辑 收藏

测试过程总结

  第一阶段:测试设计阶段
  1、 拿到测试需求后,分析理解本次测试的具体内容,需要了解的详细测试内容,联系研发进行需求调研工作。侧重业务理解包括上下游业务,重点业务点,测试指标,测试环境数据量及相关的造数据条件,涉及到的数据库,线上及测试环境部署,业务架构及提供测试时间;
  2、 需求调研完成后,梳理本次测试业务,测试点,测试数据,环境部署,根据已得到的信息,选取测试方法,确定测试计划,编写测试方案并评审;
  3、 搭建测试环境,包括修改测试环境的相关参数;
  4、 编写测试脚本并调试;
  确定性能测试点:重要功能点,频繁使用功能点,用户关心功能点,与DB密切相关的功能点,模拟用户两三年内的数据量进行测试。
  第二阶段:测试执行
  1、 按照方案进行执行,包括基准测试、负载测试、并发测试、混合测试、集合点测试、异常测试、稳定性测试、配置测试等。
  2、 若测试过程中出现问题,进行问题瓶颈定位。
  1) 可通过单条执行,查看日志,看错误日志内容,应用日志、tomcat日志、均衡服务日志、数据库日志等;
  2) 通过JVM定位是否内存问题导致;
  3) 通过jprofile监控,查看问题主要出在那个方法上,比如查看CPU使用和内存使用等;
  4) 通过tcpdump及wireshark工具查看网络包内容,是否为网络造成问题;
  5) 通过jstat –gcutil查看JVM内存回收;
  6) 通过nmon工具查看服务器资源使用;
  7) 换种压力工具,比如jmeter进行压测,是否出现同样的问题;
  8) 使用dump命令将内存内容当下来,然后用MAT工具分析是否内存问题;
  9) 通过远程配置,使用jconsole或者jvisualvm进行监控查看;
  10) Lr_set_debuy_message()调试日志信息,定位问题原因;
  3、 保存结果,完成测试;
  第三阶段:分析测试结果
  1、 分析测试结果包括资源监控结果;
  2、 编写报告文档;将问题、调优、风险、结论全部做总结写进文档;
  3、对整个项目做总结,通过本次项目学到了什么,有什么不足,及时学习
  将测试过程,形成文档,记录执行的过程中,出现的问题及解决的方法,方便最后报告的编写。及时做总结。

posted @ 2014-02-20 10:49 顺其自然EVO 阅读(196) | 评论 (0)编辑 收藏

自动化测试框架Selenium 入门

1.什么是selenium
  selenium是ThoughtWork的一款开源测试框架。
  下载selenium2.0lib包,点击http://code.google.com/p/selenium/downloads/list
  这是官方文档:http://seleniumhq.org/docs/
  2.为什么选择selenium
  自动化测试工具有很多了,QTP作为商业软件功能强大。但是要把QTP整合到已有的测试平台上面非常困难,selenium非常容易的可以整合到已有的测试平台上面去。如果你是一个刚刚不如职场的新人不懂程序代码上手selenium有点困难,selenium需要一定的程序开发能力。为以后的测试平台长远考虑,故选用selenium测试框架。
  selenium Java环境基本配置
  1.安装java虚拟机JDK1.5版本以上。
  2.下载安装火狐浏览器,并安装。
  3.下载selenium支持火狐浏览器的IDE,通过这个IDE可以使用selenium语言编写脚本也可录制操作脚本。(不是必须安装IDE)
  4.安装MyEclipse开发环境,并配置MyEclipse的环境。
  3.selenium的基本使用
  1.在Myeclipse建立一个JavaProject项目名称自定义。
  2.将下载的selenium-server-standalone-x.xx.x.jar,导入到项目的Reference Libraries中。
  3.从IDE中导出的测试脚本加入工程中。
  4.在注解@Test中可以编写自动化测试步骤或脚本了。
  选择元素
  WebElement element = driver.findElement(By.id("passwd-id"));
  在输入框中输入内容:
  element.sendKeys(“test”);
  将输入框清空:
  element.clear();
  获取输入框的文本内容:
  element.getText();
  找到下拉选择框的元素:
  Select select = new Select(driver.findElement(By.id("select")));  选择对应的选择项:
  上传文件的元素操作:
  WebElement adFileUpload =driver.findElement(By.id("WAP-upload"));
  String filePath = "C:\test\\uploadfile\\media_ads\\test.jpg";
  adFileUpload.sendKeys(filePath);
  Windows 和 Frames之间的切换
  一般来说,登录后建议是先:
  driver.switchTo().defaultContent();
  切换到某个frame:
  driver.switchTo().frame("leftFrame");
  从一个frame切换到另一个frame:
  driver.switchTo().frame("mainFrame");
  切换到某个window:
  driver.switchTo().window("windowName");
  调用Java Script
  Web driver对Java Script的调用是通过JavascriptExecutor来实现的,例如:
  JavascriptExecutor js = (JavascriptExecutor) driver;
  js.executeScript("(function(){inventoryGridMgr.setTableFieldValue('"+ inventoryId + "','" + fieldName + "','"
  + value + "');})()");


页面等待
  页面的操作比较慢,通常需要等待一段时间,页面元素才出现,但webdriver没有提供现成的方法,需要自己写。
  等一段时间再对页面元素进行操作:
public void waitForPageToLoad(longtime) {
try {
Thread.sleep(time);
} catch (Exceptione) {
}
}
  在找WebElement的时候等待:
public WebElementwaitFindElement(By by) {
returnwaitFindElement(by, Long.parseLong(CommonConstant.GUI_FIND_ELEMENT_TIMEOUT),Long
.parseLong(CommonConstant.GUI_FIND_ELEMENT_INTERVAL));
}
public WebElementwaitFindElement(By by, long timeout, long interval) {
long start = System.currentTimeMillis();
while (true) {
try {
return driver.findElement(by);
} catch(NoSuchElementException nse) {
if (System.currentTimeMillis()- start >= timeout) {
throw newError("Timeout reached and element[" + by + "]not found");
} else {
try {
synchronized(this) {
wait(interval);
}
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
  4.selenium注意点
  1.启动浏览器,如果不是默认安装需要指定启动路径 System.setProperty("webdriver.firefox.bin", PrivateDataSource.fireFoxPath);
  2.driver在每次结束的时候都需要退出使用driver.quit();可以把driver产生的零时文件删除掉,如果直接关闭浏览器或driver.close();那么零时文件不会被删除,会
  一直存在在你的C盘中,非常耗资源。
  5.自动化测试策略
  目前项目繁多而非产品那样的专业化,如果每一个项目都做自动化测试那么相对的工作量繁重,质量参差不齐无法很好的控制。
  自动化测试针对项目应注重以下点进行实施:
  1.项目进度督促使用自动化测试核心模块核心流程是否正常。
  2.把控自动化测试质量,如果质量不过关还不如不做自动化测试。
  3.进行并行开发策略,开发人员根据界面设计进行功能开发,测试人员根据界面设计进行自动化脚本开发。
  4.通过jenkins持续集成平台,每次开发人员或测试人员提交代码都会自动去执行一偏测试脚本,把执行中出现的问题暴露出来,让开发人员及时修改。
  通过学习使用selenium自动化测试框架,了解到测试不是简单的测试工作,她掺杂了项目管理,需求分析,人际沟通协调等等技能,需要不断的实践增强能力胜任更加严峻的项目工作。

posted @ 2014-02-20 10:49 顺其自然EVO 阅读(1844) | 评论 (0)编辑 收藏

python的单元测试框架nose的安装

 python单元测试框架 nose的安装
  采用easy_install的方式安装nose
  1、先安装easy_install
  easy_install  方便python 安装第三方扩展包的工具
  Easy Install 是一个Python的模块(easy_install),跟 setuptools 绑定在一起,提供自动的程序下载、编译、安装和管理 Python 的包
  1)先下载easy_install的安装包   http://pypi.python.org/pypi/setuptools
  2)python setup.py install
  2、安装nose
  在命令行下输入:easy_install nose  回车   就自动安装了
  nosetests  ***.py  就自动运行当前文件下的testcase了
  备注:nosetests 命令就是执行case的。
  它会自动查找当前目录下包含“Test”字样的目录和文件(文件里的python函数也已Test开头),然后执行,返回case执行成功与否。

posted @ 2014-02-20 10:48 顺其自然EVO 阅读(4884) | 评论 (0)编辑 收藏

性能测试中混合场景瓶颈测定

在做混合场景测试的时候,常面临一个问题:什么时候是到了应用的瓶颈了呢?
  假设有三支交易(三个脚本): register、login、update,业务比例是 15%,75%,10%. 经过比例调配,我们得到三支交易的用户比例是4,2,3.
  第一种测试结果:
  测试序列用户总数TPS响应时间
  第一组9(4/2/3)14/76/1115/20/12
  第二组18(8/4/6)29/151/2416/21/12
  第三组36(16/8/12)20/120/15100/150/180
  那么我们发现第三组的测试结果性能比第二组明显有所下降,那么在这种典型的情况下.我可以很容易知道36并发就是瓶颈了.
  第二种测试结果:
  测试序列用户总数TPS响应时间
  第一组9(4/2/3)14/76/1115/20/12
  第二组18(8/4/6)29/151/2416/21/12
  第三组36(16/8/12)20/300/47100/22/12
  此种情况,我们假设register的响应时间也符合性能要求的话.第三组的时候其余两支交易的TPS仍然维持的比例的增多,但是register反而下降了.此时我们要动态减少login和update的用户数,并增加register的并发直到他们的tps符合比例为止. 正常情况下你会发现调节后的总TPS和第二组交易差不多,也就说此时已经到达了瓶颈.
  当然,如果第三组的register的响应时间已经不符合性能要求的话,那么就更好判断了.

posted @ 2014-02-20 10:46 顺其自然EVO 阅读(486) | 评论 (0)编辑 收藏

Linux下部署Hadoop伪分布模式

 Hadoop版本为1.2.1
  Linux使用Fedora19并使用hadoop账号安装
  第一步:配置ssh本地登录证书(虽然为伪分布模式,Hadoop依然会使用SSH进行通信)
[hadoop@promote ~]$ which ssh
/usr/bin/ssh
[hadoop@promote ~]$ which ssh-keygen
/usr/bin/ssh-keygen
[hadoop@promote ~]$ which sshd
/usr/sbin/sshd
[hadoop@promote ~]$ ssh-keygen -t rsa
  然后一路回车
Generating public/private rsa key pair.
Enter file in which to save the key (/home/hadoop/.ssh/id_rsa):
Created directory '/home/hadoop/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Passphrases do not match.  Try again.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/hadoop/.ssh/id_rsa.
Your public key has been saved in /home/hadoop/.ssh/id_rsa.pub.
The key fingerprint is:
2f:a9:60:c7:dc:38:8f:c7:bb:70:de:d4:39:c3:39:87 hadoop@promote.cache-dns.local
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|                 |
|                 |
|                 |
|        S        |
|     o o o o +   |
|    o B.= o E .  |
|   . o Oo+   =   |
|      o.=o.      |
+-----------------+
  最终将在/home/hadoop/.ssh/路径下生成私钥id_rsa和公钥id_rsa.pub
  [hadoop@promote .ssh]$ cd /home/hadoop/.ssh/
  [hadoop@promote .ssh]$ ls
  id_rsa  id_rsa.pub
  修改sshd服务配置文件:
  [hadoop@promote .ssh]$ su root
  密码:
  [root@promote .ssh]# vi /etc/ssh/sshd_config
  启用RSA加密算法验证(去掉前面的#号)
RSAAuthentication yes
PubkeyAuthentication yes
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile      .ssh/authorized_keys


 修改mapred-site.xml:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>mapred.job.tracker</name>
<value>localhost:9001</value>
</property>
</configuration>
  修改hdfs-site.xml:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
</configuration>
  master中指定的SNN节点和slaves中指定的从节点位置均为本地
[hadoop@promote conf]$ cat masters
localhost
[hadoop@promote conf]$ cat slaves
localhost
  第五步:启动Hadoop
[hadoop@promote bin]$ cd ../conf/
[hadoop@promote conf]$ cd ../bin
[hadoop@promote bin]$ sh start-all.sh
starting namenode, logging to /home/hadoop/hadoop-1.2.1/libexec/../logs/hadoop-hadoop-namenode-promote.cache-dns.local.out
localhost: starting datanode, logging to /home/hadoop/hadoop-1.2.1/libexec/../logs/hadoop-hadoop-datanode-promote.cache-dns.local.out
localhost: starting secondarynamenode, logging to /home/hadoop/hadoop-1.2.1/libexec/../logs/hadoop-hadoop-secondarynamenode-promote.cache-dns.local.out
starting jobtracker, logging to /home/hadoop/hadoop-1.2.1/libexec/../logs/hadoop-hadoop-jobtracker-promote.cache-dns.local.out
localhost: starting tasktracker, logging to /home/hadoop/hadoop-1.2.1/libexec/../logs/hadoop-hadoop-tasktracker-promote.cache-dns.local.out
  可以看到所有Hadoop守护进程均已启动
 保存并退出,然后重启sshd服务
[root@promote .ssh]# service sshd restart
Redirecting to /bin/systemctl restart  sshd.service
[root@promote .ssh]# ps -ef|grep sshd
root      1995     1  0 22:33 ?        00:00:00 sshd: hadoop [priv]
hadoop    2009  1995  0 22:33 ?        00:00:00 sshd: hadoop@pts/0
root      4171     1  0 23:11 ?        00:00:00 /usr/sbin/sshd -D
root      4175  3397  0 23:12 pts/0    00:00:00 grep --color=auto sshd
  然后切换回hadoop用户,将ssh证书公钥拷贝至/home/hadoop/.ssh/authorized_keys文件中
  [root@promote .ssh]# su hadoop
  [hadoop@promote .ssh]$ cat id_rsa.pub >> authorized_keys
  修改authorized_keys文件的权限为644(这步一定要有)
  [hadoop@promote .ssh]$ chmod 644 authorized_keys
  [hadoop@promote .ssh]$ ssh localhost
  The authenticity of host 'localhost (127.0.0.1)' can't be established.
  RSA key fingerprint is 25:1f:be:72:7b:83:8e:c7:96:b6:71:35:fc:5d:2e:7d.
  Are you sure you want to continue connecting (yes/no)? yes
  Warning: Permanently added 'localhost' (RSA) to the list of known hosts.
  Last login: Thu Feb 13 23:42:43 2014
  第一次登陆将会将证书内容保存在/home/hadoop/.ssh/known_hosts文件中,以后再次登陆将不需要输入密码
  [hadoop@promote .ssh]$ ssh localhost
  Last login: Thu Feb 13 23:46:04 2014 from localhost.localdomain
  至此ssh证书部分配置完成
  第二步:安装JDK
  [hadoop@promote ~]$ java -version
  java version "1.7.0_25"
  OpenJDK Runtime Environment (fedora-2.3.10.3.fc19-i386)
  OpenJDK Client VM (build 23.7-b01, mixed mode)
  将OpenJDK换为Oracle的Java SE
  [hadoop@promote .ssh]$ cd ~
  [hadoop@promote ~]$ uname -i
  i386
  在Oracle的官网下载jdk-6u45-linux-i586.bin后上传至服务器,赋予权限并进行安装,最后删除安装包
  [hadoop@promote ~]$ chmod u+x jdk-6u45-linux-i586.bin
  [hadoop@promote ~]$ ./jdk-6u45-linux-i586.bin
  [hadoop@promote ~]$ rm -rf jdk-6u45-linux-i586.bin
  [hadoop@promote conf]$ export PATH=$PATH:/home/hadoop/jdk1.6.0_45/bin
  出现以下结果说明JDK成功安装:
  [hadoop@promote ~]$ java -version
  java version "1.6.0_45"
  Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
  Java HotSpot(TM) Client VM (build 20.45-b01, mixed mode, sharing)
  第三步:安装Hadoop
  在Hadoop官网下载hadoop-1.2.1.tar.gz并上传至服务器/home/hadoop路径下
  [hadoop@promote ~]$ tar -xzf hadoop-1.2.1.tar.gz
  [hadoop@promote ~]$ rm -rf hadoop-1.2.1.tar.gz
  [hadoop@promote ~]$ cd hadoop-1.2.1/conf/
  [hadoop@promote conf]$ vi hadoop-env.sh
  将JAVA_HOME指向第二步安装的JDK所在目录片
  # The java implementation to use.  Required.
  export JAVA_HOME=/home/hadoop/jdk1.6.0_45
  保存并退出
  第四步:修改Hadoop配置文件
  修改core-site.xml:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>fs.default.name</name>
<value>hdfs://localhost:9000</value>
</property>
</configuration>


posted @ 2014-02-19 11:23 顺其自然EVO 阅读(1320) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 146 147 148 149 150 151 152 153 154 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜