HBase笔记整理(一)

[TOC]


HBase笔记整理(一)

行列式数据库

行式数据库:

可以简单的理解为类似传统的rdbmspaint这些数据,存放的数据都是结构化的数据。
行式数据库,是有利于全表数据的扫描,不利于只查询个别字段

列式数据库:

对行式数据库的一个改进,将部分列(或者说有关联的一些列)存放到单独的文件中,其他列存在其它多个文件中,
我们在进行查询的时候,只需要读取出这些常用列即可完成工作,这样,减少了文件IO的读写,提高读写的效率(
不用再想行式数据库进行全表扫描,然后过滤相关字段)

在行式数据库里面,大数据领域有一个非常著名的产品——HBase,其有别于传统的RDBMS,被称之为列式数据库,
或者是NoSQL(Not Only SQL,是一类数据库的统称,常见的有Hbase、Redis、mechache、mongodb。。。。)中的一块数据。

能够满足对hdfs上面海量数据的告诉数据读写。

HBase概述

是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,
利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群。
HBase利用Hadoop HDFS作为其文件存储系统,利用Hadoop MapReduce来处理HBase中的海量数据,
利用Zookeeper作为协调工具。

特点:

高可靠性
高性能
面向列
可伸缩
    表的特点
        纵向扩展
        横向扩展
    部署上来说:
        分布式集群

HBase设计初衷,是为了企业中的大表,面向上百万列,上百亿条记录设计的数据库。
可以分布式存储海量的数据
具有容错能力强,数据高可靠的特点
HBase是一个列式NoSQL数据库
数据存储的结构是按照列进行存储。按照列进行存储的数据库产品,一般都有行键的概念。
使用行键,可以标示一行数据。理解行键的时候,可以简单的认为是RDBMS中的PK。
Hbase存储数据的物理结构是key-value形式。key就是行键。
同时可以非常方便的进行横向扩展(scale out,纵向扩展scale up)。

HBase安装

安装前需要保证hadoop、zookeeper、java已经安装好。

单机版本

解压   ~]$ tar -zxf /home/uplooking/soft/hbase-1.1.5-bin.tar.gz -C /home/uplooking/app
重命名 ~]$ mv /home/uplooking/app/hbase-1.1.5 /home/uplooking/app/hbase
添加至环境变量 export HBASE_HOME=/home/uplooking/app/hbase
配置 $HBASE_HOME/conf/hbase-env.sh、hbase-site.xml
  $HBASE_HOME/conf/hbase-env.sh
    export JAVA_HOME=/opt/jdk
    export HBASE_MANAGES_ZK=false
  $HBASE_HOME/conf/hbase-site.xml
  <property>
    <name>hbase.rootdir</name>
    <value>hdfs://ns1/hbase</value>
  </property>
  <property>
    <name>hbase.cluster.distributed</name>
    <value>true</value>
  </property>
  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>uplooking01,uplooking02,uplooking03</value>
  </property>
启动
  sh $HBASE_HOME/bin/start-hbase.sh
  使用jps命令,当有HMaster、HQuorumPeer(使用hbase自带的zk)、HRegionServer三个进程启动的时候,说明hbase服务已经启动成功
停止
  sh $HBASE_HOME/bin/stop-hbase.sh
单进程启动
  HMaster hbase-daemon.sh start master
  HRegionserver hbase-daemon.sh start regionserver
访问:
  web http://<ip>:16010
  cli bin/hbase shell

分布式安装

在上述的基础之上,只需要再配置一个conf/regionservers,添加两行内容:
uplooking02
uplooking03
注意:
  如果已经配置过单机版,需要将hbase在hdfs上面的目录、以及hbase在zk中的目录清除,以免和集群版本操作冲突
  zk
    rmr /hbase
  hdfs
    hdfs dfs -rm -R /hbase
拷贝master上面的数据到uplooking02和uplooking03
  scp -r app/hbase [email protected]:/home/uplooking/app/
  scp -r app/hbase [email protected]:/home/uplooking/app/
同样在slave01和slave02上面添加相关环境变量
  scp ~/.bash_profile [email protected]:/home/uplooking/
  scp ~/.bash_profile [email protected]:/home/uplooking/
  让其生效
  source ~/.bash_profile
启动hbase集群
  sh $HBASE_HOME/bin/start-hbase.sh
  这个时候在master机器上面,有一个进程HMaster,在uplooking02和uplooking03上面分别有一个HRegionServer

启动HBase出现的问题及解决方案

启动hbase出现如下问题:

Caused by: java.lang.IllegalArgumentException: java.net.UnknownHostException: ns1
    at org.apache.hadoop.security.SecurityUtil.buildTokenService(SecurityUtil.java:373)
    at org.apache.hadoop.hdfs.NameNodeProxies.createNonHAProxy(NameNodeProxies.java:258)
    at org.apache.hadoop.hdfs.NameNodeProxies.createProxy(NameNodeProxies.java:153)
    at org.apache.hadoop.hdfs.DFSClient.<init>(DFSClient.java:602)
    at org.apache.hadoop.hdfs.DFSClient.<init>(DFSClient.java:547)
    at org.apache.hadoop.hdfs.DistributedFileSystem.initialize(DistributedFileSystem.java:139)
    at org.apache.hadoop.fs.FileSystem.createFileSystem(FileSystem.java:2591)
    at org.apache.hadoop.fs.FileSystem.access$200(FileSystem.java:89)
    at org.apache.hadoop.fs.FileSystem$Cache.getInternal(FileSystem.java:2625)
    at org.apache.hadoop.fs.FileSystem$Cache.get(FileSystem.java:2607)
    at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:368)
    at org.apache.hadoop.fs.Path.getFileSystem(Path.java:296)
    at org.apache.hadoop.hbase.util.FSUtils.getRootDir(FSUtils.java:1002)
    at org.apache.hadoop.hbase.regionserver.HRegionServer.<init>(HRegionServer.java:566)
    ... 10 more
Caused by: java.net.UnknownHostException: ns1

解决方案:

第一种方式:
    source一下环境变量文件
第二种方式:
    将hdfs对应的hdfs-site.xml和core-site.xml交给hbase管理

另外需要注意的是,如果原来已经安装了单机版,如果再安装集群版本时,需要把原来相关的数据删除。

HBase体系结构

逻辑结构:

表(table)
      划分数据集合的概念,和传统的db中的表的概念是一样的。

行健(RowKey):
  一行数据的唯一标示,要想操作(read/write)一条数据,必须通过行健,其在hbase底层都是使用字节数组进行存放,所以方便我们使用rk进行排序,
   行键是字节数组, 任何字符串都可以作为行键;表中的行根据行键进行排序,数据按照Row key的字节序(byte order)排序存储;所有对表的访问都要通过行键 (单个RowKey访问,或RowKey范围访问,或全表扫描)。

列族(columnFamily)
   简单的认为是一系列“列”的集合。列族是以单独的文件进行存储。

列限定符(column Qualifier)
   或者叫列。列里面的数据定位通过列限定符 每个CF可以有一个或多个列成员(ColumnQualifier),
   列成员不需要在表定义时给出,新的列族成员可以随后按需、动态加入。时间戳(version)
   在单元格中可以存放多个版本的数据。

单元格(cell)
   Cell 由行键,列族:限定符,时间戳唯一决定,Cell中的数据是没有类型的,全部以字节码形式存贮,主要用来存储数据。

单元格的图示如下:

物理结构:

HMaster ----->NameNode
  管理节点

HRegionServer----->DataNode
  存放Region的服务器

HRegion
  存放hbase中数据的一个概念,可以简单的理解为表,存放一张表中的一部分数据,当该region中的数据超过一定量的时候,会自动进行分裂,
分裂成两个region(一份为二),从这个角度上而言,Region是对hbase中表的一个横向的划分。

HFile
  在hdfs上存放数据之前的一个物理结构,用于接收从客户端提交过来的数据。  

一个集群中有多个HRegionServer
  |-----一个HLog
  |-----多个HRegion
    |---多个Store
      |----一个CF

HBase的物理结构图示如下:

HBase操作

CLI(Command Line interface):

使用bin/hbase shell来进入命令终端
命令:
list查看当前命名空间下的所有的表,也可以查看特定命名空间下的表
  list ‘ns:abc.*‘ --->查看命名空间ns下面的所有的以表名以abc开头的表的列表
创建一张表
  create ‘t1‘, ‘cf1‘ --->在默认的命名空间下创建一张表名为t1,只有一个列族,列族名为cf1
查看一张表的所有内容:scan
  scan ‘t1‘或者scan ‘ns1:t1‘
往表中增加一条记录:put
  put ‘t1‘, ‘1‘(rowkey), ‘cf1:name‘, ‘zhangsan‘
查看其中一个具体的值
  get ‘t1‘, ‘1‘, ‘cf1:name‘
查看表的属性信息:
  describe/desc ‘t1‘
删除记录:delete
  delete ‘t1‘, ‘1‘, ‘cf1:age‘ -->删除某一个rowkey对应的cf1:age对应的单元格
  deleteall ‘t1‘, ‘2‘     -->删除rowkey=2对应的所有的单元格
删除一张表:
  注意:删除表之前,需要先确认表状态是否为disable,如果不是,需要disable ‘表名‘
  disable ‘t1‘
  drop ‘t1‘

练习:

rk column column      cf
  name  grad        course
                math  art |column
1 Tom   5       97    87
2 Jim   4       89    80
创建表
  create ‘stu‘,‘name‘, ‘grad‘,‘course‘ --->创建了表stu,有三个列族,name、grad、course
增加数据:
  put ‘stu‘, ‘1‘, ‘:name‘, ‘Tom‘    直接写成‘name‘也是可以的,也就是说name这个列族下面没有多列
  put ‘stu‘, ‘1‘, ‘:grad‘, ‘5‘
  put ‘stu‘, ‘1‘, ‘course:art‘, ‘97‘
  put ‘stu‘, ‘1‘, ‘course:math‘, ‘88‘
删除name="Jim"的art成绩
  delete ‘stu‘, ‘2‘, ‘name‘, ‘Jim‘, "course:art" --->错误的
  delete ‘stu‘, ‘2‘,"course:art" 因为每次操作,只能操作的是单一单元格,hbase的原子性操作是基于单元格的
  而一个单元格的确定是由rk、cf、col、ts(timestamp)
删除name="JIM"所在的行的而所有单元格
  deleteall ‘stu‘, ‘2‘
查看当前表有多少条记录:select count(1) from t;
  count 

HBase的java API操作

测试代码

package com.uplooking.bigdata.hbase;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.CompareFilter;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * HBase Java API 学习
 */
public class HBaseAPIOps {
    private Connection connection;
    private  Admin admin;
    @Before
    public void setUp() throws Exception {
        Configuration conf = HBaseConfiguration.create();
        connection = ConnectionFactory.createConnection(conf);
        admin = connection.getAdmin();
    }

    /*
        list ‘default:t.*‘
        TABLE
        t1
        t2
     */
    @Test
    public void testList() throws IOException {
        TableName[] tblNames = admin.listTableNames("default:t.*");
        for (TableName tblName : tblNames) {
            System.out.println(tblName.getNamespaceAsString() + ":" + tblName.getNameAsString());
        }
    }

    @Test
    public void testCreate() throws IOException {
        HTableDescriptor desc = new HTableDescriptor(TableName.valueOf("t3"));
        HColumnDescriptor family = new HColumnDescriptor("cf");
        desc.addFamily(family);
        admin.createTable(desc);
    }

    @Test
    public void testAddRecord() throws IOException {
        Table t3 = connection.getTable(TableName.valueOf("t3"));
        byte[] cf = "cf".getBytes();
        byte[] nameBytes = "name".getBytes();
        byte[] ageBytes = "age".getBytes();
        List<Put> puts = new ArrayList<Put>();
        /*Put put1 = new Put("1".getBytes());
        put1.addColumn(cf, nameBytes, "xiaofazeng".getBytes());
        put1.addColumn(cf, ageBytes, "13".getBytes());
        puts.add(put1);
        Put put2 = new Put("2".getBytes());
        put2.addColumn(cf, nameBytes, "xiaoshihao".getBytes());
        put2.addColumn(cf, ageBytes, "15".getBytes());*/
//        puts.add(put2);

        for (int i = 1000; i <= 10000; i++) {
            Put put = new Put((i + "").getBytes());
            put.addColumn(cf, nameBytes, ("xiaohuihui" + i).getBytes());
            put.addColumn(cf, ageBytes, ("" + (i % 99 + 1)).getBytes());
            puts.add(put);
        }
        t3.put(puts);
        t3.close();
    }

    @Test
    public void testGetRecord() throws IOException {
        Table table = connection.getTable(TableName.valueOf("t3"));

        List<Get> gets = Arrays.asList(
                new Get("1".getBytes()),
                new Get("2".getBytes()).addColumn("cf".getBytes(), "name".getBytes())
        );
        Result[] results = table.get(gets);
        for (Result result : results) {
            CellScanner cs = result.cellScanner();
            while(cs.advance()) {
                System.out.println("=======================================================");
                Cell cell = cs.current();
                String value = new String(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
                String cf = new String(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
                String qualifier = new String(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
                String rk = new String(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
                long timestamp = cell.getTimestamp();
                System.out.println(rk + "\t" + cf + ":" + qualifier + "\t" + timestamp + "\t" + value);

                System.out.println("cell.getValueArray() == cell.getFamilyArray()? " + (cell.getValueArray() == cell.getFamilyArray()));
                System.out.println("cell.getValueArray() == cell.getQualifierArray()? " + (cell.getValueArray() == cell.getQualifierArray()));
                System.out.println("cell.getValueArray() == cell.getRowArray()? " + (cell.getValueArray() == cell.getRowArray()));
                System.out.println("------------------------------------------------------");
                int rowOffset = cell.getRowOffset();
                short rowLength = cell.getRowLength();

                int fOffset = cell.getFamilyOffset();
                byte fLength = cell.getFamilyLength();

                int qOffset = cell.getQualifierOffset();
                int qLength = cell.getQualifierLength();

                int vOffset = cell.getValueOffset();
                int vLength = cell.getValueLength();

                byte typeByte = cell.getTypeByte();
                System.out.println("rowOffset: " + rowOffset + ", rowLength: " + rowLength);
                System.out.println("fOffset: " + fOffset + ", fLength: " + fLength);
                System.out.println("qOffset: " + qOffset + ", qLength: " + qLength);
                System.out.println("vOffset: " + vOffset + ", vLength: " + vLength);

                System.out.println("typeByte: " + typeByte);
            }
        }
        table.close();
    }

    @Test
    public void testScan() throws IOException {
        Table table = connection.getTable(TableName.valueOf("t3"));
        Scan scan = new Scan();
        ResultScanner resultScanner = table.getScanner(scan);
       /* for (Result result : resultScanner) {
            String name = new String(result.getValue("cf".getBytes(), "name".getBytes()));
            int age = Integer.valueOf(new String(result.getValue("cf".getBytes(), "age".getBytes())));
            String rowKey = new String(result.getRow());
            System.out.println(rowKey + "\t" + "cf:name-->" + name + ", cf:age-->" + age);
        }*/
        resultScanner.forEach(result -> {
            String name = new String(result.getValue("cf".getBytes(), "name".getBytes()));
            int age = Integer.valueOf(new String(result.getValue("cf".getBytes(), "age".getBytes())));
            String rowKey = new String(result.getRow());
            System.out.println(rowKey + "\t" + "cf:name-->" + name + ", cf:age-->" + age);
        });
        table.close();
    }

    /**
     * 条件查询
     * 其实说白了就是sql中的where条件,给hbase程序添加过滤器
     * @throws IOException
     */
    @Test
    public void testQueryByCondtion() throws IOException {
        Table table = connection.getTable(TableName.valueOf("t3"));
        Scan scan = new Scan();
        Filter filter1 = new SingleColumnValueFilter("cf".getBytes(),
                "age".getBytes(),
                CompareFilter.CompareOp.GREATER_OR_EQUAL,
                "13".getBytes());
        Filter filter2 = new SingleColumnValueFilter("cf".getBytes(),
                "age".getBytes(),
                CompareFilter.CompareOp.LESS_OR_EQUAL,
                "18".getBytes());
        FilterList filterList = new FilterList();
        filterList.addFilter(filter1);
        filterList.addFilter(filter2);
        scan.setFilter(filterList);
        ResultScanner resultScanner = table.getScanner(scan);
        resultScanner.forEach(result -> {
            String name = new String(result.getValue("cf".getBytes(), "name".getBytes()));
            int age = Integer.valueOf(new String(result.getValue("cf".getBytes(), "age".getBytes())));
            String rowKey = new String(result.getRow());
            System.out.println(rowKey + "\t" + "cf:name-->" + name + ", cf:age-->" + age);
        });
        table.close();
    }
    @After
    public void cleanUp() throws IOException {
        admin.close();
        connection.close();
    }
}

HBase相关maven依赖

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <hive-api.version>2.1.0</hive-api.version>
  <hadoop-api.version>2.6.4</hadoop-api.version>
  <hadoop-core.version>1.2.1</hadoop-core.version>
  <hbase-version>1.1.5</hbase-version>
</properties>

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
  </dependency>
  <!-- HBase的maven依赖-->
  <dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>${hbase-version}</version>
  </dependency>
  <dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-server</artifactId>
    <version>${hbase-version}</version>
  </dependency>
  <dependency>
    <groupId>org.apache.hive</groupId>
    <artifactId>hive-hbase-handler</artifactId>
    <version>${hive-api.version}</version>
  </dependency>
</dependencies>
<build>
  <plugins>
    <!-- compiler插件, 设定JDK版本 -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>2.3.2</version>
      <configuration>
        <encoding>UTF-8</encoding>
        <source>1.8</source>
        <target>1.8</target>
        <showWarnings>true</showWarnings>
      </configuration>
    </plugin>
    <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
      <configuration>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
          <manifest>
            <mainClass>com.uplooking.bigdata.hbase.HBase2HDFSOps</mainClass>
          </manifest>
        </archive>
      </configuration>
      <executions>
        <execution>
          <id>make-assembly</id>
          <phase>package</phase>
          <goals>
            <goal>single</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

原文地址:http://blog.51cto.com/xpleaf/2086957

时间: 2024-10-14 19:21:26

HBase笔记整理(一)的相关文章

HBase笔记整理(二)

[TOC] HBase笔记整理(二) 逻辑结构 RowKey第一位 ColumnFamily ColumnQuiauer value(TimeStamps) Cell 物理结构 HMaster ----->NameNode 管理节点,用于管理HBase中的Table和Region的结构操作,比如用户增.删.修改表的操作. 在HBase集群中,可以启动多个HMaster,但是只能有一个HMaster属于Active的状态,通过ZooKeeper和其它standby状态的HMaster进程完成,一个

Hive笔记整理(二)

[TOC] Hive笔记整理(二) Hive中表的分类 managed_table-受控表.管理表.内部表 表中的数据的生命周期/存在与否,受到了表结构的影响,当表结构被删除的,表中的数据随之一并被删除. 默认创建的表就是这种表. 可以在cli中通过desc extended tableName来查看表的详细信息,当然也可以在MySQL中hive的元数据信息表TBLS中查看. external_table-外部表 表中的数据的生命周期/存在与否,不受到了表结构的影响,当表结构被删除的,表中对应数

Hive笔记整理(三)

[TOC] Hive笔记整理(三) Hive的函数 Hive函数分类 函数的定义和java.mysql一样,有三种. UDF(User Definition Function 用户定义函数) 一路输入,一路输出 sin(30°)=1/2 UDAF(User Definition Aggregation Function 聚合函数) 多路输入,一路输出 max min count sum avg等等 UDTF(User Definition Table Function 表函数) 一路输入,多路输

Sqoop笔记整理

[toc] Sqoop笔记整理 概述 SQOOP ---数据搬用工 可以将外部数据迁移到hdfs目录或者hive表或者hbase表 import原理 从传统数据库获取元数据信息(schema.table.field.field type),把导入功能转换为只有Map的Mapreduce作业, 在mapreduce中有很多map,每个map读一片数据,进而并行的完成数据的拷贝. export原理 获取导出表的schema.meta信息,和Hadoop中的字段match:多个map only作业同时

Kafka笔记整理(一)

[TOC] Kafka笔记整理(一) Kafka简介 消息队列(Message Queue) 消息 Message 网络中的两台计算机或者两个通讯设备之间传递的数据.例如说:文本.音乐.视频等内容. 队列 Queue 一种特殊的线性表(数据元素首尾相接),特殊之处在于只允许在首部删除元素和在尾部追加元素.入队.出队. 消息队列 MQ 消息+队列,保存消息的队列.消息的传输过程中的容器:主要提供生产.消费接口供外部调用做数据的存储和获取. MQ分类 MQ主要分为两类:点对点(p2p).发布订阅(P

WPF笔记整理 - Bitmap和BitmapImage

项目中有图片处理的逻辑,因此要用到Bitmap.而WPF加载的一般都是BitmapImage.这里就需要将BitmapImage转成Bitmap 1. 图片的路径要用这样的,假设图片在project下的Images目录,文件名XXImage.png. pack://application:,,,/xxx;component/Images/XXImage.png 2. 代码: Bitmap bmp = null; var image = new BitmapImage(new Uri(this.X

WPF笔记整理--DataBinding(2)

图片绑定时的一个问题.场景如下: 有2个窗口A和B,A窗口的业务逻辑是编辑生成图片.然后从A窗口可以打开B窗口.B窗口是由A生成所有图片的列表.当在A窗口编辑生成图片并保存后打开B窗口就会看到刚刚生成的图片.关闭B窗口,可以在A窗口中继续编辑图片,再次保存图片并打开B窗口,就会看到最新的图片的变化.图片是保存在本地路径. 解决方案:定义一个Converter,将图片读到MemoryStream中,然后再Binding.代码如下: public object Convert(object valu

WPF笔记整理--DataBinding(1)

WPF的数据绑定是一大亮点.如果用WPF而不用数据绑定,那就太失败了. 也不多废话,如果不知道如何绑定,请百度一下.这里简单的提几点: 1. ObservableCollection可用于集合绑定,由于已经实现了INotifyPropertyChanged,可以通过添加删除集合中的元素来实现对UI列表项更新.注意,当一个ObservableCollection已经有元素,通过再次new集合并不能实现清空页面已显示的内容. 如果希望ObservableCollection中某列表项的属性值改变显示

Deep Learning(深度学习)学习笔记整理系列七

Deep Learning(深度学习)学习笔记整理系列 声明: 1)该Deep Learning的学习系列是整理自网上很大牛和机器学习专家所无私奉献的资料的.具体引用的资料请看参考文献.具体的版本声明也参考原文献. 2)本文仅供学术交流,非商用.所以每一部分具体的参考资料并没有详细对应.如果某部分不小心侵犯了大家的利益,还望海涵,并联系博主删除. 3)本人才疏学浅,整理总结的时候难免出错,还望各位前辈不吝指正,谢谢. 4)阅读本文需要机器学习.计算机视觉.神经网络等等基础(如果没有也没关系了,没