问题描述: 笔者在Flink中通过Phoenix连接到HBase,其实一开始是没什么问题的,但是笔者做的是离线部分,剧需要考虑到当数据很大很大的情况下要保证数据源这块不会崩溃。所以笔者特地找了淘宝的用户行为数据,千万条数据,接近3.2G,用以测试。果不其然,在获取数据的时候出现如下的报错信息。

测试环境:

操作系统版本:CentOS release 6.8 (Final)

内核版本:Linux version 2.6.32-642.el6.x86_64

Phoenix版本:phoenix-4.7.0

HBase版本:HBase-1.1.2

表 USER_BEHAVIOR 数据库量:1884-9153

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Caused by: java.lang.IllegalArgumentException: open() failed.org.apache.phoenix.exception.PhoenixIOException: Failed after attempts=36, exceptions:
Thu Nov 28 11:35:17 GMT+08:00 2019, null, java.net.SocketTimeoutException: callTimeout=60000, callDuration=60301: row '' on table 'USER_BEHAVIOR' at region=USER_BEHAVIOR,,1574755082182.426f25646704cb2a7ea0f1c1169ce596., hostname=hadoop101,16020,1574911292617, seqNum=40446

at org.apache.flink.api.java.io.jdbc.JDBCInputFormat.open(JDBCInputFormat.java:250)
at org.apache.flink.runtime.operators.DataSourceTask.invoke(DataSourceTask.java:173)
at org.apache.flink.runtime.taskmanager.Task.doRun(Task.java:705)
at org.apache.flink.runtime.taskmanager.Task.run(Task.java:530)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.phoenix.exception.PhoenixIOException: org.apache.phoenix.exception.PhoenixIOException: Failed after attempts=36, exceptions:
Thu Nov 28 11:35:17 GMT+08:00 2019, null, java.net.SocketTimeoutException: callTimeout=60000, callDuration=60301: row '' on table 'USER_BEHAVIOR' at region=USER_BEHAVIOR,,1574755082182.426f25646704cb2a7ea0f1c1169ce596., hostname=hadoop101,16020,1574911292617, seqNum=40446

at org.apache.phoenix.util.ServerUtil.parseServerException(ServerUtil.java:111)
at org.apache.phoenix.iterate.BaseResultIterators.getIterators(BaseResultIterators.java:695)
at org.apache.phoenix.iterate.BaseResultIterators.getIterators(BaseResultIterators.java:638)
at org.apache.phoenix.iterate.MergeSortResultIterator.getMinHeap(MergeSortResultIterator.java:72)
at org.apache.phoenix.iterate.MergeSortResultIterator.minIterator(MergeSortResultIterator.java:93)
at org.apache.phoenix.iterate.MergeSortResultIterator.next(MergeSortResultIterator.java:58)
at org.apache.phoenix.iterate.MergeSortTopNResultIterator.next(MergeSortTopNResultIterator.java:85)
at org.apache.phoenix.jdbc.PhoenixResultSet.next(PhoenixResultSet.java:778)
at org.apache.flink.api.java.io.jdbc.JDBCInputFormat.open(JDBCInputFormat.java:248)
... 4 more

问题分析:

我们可以看到的是,这个原因目前主要是我们的callTimeout=60000,超时了。hbase.ipc.CallTimeout 即在phoenix客户端进行操作的时候,存在调用的时间超过了默认设置的超时时间。

我们修改 hbase-site.xml

增加如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!-- Phoenix 的超时连接 -->
<property>
<name>phoenix.query.timeoutMs</name>
<value>1800000</value>
</property>
<property>
<name>hbase.regionserver.lease.period</name>
<value>1800000</value>
</property>
<property>
<name>hbase.rpc.timeout</name>
<value>1800000</value>
</property>
<property>
<name>phoenix.query.keepAliveMs</name>
<value>1800000</value>
</property>
<property>
<name>hbase.client.operation.timeout</name>
<value>1800000</value>
</property>
<property>
<name>hbase.client.scanner.caching</name>
<value>4096</value>
</property>
<property>
<name>hbase.client.scanner.timeout.period</name>
<value>1800000</value>
</property>
<property>
<name>hbase.client.ipc.pool.type</name>
<value>RoundRobinPool</value>
</property>
<property>
<name>hbase.client.ipc.pool.size</name>
<value>128</value>
</property>

然后我们启动我们的HBase,在进入到Phoenix的命令行,我们查询数据的话,先建立起一个索引,这样速度也会快点,索引我这里建立的是异步索引,关于索引的创建,可以参考我的另一篇文章有说明:大数据框架开发基础之Phoenix入门

1
CREATE INDEX ASYNC_INDEX_USERID_ITEMID_TIME ON user_behavior ("userID","itemID","__time") ASYNC

这个索引的创建大概用了几秒左右吧,不超过3秒的。

然后我们再做一个查询,在千万级别的情况下来做这个查询:

1
select * from user_behavior; -- 要很久额

我们先查一下这个,看看能不能查出来。查不出来我们再找原因,程序一会再去执行。

创建一个同步索引:

1
CREATE INDEX GLOBAL_INDEX_USERID_ITEMID_TIME_CATEGORYID ON user_behavior("userID","itemID","categoryID");

这个时间就比较久了。
但是上面讲了那么多,都只是在解决一个基本问题,就是Phoenix创建索引超时怎么解决,并没有涉及到老夫的问题。那对于老夫的问题,怎么解决呢?
我们知道,HBase的数据,不是在HBase上的,而是在我们的HDFS上,也就是说,这里的超时不是phoenix到hbase之间的超时引起的问题,而是hbase拿取hdfs数据出了问题。这个问题找到了,就比较好解决了,我们增大这个hdfs的时间阈值。可参看老夫的大数据存储框架之HBase(4) HBase优化
hdfs-site.xml
属性:dfs.image.transfer.timeout
解释:如果对于某一次数据操作来讲,延迟非常高,socket需要等待更长的时间,建议把该值设置为更大的值(默认60000毫秒),以确保socket不会被timeout掉。

1
2
3
4
5
6
<property>
<name>dfs.image.transfer.timeout</name>
<value>3600000</value>
<description>如果对于某一次数据操作来讲,延迟非常高,socket需要等待更长的时间,建议把该值设置为更大的值(默认60000毫秒),以确保socket不会被timeout掉。</description>
</property>