截止最新的Phoenix-4.14.0, alter table 并没有修改 现有表 现有字段 数据类型的功能, 因此, 想要实现修改数据类型的功能需要另辟蹊径, 或者使用简单粗暴的删表建表, 但是生产上显然这是不可取的.
Phoenix表是映射的hbase表, hbase存储的数据都是字节数组, 因此, 限制数据类型的只能是Phoenix自己, 所以, 我们就可以通过修改Phoenix元数据的方式修改表中字段的数据类型.
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.SEQUENCE
SYSTEM.STATS
Phoenix的系统表有以上五张表, 其中SYSTEM.CATALOG表保存了我们新建表的元数据信息.
这种方法只能修改char, varchar, DECIMAL类型的长度, 宽度等等, 不能将varchar修改为integer类型
create table test01 (
a integer not null primary key,
b varchar(2),
c varchar(5),
d decimal(4,2),
e decimal(6,3)
);
upsert into test01 values(1, '12', '12345', 12.12, 123.123);
插入数据正常显示
upsert into test01 values(2, '123', '12345', 12.12, 123.123);
报错了: Error: ERROR 206 (22003): The data exceeds the max capacity for the data type. value='123' columnName=B (state=22003,code=206)
查看SQL:
select TENANT_ID,TABLE_SCHEM,TABLE_NAME,COLUMN_NAME,COLUMN_FAMILY,DATA_TYPE,COLUMN_SIZE,DECIMAL_DIGITS from SYSTEM.CATALOG where TABLE_NAME='TEST01';
注意: where 条件中的 表名TEST01 一定要大写, 因为建表语句中的 表名, 字段名等等只要不加引号, Phoenix都会默认转换为大写保存为元数据的.
显示字段含义:
TENANT_ID | 租户ID(这个不用管,所租户用的) |
TABLE_SCHEM | 表的schema |
TABLE_NAME | 表名 |
COLUMN_NAME | 列名 |
COLUMN_FAMIL | hbase底层的列族名 |
DATA_TYPE | 列的数据类型 |
COLUMN_SIZE | 列的数据长度(一般指char,varchar和decimal的长度) |
DECIMAL_DIGITS | decimal类型的小数长度 |
b 的数据类型是 varchar(2), 而我们插入的是 '123' , 明显不够, 所以我准备将其修改为 varchar(4)
修改SQL:
upsert into SYSTEM.CATALOG (TENANT_ID,TABLE_SCHEM,TABLE_NAME,COLUMN_NAME,COLUMN_FAMILY,COLUMN_SIZE) values('','','TEST01','B','0',4);
注意: TENANT_ID,TABLE_SCHEM,TABLE_NAME,COLUMN_NAME,COLUMN_FAMILY 是主键, 必须要有
再次查看元数据可知, 已经修改好了.
upsert into test01 values(2, '123', '12345', 12.12, 123.123);
还是报错, 难道修改失败了??? 别着急, 退出Phoenix shell客户端, 再次进入Phoenix, 就好了, 因为我修改了phoenix元数据更新时间为一天, 需要退出客户端重新进一下, 重新加载元数据, 然后就好了, 如果没手动修改, 默认是always, 每个sql都会更新元数据, 应该不会有这个问题
如果重新进入phoenix还是不行, 那就需要重启hbase了, 具体原因我也没搞懂, 有时候需要重启hbase, 有时候不重启也会生效.
上面是数据类型与名字的对照表, 用同样的方法, upsert into的方式, 可以修改数据类型DATA_TYPE , 数据长度COLUMN_SIZE, 小数点长度DECIMAL_DIGITS等等.
猜想(根据测试现象得出的结论):
1. 用upsert语句更新phoenix catalog元数据, hbase底层的数据是已经修改了的, 我分别从客户端命令行scan和jdbc scan两个方面做了印证
2. 由上面一条可以得出, phoenix客户端读取元数据并不是直接读取hbase catalog表底层数据, 而是它去读了自己的缓存, 它把元数据缓存在了某个地方
3. 这个缓存在重启hbase的时候会重新加载, 因为重启之后元数据一定会生效
4. 由上面几条可以得出, 这个缓存一定是服务端缓存, 因为我的jdbc测试代码, 每次启动都是一个全新的客户端, 不存在客户端缓存, 所以一定是服务端缓存, 重启hbase生效这一点也印证了这个猜想
5. 这个缓存是表级别的, 因为我分别创建了两张表, 都修改了元数据, 都没有生效, 不知道什么原因, 导致其中一个表的元数据突然生效了, 可以upsert一条原来插不进去的数, 但是另外一张表依然没有生效, 由此猜想, 这个缓存是表级别的
以上都是根据测试结果得出的猜想, 不一定正确, phoenix的元数据到底咋回事, 需要仔细看源代码研究一下, 以后研究透彻了再更新文章吧, 先写一下猜想, 做个记录
解决办法
1. 上文中说的重启hbase, 简单, 快速, 但是生产上的代价比较大
2. 等一天试试, 或许会因为某些未知原因触发了元数据缓存的自动更新, 或者我感觉元数据会定期更新缓存, 不着急的话等一天应该会有惊喜, 没准用不了一天