Hbase

一、Hbase的特点
1.海量存储:
Hbase适合存储PB级别的海量数据,在PB 级别的数据以及采用廉价PC存储的情况下,能在几十到百毫秒内返回数据。
2.列式存储:
这里的列式存储其实说的是列族存储,Hbase是根据列族来存储数据的,列族下面可以有很多的列,列族在创建表的时候必须指定。
3.极易扩展:
Hbase的扩展性主要体现在两个方面,一个是基于上层处理能力的扩展,一个是基于存储的扩展(HDFS)
通过横向添加RegionServer及其,进行水平扩展,提高Hbase上层处理能力,提升Hbase服务更多的Region的能力。
4.高并发:
由于之前大部分使用Hbase的架构,都是采用廉价的PC,因此单个IO的延迟其实并不小,一般在几十到上百ms之间。这里说的高并发,主要是在并发的情况下,HBase的单个IO延迟下降并不多。能获得高并发、低延迟的服务。
5.稀疏
稀疏主要是针对Hbase列的灵活性,在列族中,你可以指定任意多的列,在列数据为空的情况下,是不会占用存储空间的。
6.Hbase支持随机写
Hbase的读写操作还是借助HDFS完成,要完成随机写,根本上还是要符合HDFS的特性!
HDFS只支持追加写,随机的操作:Update+Delete 借助 追加写+时间戳(版本号)。只允许客户端查询时返回时间戳最新的数据!
7.Hbase支持海量数据的实时读写
①分布式
②索引,LSM树
③kv
④吃内存
⑤列式存储
⑥布隆过滤器
8.Hbase是基于hadoop的数据库,它提供一个十亿级别*百万列级别的表存储,对表中的数据提供实时的随机读写操作!
9.Hbase的安装和配置
①保证已经配置了JAVA_HOME,HADOOP_HOME环境变量
②编辑conf/env.sh
128行: export HBASE_MANAGES_ZK=false
注释掉46,47行!
③配置conf/hbase-site.xml

<property>
            <name>hbase.rootdir</name>
            <value>hdfs://hadoop101:9000/HBase</value>
    </property>
    <property>
            <name>hbase.cluster.distributed</name>
            <value>true</value>
    </property>
    <property>
            <name>hbase.zookeeper.quorum</name>
         <value>hadoop102:2181,hadoop103:2181,hadoop101:2181</value>
    </property>
    <property>
            <name>hbase.zookeeper.property.dataDir</name>
         <value>/opt/module/zookeeper-3.4.10/datas</value>
    </property>

10.Hbase的启动和关闭
①启动hdfs,zookeeper
②启动hbase:
三台机器都启动regionserver:xcall.sh /opt/module/hbase/bin/hbase-daemon.sh start regionserver
选择一台启动master;opt/module/hbase/bin/hbase-daemon.sh start master
③查看的方法:
jps查看
访问web界面:访问master进程所在机器:16010
16000是master进程的RPC端口!
16010是master进程的http端口!
16020是RegionServer进程的RPC端口!
16030是RegionServer进程的http端口!
④停止:三台机器都停止regionserver: xcall /opt/module/hbase/bin/hbase-daemon.sh stop regionserver
选择一台停止master: /opt/module/hbase/bin/hbase-daemon.sh stop master
⑤群起和群停
hadoop-daemons.sh 先读取 HADOOP_HOME/etc/hadoop/slaves 中所有的主机名!
对这些主机,执行hadoop-daemon.sh start datanode,再启动NN!
hbase-daemons.sh 先读取 conf/regionservers 中所有的主机名!
注意: hbase-daemons.sh或start-hbase.sh或stop-hbase.sh的前提是先配置
要执行这些命令所在的机器的conf/regionservers文件!

二、Hbase的表现

1.默认有两张系统表
Hbase:meta:保存的是用户的表和region的对应信息
Hbase:namespace:保存的是用户自己创建的namespace的信息
2.Hbase中的对象的表现形式
库以目录的形式存放在/Hbase/data中
表是以子目录的形式存在/Hbase/data/库名 中
region也是以子目录的形式存在 /Hbase/data/库名/表名/region 中
列族也是以子目录的形式存放在 /Hbase/data/库名/表名/region/列族 目录中
3.Hbase shell的使用
①在Hbase shell中不要敲;,如果敲了;,需要敲两个单引号结束!
②在Hbase shell中如果需要使用上下方向键查找历史命令,需要查看xshell的设置配置图!
③查看帮助
help:查看所有命令的帮助
help ‘命令’:查看某个命令的帮助
help ‘组名’:查看某组命令的帮助
Hbase shell使用ruby编写,不支持中文!

三、Hbase客户端的操作

1.启动客户端:hbase shell
2.查询帮助
help : 查看当前hbase客户端所有命令的帮助
help ‘命令’: 查看某个具体命令的帮助
help ‘组名’: 查看某个组命令的帮助
exit:退出客户端
3.库操作
list_namespaces:查看所有的库
create_namespaces 库名:创建库
delete_namespaces 库名:删除库,只能删除空库
4.表操作
create 表名 列族名:创建表时至少指定一个列族
delete 表名:删除表
truncate 表名:清空表
alter 表名 列族名:修改表
describe 表名:查看表的描述

is_enable 表名:判断表是否启用
is_disable 表名:判断表是否禁用
enable 表名:启动表
disable 表名:禁用表
只有表启用后,才能向表中插入数据!

5.数据操作
put 表名 列名(列族:列名) rowkey value [ts] :新增或者修改
delete 表名 rowkey [列族|列名] [ts] :删除数据
get 表名 rowkey:查询单行
scan 表名 {STARTROW=>XXX,STOPROW=>XXX,LIMIT=>X}:查询多行

四、Hbase的原理

1.进程组成
①master(1个):负责对表的增删改查,master负责分配region到regionserver,自动监控rs状态;在一个集群中,工作状态的master只能有一个,可以有多个备用的!
②regionserver:负责接受客户端的请求,对数据进行读写操作,负责对region进行处理,例如region的切分和合并!
③Zookeeper:hbase需要依赖Zookeeper保存一些元数据信息!
2.regionserver的架构
regionserver负责接受客户端的请求,对数据进行读写操作!
一个region由一个regionserver负责!一个regionserver可以负责多个region的请求!
regionserver中有如下的组件:
①一个regionserver有一个WAL对象,这个WAL对象负责当前regionserver的的预写日志!
预写日志主要是为了防止memstore中的数据没有及时刷写到磁盘时,丢失,如果丢失可以使用预写日志恢复!
②每个region都有多个列族,每个列族会创建一个store对象,一个store对象会维持一个memstore(写缓冲区)存储数据,并且这个列族的目录中有多少个文件,就会创建多少个storefile对象,对应文件!
③blockcache(读缓存):客户端经常查询的数据,会放到blockcache中,如果blockcache中有对应的数据,就不会再扫描storefile!
3.写流程
找插入数据对应的rs:
①请求Zookeeper,查询/hbase/meta-region-server节点,获取meta表所在的rs
②向meta表所在的rs发送读请求,将读取到的内容缓存到客户端本地,此后就不需要频繁查询meta表
③从meta表中,根据region和regionserver的对应关系,找到rowkey所属的Regina的regionserver
写入数据:
④向regionserver发put请求,regionserver会使用WAL对象记录写请求,将写的数据存储在memstore中
⑤响应客户端,写操作完成

写入操作的微观流程:
①尝试尽可能获取多个锁,至少需要获取一把
②如果当前数据没有设置时间戳,更新时间戳
③构建WAL对象
④写入WAL对象的buffer中,但是并不sync到磁盘
⑤写入memstore
⑥将WAL对象的buffer中的数据sync到磁盘
⑦滚动MVCC版本号,滚动后,客户端就可以查询到数据
⑧如果在整个写入过程中发生异常,此时,会将已经写入到memstore中的数据回滚!

五、读流程

找读取数据对应的rs:
①请求zookeeper,查询 /hbase/meta-region-server 节点,获取meta表所在的rs
②向meta表所在的rs发送读请求,讲读取到的内容缓存到客户端本地,此后就不需要频繁查询
meta表
③从meta表中,根据region和regionserver的对应关系,找到rowkey所属的region的regionserver

读取数据:
读取的数据存储在列族(store)中!列族在HDFS上就是一个目录,这个目录下存储了很多文件(storefile)
数据如果是刚写入到store中,还没有刷写到磁盘,当前数据就存储在memstore中,
有可能这个列的历史版本的数据已经刷写到磁盘存在storefile中,
在扫描时,需要既扫memstore,又扫磁盘上的storefile,扫描出当前列的所有版本的数据,从这些
数据中挑选出ts最大的返回!
如果扫描历史版本的数据,是扫storefile,那么会发送磁盘IO,效率低,因此可以把扫描到的数据
所在的块(block)缓存到内存中,在内存中保存缓存块的区域,称为blockcache!
在以后的查询中,如果查询的数据在blockcache中有,那么就不需要再扫描storefile了!如果没有,
再扫描storefile,讲数据所在的block缓存到blockcache!
Blockcache在RS中的读缓存,blockcache默认大小为当前RS所在堆缓存的40%,有LRU的回收策略!
block不是HDFS上中的block,是HFile中的block(64k)!

get t1,r1 :
扫描r1所在region的所有列族的memstore,从中找r1行的所有列的每个版本的最近数据
扫描r1所在region的所有列族的storefile,从中找r1行的所有列的每个版本的历史数据
讲最近的数据和历史数据,汇总,挑选每个列最新的数据!
讲刚刚扫描storefile数据所在的block,缓存到blockcache中

put t1,r1,cf1:name,jack: 当对数据作了修改时,此时blockcache中缓存就失效了!

scan t1 ,{STARTROW=>r1,STOPROW=>r4}: 扫描r1到r3行
扫描r1所在region的所有列族的memstore,从中找r1-r3行的所有列的每个版本的最近数据
扫描r1所在region的所有列族的storefile,从中找r2-r3行的所有列的每个版本的历史数据
从blockcache中扫描r1行所有的数据
将刚刚扫描storefile数据所在的block,缓存到blockcache中

六、VERSIONS

  1. 每个不同时间戳的cell就是一个版本,时间戳就是版本

  2. 可以设置列族的VERSIONS属性,当执行flush操作时,put的记录
    会根据时间戳选择最新的VERSIONS个版本的数据flush到磁盘中!

     每次flush,最多flush VERSIONS个版本的数据!
    

七、flush

1.将memstore中的数据,刷写到磁盘,生成storefile的过程称为flush!
flush的目的是在memstore中对数据的rowkey进行排序,排序后刷写到磁盘上的数据时有序的,方便检索!
2.实现flush:
hbase shell: flush ‘表名’ | ‘region名’
3.自动flush
①从容量上来说
a) 如果单个memstore使用的容量超过hbase.hregion.memstore.flush.size(默认128M),整个region的memstore都会刷写!
在刷写时,客户端依然可以向rs发起请求,写的数据依然是存储在memstore中,但是mememstore使用的容量超过hbase.hregion.memstore.flush.size(默认128M)hbase.hregion.memstore.block.multiplier(默认值是4),此时memstore会自动block(阻塞),客户端无法再执行写入!
b) 整个rs所有的memstore使用的容量总和超过java_heapsize
hbase.regionserver.global.memstore.size(默认值0.4)*hbase.regionserver.global.memstore.size.lower.limit(默认值0.95)
整个rs所有的memstore会依次按照大小顺序刷写。
在刷写时,如果rs所有的memstore使用的容量总和超过java_heapsize
*hbase.regionserver.global.memstore.size(默认值0.4),此时所有的memstore也会block
②从时间上来说
每间隔hbase.regionserver.optionalcacheflushinterval(1h),自动flush
③从WAL正在写入的日志数量上来说
如果有大量的正在使用的WAL日志,说明memstore中有大量尚未刷写的数据,一旦数据过多,
rs进程崩溃,此时恢复数据时间过长。所以,一旦正在使用的WAL日志文件的数量超过
hbase.regionserver.max.logs(32),此时,会根据WAL中记录的日志的先后顺序依次刷写memstore!

八、compact

目的:对store中的多个hfile文件定期合并,消除每次flush产生的大量的小文件。
对hfile中无效的过期的数据,进行合并整理,较少数据量!
分类: minor_compact: 将临近的多个小文件,合并为一个大文件。不会删除delete类型的数据(0.94之前)!
major_compact: 将store目录中所有的文件,合并为一个大文件,会删除delete类型的数据!
minor_compact和major_compact的区别是minor_compact只能合并有限数量的hfile!
major_compact是合并目录下所有的文件!
对于major_compact建议取消自动合并的设置,改为在集群空闲时,手动执行合并!

九、region的切分

每张表在创建时,只有一个region.随着这个region中写入数据越来越多,此时,region
会自动完成切分,切分后的region有可能出于负载均衡的目的,会分配给其他的rs负责!
自动切分:
①统计当前regionserver中负责的 当前表的region个数,称为tableRegionCount
②0=tableRegionCount 或 tableRegionCount>100,此时某个region超过hbase.hregion.max.filesize(10G)时切分(一分为二)!
③0<tableRegionCount<=100,此时使用
initialSize * tableRegionsCount * tableRegionsCount * tableRegionsCount
和 hbase.hregion.max.filesize(10G)进行对比取较小值
④initialSize,取决于用户的配置,由hbase.increasing.policy.initial.size自定义!
如果没有自定义通常为 2*hbase.hregion.memstore.flush.size,为 256M

十、API

1.常用的API
Connection:代表客户群和集群的一次连接,Connection的创建时重量级的,是线程安全的!
因此可以在多个线程中共享,建议一个应用只创建一个Connection!
Admin:对hbase执行管理性命令的客户端对象!
对库的创建,查看,删除等
对表 的创建,查询,删除等
Admin 的创建是轻量级的,不是线程安全的,建议每个线程都有自己的Admin对象,Connection.getAdmin()
Table:对表中的数据执行增删改查!
Table的创建是轻量级的,建议每个线程都有自己的Table对象!Connection.getTable();
TableName:代表表的名称
HTableDescriptor: 代表表的定义和描述,在这个对象中可以对表中的列族进行设置!
HColumnDescriptor:代表列族的定义和描述!
NameSpaceDescriptor:名称空间的定义和描述!
Put:对单行数据执行put操作的对象!在put中可以定义每次单行插入的数据!
Get:对单行数据查询操作的对象,在get中可以定义每次查询的参数!
Result:单行查询返回的结果集!在result中包含若干个cell
工具类:CellUtil.cloneXxx():将cell中的某个数据克隆为byte[]
Bytes.toXxx():从byte[]转化为常用的基本类型
Bytes.toByte[]:将常用的基本类型转化为byte[]

十一、MR和Hbase的集成

1.Hbase可以做简单的查询,但是无法对查询的结果进行深加工!
可以使用MR来进行Hbase中数据的深加工
2…MR必须持有可以读取Hbase数据的API才可以!
在MR启动时,在MR程序的类路径下,把读取Hbase的jar包加入进去!
①使用MR读取hbase,需要哪些jar包?
通过执行hbase mapredcp查看
②如何让MR运行时,提前将这些jar包加入到MR环境中?
hadoop jar MRjar包 主类名 参数
hadoop命令一执行,先去读取hadoop-config.sh(hadoop环境的配置脚本,用来配置环境变量),hadoop-config.sh读取hadoop-env.sh(建议将hadoop运行的变量配置在此脚本中)
在hadoop-env.sh 44行后,添加:
export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:/opt/module/hbase/lib/*
3.测试官方案例
①hadoop jar hbase-server-1.3.1jar CellCounter t1 /hbasemr/cellcount,
②hadoop jar hbase-server-1.3.1.jar rowcounter t1
③向hbase中导入数据,需要手动建表,需要把数据上传到HDFS,注意数据中字段的顺序要和-Dimporttsv.columnns的顺序一致
hadoop jar hbase-server-1.3.1.jar importtsv
-Dimporttsv.columns=HBASE_ROW_KEY,info:name,info:age,info:gender t2 /hbaseimport
HBASE_ROW_KEY: 代表rowkey列
在示例程序中添加参数,在core-site.xml中添加!

十二、Hive和hbase的集成

Hbase负责存储,Hive负责分析!
hive的本质是使用HQL语句,翻译为MR程序,进行计算和分析!
①环境配置
让Hive持有可以读写hbase的jar包,
在HIVE_HOME/lib/下,将操作hbase的jar包以软连接的形式,持有!
②在hive中建表,这个表需要和hbase中的数据进行映射,只能创建external non-native table
a)数据已经在hbase中,只需要在hive中建表,查询即可
create external table hbase_t3(
id int,
age int,
gender string,
name string
)
STORED BY ‘org.apache.hadoop.hive.hbase.HbaseStoreHander’
WITH SERDEPROPERTIES(“hbase.columns.mapping”=":key,info:age,info:gender,info:name")
TBLPROPERTIES(“hbase.table.name”=“t3”);
b)数据还尚未插入到hbase,可以在hive中建表,建表后,在hive中执行数据的导入
将数据导入到hbase,正在分析,表必须是managed non-native table!
①建表
CREATE TABLE ‘hbase_emp’(
‘empno’ int,
‘ename’ string,
‘job’ string,
‘mgr’ int,
‘hiredate’ string,
‘sal’ double,
‘comm’ double,
‘deptno’ int)
STORED BY ‘org.apache.hadoop.hive.hbase.HBaseStorageHandler’
WITH SERDEPROPERTIES (“hbase.columns.mapping” = “:key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:comm,info:deptno”)
TBLPROPERTIES (“hbase.table.name” = “emp”);
②替换hive-hbase-handler。jar
③使用insert向表中导入数据
insert into table hbase_emp select *from emp
④注意事项
a)在建表时,hive中的字段的类型要和hbase中的表列的类型一致,以避免类型转换失败,造成数据丢失
b)row format的作用是指定表在读取数据时,使用什么分隔符来切割数据,只有正确的切割符,才能切割正确的字段!
c)管理表:managed_table
由hive的表管理数据的生命周期!在hive中,执行droptable时,不仅将hive表中的元数据删除,还把表目录中的数据删除!
外部表:external_table
hive表不负责数据的生命周期,在hive中,执行droptable时,
只会将hive表中的元数据删除,不会把表目录中的数据删除

十三、hive集成hbase理论

1.Storage Handlers
Storage Handlers是一个扩展模块,帮助hive分析不在hdfs存储的数据!
例如数据存储在hbase上,可以使用hive提供的对hbase的Store Handlers, 来读取hbase中的数据!
native table:本地表 hive无需通过Storage Handlers就能访问的表。
non-native table:hive必须通过Storage Handlers才能访问的表!
2. 在建表时
创建native表:

[ROW FORMAT row_format] [STORED AS file_format]
file_format: ORC|TEXTFILE|SEQUNCEFILE|PARQUET

都是hive中支持的文件格式,由hive负责数据的读写!
或创建non-native表:
STORED BY ‘storage.handler.class.name’ [WITH SERDEPROPERTIES (…)]
数据在外部存储,hive通过Storage Handlers来读写数据!
3.SERDE:序列化器和反序列化器
表中的数据是什么样的格式,就必须使用什么样的SerDe!
纯文本:使用 row format delimited,默认使用LazySimpleSerDe
JSON格式:使用 JsonSerDe
ORC:使用读取ORC的SerDe
Paquet:使用读取PaqueSerDe
普通文件数据,以及在建表时,如果不指定SerDe,默认使用LazySimpleSerDe!
LazySimpleSerDe只能处理有分隔符的普通文本!
当数据是JSON格式时,只能使用JSONSerDe
create table testSerDe(
name string,
friends string,
friends array
)
ROW FORMAT SERDE
‘org.apache.hive.hcatalog.data.JasonSerDe’
STORED AS TEXTFILE

十四、Hbase的高可用

regionserver由master负责高可用,一个regionserver挂掉,它负责的region会自动分配给其它的regionserver!
需要配置的是master的高可用,需要在conf/backup-masters,列出备用的master!

十五、预分区

1.目的
通常情况下,每次建表时,默认只有一个region,随着这个region的数据不断增多,region会自动切分!
自动切分:将当前region中的所有rowkey进行排序,排序后取start-key和stop可以中间的rowkey,由这个rowkey一分为二,一分为二后,生成两个region,新增的region,新增的region会交给其它的regionserver负责,目的是为了达到负载均衡,但是通常会适得其反!
为避免某些热点region同时分配到一个regionserver,可以在建表时,自己提前根据数据的特征规划region!
2.注意:
如果使用的是HexStringSplit算法,随机生成region的边界!
在插入一条数据时,rowkey必须先采用HexString,转化为16进制,再插入到表中!

十六、Rowkey的设计

1.使用字符串拼接设计Rowkey
原则:
①rowkey作为数据的唯一主键,需要紧密和业务相关,从业务中选择某个代表性的字段作为rowkey
②保证rowkey字段选取时的唯一性,不重复性
③rowkey足够散列,负载均衡
④让有业务关联的rowkey尽量分布到一个region中

例如: 转账的场景

流水号 转入账户 转出账户 时间 金额 用户

流水号适合作为rowkey,讲流水号再拼接字符串,生成完整的rowkey!
格式: 流水号+时间戳 时间戳+流水号
流水号+随机数 随机数+流水号
如果流水号在设计时,足够散列,可以使用流水号在前,拼接随机数!
如果流水号不够散列,可以使用函数计算其散列值,或拼接一个散列的值!
举例:如何让一个月的数据,分布到同一个Region!可以取月份的时间,作为计算的参数,使用hash运算,讲运算后的字符串,拼接到rowkey前部!
2.内存的分配
在conf/hbase-env.sh 中,编写regionserver进程启动时的JVM参数!
-Xms : JVM堆的起始值
-Xmx : JVM堆的最大值

export HBASE_HEAPSIZE=1G 或
		export HBASE_OPTS="-XX:+UseConcMarkSweepGC"

十七、布隆过滤器

  1. 布隆是个人,发明了布隆算法,基于布隆算法实现的组件,称为布隆过滤器!
    这个组件一般是用作过滤!

     过滤功能:  在海量数据中,用非常高的效率和性能,判断一个数据是否在集合中存在!
     
     作用: 布隆过滤器只能判断一个数据要么一定在集合中不存在,要么在集合中可能存在!
     
     误判:  布隆过滤器判断数据可能存在,实际扫描后,发现不存在,这种情况有存在的几率!
     		
     		
     布隆过滤器是可以提升读的性能!存在误判率!
    
  2. HBase中如何设置
    HBase中通过列族设置过滤器。

     HBase支持两种布隆过滤器:  ROW|ROWCOL
     
     ROW: 布隆过滤器在计算时,使用每行的rowkey作为参数,进行判断!
     
     举例:   		info				info1
     
     info storefile1: (r1,info:age,20) ,(r2,info:age,20) 
     info1 storefile2: (r3,info1:age,20) ,(r4,info1:age,20) 
     
     查询r1时,如果命中,判断storefile2中一定没有r1的数据,在storefile1中可能有!
     
     
     ROWCOL: 布隆过滤器在计算时,使用每行的rowkey和column一起作为参数,进行判断!
     
     举例:   		info				info1
     
     info storefile1: (r1,info:age,20) ,(r2,info:age,20) 
     info1 storefile2: (r3,info1:age,20) ,(r4,info1:age,20) 
     
     查询rowkey=r1,只查info:age=20 列时,如果命中,判断storefile2中一定没有此数据,
     在storefile1中可能有!
     
     
     注意: 旧版本,只有get操作,才会用到布隆过滤器,scan用不到!
     		1.x之后,scan也可用用布隆过滤器,稍微起点作用!
     		
     		启用布隆过滤器后,会占用额外的内存,布隆过滤器通常是在blockcache和memstore中!
     		
     举例: 执行  get 't1','r1'
     
     	①扫描r1所在region的所有列族的memstore,扫memstore时,先通过布隆过滤器判断r1是否
     	存在,如果不存在,就不扫!可能存在,再扫描!
     	
     	②扫描Storefile时,如果storefile中,r1所在的block已经缓存在blockcache中,直接扫blockcache
     	在扫描blockcache时,先使用布隆过滤器判断r1是否存在,如果不存在,就不扫!可能存在,再扫描!			
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值