Happy Coding
记录一个 Impala JDBC 驱动问题

现象

洪敏在用 Metabase 写报表过程中,使用 Metabase 的日期变量,发现的诡异问题。两条语句仅有部分差异,返回的结果却差了一天。

image-20200807173209949

期望结果。

image-20200807173143617

与期望结果差了一天。

在 CDH 中查看 impala 服务器接收到的请求。

得到期望结果的SQL:

dt 是字符串,内容是日期,参数是TIMESTAMP,SQL引擎会自动转为两个 TIMESTAMP比较。

SELECT `a`.`dt`, (`a`.`x` - `b`.`x`) as `净增人数` FROM (SELECT `f_ods_wx_subscribe_di`.`dt`, COUNT(DISTINCT `f_ods_wx_subscribe_di`.`userinfo_openid`) as `x` FROM `product`.`f_ods_wx_subscribe_di` WHERE ((`f_ods_wx_subscribe_di`.`dt` < CAST( '2020-08-05 00:00:00.0' AS TIMESTAMP)) AND ((`f_ods_wx_subscribe_di`.`mid` = 'jt20191017175015910985') AND (`f_ods_wx_subscribe_di`.`dt` >= CAST( '2020-08-01 00:00:00.0' AS TIMESTAMP)))) GROUP BY `f_ods_wx_subscribe_di`.`dt`) AS a LEFT JOIN (SELECT `f_ods_wx_unsubscribe_di`.`dt`, COUNT(DISTINCT `f_ods_wx_unsubscribe_di`.`userinfo_openid`) as `x` FROM `product`.`f_ods_wx_unsubscribe_di` WHERE ((`f_ods_wx_unsubscribe_di`.`dt` < CAST( '2020-08-05 00:00:00.0' AS TIMESTAMP)) AND ((`f_ods_wx_unsubscribe_di`.`mid` = 'jt20191017175015910985') AND (`f_ods_wx_unsubscribe_di`.`dt` >= CAST( '2020-08-01 00:00:00.0' AS TIMESTAMP)))) GROUP BY `f_ods_wx_unsubscribe_di`.`dt`) AS b ON (`a`.`dt` = `b`.`dt`)

得到错误结果的SQL:

dt 本身是字符串,参数也是字符串。变成两个字符串比较,结果确实差一天。只是在select里使用了一个函数,就造成SQL巨大差异,很神奇。

 select a.dt ,(a.x - nvl(b.x,0)) as `净增人数` from ( SELECT dt, count(distinct userinfo_openid) as x FROM f_ods_wx_subscribe_di WHERE mid = 'jt20191017175015910985' AND dt >= '2020-08-01 00:00:00.0' and dt < '2020-08-05 00:00:00.0' group by dt ) a left join ( SELECT dt, count(distinct userinfo_openid) as x FROM f_ods_wx_unsubscribe_di WHERE mid = 'jt20191017175015910985' AND dt >= '2020-08-01 00:00:00.0' and dt < '2020-08-05 00:00:00.0' group by dt ) b on a.dt = b.dt

metabase,metabase impala driver,impala JDBC driver都会有问题。

Metabase 打印日志 query, param

native_form/query 构造完成后,会执行 executef,底层是driver/execute-reducible-query,就是JDBC prepareStatement构造与执行。

在executef方法记录日志。

image-20200807174516355

query的区别如之前、params是完全一致的。所以,不是Metabase的问题。

image-20200807175049064

而 metabase impala driver 也没有重载 driver/execute-reducible-query 方法。也基本排除了嫌疑。

复现问题

目标锁定在 impala JDBC driver。写一个Java程序复现。

  1. 使用相同的 Impala JDBC 驱动。
  2. 同样是参数化提交 SQL。
  3. 准备类似的SQL Statement。

image-20200807172918590

核心逻辑。代码在此

                    connection = DriverManager.getConnection(CONNECTION_URL);
                    ps = connection.prepareStatement(sql);
                    ps.setString(1, "jt20191017175015910985");
                    ps.setTimestamp(2, new Timestamp(2020 - 1900, 8 - 1, 1, 0, 0, 0, 0));
                    ps.setTimestamp(3, new Timestamp(2020 - 1900, 8 - 1, 5, 0, 0, 0, 0));
                    ps.setString(4, "jt20191017175015910985");
                    ps.setTimestamp(5, new Timestamp(2020 - 1900, 8 - 1, 1, 0, 0, 0, 0));
                    ps.setTimestamp(6, new Timestamp(2020 - 1900, 8 - 1, 5, 0, 0, 0, 0));
                    resultSet = ps.executeQuery();
                    ResultSetMetaData rsmd = resultSet.getMetaData();
                    int columnsNumber = rsmd.getColumnCount();
                    while (resultSet.next()) {
                        for (int i = 1; i <= columnsNumber; i++) {
                            if (i > 1) System.out.print(",  ");
                            String columnValue = resultSet.getString(i);
                            System.out.print(columnValue + " " + rsmd.getColumnName(i));
                        }
                        System.out.println("");
                    }

服务器上执行,结果复现。

$ java -jar impala-jdbc-sql-rewrite-1.0-SNAPSHOT.jar 1.sql
2020-08-05 dt,  29257 净增人数
2020-08-02 dt,  37285 净增人数
2020-08-04 dt,  24640 净增人数
2020-08-03 dt,  29253 净增人数

$ java -jar impala-jdbc-sql-rewrite-1.0-SNAPSHOT.jar 2.sql
2020-08-02 dt,  37285 净增人数
2020-08-04 dt,  24640 净增人数
2020-08-03 dt,  29253 净增人数
2020-08-01 dt,  37235 净增人数

排查过程中的误区

一直以为Metabase服务返回的native_form/query是实际提交给JDBC驱动的sql。

image-20200807171929947

其实并不然。native_form/query是拼接好的SQL,参数已经填充进了SQL语句中。而实际执行SQL,使用的是参数化提交。源码表示返回的native_form/query是query实际执行后的产物。所以对排查问题有误导作用。

image-20200807172259322

临时解决方案

驱动的问题暂时无法解决,Workaround是有的。只要保证日期比较的时候,条件表达式左右之一至少是TIMESTAMP类型。


Last modified on 2020-08-07