IOT数据采集表
采集IOT设备每秒钟数据,存入hbase,rowkey为时间戳+设备名,按时间进行预分区;
其中,一张IOT表里,有10个设备每秒钟采集6个自己区域的监测值,一天的数据为10*(60*60*24) = 844000
条数据
列为:每个设备的监测区域ID(如AacW6cTXG20、AacW6cTXG21、AacW6cTXG23…)+ 设备ID,一共10*6+1=61列。
如果,要通过Pheonix查询,需要在pheonix中建立相应映射表。直接查habse中的表,pheonix会显示表不存在。
IOT状态记录表:
CREATE TABLE IF NOT EXISTS TEST_IOT_STATE (
TIME VARCHAR not null,
DEVICEID VARCHAR not null,
PERMISSION VARCHAR ,
ONLINE INTEGER,
OFFLINE INTEGER,
ERROR INTEGER,
STANDBY INTEGER,
CONSTRAINT pk PRIMARY KEY (TIME,DEVICEID)) COMPRESSION = 'GZ', SALT_BUCKETS = 6;
在开始阶段,数据量较小,数据读写没有问题,
进行rowkey散列设计,与预分区策略设计
设计目的:
rowkey散列设计
思路:01-设备ID-时间,
将各个设备采集的数据均匀保存到不同分区,
String iotID ;
long time = System.currentTimeMillis();
int region=Math.abs((iotID).hashCode())%10;
String rowkey = "0" + region+ '.'+ time + "." + iot ID;
建表 预分区:
create 'test03','fdata',SPLITS => ['00|','01|','02|','03|','04|','05|','06|','07|','08|']
由于通过rowkey设计与预分区后,IOT表的rowkey,在时间戳+设备ID前多加了具体的分区后,查询发生变化
每次查询时,可根据分区策略,反推出分区号,在组合形成新的rowkey,在进行查询。
public static String RK(String uid,String date) throws ParseException {
String Time = dateToStamp(date);
int reg = Math.abs((uid).hashCode()) % 10;
String rk = "0" + reg + '.' + Time + "." + uid;
return rk;
}
对于hbase查询:
hbase的rowkey按照字典形式保存,通过rowkey查相应数据最快,因此一般将重要信息设计在rowkey中。
hbase的filler过滤查询时,如果指定rowkey范围,会进行全表查询,因此设计rowkey时,将时间戳包含在内。
Scan scan = new Scan();//若没有指定startRow以及stopRow,则全表扫描
//扫描指定列族
scan.addFamily("fdata".getBytes());
//扫描指定的列
scan.addColumn("fdata".getBytes(), column.getBytes());
//指定扫描的行键范围, 前闭后开
scan.setStartRow(RK(iotID,startTime).getBytes());
scan.setStopRow(RK(iotID,endTime).getBytes());
//设置每批次返回客户端的数据条数
// scan.setBatch(20);
//从cacheBlock中读取数据
scan.setCacheBlocks(true);
// scan.setMaxResultSize(4);
// scan.setMaxVersions(2);//获取历史2个版本
//多维过滤器
FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
// //行键过滤 查询00开头的行键
PrefixFilter prefixFilter = new PrefixFilter("00.".getBytes());
filterList.addFilter(prefixFilter);
// 正则行键过滤
Filter Rowfilter = new RowFilter(CompareFilter.CompareOp.EQUAL,new RegexStringComparator(".*"+iotID));
// filterList.addFilter(Rowfilter);
// 查询 diviceid = “XXX”的某一行
SingleColumnValueFilter deviceid1 = new SingleColumnValueFilter("fdata".getBytes(), "deviceid".getBytes(), CompareFilter.CompareOp.EQUAL, iotID.getBytes());
filterList.addFilter(deviceid1);
scan.setFilter(filterList);
//通过getScanner查询获取到了表里面所有的数据,是多条数据
ResultScanner scanner = table.getScanner(scan);
//遍历ResultScanner 得到每一条数据,每一条数据都是封装在result对象里面了
1、试用用户IOT表
2、正式用户IOT表
hbase.client.scanner.caching
这个参数设置成500或者1000。get[table.get(List)
,进行读取请求不同列族的数据分开存储在不同的目录下。如果一个表有多个列族,只是根据Rowkey而不指定列族进行检索的话不同列族的数据需要独立进行检索,性能必然会比指定列族的查询差很多,很多情况下甚至会有2倍~3倍的性能损失,
在查询的时候指定列簇或者列进行精确的查找
//扫描指定列族
scan.addFamily("fdata".getBytes());
//扫描指定的列
scan.addColumn("fdata".getBytes(), column.getBytes());
scan.setBlockCache(false)
一般服务端端问题一旦导致业务读请求延迟较大的话,通常是集群级别的,即整个集群的业务都会反映读延迟较大。
可以从4个方面入手:
默认情况下,BlockCache和Memstore的配置相对比较均衡(各占40%),可以根据集群业务进行修正,
另一方面,BlockCache的策略选择也很重要,不同策略对读性能来说影响并不是很大,但是对GC的影响却相当显著,尤其BucketCache的offheap模式下GC表现很优越。另外,HBase 2.0对offheap的改造(HBASE-11425)将会使HBase的读性能得到2~4倍的提升,同时GC表现会更好!
所以建议观察所有RegionServer的缓存未命中率、配置文件相关配置项一级GC日志,确认BlockCache是否可以优化。如果缓存命中较低,建议使用offheap模式;
JVM内存配置量 < 20G,BlockCache策略选择LRUBlockCache;否则选择BucketCache策略的offheap模式
一个store可能包含很多的HFile文件,文件越多,检索所需的IO次数必然越多,读取延迟也就越高。
文件数量通常取决于Compaction的执行策略,一般和两个配置参数有关:hbase.hstore.compactionThreshold
和hbase.hstore.compaction.max.size
前者表示一个store中的文件数超过多少就应该进行合并,后者表示参数合并的文件大小最大是多少,超过此大小的文件不能参与合并。
这两个参数不能设置太’松’(前者不能设置太大,后者不能设置太小),导致Compaction合并文件的实际效果不明显,进而很多文件得不到合并。这样就会导致HFile文件数变多。
所以建议观察RegionServer级别以及Region级别的storefile数,确认HFile文件是否过多,hbase.hstore.compactionThreshold
设置不能太大,默认是3个;设置需要根据Region大小确定,
通常可以简单的认为hbase.hstore.compaction.max.size= RegionSize / hbase.hstore.compactionThreshold
Compaction是将小文件合并为大文件,提高后续业务随机读性能,但是也会带来IO放大以及带宽消耗问题(数据远程读取以及三副本写入都会消耗系统带宽)。
正常配置情况下Minor Compaction并不会带来很大的系统资源消耗,除非因为配置不合理导致Minor Compaction太过频繁,或者Region设置太大情况下发生Major Compaction。
观察系统IO资源以及带宽资源使用情况,再观察Compaction队列长度,确认是否由于Compaction导致系统资源消耗过多
Minor Compaction设置:hbase.hstore.compactionThreshold
设置不能太小,又不能设置太大,因此建议设置为5~6;
Major Compaction设置:大Region读延迟敏感业务( 100G以上)通常不建议开启自动MajorCompaction,手动低峰期触发。小Region或者延迟不敏感业务可以开启Major Compaction,但建议限制流量
HDFS作为HBase最终数据存储系统,通常会使用三副本策略存储HBase数据文件以及日志文件。
从HDFS的角度望上层看,HBase即是它的客户端,HBase通过调用它的客户端进行数据读写操作,因此HDFS的相关优化也会影响HBase的读写性能。
这里主要关注如下三个方面:
数据本地率:HDFS数据通常存储三份,
据本地率太低,会产生大量的跨网络IO请求,必然会导致读请求延迟较高,因此提高数据本地率可以有效优化随机读性能。
所以避免Region无故迁移,比如关闭自动balance、RS宕机及时拉起并迁回飘走的Region等;在业务低峰期执行major_compact提升数据本地率
问题
修改hbase-site.xml
中
问题
zk最大连接数过小,默认值是300,在查询量与记录数量特大的集群中,可能会造成相互间通信连接过多从而拒绝连接服务。
修改hbase-site.xml
中
hbase内存堆大小设置太小,修改hbase-env.sh
文件,把堆大小设置大一些。默认1000m
export HBASE_HEAPSIZE=30720
一个RegionServer上有n个region,按照一个表中start rowkey与end rowkey 划分为一个region。Region合理,负载均衡,各RegionServer平均分担的Region。如果region太多,超出此内存(memstore使用率过高),可能会导致无响应服务器或压缩风暴。
Region的最大数量设置,主要由memstore内存使用量决定,计算集群region数量的公式:
((RS Xmx) * hbase.regionserver.global.memstore.size) / (hbase.hregion.memstore.flush.size * (# column families))
假设一个RS有16GB内存,那么16384*0.4/128m 等于51个活跃的region。如果写很重的场景下,可以适当调高hbase.regionserver.global.memstore.size,这样可以容纳更多的region数量。参考:Hbase官方文档
region太多,可能由于本身所建立的表太多,以及一些表分区太多。
region合并
merge_region 'c2212a3956b814a6f0d57a90983a8515','553dd4db667814cf2f050561167ca030'
删除一些测试表
数据加载到本地就存放在scan缓存中,默认100条数据大小。一般情况下,可能100条缓存数量就够了,但是遇到啊一些比较大的scan请求,可能查询几万或者10几万,那么缓存的命中率就很低了,
hbase-env.sh
文件中,hbase.client.scanner.caching
=500或者100当hbase.hregion.max.filesize
比较小时,触发split的机率更大,系统的整体访问服务会出现不稳定现象。
当hbase.hregion.max.filesize
比较大时,由于长期得不到split,同一个region内发生多次compaction的机会增加,也会降低系统的性能、稳定性,因此平均吞吐量会受到一些影响而下降。
hbase-env.sh
文件中,hbase.hregion.max.filesize
=5-10GB关闭某些重要场景的HBase表的major_compact,在非高峰期的时候再去调用major_compact,这样可以减少split的同时,显著提供集群的性能,吞吐量、非常有用。通过HBase的UI控制台都可以监控到region的数量&大小指标。
一个RegionServer上有n个region,每个region会根据不同的列族数,拥有不同的store,每个store有一块自己的memstore内存区和多个HFile文件。RegionServer将总内存的一小部分专用于memstore存储。每个region都有自己的memstore,memstore大小可配置,通常在128-256 MB范围内,
HBase中数据一开始会写入memstore,满128MB(默认)以后,会flush到disk上而成为storefile。当storefile数量超过触发因子时,会启动compaction过程将它们合并为一个storefile,对集群的性能有一定影响。而当合并后的storefile大于max.filesize,会触发分割动作,将它切分成两个region。
hbase-env.sh
文件中:
hbase-env.sh
文件中:
LRUBlockCache
;offheap
模式一个store可能包含很多的HFile文件,越多检索所需的IO次数必然越多,读取延迟也就越高。
HFile文件数量,通常取决于Compaction的执行策略,一般和两个配置参数有关:hbase.hstore.compactionThreshold
(store中的文件数超过多少就应该进行合并)和hbase.hstore.compaction.max.size
(参数合并的文件大小最大是多少,超过此大小的文件不能参与合并)。
这两个参数不能设置太’松’(前者不能设置太大,后者不能设置太小),导致Compaction合并文件的实际效果不明显,进而很多文件得不到合并。这样就会导致HFile文件数变多。可观察RegionServer级别以及Region级别的storefile数,确认HFile文件是否过多,
修改hbase-env.sh
文件中: hbase.hstore.compactionThreshold
默认是3个,建议设置为5~6;
Major Compaction设置:大Region读延迟敏感业务( 100G以上)通常不建议开启自动MajorCompaction,手动低峰期触发。小Region或者延迟不敏感业务可以开启Major Compaction,但建议限制流量
hbase-env.sh
文件中:setAutoFlush=false
<property>
<name>hbase.client.scanner.cachingname>
<value>500value>
property>
<property>
<name>hbase.hregion.majorcompactionname>
<value>604800000value>
property>
<property>
<name>hbase.hregion.majorcompaction.jittername>
<value>0.50value>
property>
<property>
<name>hbase.hregion.max.filesizename>
<value>32212254720value>
property>
<property>
<name>hbase.hregion.memstore.flush.sizename>
<value>134217728value>
property>
<property>
<name>hbase.hstore.compactionThresholdname>
<value>6value>
property>
<property>
<name>hbase.regionserver.handler.countname>
<value>100value>
property>
<property>
<name>hfile.block.cache.sizename>
<value>0.5value>
property>
<property>
<name>hbase.regionserver.global.memstore.sizename>
<value>0.3value>
property>
<property>
<name>hbase.bucketcache.sizename>
<value>16384value>
property>
<property>
<name>hbase.regionserver.wal.codecname>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodecvalue>
property>
<property>
<name>hbase.region.server.rpc.scheduler.factory.classname>
<value>org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactoryvalue>
property>
<property>
<name>hbase.rpc.controllerfactory.classname>
<value>org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactoryvalue>
property>
<property>
<name>hbase.table.sanity.checksname>
<value>falsevalue>
property>
<property>
<name>hbase.zookeeper.property.maxClientCnxnsname>
<value>15000value>
property>
本次测试,只是在本机虚拟机中进行,还未在服务器中测试。
YCSB(Yahoo! Cloud Serving Benchmark)
PE(PerformanceEvaluation),这里我们会使用YCSB,YCSB对比PE有点明显
运行一个压力测试需要 6 步:
(1)下载YCSB包
登入YCSB github网,找到下载地址,这里我们选择最新版0.17,并往下拉找到hbase12的版本。
workload
Workload 定义了如何向数据库中加载测试数据,包括两个部分:
YCSB 的 CoreWorkload 预置了一些标准测试数据,可以直接使用,包括 6 个不同的类型:
Workload A:重更新,50% 读 50% 写,例如 session sotre
Workload B:读多写少,95% 读 5% 写,例如 photo tagging
Workload C:只读:100% 读,例如 user profile cache
Workload D:读最近更新:这个 workload 会插入新纪录,越新的纪录读取概率越大,例如:user status updates
Workload E:小范围查询:这个 workload 会查询小范围的纪录,而不是单个纪录,例如:threaded conversations
Workload F:读取-修改-写入:这个 workload 会读取一个纪录,然后修改这个纪录,最后写回,例如:user database
可以根据测试需求选择合适的 workload,也可以新建一个新的 workload。
运行时参数
除了在 workload 中配置参数外,YCSB 还支持这些运行时参数:
threads:客户端线程数,默认为 1
target:每秒的目标操作数,默认为无限制(尽可能快地完成操作)。例如一个操作需要 100 ms,那么一个线程 1s 内可以完成 10 个操作,通过 -target 参数可以将操作放缓,控制在 10 个以下
s:每 10s 打印一次客户端状态,用于调试
(1)创建测试表
这里建议预分区的个数为 10 * RegionServers
n_splits = 12 # HBase recommends (10 * number of regionservers)
create 'usertable', 'cf', {SPLITS => (1..n_splits).map {|i| "user#{1000+i*(9999-1000)/n_splits}"}}
或直接:
create 'usertable', 'family'
(2)加载数据
./bin/ycsb load hbase12 -P workloads/workloada -p threads=10 -p table=usertable -p columnfamily=family -p recordcount=10000
或:
./bin/ycsb load hbase10 -P workloads/workloada -p threads=10 -p table=usertable -p columnfamily=family -p recordcount=10000 -s > logs/load.log
参数解释如下:
load 表示这是一次加载数据操作;
hbase10指明了所用的数据库类型;
-P指明了所用的配置文件的路径;
-p 可以显示修改YCSB内置的默认配置,例如这里配置
recordcount=10000来覆盖之前说过的workloada中默认的recordcount=1000;
-s一次加载数据或执行测试的过程实践很长,YCSB客户端需要定时(默认10s)发送状态信息;
logs/load.log表示结果日志写入位置(若没有logs文件夹就自己新建一个,也可以不加此参数,则运行结果直接打印到屏幕上);
(3)执行测试
bin/ycsb run hbase12 -P workloads/workloada -threads 10 -p operationcount=1000 -p table=usertable -p columnfamily=family -p measurementtype=timeseries -p timeseries.granularity=2000 -s > logs/transaction-workloadAA.log
优化前:
优化后:
优化前 | 优化后 | |||
---|---|---|---|---|
数据加载时间(毫秒) | 10528 | 1379ms | ||
平均并发量(每秒/条) | 94.98 | 725.1613 | ||
read操作总数 | 524 | 506 | ||
每次read平均延时(微秒) | 81067 | 4148.478 | ||
read操作最大延时(微秒) | 1652 | 512 | ||
read操作最小延时(微秒) | 5128191 | 89535 |
(1)建表
n_splits = 12 # HBase recommends (10 * number of regionservers)
create 'usertable', 'cf', {SPLITS => (1..n_splits).map {|i| "user#{1000+i*(9999-1000)/n_splits}"}}
(2)加载数据
./bin/ycsb load hbase10 -P workloads/workloada -p table=usertable -p columnfamily=cf -p recordcount=1000000 -p operationcount=1000000
(3)执行测试
./bin/ycsb run hbase10 -P workloads/workloada -p table=usertable -p columnfamily=cf -p recordcount=1000000 -p operationcount=1000000