HBase(Hadoop Database)是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,我们可以利用HBase技术在廉价的PC上搭建起大规模结构化存储集群。同Google的Bigtable基于GFS(Google FileSystem)所提供分布式数据存储服务一样,HBase基于HDFS之上也能提供类似的分布式数据存储服务。HBase利用Hadoop HDFS作为其文件存储系统,利用Hadoop MapReduce来处理HBase中的海量数据,利用Zookeeper作为协调工具。HBase为海量数据的及时查询提供了一个很好的开源解决方案,在大数据处理时代扮演者重要的角色。

一、HBase基础知识

1、逻辑数据模型

   HBase不支持条件查询和Order By等复杂查询,读取记录时只能按行键或全表扫描。HBase表中存储的数据都是字节数组,并且数据是以行键的物理顺序存储。它介于NoSQL与RDBMS之间,仅支持单行事务(可通过hive来支持实现多表join等复杂操作),主要用来存储非结构化和半结构化数据。

   几个关键概念:

   1) 表(table):用于存储管理数据,具有稀疏的、面向列的特点。HBase中的每一张表,就是所谓的大表(Bigtable),可以有上亿行,上百万列。对于为值为空的列,并不占用存储空间,因此表可以设计的非常稀疏。

   2) 行键(RowKey):类似于MySQL中的主键,HBase根据行键来快速检索数据,一个行键对应一条记录。与MySQL主键不同的是,HBase的行键是天然固有的,每一行数据都存在行键。

   3) 列族(ColumnFamily):是列的集合。列族在表定义时需要指定,而列在插入数据时动态指定。列中的数据都是以二进制形式存在,没有数据类型。在物理存储结构上,每个表中的每个列族单独以一个文件存储(参见图1.2)。

   4) 时间戳(TimeStamp):是列的一个属性,是一个64位整数。由行键和列确定的单元格,可以存储多个数据,每个数据含有时间戳属性,数据具有版本特性。可根据版本(VERSIONS)或时间戳来指定查询历史版本数据,如果都不指定,则默认返回最新版本的数据。

   5) 区域(Region):HBase自动把表水平划分成的多个区域,划分的区域随着数据的增大而增多。

图1.1 HBase-Regin存储结构

图1.2 HBase逻辑数据模型与物理数据模型

2、物理数据模型

   HBase表中所有行按照行键的字典序列排序,并且在行的方向上分割为多个HRegion。

   Region按大小分割的,每个表一开始只有一个Region,随着数据不断插入表,region不断增大,当增大到一个阀值的时候,HRegion就会等分为两个新的HRegion。当table中的行不断增多,就会有越来越多的HRegion。Hregion是Hbase中分布式存储和负载均衡的最小单元,最小单元就表示不同的Hregion可以分布在不同的HRegion server上,但一个Hregion是不会拆分到多个server上的。需要注意的是,HRegion虽然是分布式存储的最小单元,但并不是存储的最小单元。

图1.3 HBase的数据模型

3、架构体系

   1) Client:①使用HBase RPC机制与HMaster和HRegionServer进行通信;②Client与HMaster进行通信进行管理类操作;③Client与HRegionServer进行数据读写类操作。

   2) Zookeeper:①保证任何时候,集群中只有一个running master,避免单点问题;②存贮所有Region的寻址入口,包括-ROOT-表地址、HMaster地址;③实时监控Region Server的状态,将Region server的上线和下线信息,实时通知给Master;④存储Hbase的schema,包括有哪些table,每个table有哪些column family。

   3) HMaster:可以启动多个HMaster,通过Zookeeper的Master Election机制保证总有一个Master运行。角色功能:①为Region server分配region;②负责region server的负载均衡;③发现失效的region serve并重新分配其上的region;④GFS上的垃圾文件回收;⑤处理用户对标的增删改查操作。

   4) HRegionServer:HBase中最核心的模块,主要负责响应用户I/O请求,向HDFS文件系统中读写数据。作用:①维护Master分配给它的region,处理对这些region的IO请求;②负责切分在运行过程中变得过大的region。此外,HRegionServer管理一些列HRegion对象,每个HRegion对应Table中一个Region,HRegion由多个HStore组成,每个HStore对应Table中一个Column Family的存储,Column Family就是一个集中的存储单元,故将具有相同IO特性的Column放在一个Column Family会更高效。

   5) HStore:HBase存储的核心,由MemStore和StoreFile组成。用户写入数据的流程为:client写入 -> 存入MemStore,一直到MemStore满 -> Flush成一个StoreFile,直至增长到一定阈值 -> 触发Compact合并操作 -> 多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除 -> 当StoreFiles Compact后,逐步形成越来越大的StoreFile -> 单个StoreFile大小超过一定阈值后,触发Split操作,把当前Region Split成2个Region,Region会下线,新Split出的2个孩子Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上,如图所示。

图1.4 HStore写入流程

   6) HLog:在分布式系统环境中,无法避免系统出错或者宕机,一旦HRegionServer意外退出,MemStore中的内存数据就会丢失,引入HLog就是防止这种情况。工作机制:每个HRegionServer中都会有一个HLog对象,HLog是一个实现Write Ahead Log的类,每次用户操作写入Memstore的同时,也会写一份数据到HLog文件,HLog文件定期会滚动出新,并删除旧的文件(已持久化到StoreFile中的数据)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知,HMaster首先处理遗留的HLog文件,将不同region的log数据拆分,分别放到相应region目录下,然后再将失效的region重新分配,领取到这些region的HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复。

   架构体系如图1.5所示:

图1.5 HBase架构体系图

4、两张特殊的表

   HBase中存有两张特殊的表,-ROOT-和.META.。

   1) .META.:记录了用户表的Region信息,.META.可以有多个regoin。

   2) -ROOT-:记录了.META.表的Region信息,-ROOT-只有一个region。Zookeeper中记录了-ROOT-表的location。

   Client访问用户数据之前需要首先访问zookeeper,然后访问-ROOT-表,接着访问.META.表,最后才能找到用户数据的位置去访问,如图1.6所示:

图1.6 Client访问HBase数据顺序图

二、HBase安装

  本实例安装环境:① 操作系统:CentOS;② HBase版本:1.0.1(hbase-1.0.1-bin.tar.gz)。

1、伪分布式模式安装

   1) 上传hbase压缩包到/usr/local下并解压,然后重新命名为hbase,执行命令:① tar -zxvf hbase-0.94.7-security.tar.gz;② mv hbase-0.94.7-security hbase;

   2) 配置环境变量,增加一行hbase环境配置HBASE_HOME=/usr/local/hbase,并在PATH中增加$HBASE_HOME/bin,执行命令:① vi /etc/profile;② source /etc/profile;

   3) 修改配置文件,执行命令:vi /usr/local/hbase/conf/hbase-env.sh,将# export JAVA_HOME=/usr/java/jdk1.6.0/行去掉注视,并更改为正确路径/usr/local/jdk/,然后去掉# export HBASE_MANAGES_ZK=true行的注视;

   4) 修改文件hbase-site.xml,执行命令:vi /usr/local/hbase/conf/hbase-site.xml,增加如下内容:

<property>
<name>hbase.rootdir</name>
<value>hdfs://hadoop0:9000/hbase</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<property>
<name>hbase.zookeeper.quorum</name>
<value>hadoop0</value>
</property>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>

   5) (可选)修改regionservers为localhost;

   6) 启动HBase,执行命令:start-hbase.sh,注意启动hbase之前必须确保hadoop已启动并可写入数据。启动后,会增加HMaster、HQuorumPeer、HRegionServer三个进程。

   注:HBase也伪分布式模式也支持使用本地磁盘作为存储路径,不需要hdfs的支持。只需要修改hbase-env.sh中的JAVA_HOME和hbase-site.xml中的hbase.rootdir,其中hbase.rootdir的值以file://开头,如file:///Downloads/hbase_data,启动之后只有HMaster一个进程。

2、集群模式安装(在伪分布模式基础上进行搭建)

   本集群安装实例结构为:主节点(hmaster)是hadoop0,从节点(region server)是hadoop1和hadoop2。

   1) 修改主节点hadoop0上的几个hbase配置文件,修改明细如下:

   ▶ 修改hbase-env.sh的最后一行:export HBASE_MANAGES_ZK=false;

   ▶ 修改hbase-site.xml文件的hbase.zookeeper.quorum的值:hadoop0,hadoop1,hadoop2; 

   ▶ 修改regionservers文件(存放的region server的hostname),内容修改为:hadoop1(\r\n换行)hadoop2;

   2) 如果hbase.rootdir配置了NameNode HA的逻辑nameservice名称,那么需要将hadoop中的core-site.xml和hdfs-site.xml配置文件拷贝至hbase的conf目录下;

   3) 复制hadoop0中的hbase文件夹到hadoop1、hadoop2中,在hadoop0上执行命令:scp /usr/local/hbase hadoop1:/usr/local/;

   4) 复制hadoop0中的/etc/profile到hadoop1、hadoop2中,在hadoop0上执行命令:scp /etc/profile hadoop1:/etc/,然后分别在hadoop1和hadoop2上执行命令:source /etc/profile;

   5) 启动,记住启动顺序:① 启动zookeeper集群;② 启动hadoop集群;③ 启动hbase集群;

   6) 添加Backup Masters,选择节点(这里的节点必须不是hbase从节点,即regionservers中配置的节点,节点可以选择为非zookeeper节点)并执行命令:hbase-daemon.sh start master,可以在当前节点启动HMaster进程,多个HMaser节点依靠Zookeeper来协调,当active的HMaster挂掉后,其他Backup Master节点就会通过Zookeeper选择一个并激活为active状态;

   注:可通过“http://hadoop0:16010/master-status”查看HBase集群状态,启动hbase后,会在HDFS根目录下新建hbase目录。这里不同版本查看的web默认端口可能不一样,早期版本端口号应该是60010,hbase-1.x版本之后默认改成了16010,默认配置见hbase-x.x.x-src/hbase-common/src/main/resources/hbase-default.xml中的hbase.master.info.port值,也可以在hbase-site.xml中配置自定义查看端口。

三、HBase的Shell命令操作

  进入hbase命令行终端,执行命令:hbase shell,退出命令:quit。 官方文档:http://abloz.com/hbase/book.html,一些常用HBase shell命令见下表:

1、创建表,关键字create

   语法:create '表名称', '列族名称1','列族名称2','列族名称N'

   范例:create 'users','user_id','address','info'   --创建表user,该表有三个列族user_id,address,info

2、添加记录,关键字put

   语法:put '表名称', '行名称', '列名称:', '值'

   范例:put 'users','hans','info:age','28'   --在表users中插入数据,行名称为hans,列族info下列age(动态新增)的值设置为28

3、查看记录,关键字get

   语法:get '表名称', '行名称' [,'列名称']

   范例:get 'user','hans','info:age'   --查看表users中行名称为hans,列族info下列age的最新的值

4、查看所有记录,关键字scan

   语法:scan '表名称'

      scan '表名称',{COLUMNS=>'列族名称:列名称'}   --查看表中某个列中的所有数据,COLUMNS必须大写

   范例:scan 'users'   --查看表users下所有的记录

5、删除记录,关键字delete

   语法:delete  '表名称' ,'行名称' , '列名称'   --删除某行的某列记录

      deleteall   '表名称' ,'行名称'   --删除整行记录

   范例:delete 'user','hans','info:age'   --删除表users中行名称为hans的列族info下列age的记录

6、删除表,关键字drop

   语法:disable '表名称'   --删除表之前必须先使其失效

      drop '表名称'   --删除表

7、查看表中的记录总数,关键字count

   语法:count '表名称'

8、查看表结构信息,关键字describe

   语法:describe '表名称'

   注意:结果中列族的versions指定了最多版本数

9、其他操作

   truncate '表名称'   --清空表数据

   exists '表名称'   --是否存在某表

   is_enabled '表名称'   --某表是否可用

   is_disabled '表名称'   --某表是否已失效   

   help 或 help 'create'   -帮助命令

四、使用Java API操作HBase 

1、获得特殊的Configuration,HBase操作的Configuration通过HBaseConfiguration.create()获取,并设置hbase.rootdir和hbase.zookeeper.quorum属性,获取方法如下:

//创建HBase的Configuration
private static Configuration getConfiguration() {
Configuration conf = HBaseConfiguration.create();
//客户端通过zookeeper去访问hbase
conf.set("hbase.zookeeper.quorum", "hadoop0:2181,hadoop1:2181,hadoop2:2181");
return conf;
}

2、创建、删除表使用HBaseAdmin,插入记录、查询记录、遍历记录使用HTable,详细操作实例如下所示:

package com.hicoor.hbase;

import java.io.IOException;
import java.util.List; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan; public class HBaseDemo { public static final String TABLE_NAME = "table1";
public static final String FAMILY_NAME = "family1";
public static final String ROW_KEY = "rowkey1";
public static void main(String[] args) throws Exception {
Configuration conf = getConfiguration(); //创建、删除表使用HBaseAdmin
HBaseAdmin hBaseAdmin = new HBaseAdmin(conf); //创建表
//createTable(hBaseAdmin,conf);
//删除表
//deleteTable(hBaseAdmin); //插入记录、查询记录、遍历记录使用HTable
HTable hTable = new HTable(conf,TABLE_NAME);
//插入记录
//insertRecord(hTable);
//查询数据
//searchRecord(hTable);
//遍历数据
scanRecord(hTable);
//关闭HTable
//hTable.close();
} private static void scanRecord(HTable hTable) throws IOException {
Scan scan = new Scan();
ResultScanner scanner = hTable.getScanner(scan);
//scanner包含所有行 result为一行(同一个行键)
for (Result result : scanner) {
List<Cell> cells = result.listCells();
//result.getRow()获取当前行键
System.out.println("行键:"+new String(result.getRow()));
for(Cell cell:cells){
System.out.println("\t"+cell.toString()+"\t"+new String(CellUtil.cloneValue(cell)));
}
}
} private static void searchRecord(HTable hTable) throws IOException {
Get get = new Get(ROW_KEY.getBytes());
//根据指定列查询
get.addColumn(FAMILY_NAME.getBytes(), "age".getBytes());
Result result = hTable.get(get);
List<Cell> list = result.listCells();
for(Cell c:list){
System.out.println(c.toString()+"\t"+new String(CellUtil.cloneValue(c)));
}
} private static void insertRecord(HTable hTable) throws IOException {
//指定行键
Put put = new Put(ROW_KEY.getBytes());
//指定列族、列名、值 同hbase shell语句:put 'table1','rowkey1','family:age','25'
put.addColumn(FAMILY_NAME.getBytes(), "age".getBytes(), "29".getBytes());
hTable.put(put);
} private static void deleteTable(HBaseAdmin hBaseAdmin) throws IOException {
if(hBaseAdmin.tableExists(TABLE_NAME)){
//删除表之前先让表失效
hBaseAdmin.disableTable(TABLE_NAME);
hBaseAdmin.deleteTable(TABLE_NAME);
}
} private static void createTable(HBaseAdmin hBaseAdmin,Configuration conf) throws Exception {
if(hBaseAdmin.tableExists(TABLE_NAME)){
System.out.println("table exists!");
} else {
HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(TABLE_NAME));
//添加列族
HColumnDescriptor family = new HColumnDescriptor(FAMILY_NAME);
tableDescriptor.addFamily(family);
hBaseAdmin.createTable(tableDescriptor);
System.out.println("table create success");
}
} //创建HBase的Configuration
private static Configuration getConfiguration() {
Configuration conf = HBaseConfiguration.create();
//客户端通过zookeeper去访问hbase
conf.set("hbase.zookeeper.quorum", "hadoop0:2181,hadoop1:2181,hadoop2:2181");
return conf;
} }

3、通过MapReduce批量导入数据到HBase,下述例子表示将手机流量数据上(格式详见《Hadoop学习(4)-- MapReduce》中的“手机流量数据统计分析”示例)传到HBase数据库中,源码如下所示(注意自定义reduder继承TableReducer):

package hbase;

import java.text.SimpleDateFormat;
import java.util.Date; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.mapreduce.TableOutputFormat;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Counter;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat; public class BatchImportHBaseByMR { public static void main(String[] args) throws Exception {
final Configuration configuration = new Configuration();
//设置zookeeper
configuration.set("hbase.zookeeper.quorum", "hadoop0");
//设置hbase表名称
configuration.set(TableOutputFormat.OUTPUT_TABLE, "wlan_log");
//将该值改大,防止hbase超时退出
configuration.set("dfs.socket.timeout", "180000"); final Job job = new Job(configuration, "HBaseBatchImport"); job.setMapperClass(BatchImportMapper.class);
job.setReducerClass(BatchImportReducer.class);
//设置map的输出,不设置reduce的输出类型
job.setMapOutputKeyClass(LongWritable.class);
job.setMapOutputValueClass(Text.class); job.setInputFormatClass(TextInputFormat.class);
//不再设置输出路径,而是设置输出格式类型
job.setOutputFormatClass(TableOutputFormat.class); FileInputFormat.setInputPaths(job, "hdfs://hadoop0:9000/input"); job.waitForCompletion(true);
} static class BatchImportMapper extends Mapper<LongWritable, Text, LongWritable, Text>{
SimpleDateFormat dateformat1 = new SimpleDateFormat("yyyyMMddHHmmss");
Text v2 = new Text(); protected void map(LongWritable key, Text value, Context context) throws java.io.IOException ,InterruptedException {
final String[] splited = value.toString().split("\t");
try {
final Date date = new Date(Long.parseLong(splited[0].trim()));
final String dateFormat = dateformat1.format(date);
String rowKey = splited[1]+":"+dateFormat;
v2.set(rowKey+"\t"+value.toString());
context.write(key, v2);
} catch (NumberFormatException e) {
final Counter counter = context.getCounter("BatchImport", "ErrorFormat");
counter.increment(1L);
System.out.println("出错了"+splited[0]+" "+e.getMessage());
}
};
} static class BatchImportReducer extends TableReducer<LongWritable, Text, NullWritable>{
protected void reduce(LongWritable key, java.lang.Iterable<Text> values, Context context) throws java.io.IOException ,InterruptedException {
for (Text text : values) {
final String[] splited = text.toString().split("\t"); final Put put = new Put(Bytes.toBytes(splited[0]));
put.add(Bytes.toBytes("cf"), Bytes.toBytes("date"), Bytes.toBytes(splited[1]));
//省略其他字段,调用put.add(....)即可
context.write(NullWritable.get(), put);
}
};
} }

  参考:http://www.cnblogs.com/johnnyflute/p/3654426.html?utm_source=tuicool

HBase -- 基于HDFS的开源分布式NoSQL数据库的更多相关文章

  1. Cassandra——类似levelDB的基于p2p架构的分布式NOSQL数据库

    C: Consistency 一致性 • A: Availability 可用性(指的是快速获取数据) • P: Tolerance of network Partition 分区容忍性(分布式) 1 ...

  2. 《滴滴自研分布式 NoSQL 数据库 Fusion 的演进之路》

    SSD:采用闪存: 读的速度很快:写入数据时,因为需要通过加压的方式对存储单元进行电子填充,所以速度略慢:擦除速度最慢,擦除块的时间在ms级.在使用SSD的时,需要考虑到SSD的读写不平衡的特性. 滴 ...

  3. [转载]12款免费与开源的NoSQL数据库介绍

    Naresh Kumar是位软件工程师与热情的博主,对于编程与新事物拥有极大的兴趣,非常乐于与其他开发者和程序员分享技术上的研究成果.近日,Naresh撰文谈到了12款知名的免费.开源NoSQL数据库 ...

  4. 12款免费与开源的NoSQL数据库

    Naresh Kumar是位软件工程师与热情的博主,对于编程与新事物拥有极大的兴趣,非常乐于与其他开发者和程序员分享技术上的研究成果.近日,Naresh撰文谈到了12款知名的免费.开源NoSQL数据库 ...

  5. 全方位认识HBase:一个值得拥有的NoSQL数据库(一)

    前言:说起HBase这门技术,在认知上对于稍微接触或使用过它的人来讲,可能只是百千数据库中一个很普通的库,大概就像我对Redis的认知一样:缓存嘛!可对于HBase,我确实是带着某些感情在的.今日突然 ...

  6. 高性能、高容错、基于内存的开源分布式存储系统Tachyon的简单介绍

    Tachyon是什么? Tachyon是一个高性能.高容错.基于内存的开源分布式存储系统,并具有类Java的文件API.插件式的底层文件系统.兼容Hadoop MapReduce和Apache Spa ...

  7. 腾讯重磅开源分布式NoSQL存储系统DCache

    当你在电商平台秒杀商品或者在社交网络刷热门话题的时候,可以很明显感受到当前网络数据流量的恐怖,几十万商品刚开抢,一秒都不到就售罄:哪个大明星出轨的消息一出现,瞬间阅读与转发次数可以达到上亿.作为终端用 ...

  8. 分布式NoSQL数据库MongoDB初体验-v5.0.5

    概述 定义 MongoDB官网 https://www.mongodb.com/ 社区版最新版本5.0,其中5.2版本很快也要面世了 MongoDB GitHub源码 https://github.c ...

  9. 主流开源分布式图数据库 Benchmark

    本文由美团 NLP 团队高辰.赵登昌撰写 首发于 Nebula Graph 官方论坛:https://discuss.nebula-graph.com.cn/t/topic/1377 1. 前言 近年 ...

随机推荐

  1. Tesla-> Fermi (550Ti) -> Kepler(680) -> Maxwell (750Ti) -> Volta(was Pascal)

    Pascal GPU Pascal (from French mathematician Blaise Pascal) is Maxwell successor. In this news, we l ...

  2. jQuery 找到当前元素之前最后一次出现的某个同辈元素

    DOM 树状图如下所示,要找到 div id = 'a' 的元素之前的(同辈)离该 div 最近的一个 div class = 'a' 的元素(图中左至右第 2 个 div class = 'a' 的 ...

  3. java int转integer方法

    由于AutoBoxing的存在,以下代码在JDK1.5的环境下可以编译通过并运行. int i = 0; Integer wrapperi = i; 还有其他方法? JDK1.5为Integer增加了 ...

  4. eclipse编译异常修正:the project cannot be built until its prerequisite...

    close错误的project,重新open , 再 clean下 .

  5. Stationary point

    https://en.wikipedia.org/wiki/Stationary_point https://zh.wikipedia.org/wiki/驻点

  6. nginx下php频繁卡死502

    解决:[WARNING] fpm_children_bury(), line 215: child 2736 (pool default) exited on signal 15 SIGTERM af ...

  7. NRF51822之GPIOTE介绍

    Note This library is obsolete and should not be used in new designs. Instead, you should use GPIOTE ...

  8. percona

     三.      mysql安装 安装 Percona Server:vi /etc/yum.repos.d/Percona.repo[percona]name = CentOS $releaseve ...

  9. yaf在windows7下32位的安装教程

    首先下载php_yaf.dll文件http://pecl.php.net/package/yaf/2.2.9/windows 打开扩展extension=php_yaf.dll 然后下载工具 http ...

  10. Android笔记:真机调试

    参考:android通过USB使用真机调试程序 手机:华为Y511-U00 1.将手机USB调试模式连接到PC(直接用数据线连接到PC一般可以自动切换) 2.连接成功后提示安装驱动,我选择的是自动安装 ...