1. 目的
测试 phoenix 引擎在 hbase 上的数据查询与数据写入的性能,探索 phoenix 的特性和限制。
2. 软件版本
phoenix-4.8.0-HBase-1.1
HBase-1.1.6
3. 硬件环境
四台配置相同的PC机搭建HBase集群环境,其中一台做HMaster,另外三台做Region
部件 | 规格 |
---|---|
CPU | Intel(R) Core(TM) i7-4820K CPU @ 3.70GHz 1U * 4Core |
MEM | 64G |
4. 测试工具
squirrel-sql-3.5.3
apache-jmeter-3.0
5. 测试说明
使用基于 jdbc 连接的 squirrel-sql-3.5.3 进行单用户下的sql性能测试
使用基于 jdbc 连接的 apache-jmeter-3.0 进行多线程并发测试
6. 测试数据
应用场景:仅用于存放测点的档案信息,用于查询范围的条件检索过滤时使用
Reinfo | 测点基础信息表 | 测试数据规模:5千 | |
---|---|---|---|
列名 | 类型 | 注释 | 是否主键 |
Data_id | bigint | 测点ID | PK |
DataName | varchar(300) | 测点名 | |
DataType | varchar(100) | 测点的数据类型 (电压、电流、有功、无功等) | |
byzid | varchar(300) | 变电站编号 | |
orgid | varchar(300) | 组织机构编号 |
应用场景:按测点编号进行查询,每次通常查询1个测点的数据,但有时也会查询几个测点的数据
CurrData | 当日数据表 | 测试数据规模:7百万 | |
---|---|---|---|
列名 | 类型 | 注释 | 是否主键 |
Data_id | bigint | 测点ID | PK |
date_time | DATE | 时点 (2016-09-19 00:00,2016-09-19 00:01 … 2016-09-19 23:59) | PK |
data | float | 测试值 | |
name | varchar(300) | 测点名(根据reinfo冗余字段) | |
DataType | varchar(100) | 测点的数据类型 (电压、电流、有功、无功等)(根据reinfo冗余字段) | |
byzid | varchar(300) | 变电站编号(根据reinfo冗余字段) | |
orgid | varchar(300) | 组织机构编号(根据reinfo冗余字段) |
应用场景:按测点编号进行查询,每次通常查询1个测点的数据,但有时也会查询几个测点的数据
HisData | 历史数据表 | 测试数据规模:5千万 | |
---|---|---|---|
列名 | 类型 | 注释 | 是否主键 |
Data_id | bigint | 测点ID | PK |
date_time | DATE | 时点:(2016-09-19 00:00 - — 2016-09-19 23:59) | PK |
data | float | 测试值 | |
name | varchar(300) | 测点名(根据reinfo冗余字段) | |
DataType | varchar(100) | 测点的数据类型(电压、电流、有功、无功等)(根据reinfo冗余字段) | |
byzid | varchar(300) | 变电站编号(根据reinfo冗余字段) | |
orgid | varchar(300) | 组织机构编号(根据reinfo冗余字段) |
7. 数据写入测试
评估数据写入的性能,分别对数据导入和数据插入两种方式评测。
数据导入:将文本文件数据装载至目标表。
数据插入:使用dml插入数据至目标表。
评估并发写的效率
7.1 测试条件
- 数据导入
./psql.py hostname-2 -t CURRDATA /home/currdata1.csv
- 数据插入
currdata1与currdata结构一致
upsert into currdata select * from currdata1;
7.2 测试结果
- 写入时长:
记录行 | 720w*1 | 720w*2 | 720w*4 |
---|---|---|---|
数据文件大小 | 350mb*1 | 350mb*2 | 350mb*4 |
并行度(进程数) | 1 | 2 | 4 |
CASE 1:数据导入 | 201s | 247s | 282s |
CASE 2:数据插入 | 92s | 132s | 158s |
- 写入效率:
记录行 | 720w*1 | 720w*2 | 720w*4 |
---|---|---|---|
数据文件大小 | 350mb*1 | 350mb*2 | 350mb*4 |
并行度(进程数) | 1 | 2 | 4 |
CASE 1:数据导入 | 1.74mb/s | 2.83mb/s | 4.96mb/s |
CASE 2:数据插入 | 3.8mb/s | 5.3mb/s | 8.8mb/s |
7.3 结果分析
HBase 是一个写很快的系统,并行度越高,写性能越好,经过测试发现,使用四个并行任务的情况下,集群cpu资源使用率不超过30%,磁盘繁忙程度大致在 70%~90%,所以只要条件允许,建议配置高速磁盘,尽可能地通过并行方式写入数据,最大化 Hbase 的效率。
8. K-V(RowKey)查询测试
根据表的主键(PK)进行K-V查询,场景主要有:单测点单日,单测点多日,多测点单日,多测点多日
根据电力客户提供的需求,选用720w的数据量作为测试用例,电力客户拥有 5000 个测点,每分钟一条记录,保留一天的数据,即:50006024=7200000,5kw 即一周的数据
8.1 测试示例
- 单测点单日
select
data_id,date_time
,data
from hisdata
where data_id = 104708704221266921
and date_time <= TO_DATE('2016-09-20', 'yyyy-MM-dd')
and date_time >= TO_DATE('2016-09-20', 'yyyy-MM-dd')
order by date_time;
- 单测点多日
select
data_id
,date_time
,data
from hisdata
where data_id = 104708704221266921
and date_time <= TO_DATE('2016-09-20', 'yyyy-MM-dd')
and date_time >= TO_DATE('2016-09-18', 'yyyy-MM-dd')
order by data_id,date_time;
- 多测点单日
select
data_id
,date_time
,data
from hisdata
where data_id >= 104708704221266921
and data_id <= 104708704221266931
and date_time <= TO_DATE('2016-09-17', 'yyyy-MM-dd')
and date_time >= TO_DATE('2016-09-17', 'yyyy-MM-dd')
order by data_id,date_time;
- 多测点多日
select
data_id
,date_time
,data
from hisdata
where data_id >= 104708704221266921
and data_id <= 104708704221266930
and date_time <= TO_DATE('2016-09-21', 'yyyy-MM-dd')
and date_time >= TO_DATE('2016-09-18', 'yyyy-MM-dd')
order by data_id,date_time
8.2 测试结果
数据量 | 720w | 5kw |
---|---|---|
1:单测点单日 | 20ms | 40ms |
2:单测点多日 | 30ms | 50ms |
3:多测点单日 | 30ms | 40ms |
4:多测点多日 | 120ms | 290ms |
8.3 结果分析
基于 rowkey 的查询效率比较高,这也符合 HBase 的 Key-Value 特性,数据量越大,查询效率越好,数据量从 720w 到 5kw 增长了 6X,但查询耗时仅增加了 30%~100%。
但是,返回的结果集应尽量控制在 10w 以内,返回的记录行越少,响应时间越快。以 多测点多日 为例,修改sql示例中的过滤条件使其分别返回 1w,10w,100w 结果集,测试结果如下:
结果集行数 | 查询响应时间 |
---|---|
1w | 120ms |
10w | 990ms |
100w | 9.7s |
9. 即席查询测试
即席查询的意思是即兴或临时查询。是在需要时进行的查询或搜索,而不是预先计划或准备好的查询。即席查询通常用于解决突发问题或获取即时信息。
模拟OLAP环境下的ad hoc查询,包括聚集查询,连接查询,明细查询等,即席查询主要针对非K-V方式的查询场景,同样也分为720w和5kw测试
9.1 测试示例
- 带过滤条件和排序操作的明细查询
select
data_id
,date_time
,data
from hisdata
where byzid = '0001303'
and orgid = '8000041'
and date_time <= TO_DATE('2016-09-20', 'yyyy-MM-dd')
and date_time >= TO_DATE('2016-09-18', 'yyyy-MM-dd')
order by data_id,date_time
- 全表聚合查询
select
count(*)
from hisdata
- 关联查询
select
count(*)
from hisDATA c,REINFO r
where c.data_id = r.data_id
- 带过滤条件的聚合查询
select
byzid
,orgid
,max(data)
,min(data)
,avg(data)
from hisdata
where DATATYPE = '无功'
and date_time <= TO_DATE('2016-09-20', 'yyyy-MM-dd')
and date_time >= TO_DATE('2016-09-18', 'yyyy-MM-dd')
group by byzid,orgid;
9.2 测试结果
数据量 | 720w | 5kw |
---|---|---|
带过滤条件和排序操作的明细查询 | 1.8s | 13.3s |
全表聚合查询 | 1.7s | 11.4s |
关联查询 | 3.3s | 18.3s |
带过滤条件的聚合查询 | 2s | 16.1s |
9.3 结果分析
HBase 不适合做 BI 应用中常见的 即席查询,Phoenix 进行非 K-V 类的即席查询时,是将所有的目标数据先填入内存,在内存中计算,这样的话效率主要依赖其 CBO(查询优化器)和 执行计划。测试用例采用的比较简单的查询 sql,而生产环境中的统计查询 sql 比上述用例复杂的多得多,所以,HBase 只适合 K-V 方式访问数据。
10. 并发查询
根据 HBase 的特点,仅针对 KV 方式的查询进行并发测试,为更好地评估软件的性能,将采用 5kw 的历史数据表(Hisdata)作为测试素材。以下是单用户、10用户、50用户、100用户、200用户的性能测试结果
10.1 测试示例
- 单测点单日(短查询)
select
data_id
,date_time
,data
from hisdata
where data_id = 104708704221266921
and date_time <= TO_DATE('2016-09-20', 'yyyy-MM-dd')
and date_time >= TO_DATE('2016-09-20', 'yyyy-MM-dd')
order by date_time;
- 单测点多日(短查询)
select
data_id
,date_time
,data
from hisdata
where data_id = 104708704221266921
and date_time <= TO_DATE('2016-09-20', 'yyyy-MM-dd')
and date_time >= TO_DATE('2016-09-18', 'yyyy-MM-dd')
order by data_id,date_time;
- 多测点单日(短查询)
select
data_id
,date_time
,data
from hisdata
where data_id >= 104708704221266921
and data_id <= 104708704221266931
and date_time <= TO_DATE('2016-09-17', 'yyyy-MM-dd')
and date_time >= TO_DATE('2016-09-17', 'yyyy-MM-dd')
order by data_id,date_time;
- 多测点多日(长查询)
select
data_id
,date_time
,data
from hisdata
where data_id >= 104708704221266921
and data_id <= 104708704221266930
and date_time <= TO_DATE('2016-09-21', 'yyyy-MM-dd')
and date_time >= TO_DATE('2016-09-18', 'yyyy-MM-dd')
order by data_id,date_time
10.2 测试结果
10.2.1 单用户查询结果
单位:ms | avg | 中位数 | 90% Line | 95% Line | 99% Line | min | max |
---|---|---|---|---|---|---|---|
单测点单日 | 13 | 7 | 9 | 13 | 222 | 6 | 238 |
单测点多日 | 35 | 25 | 37 | 44 | 379 | 22 | 430 |
多测点单日 | 22 | 14 | 29 | 45 | 197 | 11 | 261 |
多测点多日 | 292 | 277 | 292 | 312 | 498 | 263 | 1325 |
多测点多日与另外三个示例相比,响应时长要高一个量级,因此,单测点单日、单测点多日、多测点单日归为短查询的并发测试场景,多测点多日归为长查询的并发测试场景
10.2.2 10用户并发查询结果(短查询)
avg | 中位数 | 90% Line | 95% Line | 99% Line | min | max | tps | |
---|---|---|---|---|---|---|---|---|
单测点单日 | 98 | 36 | 147 | 456 | 459 | 14 | 459 | 3.144456 |
单测点多日 | 250 | 196 | 260 | 746 | 806 | 131 | 858 | 3.171985 |
多测点单日 | 119 | 83 | 126 | 483 | 508 | 33 | 510 | 3.24823 |
总体 | 156 | 115 | 260 | 483 | 759 | 14 | 858 | 8.798686 |
10.2.3 10用户并发查询结果(长查询)
avg | 中位数 | 90% Line | 95% Line | 99% Line | min | max | tps | |
---|---|---|---|---|---|---|---|---|
多测点多日 | 2355 | 2323 | 2733 | 2785 | 2869 | 1136 | 2871 | 1.895303 |
总体 | 2355 | 2323 | 2733 | 2785 | 2869 | 1136 | 2871 | 1.895303 |
在 10 用户并发下,长查询的 tps 只有 1.9 不到,而短查询约为 8.8。不仅如此,并发情况下,单个查询的响应时间明显增加,例如:单用户下测试单测点单日的查询响应时间均值是 13ms,10用户并发下达到了 98ms,而多测点多日的查询响应时间则由 292ms 升至 2.4s 左右。
10.2.4 50用户并发查询结果(短查询)
avg | 中位数 | 90% Line | 95% Line | 99% Line | min | max | tps | |
---|---|---|---|---|---|---|---|---|
单测点单日 | 139 | 72 | 248 | 631 | 639 | 15 | 644 | 12.71294 |
单测点多日 | 789 | 778 | 971 | 1155 | 1238 | 38 | 1258 | 12.64191 |
多测点单日 | 231 | 154 | 380 | 785 | 790 | 53 | 793 | 13.00187 |
总体 | 386 | 174 | 827 | 903 | 1180 | 15 | 1258 | 35.42164 |
10.2.5 50用户并发查询结果(长查询)
avg | 中位数 | 90% Line | 95% Line | 99% Line | min | max | tps | |
---|---|---|---|---|---|---|---|---|
多测点多日 | 19501 | 8001 | 39897 | 118340 | 125156 | 2366 | 126657 | 1.86617 |
总体 | 19501 | 8001 | 39897 | 118340 | 125156 | 2366 | 126657 | 1.86617 |
50 用户并发下长查询的 tps 仍然只有 1.9 不到,而响应时间均值却达到 19.5s,远远超过可接受范围,由此可见,基于测试环境的软硬件配置,长查询的并发数在 10 个左右比较适宜,最大不应超过 20。
短查询在 50 并发下,均值仍在 1s 以内,tps 升至 35,说明短查询场景还可以承受更大的并发数。
10.2.6 100用户并发(短查询)
avg | 中位数 | 90% Line | 95% Line | 99% Line | min | max | tps | |
---|---|---|---|---|---|---|---|---|
单测点单日 | 141 | 69 | 364 | 601 | 630 | 11 | 669 | 20.56767 |
单测点多日 | 1603 | 1571 | 2003 | 2098 | 2174 | 69 | 2209 | 20.08153 |
多测点单日 | 370 | 311 | 443 | 982 | 988 | 42 | 989 | 20.82119 |
总体 | 704 | 328 | 1676 | 1907 | 2129 | 11 | 2209 | 56.69684 |
10.2.7 200用户并发(短查询)
avg | 中位数 | 90% Line | 95% Line | 99% Line | min | max | tps | |
---|---|---|---|---|---|---|---|---|
单测点单日 | 189 | 76 | 244 | 1075 | 1114 | 22 | 1124 | 30.12956 |
单测点多日 | 3156 | 3099 | 3742 | 3967 | 4228 | 258 | 4264 | 29.18898 |
多测点单日 | 519 | 431 | 648 | 1149 | 1159 | 56 | 1163 | 30.91716 |
总体 | 1288 | 445 | 3273 | 3603 | 4095 | 22 | 4264 | 83.22006 |
在 200 用户并发下,只有单测点多日的均值超过 3s,另外两个用例的均值仍在 1s 以内,tps 达到 83。理论上讲,假如生产环境中的多日查询的频率远低于单日查询,那么此并发数还可以继续增大,限于时间和篇幅,此处不再做进一步的测试。
10.3 结果分析
对于短查询,Phoenix + HBase在 4 个节点环境下可以承受 200 个并发的压力,平均每秒能响应 83 个请求。而长查询,并发数控制在 10个左右较为合适,每秒最多响应1.9个请求。
11. 结论
- HBase 是一个写很快的系统,并行写/分布式写能充分发挥集群的计算资源;
- HBase 的 Key-Value 特性决定了基于 rowkey 或 主键 查询的效率很高;
- 返回的结果集越小,响应时间越快,一般建议在一万行以内;
- HBase 不适合做多维查询,Sql on HBase 只适合 主键 / RowKey 方式查询;
- 短查询和长查询的 TPS 差别巨大,优化数据模型,尽可能地优化应用中的查询为短查询。
12. 使用建议
- 在Phoenix中建表时,可以使用 SALT_BUCKETS 参数把数据分布到不同的 region 上,这样有利于 phoenix 并发的读写操作,示例:
CREATE TABLE TEST (HOST VARCHAR NOT NULL PRIMARY KEY, DESCRIPTION VARCHAR) SALT_BUCKETS = 16
- Phoenix 默认采用 hash join 进行多表关联,编辑 sql 时需要将最小的表放在最后,否则查询效率十分低下,未来会根据 statistics 自动优化
- 若无法确定关联操作中表的大小时,可以使用hint调整 join 方式,如:
SELECT /*+ USE_SORT_MERGE_JOIN*/ ...
或者调大phoenix.query.maxServerCacheBytes
参数使其能容纳大表的中间结果 - 主键索引要按创建时的顺序引用。如
primary key(id,name,add)
,那么会隐式的创建(id),(id,name),(id,name,add)
三个索引,在 where 中用这三个条件时会用到索引,而其他组合则无法使用索引 - 对于二级索引,除了要按创建时的顺序引用外,如果
select
中的任意列不在索引中则无法使用索引