hbase简介

1.HBase简介

1.1 定义

HBase 是一种分布式、可扩展、支持海量数据存储的 NoSQL 数据库。

1.2 HBase数据模型

1.2.1逻辑结构

逻辑上,HBase 的数据模型同关系型数据库很类似,数据存储在一张表中,有行有列。但从 HBase 的底层物理存储结构(K-V)来看,HBase 更像是一个 multi-dimensional map


实际上逻辑表中的数据是稀疏的,有些cell没有值    

1.2.2 HBase物理存储结构

以上即为稀疏数据的存储

1.2.3 数据模型

Name Space
命名空间,类似于关系型数据库的 DatabBase 概念,每个命名空间下有多个表。HBase 有两个自带的命名空间,分别是 hbase 和 default,hbase 中存放的是 HBase 内置的表, default 表是用户默认使用的命名空间

Table
类似于关系型数据库的表概念。不同的是,HBase 定义表时只需要声明列族即可,不需 要声明具体的列。这意味着,往 HBase 写入数据时,字段可以动态、按需指定。因此,和关 系型数据库相比,HBase 能够轻松应对字段变更的场景

 Row
HBase 表中的每行数据都由一个 RowKey 和多个 Column(列)组成,数据是按照 RowKey 的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索,所以 RowKey 的设计十分重要

Column
HBase 中的每个列都由 Column Family(列族)和 Column Qualifier(列限定符)进行限 定,例如 info:name,info:age。建表时,只需指明列族,而列限定符无需预先定义 

Time Stamp
用于标识数据的不同版本(version),每条数据写入时,如果不指定时间戳,系统会 自动为其加上该字段,其值为写入 HBase 的时间。

Cell
由{rowkey, column Family:column Qualifier, time Stamp} 唯一确定的单元。cell 中的数 据是没有类型的,全部是字节码形式存贮

 

1.3 HBase 架构

1.3.1 Master

Master 是所有 Region Server 的管理者,其实现类为 HMaster(服务器上有个HMaster进程),主要作用如下: 对于表的操作:create, delete, alter对于 RegionServer 的操作:分配 regions 到每个 RegionServer,监控每个 RegionServer的状态,负载均衡和故障转移。即负责ddl操作

1.3.2 Region Server

Region Server 为 Region 的管理者,其实现类为 HRegionServer(服务器上有个HRegionServer进程),主要作用如下: 对于数据的操作:get, put, delete(负责dml操作);对于 Region 的操作:splitRegion、compactRegion。一个Region Server管理多个Region
 

1.3.3 Zookeeper

HBase 通过 Zookeeper 来做 Master 的高可用、RegionServer 的监控、元数据的入口以及 集群配置的维护等工作。

1.3.4 HDFS

HDFS 为 HBase 提供最终的底层数据存储服务,同时为 HBase 提供高可用的支持

2.HBase入门

2.1安装部署

拉取镜像 docker pull harisekhon/hbase:1.3

指定端口16010
docker run -d --name hbase -p 16010:16010 docker.io/harisekhon/hbase:1.3

ip:16010/master-status,记得安全组开放端口,比如我的:
http://wzlodq.cn:16010/master-status

 2.2 HBase Shell

启动关闭相关
启动hbase bin/start-hbase.sh 查看启动情况,单机版只会有 HMaster 进程
关闭hbasebin/stop-hbase.sh
 
基础命令
进入 HBase 客户端命令行 bin/hbase shell 查看帮助命令 help 查看当前数据库中有哪些表list 

表空间
创建namespace create_namespace 'nametest'  删除namespace drop_namespace 'nametest'  查看namespace describe_namespace 'nametest'   列出所有namespace list_namespace  

在namespace下创建表
create 'nametest:testtable', 'fm1'   查看namespace下的表 list_namespace_tables 'nametest'  
 
表的操作

创建表 , student 为表名,info为列族
create 'student','info'
-- 插入数据到表  1001 为rowkey , info:sex 为列族和列名,male为值
put 'student','1001','info:sex','male'
put 'student','1002','info:sex','female'

扫描查看表数据
scan 'student'
scan 'student',{LIMIT=>10}
scan 'student',{STARTROW => '1001', STOPROW => '1001'}
scan 'student',{STARTROW => '1001'}
scan 'student',{LIMIT => 10,INTERVAL => 10000,CACHE => 10000}. # 一次查询10000行,cache 为10000. https://hbase.apache.org/book.html#_commands

查看表结构
describe `student`

更新指定字段的数据
put 'student','1001','info:name','Nick'
put 'student','1001','info:age','100'

查看“指定行”或“指定列族:列”的数据
get 'student','1001'
get 'student','1001','info:name'
get 'student','1001',{COLUMN=>'info:name',VERSIONS=>1}

统计表数据行数
count 'student',INTERVAL => 10000

删除数据
 删除某 rowkey 的全部数据: deleteall 'student','1001' 删除某 rowkey 的某一列数据:
put 'student','1001','info:sex','male'
put 'student','1001','info:age','18'
delete 'student','1002','info:sex'

清空表数据
truncate 'student' 提示:清空表的操作顺序为先 disable,然后再 truncate  删除表,首先需要先让该表为 disable 状态 disable 'student' 然后才能 drop 这个表: drop 'student'
提示:如果直接 drop 表,会报错:ERROR: Table student is enabled. Disable it first. 

变更表信息
将 info 列族中的数据存放 3 个版本:
alter 'student',{NAME=>'info',VERSIONS=>3} 
get 'student','1001',{COLUMN=>'info:name',VERSIONS=>3}

2.3 Shell Fiter

2.3.1 Fiter 语法介绍

scan命令我们经常会大量使用Filter,hbase shell提供的filter都可以在hbase client包中找到对应的类,它们都是Filter的子类,很多命令都是通过filter来进行实现的 
{}中的语法是ruby的map的语法,FILTER必须大写,filter的参数是根据构造方法来的,也就是相当于java中的new Filter('param1','param2')等,这里只是省略了new参数而已。

scan 'hbase:meta',{FILTER=>"PrefixFilter('123')"}
# 查询hbase:meta表中行键以123开头的所有数据

scan 'student',FILTER=>"PrefixFilter('001')"  
# 扫描前缀为001的行键

当然同样可以使用ruby中new对象的方式,只是那样就必须使用全限定名称。后面会举一个全限定名称的例子

使用show_filters命令查看shell中定义了哪些filter常量,如果想要使用shell中未定义的常量,在使用的时候必须手动import filter的全路径。各类的使用说明: org.apache.hadoop.hbase.filter

DependentColumnFilter
KeyOnlyFilter
ColumnCountGetFilter
SingleColumnValueFilter
PrefixFilter
SingleColumnValueExcludeFilter
FirstKeyOnlyFilter
ColumnRangeFilter
TimestampsFilter
FamilyFilter
QualifierFilter
ColumnPrefixFilter
RowFilter
MultipleColumnPrefixFilter
InclusiveStopFilter
PageFilter
ValueFilter
ColumnPaginationFilter

2.3.2 HBase的filter有四种比较器:

二进制比较器:如’binary:abc’,按字典排序跟’abc’进行比较
二进制前缀比较器:如’binaryprefix:abc’,按字典顺序只跟’abc’比较前3个字符
正则表达式比较器:如’regexstring:ab*yz’,按正则表达式匹配以ab开头,以yz结尾的值。这个比较器只能使用=、!=两个比较运算符。
子串比较器:如’substring:abc123’,匹配以abc123开头的值。这个比较顺也只能使用=、!=两个比较运算符。

2.3.3 比较运算符

HBase的filter中有7个比较运算符:
LESS (<)
LESS_OR_EQUAL (<=)
EQUAL (=)
NOT_EQUAL (!=)
GREATER_OR_EQUAL (>=)
GREATER (>)
NO_OP (no operation)

2.3.4 使用

ruby中正则表达式
 过滤 test 表中 cf a,列为 info,值不为 空的数据 
scan 'test',{LIMIT => 10,FILTER => "SingleColumnValueFilter('a','info',=,'regexstring:\\S')"}

列前缀为  parent的数据
scan 'test',{LIMIT => 10,FILTER => "ColumnPrefixFilter('parent')"}

 

还可以有如下写法

import org.apache.hadoop.hbase.filter.SingleColumnValueFilter
import org.apache.hadoop.hbase.filter.CompareFilter
import org.apache.hadoop.hbase.filter.BinaryComparator
scan ‘tweet0’, { FILTER => SingleColumnValueFilter.new(Bytes.toBytes(‘info’), Bytes.toBytes(‘id’), CompareFilter::CompareOp.valueOf(‘EQUAL’), BinaryComparator.new(Bytes.toBytes(‘1001158684’))), COLUMNS=>[‘info:id’]}

 注意:
1)如果scan中指定了COLUMNS,则FILTER中所使用的列需要包含在所指定的COLUMNS中,否则,filter不起作用。
2)HBase中主要的操作对象是一个个的cell,每个cell都可以有多个版本。如果使用过滤器ValueFilter,就会只有那些符合条件的cell被查出来。跟关系数据库的查询不同,关系数据库查出来的结果中各行都有相同的列。而HBase,查出来的结果中,不同的行会有不同的列。
3)filter不会降低服务方的IO,它会把符合条件的子集传给客户端。即,它是在对查出的结果进行过滤,而不是象原来sql中的where子句。所以,如果要查出的结果中不包含filter需要的列,则filter就不能发挥作用。

 

3.HBase进阶

3.1架构原理

Region
table在行的方向上分隔为多个Region。Region是HBase中分布式存储和负载均衡的最小单元,即不同的region可以分别在不同的Region Server上,但同一个Region是不会拆分到多个server上。Region按大小分隔,表中每一行只能属于一个region。随着数据不断插入表,region不断增大,当region的某个列族达到一个阈值,时就会分成两个新的region。

Store
每一个region有一个或多个store组成,至少是一个store,hbase会把一起访问的数据放在一个store里面,即为每个ColumnFamily建一个store(即有几个ColumnFamily,也就有几个Store)。一个Store由一个memStore和0或多个StoreFile组成。

StoreFile
保存实际数据的物理文件,StoreFile 以 HFile 的形式存储在 HDFS 上。每个 Store 会有一个或多个 StoreFile(HFile),数据在每个 StoreFile 中都是有序的

WAL(Hlog)
由于数据要经 MemStore 排序后才能刷写到 HFile,但把数据保存在内存中会有很高的 概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做 Write-Ahead logfile 的文件 中,然后再写入 MemStore 中。所以在系统出现故障的时候,数据可以通过这个日志文件重 建

MemStore
写缓存,由于 HFile 中的数据要求是有序的,所以数据是先存储在 MemStore 中,排好序后,等到达刷写时机才会刷写到 HFile,每次刷写都会形成一个新的 HFile

个人理解:
store(源码中一个类)理解为对应管理column Family,一个列族对应hdfs文件系统中一个文件夹(和hive中分区类似)
一个store可以多个storefile,其文件格式为hfile(类似hive中orc,parquet)

刷写一次产生一个hfile

zk分担了和客户端和hmaster的交互。即客户端进行读写请求直接查询zk,然后去操作HRegion Server。因此,如果只是读写数据,HMaster挂了也同样可以正常运转,如果是修改元数据则会报错(ddl)
 

3.2、Hlog理解为对应mysql中binlog

3.2 .1 写流程


写流程:
Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server
访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey, 查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问
与目标 Region Server 进行通讯
将数据顺序写入(追加)到 WAL
将数据写入对应的 MemStore,数据会在 MemStore 进行排序
向客户端发送 ack
等达到 MemStore 的刷写时机后,将数据刷写到 HFile

3.3 MemStore Flush

3.3.1 触发条件

HBase会在如下几种情况下触发flush操作,需要注意的是MemStore的最小flush单元是HRegion而不是单个MemStore。可想而知,如果一个HRegion中Memstore过多,每次flush的开销必然会很大,因此我们也建议在进行表设计的时候尽量减少ColumnFamily的个数。

Memstore级别限制:当Region中任意一个MemStore的大小达到了上限(hbase.hregion.memstore.flush.size,默认128MB),会触发Memstore刷新。
Region级别限制:当Region中所有Memstore的大小总和达到了上限(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认 2* 128M = 256M),会触发memstore刷新。
Region Server级别限制:当一个Region Server中所有Memstore的大小总和达到了上限(hbase.regionserver.global.memstore.upperLimit * hbase_heapsize,默认 40%的JVM内存使用量),会触发部分Memstore刷新。Flush顺序是按照Memstore由大到小执行,先Flush Memstore最大的Region,再执行次大的,直至总体Memstore内存使用量低于阈值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,默认 38%的JVM内存使用量)。
当一个Region Server中HLog数量达到上限(可通过参数hbase.regionserver.maxlogs配置)时,系统会选取最早的一个 HLog对应的一个或多个Region进行flush
HBase定期刷新Memstore:默认周期为1小时,确保Memstore不会长时间没有持久化。为避免所有的MemStore在同一时间都进行flush导致的问题,定期的flush操作有20000左右的随机延时。
手动执行flush:用户可以通过shell命令 flush ‘tablename’或者flush ‘region name’分别对一个表或者一个Region进行flush。

3.3.2 Memstore Flush流程

为了减少flush过程对读写的影响,HBase采用了类似于两阶段提交的方式,将整个flush过程分为三个阶段:

prepare阶段:遍历当前Region中的所有Memstore,将Memstore中当前数据集kvset做一个快照snapshot,然后再新建一个新的kvset。后期的所有写入操作都会写入新的kvset中,而整个flush阶段读操作会首先分别遍历kvset和snapshot,如果查找不到再会到HFile中查找。prepare阶段需要加一把updateLock对写请求阻塞,结束之后会释放该锁。因为此阶段没有任何费时操作,因此持锁时间很短。
flush阶段:遍历所有Memstore,将prepare阶段生成的snapshot持久化为临时文件,临时文件会统一放到目录.tmp下。这个过程因为涉及到磁盘IO操作,因此相对比较耗时。
commit阶段:遍历所有的Memstore,将flush阶段生成的临时文件移到指定的ColumnFamily目录下,针对HFile生成对应的storefile和Reader,把storefile添加到HStore的storefiles列表中,最后再清空prepare阶段生成的snapshot。
上述flush流程可以通过日志信息查看:

/******* prepare阶段 ********/
2016-02-04 03:32:41,516 INFO  [MemStoreFlusher.1] regionserver.HRegion: Started memstore flush for sentry_sgroup1_data,{\xD4\x00\x00\x01|\x00\x00\x03\x82\x00\x00\x00?\x06\xDA`\x13\xCAE\xD3C\xA3:_1\xD6\x99:\x88\x7F\xAA_\xD6[L\xF0\x92\xA6\xFB^\xC7\xA4\xC7\xD7\x8Fv\xCAT\xD2\xAF,1452217805884.572ddf0e8cf0b11aee2273a95bd07879., current region memstore size 128.9 M

/******* flush阶段 ********/
2016-02-04 03:32:42,423 INFO  [MemStoreFlusher.1] regionserver.DefaultStoreFlusher: Flushed, sequenceid=1726212642, memsize=128.9 M, hasBloomFilter=true, into tmp file hdfs://hbase1/hbase/data/default/sentry_sgroup1_data/572ddf0e8cf0b11aee2273a95bd07879/.tmp/021a430940244993a9450dccdfdcb91d

/******* commit阶段 ********/
2016-02-04 03:32:42,464 INFO  [MemStoreFlusher.1] regionserver.HStore: Added hdfs://hbase1/hbase/data/default/sentry_sgroup1_data/572ddf0e8cf0b11aee2273a95bd07879/d/021a430940244993a9450dccdfdcb91d, entries=643656, sequenceid=1726212642, filesize=7.1 M

3.3.3 Memstore Flush对业务读写的影响

上文介绍了HBase在什么场景下会触发flush操作以及flush操作的基本流程,想必对于HBase用户来说,最关心的是flush行为会对读写请求造成哪些影响以及如何避免。因为不同触发方式下的flush操作对用户请求影响不尽相同,因此下面会根据flush的不同触发方式分别进行总结,并且会根据影响大小进行归类:

3.3.3.1、影响甚微

正常情况下,大部分Memstore Flush操作都不会对业务读写产生太大影响,比如这几种场景:HBase定期刷新Memstore、手动执行flush操作、触发Memstore级别限制、触发HLog数量限制以及触发Region级别限制等,这几种场景只会阻塞对应Region上的写请求,阻塞时间很短,毫秒级别。

3.3.3.2、影响较大

然而一旦触发Region Server级别限制导致flush,就会对用户请求产生较大的影响。会阻塞所有落在该Region Server上的更新操作,阻塞时间很长,甚至可以达到分钟级别。一般情况下Region Server级别限制很难触发,但在一些极端情况下也不排除有触发的可能,下面分析一种可能触发这种flush操作的场景:

相关JVM配置以及HBase配置:

maxHeap = 71
hbase.regionserver.global.memstore.upperLimit = 0.35
hbase.regionserver.global.memstore.lowerLimit = 0.30

基于上述配置,可以得到触发Region Server级别的总Memstore内存和为24.9G,如下所示:

2015-10-12 13:05:16,232 INFO  [regionserver60020] regionserver.MemStoreFlusher: globalMemStoreLimit=24.9 G, globalMemStoreLimitLowMark=21.3 G, maxHeap=71 G

假设每个Memstore大小为默认128M,在上述配置下如果每个Region有两个Memstore,整个Region Server上运行了100个region,根据计算可得总消耗内存 = 128M * 100 * 2 = 25.6G > 24.9G,很显然,这种情况下就会触发Region Server级别限制,对用户影响相当大。

根据上面的分析,导致触发Region Server级别限制的因素主要有一个Region Server上运行的Region总数,一个是Region上的Store数(即表的ColumnFamily数)。对于前者,根据读写请求量一般建议线上一个Region Server上运行的Region保持在50~80个左右,太小的话会浪费资源,太大的话有可能触发其他异常;对于后者,建议ColumnFamily越少越好,如果从逻辑上确实需要多个ColumnFamily,最好控制在3个以内。

3.4、读流程

Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server
访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey, 查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以 及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问与目标 Region Server 进行通讯
分别在 Block Cache(读缓存),MemStore 和 Store File(HFile)中查询目标数据,并将 查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不 同的类型(Put/Delete)
将从文件中查询到的数据块(Block,HFile 数据存储单元,默认大小为 64KB)缓存到 Block Cache
将合并后的最终结果返回给客户端

3.5 StoreFile Compaction

由于 memstore 每次刷写都会生成一个新的 HFile,且同一个字段的不同版本(timestamp) 和不同类型(Put/Delete)有可能会分布在不同的 HFile 中,因此查询时需要遍历所有的 HFile为了减少 HFill的个数,以及清理掉过期和删除的数据,会进行 StoreFile Compaction

Compaction 分为两种,分别是 Minor Compaction 和 Major Compaction。

1)Minor Compaction会将临近的若干个较小的HFile 合并成一个较大的 HFile,但不会清理过期和删除的数据。

2)Major Compaction 会将一个 Store 下的所有的 HFile 合并成一个大 HFile,并且会清理掉过期 和删除的数据

触发机制:
由hbase.hregion.majorcompaction参数指定,默认为7天(7天触发一次)。此操作比较耗资源,生产环境建议关掉,改手动触发
hbase.hstore.compactionThreshold设置阈值,默认为3,当hfile超过3时(不包括3),自动触发合并

 

3.6 Region Split

默认情况下,每个 Table 起初只有一个 Region,随着数据的不断写入,Region 会自动进 行拆分。刚拆分时,两个子 Region 都位于当前的 Region Server,但处于负载均衡的考虑, HMaster 有可能会将某个 Region 转移给其他的 Region Server。
Region Split 时机:
当 1 个 region 中的某个 Store 下所有 StoreFile 的总大小超过 hbase.hregion.max.filesize(10g), 该 Region 就会进行拆分(0.94 版本之前)。
当 1 个 region 中的某个 Store 下所有 StoreFile 的总大小超过 Min(R^2 * "hbase.hregion.memstore.flush.size",hbase.hregion.max.filesize"),该 Region 就会进行拆分,其 中 R 为当前 RegionServer 中属于该 Table 的region个数(0.94 版本之后)    hbase.hregion.max.filesiz默认为10g;hbase.hregion.memstore.flush.size默认为128

 

4.HBaseAPI

4.1HBaseAPI

4.2 HBase 与 Hive 的对比

Hive
数据仓库:Hive 的本质其实就相当于将 HDFS 中已经存储的文件在 Mysql 中做了一个双射关系,以 方便使用 HQL 去管理查询。
用于数据分析、清洗:Hive 适用于离线的数据分析和清洗,延迟较高。
基于 HDFS、MapReduce:Hive 存储的数据依旧在 DataNode 上,编写的 HQL 语句终将是转换为 MapReduce 代码执 行

HBase
数据库:是一种面向列族存储的非关系型数据库
用于存储结构化和非结构化的数据 适用于单表非关系型数据的存储,不适合做关联查询,类似 JOIN 等操作
基于 HDFS:数据持久化存储的体现形式是 HFile,存放于 DataNode 中,被 ResionServer 以 region 的形 式进行管理
延迟较低,接入在线业务使用:面对大量的企业数据,HBase 可以直线单表大量数据的存储,同时提供了高效的数据访问 速度

5.HBase优化

5.1高可用

在 HBase 中 HMaster 负责监控 HRegionServer 的生命周期,均衡 RegionServer 的负载, 如果 HMaster 挂掉了,那么整个 HBase 集群将陷入不健康的状态,并且此时的工作状态并 不会维持太久。所以 HBase 支持对 HMaster 的高可用配置。只需 conf 目录下创建 backup-masters 文件,写入备用hmaster即可

HMaster和zk进行交互,当HMaster挂了后,backup-Master和zk通信升级成HMaster,如果有多个backup,则谁先到谁成HMaster

5.2 预分区

每一个 region 维护着 StartRow 与 EndRow,如果加入的数据符合某个 Region 维护的 RowKey 范围,则该数据交给这个 Region 维护。那么依照这个原则,我们可以将数据所要 投放的分区提前大致的规划好,以提高 HBase 性能
RowKey 设计,设计rowkey 统一采用规则:concat(substring(md5(biz_key),1,4),'_',biz_key)

5.3 内存优化

HBase 操作过程中需要大量的内存开销,毕竟 Table 是可以缓存在内存中的,一般会分配整个可用内存的 70%给 HBase 的 Java 堆。但是不建议分配非常大的堆内存,因为 GC过 程持续太久会导致 RegionServer 处于长期不可用状态,一般 16~48G 内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死

6 Hbase数据结构&存储结果

6.1 Hbase之LSM

6.1.1 核心思想

传统关系型数据库,一般都选择使用B+树作为索引结构,而在大数据场景下,HBase、Kudu这些存储引擎选择的是LSM树。LSM树,即日志结构合并树(Log-Structured Merge-Tree)。

LSM树主要目标是快速建立索引
B+树是建立索引的通用技术,但如果并发写入压力较大时,B+树需要大量的磁盘随机IO,而严重影响索引创建的速度,在一些写入操作非常频繁的应用场景中,就不太适合了
LSM树通过磁盘的顺序写,来实现最好的写性能

LSM 的主要思想是划分不同等级的结构,换句话来理解,就是LSM中不止一个数据结构,而是存在多种结构
一个结构在内存、其他结构在磁盘(HBase存储结构中,有内存——MemStore、也有磁盘——StoreFile)
内存的结构可以是B树、红黑树、跳表等结构(HBase中是跳表),磁盘中的树就是一颗B+树
C0层保存了最近写入的数据,数据都是有序的,而且可以随机更新、随机查询
C1到CK层的数据都是存在磁盘中,每一层中key都是有序存储的

6.1.2 LSM的数据写入操作

首先将数据写入到WAL(Write Ahead log),写日志是顺序写,效率相对较高(PUT、DELETE都是顺序写)
数据项写入到内存中的C0结构中
只有内存中的C0结构超过一定阈值的时候,将内存中的C0、和C1进行合并。这个过程就是Compaction(合并)
合并后的新的C1顺序写磁盘,替换之前的C1
但C1层达到一定的大小,会继续和下层合并,合并后旧的文件都可以删除,只保留最新的
整个写入的过程只用到了内存结构,Compaction由后台异步完成,不阻塞写入

6.1.3 LSM的数据查询操作

先在内存中查C0层
如果C0层中不存在数据,则查询C1层
不断逐层查询,最早的数据在CK层
C0层因为是在内存中的结构中查询,所以效率较高。因为数据都是分布在不同的层结构中,所以一次查询,可能需要多次跨层次结构查询,所以读取的速度会慢一些。
根据以上,LSM树结构的程序适合于写密集、少量查询的场景

6.2 布隆过滤器

6.2 .1、简介

客户端:这个key存在吗?
服务器:不存在/不知道

本质上,布隆过滤器是一种数据结构,是一种比较巧妙的概率型数据结构。它的特点是高效地插入和查询。但我们要检查一个key是否在某个结构中存在时,通过使用布隆过滤器,我们可以快速了解到「这个key一定不存在或者可能存在」。相比于以前学习过的List、Set、Map这些数据结构,它更加高效、占用的空间也越少,但是它返回的结果是概率性的,是不确切的。

6.2 .2、应用场景

缓存穿透
为了提高访问效率,我们会将一些数据放在Redis缓存中。当进行数据查询时,可以先从缓存中获取数据,无需读取数据库。这样可以有效地提升性能。

在数据查询时,首先要判断缓存中是否有数据,如果有数据,就直接从缓存中获取数据。
但如果没有数据,就需要从数据库中获取数据,然后放入缓存。如果大量访问都无法命中缓存,会造成数据库要扛较大压力,从而导致数据库崩溃。而使用布隆过滤器,当访问不存在的缓存时,可以迅速返回避免缓存或者DB crash。

判断某个数据是否在海量数据中存在,hbase中存储着非常海量数据,要判断某个ROWKEYS、或者某个列是否存在,使用布隆过滤器,可以快速获取某个数据是否存在。但有一定的误判率。但如果某个key不存在,一定是准确的。

6.2 .3、 HashMap的问题

要判断某个元素是否存在其实用HashMap效率是非常高的。HashMap通过把值映射为HashMap的Key,这种方式可以实现O(1)常数级时间复杂度。
但是,如果存储的数据量非常大的时候(例如:上亿的数据),HashMap将会耗费非常大的内存大小。而且也根本无法一次性将海量的数据读进内存。

6.2 .4、理解布隆过滤器

布隆过滤器是一个bit数组或者称为一个bit二进制向量

这个数组中的元素存的要么是0、要么是1

k个hash函数都是彼此独立的,并将每个hash函数计算后的结果对数组的长度m取模,并将对一个的bit设置为1(蓝色单元格)

我们将每个key都按照这种方式设置单元格,就是「布隆过滤器」

根据布隆过滤器查询元素

假设输入一个key,我们使用之前的k个hash函数求哈希,得到k个值
判断这k个值是否都为蓝色,如果有一个不是蓝色,那么这个key一定不存在
如果都有蓝色,那么key是可能存在(布隆过滤器会存在误判)
因为如果输入对象很多,而集合比较小的情况,会导致集合中大多位置都会被描蓝,那么检查某个key时候为蓝色时,刚好某个位置正好被设置为蓝色了,此时,会错误认为该key在集合中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吹老师个人app编程教学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值