Hbase学习总结

1.HBase概述及部分存储知识概述:

架构简介:

简述: Hbase基于hadoop的HDFS系统,其文件存储都是存储在HDFS上的。由上图看到HBase主要处理两种文件:一种是预写日志(Write-Ahead Log,WAL)即图中的HLog。另一种文件是实际的数据文件HFile(HFile本事就是HDFS的文件,文件格式遵循HBase可导入的格式)。预写日志WAL是可开关的,器主要保障数据的稳定性。当机器结点挂掉,可以通过回放日志达到数据恢复的目的。
HBase中最核心的HRegion模块如下图:


简述:上图就是HRegionServer的具体的系统的架构。具体的关系一目了然。各个部件的主要功能如下: HMaster:
HMaster没有单点问题,HBase中可以启动多个HMaster,通过Zookeeper的Master Election机制保证总有一个Master在运行,主要负责Table和Region的管理工作:
(1)管理用户对表的增删改查操作
(2)管理HRegionServer的负载均衡,调整Region分布
(3)Region Split后,负责新Region的分布
(4)在HRegionServer停机后,负责失效HRegionServer上Region迁移

Client:
(1)使用HBase RPC机制与HMaster和HRegionServer进行通信
(2)Client与HMaster进行通信进行管理类操作
(3)Client与HRegionServer进行数据读写类操作

Zookeeper:
(1)Zookeeper Quorum存储-ROOT-表地址、HMaster地址
(2)HRegionServer把自己以Ephedral方式注册到Zookeeper中,HMaster随时感知各个HRegionServer的健康状况
(4)Zookeeper避免HMaster单点问题 ps:HRegionServer管理一些列HRegion对象;每个HRegion对应的Table中一个Region,HRegion有多个HStore组成;每个HStore对应Table中一个Column Family的存储;

2.存储架构:

HBase的存储系统简单描述一下,HBase和BigTable(BigTable的论文如下:http://box.cloud.taobao.com/file/downloadFile.htm?shareLink=1G0bDlGn)一样,都是基于LSM树的系统。
下面简述一下LSM树:
在LSM树系统中:输入数据首先被存储在日志文件,这些文件内的数据完全有序。当有日志文件被修改时,对应的更新会被保存在内存中来加速查询。从宏观上来说,LSM树原理是把一个大树拆分成N棵小树,它首先写入内存中,对着小树越来越大,内存中的小树会flush到磁盘中,磁盘定期可以做merge操作,合并成一棵大树,以优化读性能。
附稿:
BigTable论文中关键信息总结:

2.1 Bigtable是一个分布式的结构化数据存储系统,他被设计用来处理海量数据:通常是分布在数千台普通服务器上的PB级数据。<br>
2.2 列族:列关键字组成的集合叫做“列族”,列族是访问控制的基本单位。存放在同一列族下的所有数据通常都属于同一个类型(我们可以把同一个列族下的数据压缩在一起)。<br>
2.3 列族创建后,其中的任何一个列关键字都可以存放数据。根据我们的设计意图,一张表中的列族不能太多(最多几百个),并且列族在运行期间很少改变。与之对应的,一张表可以由无限多个列。<br>
2.4 时间戳:在BigTable中,表的每个数据项都可以包含同一份数据的不同版本;不同版本的数据通过时间戳来索引。BigTable时间戳的类型是64位整型。BigTable可以给时间戳赋值,用来表示精确到毫秒的“实时”时间;用户程序也可以给时间赋值。如果应用程序需要避免数据版本冲突,那么必须自己生成具有唯一性的时间戳。数据项中,不同版本的数据按照时间戳倒叙排序,及最新的数据排在最前面。<br>
2.5 BigTable支持单行上的事物处理,利用这个功能,用户可以对存在一个行关键字下的数据进行原子性的读-写-更新 操作。虽然BigTable提供了一个允许用户跨行批量写入的数据的接口,但是,BigTable目前还不支持通用的跨行事物处理。<br>
2.6 BigTable是建立在其他的几个Google基础构件上的。BigTable使用Google的分布式文件系统(GFS)存储日志文件和数据文件。<br>
2.7 BigTable内部存储数据的文件是Google SSTable格式的。SSTable是一个持久化的、排序的、不可更改的Map结构,而Map是一个key-value映射数据结构,key和value的值都是任意的Byte串。
2.8 BigTable还依赖一个高可用的,序列化的分布式锁组件,叫做Chubby。(对应apache的zookeeper)。<br>
2.9 Bigtable包括了三个主要的组件:链接到客户程序中的库、一个Master服务器和多个Tablet服务器。针对系统工作负载的变化情况,BigTable可以动态的向集群中添加(或者删除)Tablet服务器。<br>
2.10 Bloom过滤器(又叫:布隆过滤器):它实际上是一个很长的二进制矢量和随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的有点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。<br>

3.WAL:WAL是标准的Hadoop SequenceFile,并且存储了HLogKey实例。这些键包括序列号和实际数据,所以在服务器崩溃时可以回滚还没有持久化的数据。
ps:一旦数据被写入WAL中,数据就会被放到MemStore中。同时还会检查Memstore是否已经满了,如果满了,就会被请求刷写到磁盘中去。
4.Table&Region:(1)Table随着记录增多不断变大,会自动分裂成多份Splits,成为Regions。(2)一个region由[startkey,endkey)表示。(3)不同region会被Master分配给相应的RegionServer进行管理。
5.数据访问结构:
 首先会有zookeeperfile获取-ROOT-结点的地址,-ROOT-是一张特殊的表,并且只会存在一个Region上,记录着.META.表的位置,.META.用户表的region信息,.META.本身可以横跨多个Region。客户请求获取地址信息的过程,就是这样一个树状结构,逐级请求。
6.HBase的一个特殊的功能是,能为一个单元格(一个特定列的值)存储多个版本的数据。这是通过每个版本使用一个时间戳。并且按照降序存储来实现的。每个时间戳是一个长整型值,以毫秒为单位。

2.客户端API:

  1. Hbase客户端简单的CRUD操作:
    (1) put方法,一类作用于单行,另一类作用于多行。 ps:当每一次不指定时间戳调用Put类的add方法时,Put实例会使用来自构造函数的可选的时间戳参数(也称作ts),如果用户在构造Put实例时也没有时间指定时间戳,则时间戳将会有Region服务器设定。
    (2) 原子性的put操作(compare-and-set):

    接口实例:
    boolean checkAndPut(byte[] row,byte[] family,byte[] qualifier,byte[] value,Put put) throws IOException
    

    (3) get方法:
    I.get方法分为两类,一类是一次获取一行数据;另一类是一次获取多行数据。获取数据时可以获取一个或多个列族的数据,也可以指定获取的列族内数据的特定的列。或者指定时间戳范围或者版本数目进行查询。
    II.获取多行数据get方法的方法签名如下:

    Result[] get(List<Get> gets) throws IOException
    

    ps:用户可以一次请求获取多行数据。它允许用户快速高效地从远程服务器获取相关的或完全随机的多行数据。
    (4) exists方法通过RPC验证请求的数据是否存在,但不会从远程服务器返回请求的数据,只返回一个布尔值表示这个结果。

    方法签名如下:
    boolean exists(Get get) throws IOException
    

    ps:用户通过这种方法只能避免网络数据传输的开销,不过需要检查或频繁检查一个比较大的列时,这种方法还是十分实用的。 (5) Delete 方法:
    Delete方法可以删除一列中特定的版本,一列中全部版本,给定版本或更旧的版本,删除整个列族,或者删除列族中所有列的给定版本或更旧的版本。
    另外和Put方法类似,也可以删除一个Delete列表对象:
    接口如下:

    void delete(List<Delete> deletes) throws IOException
    

    (5) 原子性操作 compare-and-delete:
    方法签名如下:

    boolean checkAndDelete(byte[] row,byte[] family,byte[] qualifier,byte[] value,Delete delete) throws IOException
    

    (6) 批量操作:
    上面总结了对Hbase单行的操作,Hbase还提供了批量操作的接口,可以对多行进行操作,并且可以实现多行的Put,Get或Delete操作。
    方法签名如下:

    void batch(List<Row> actions,Object[] results) throws IOException,InterruptedException
    Object[] batch(List<Row> actions) throws IOException,InterruptedException
    

    (7) 数据扫描 Scan:
    顾名思义Scan操作能扫描遍历Hbase数据库中的数据,扫描器的获取通过 HTable.getScanner()获取,此方法返回真正的扫描器实例的同时,用户也可以使用它迭代获取数据,方法签名如下:

    ResultScanner getScanner(Scan scan) throws IOException
    ResultScanner getScanner(byte[] family) throws IOException
    ResultScanner getScanner(byte[] family,byte[] qualifier) throws IOException
    

    同时Scan类拥有以下构造器:

    Scan()
    Scan(byte[] startRow,Filter filter)
    Scan(byte[] startRow)
    Scan(byte[] startRow,byte[] stopRow)
    

    (8) ResultScanner:
    扫描操作一次可能请求大量的行,如果全部返回可能占用大量的内存和带宽,ResultScanner把扫描操作转换为类似get操作,将每一行数据封装成一个Result实例。
    ResultScanner的一些方法如下:

    Result next() throws IOException
    Result[] next(int nbRows) throws IOException
    void close()
    

    2.过滤器:
    Hbase过滤器(filter)提供了非常强大的特性来帮助用户提高其处理表中数据的效率。用户不仅可以使用Hbase中预定义好的过滤器,而且可以实现自定义的过滤器。
    Get和Scan两个类都支持过滤器,理由如下:这类对象不能对行键、列名或列值进行过滤,但是通过过滤器可以达到这个目的。除了Hbase内置的filter外,用户可以实现Filter接口来自定义需求。所有过滤器都在服务器端生效,叫做谓词下推。

(1)比较过滤器 CompareFilter:
方法签名:

    CompareFilter(CompareOp valueCompareOp,WritableByteArrayComparable valueComparator)

用户使用时需要传入操作符,和待比较的值,比如大于或小于多少才从服务器端返回数据。
(2)行过滤器 RowFilter:
方法签名:

    RowFilter(CompareOp valueCompareOp,BinaryComparator rowkey)

可以过滤大于、小于或等于指定行键的数据。
(3)列族过滤器(FamilyFilter):
方法签名和行过滤器类似:

    FamilyFilter(CompareOp valueCompareOp,BinaryComparator rowkey)

用来过滤满足条件的列族。
(4)值过滤器(ValueFilter):
方法签名:

    ValueFilter(CompareOp valueCompareOp,BinaryComparator value)

用来过滤满足条件的列值。
(5)参考过滤器(DependentColumnFilter):
简述:这个是一种特殊的过滤器,这个过滤器的比较传入的参考值不再是指定的常量,而是相对于Hbase自身的某一行做比较。
方法签名如下:

    DependentColumnFilter(byte[] family,byte[] qualifiter)

方法参数分别是参考列的列族和列名,如果某列和该列含有不同的时间戳,则被丢弃掉。
+++ DependentColumnFilter(byte[] family,byte[] qualifiter,boolean dropDependentColumn) 这个方法和上面的方法类似,只不过增加了一个布尔值定义是否包含参考咧。 +++

    DependentColumnFilter(byte[] family,byte[] qualifiter,boolean dropDependentColumn,CompareOp valueCompareOp,WritableByteArrayComparable valueComparator)

这个方法定义了所有列和指定列的值的比较。 这个过滤器从参数名称上不太好理解,可以参考官网的wiki(http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/filter/DependentColumnFilter.html)
(6)单列值过滤器(SingleColumnValueFilter):
简述:用户针对如下情况是可以使用该过滤器:用一列的值决定是否一行数据被过滤。首先设定待检查的列,然后设置待检查的列的对应值。方法签名如下:

    SingleColumnValueFilter(byte[] family,byte[] qualifiter,CompareOp compareOp,byte[] byte[] value)

(7)单列值排除过滤器(SingleColumnValueExcludeFilter):
简述:单列值排除过滤器集成自SingleColumnValueFilter,经过拓展后提供一种略微不同的语意:参考列不被包括在结果中。
(8)前缀过滤器(PrefixFilter):
简述:当构造当前过滤器时传入一个前缀,所有与前缀匹配的行都会被返回客户端。构造函数如下:

    public PrefixFilter(byte[] prefix)

(9)分页过滤器(PageFilter):
简述:用户可以使用这个过滤器对结果按行分页。当用户创建当前过滤器实例时需要指定pagesize参数,这个参数可以控制每页返回的行数。
实例代码如下:

    Filter filter = new PageFilter(15)int totalRows = 0;byte[] lastRow = null;while(true) {Scan scan = new Scan();scan.setFilter(filter);if(lastRow != null) {byte[] startRow = Bytes.add(lastRow,POSTFIX);scan.setStartRow(startRow);}ResultScanner scanner = table.getScanner(scan);int localRows = 0;Result result;while((result = scanner.next()) != null) {totalRows++;lastRow = result.getRow();}scanner.close();if(localRows == 0) {break;}}

ps:Hbase中的行键是按字典序排列的,因此返回的结果也是如此排序的,并且起始行是被包括在结果中的。

(10)行键过滤器(KeyOnlyFilter):
简述:在一些应用中只需要将结果中的KeyValue中的键返回,而不需要返回实际的数据。利用这个过滤器可以实现这个目的。

(11)首次行键过滤器(FirstKeyOnlyFilter):
简述:这个类使用了过滤器框架提供的另一个优化特性:他在检查完第一列之后会通知region服务器结束对当前列的扫描,并跳到下一行,与全表扫描相比,其性能得到了提升。
这种过滤器通常在行数统计(row counter)的应用场景中使用。具体使用参考blog:http://blog.csdn.net/liuxiaochen123/article/details/7878580

(12)包含结束过滤器(InclusiveStopFilter):
简述:扫描操作中的开始行被包含在结果中,但终止行被排除在外。使用这个过滤器是,用户也可以将结束行包含到结果中。

(13)时间戳过滤器(TimeStampsFilter):
简述:当用户需要在扫描结果中对版本进行细粒度的控制时,这个过滤器可以满足需求。用户需要传入一个装载了时间戳list实例。

(14)列计数过滤器(ColumnCountGetFilter):
简述:用户可以使用这个过滤器来限制每行最多取回多少列。当一行列的列数达到设定的最大值时,这个过滤器会停止这个扫描操作,所以它不太适合扫描操作,反而比较适合在get()方法中使用。

(15)列前缀过滤器(ColumnPrefixFilter):
简述:类似于PrefixFilter,这个过滤器通过列名称进行前缀匹配过滤。用户需要指定一个前缀来创建过滤器。所有与设定前缀匹配的列都会包含在结果中。

(16)随机行过滤器(RandomRowFilter):
简述:这个过滤器可以让结果中包含随机的行。构造函数需要传入参数chance,chance取值区间在0.0和1.0之间。其内部实现原理用到了java 的 Random.nextFloat,当chance取负数时,将包含排除所有的行,但取值大于1.0时,将包含所有的行。

(17)跳转过滤器(SkipFilter):
简述:这个过滤器包装了一个用户提供的过滤器,当被包装的过滤器遇到一个需要过滤的KeyValue实例时,用户可以拓展并过滤掉整行数据。换句话说,当过滤器发现某一行中的一列需要过滤时,那么整行数据将被过滤掉。

(18)全匹配过滤器(WhileMatchFilter):
简述:当被过滤的数据中,遇到一行不匹配时,则放弃整个过滤操作。即列匹配才会继续执行,遇到不匹配的列则中断执行(估计也是性能优化点)。

(19)FilterList:
简述:实际应用中,用户可能需要多个过滤器共同限制返回到客户端的结果,FilterList(过滤器列表)提供了这项功能。

(20)自定义过滤器:
简述:最后,用户可能需要按各自的需求实现自定义过滤器。用户可以实现Filter接口或者直接继承FilterBase类,后者已经为接口中所有成员方法提供了默认实现。

ps:过滤器设在在Scan和Get对象当中,具体过滤器的执行全部多事在服务端执行,用以减少服务端和客户端的网络IO。

3.计数器:
Hbase内部提供了计数器,可以用来统计Hbase表的一些信息,例如:用于记录某个列值的个数。

4.客户端管理API:
除了进行数据处理的客户端API,HBase还提供了数据描述的API,类似于传统RDBMS中的DDL和DML。因为生产环境,作为基于Hbase的开发人员基本无法获取HBaseAdmin的管理权限,针对DDL和DML的API就不详细介绍了,这部分内容了解一下就可以了。

3.HBase涉及的技术亮点:

  1. Hbase的一个特殊功能是,能为一个单元格(一个特定列的值)存储多个版本的数据。这是通过每个版本使用一个时间戳。并且按照降序存储来实现的。每个时间戳是一个长整型值,以毫秒为单位。
  2. KeyValue实例代表了一个唯一的数据单元格,类似于一个协调系统,该系统使用行键、列族、列限定符、时间戳指向一个单元格的值。KeyValue是Hbase在存储架构中最低层的类。
  3. 行锁:像put(),delete(),checkAndPut()这样的修改操作是独立执行的,这意味着在一个串行的方式的执行中,对于每一行必须保证行级别的操作是原子性的。region提供了一个行锁(row lock)的特性,这个特性保证了只有一个客户端能获取一行数据相应的锁,同时对改行进行修改。
  4. Hbase作为KeyValue的结构存储,以rowkey的字典序进行排序。
  5. Hbase作为一个列数据库,其也和其他数据库一样,为了提高性能,再起服务端提供了一个连接池HTablePool来管理连接。
  6. 事物:HBase本身也提供了事物的支持,有如下几个项目用来支持HBase的事物:
    (1)事物型HBase:带索引的事物型HBase项目有一些取代默认客户端类和服务端类的扩展类,它们增加了跨行跨表的事物支持。在Region服务器中,更准确的说,每个Region都保持了一个事物的列表。该列表是有beginTransaction()调用初始化,并且相应的commit()调用结束。每次读写操作都有一个事物ID,以保护调用不受其他事物影响。
    (2)用Zookeeper来维持事物:Zookeeper也提供了一个能够被用于实现两阶段提交的事务锁解决方案。
    7.布隆过滤器(Bloom Filter):布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。布隆过滤器的原理是Hash表,其用Hash函数将某个元素映射成位列阵中的某个点,用以高效的识别集合是否包含这个元素。而HBase中也内置了布隆过滤器的功能,例如可以快速的判断某个列簇文件中是否包含某个值。

8.Bulkload数据导入,HBase提供了Bulkload数据导入的方式,可以通过Hbase自带的工具和利用其API自己写MR的方式将HDFS文件转换成HFILE的文件格式,其实其还是HDFS文件,不过文件格式变成了HBase的存储文件,然后通过其load API就能导入到数据库中。
Hfile文件格式如下图:

ps:可见其存储都是keyvalue的形式,还没完全包明白暂时不详细总结了。

HBase内置的用于生成HFILE的API是:org.apache.hadoop.hbase.mapreduce.ImportTsv
具体的调用方式如下:

$HBASE_HOME/bin/hbase org.apache.hadoop.hbase.mapreduce.ImportTsv \-Dimporttsv.bulk.output=$HFILE_PATH \-Dimporttsv.separator=$SEPARATER \-Dimporttsv.columns=$COLUMNS $TABLE_NAME $HDFS_PATH

ps: HFILEPATH:就是生成的Hfile的存储路径。SEPATATER就是数据行的分隔符。COLUMNS就是需要导入的列名,这个比较讲究,这个是根据HDFS的列的数据来定义的,比如写成:HBASEROW_KEY,cf:value1,cf:value2 就表示HDFS的第一列作为ROWKEY,这个HBASEROWKEY是约定好的关键字,这个关键字放在什么位置就表示HDFS的第几列作为rowkey,同时也就要求被转换的HDFS文件必须某一列可以作为rowkey(可能需要预处理)。然后cf就是列族,value就是对应的列名。

《HBase权威指南》学习总结相关推荐

  1. 《HBase权威指南》一导读

    前 言 HBase权威指南 你阅读本书的理由可能有很多.可能是因为听说了Hadoop,并了解到它能够在合理的时间范围内处理PB级的数据,在研读Hadoop的过程中发现了一个处理随机读写的系统,它叫做H ...

  2. 《JS权威指南学习总结--开始简介》

    本书共分成了四大部分: 1.JS语言核心 2.客户端JS 3.JS核心参考 4.客户端JS核心参考 其中 <JS权威指南学习总结--1.1语法核心> 是:第一部分JS语言核心 各章节重点 ...

  3. JavaScript 权威指南-学习笔记(一)

    本文所有教程及源码.软件仅为技术研究.不涉及计算机信息系统功能的删除.修改.增加.干扰,更不会影响计算机信息系统的正常运行.不得将代码用于非法用途,如侵立删! JavaScript 权威指南-学习笔记 ...

  4. Hadoop权威指南学习笔记三

    HDFS简单介绍 声明:本文是本人基于Hadoop权威指南学习的一些个人理解和笔记,仅供学习參考.有什么不到之处还望指出,一起学习一起进步. 转载请注明:http://blog.csdn.net/my ...

  5. HBase权威指南 高清中文版 PDF(来自linuxidc)

    内容提要 <HBase权威指南>由乔治(Lars George)著,探讨了 如何通过使用与HBase高度集成的Hadoop将 HBase的可 伸缩性变得简单:把大型数据集分布到相对廉价的商 ...

  6. Hadoop权威指南学习笔记一

    Hadoop简单介绍 声明:本文是本人基于Hadoop权威指南学习的一些个人理解和笔记,仅供学习參考,有什么不到之处还望指出.一起学习一起进步. 转载请注明:http://blog.csdn.net/ ...

  7. 802.11基本概念介绍【802.11 无线网络权威指南学习总结1】

    802.11基本概念介绍[802.11 无线网络权威指南学习总结1] 1.802.11网络技术介绍 IEEE 802 规格的重心放在 OSI 模型最底下的两层,因为它们同时涵盖了实体(physical ...

  8. mysql权威指南 代码_mysql权威指南学习札记

    mysql权威指南学习笔记 1,mysql的标示符最多就64个字符 2,drop table table1,table2,table3;删除多个table的时候用,号分隔开,为了避免不必要的错误,我们 ...

  9. symfony权威指南学习之一:symfony 介绍

    symfony权威指南学习之一:symfony 介绍 一.symfony 简介        symfony 是一个完整的 web 应用开发框架,它为加速开发提供了几个关键功能. 首先,它把 web ...

  10. Http权威指南学习研究

    学习时间:                                   该学习:第六章  6.6小节   加油   185页 2017年5月15日15:13:00 今天任务: 看完前两章节: ...

最新文章

  1. 【Python】轻松识别文字,这款Python OCR库支持超过80种语言
  2. Python视频处理库:scikit-video
  3. mysql 性能剖析-profiles
  4. Emmet插件:HTML/CSS代码快速编写神器
  5. 来试试读论文的新神器!AMiner发布“论文背景文献”一键生成工具,帮你搞清一篇论文的“来龙去脉”...
  6. sqlite3 外键支持
  7. 除留余数法构造哈希表_哈希表算法原理
  8. 检测到目标url存在框架注入漏洞_HOST注入攻击剖析
  9. [转]快速清除SQL Server日志的两种方法
  10. vps搭建网站服务器,vps如何架设网站服务器
  11. 双线服务器有什么作用,双线服务器什么意思
  12. 移动GIS开发:手机基站定位+离线切片地图(矢量vtpk+栅格tpk)导航安卓APP
  13. DBA01 - 数据库服务概述 构建MySQL服务器、数据库基本管理 MySQL数据类型
  14. 怎样用excel剔除异常数据_如何剔除异常数据?
  15. 高速数据采集卡之FMC子板丨FMC接口AD/DA子卡丨坤驰科技
  16. 2021年育婴员(五级)复审考试及育婴员(五级)实操考试视频
  17. golang gin 服务器部署
  18. Lua语言历险记1.1——最简单最便捷的安装编译环境方式
  19. 中国光无源器件行业市场研究与投资战略报告(2022版)
  20. 留给房产公司抛售房子的时间最多5个月

热门文章

  1. python科学计算基础教程pdf下载-python科学计算 第二版
  2. 大学python笔记整理_python 笔记整理
  3. sql相关日期截取函数
  4. arduino控制雨滴传感器
  5. 一键导出所有微信联系人的小工具,搞私域、搞网销客户交接时可能可以用到,【微信通讯录抽水机】
  6. linux 命令:nohup 详解
  7. Vlfeat (Matlab安装)
  8. cmos和ttl_TTL和CMOS有什么区别?
  9. 学校计算机学院教学管理ER图,学校课程管理ER图
  10. 【能量检测】基于认知无线电的能量检测算法的matlab仿真