Hive 未关闭表的事务功能(ACID)的问题
一、Hive 未关闭表的事务功能(ACID)的所引发的问题
记录一次HDP3.0 的hive 3.1.2由于未关闭ACID功能,导致使用到用户画像的Spark计算引擎报错,无法处理数据,impala无法查询的问题。由于hive 3.0之后默认开启ACID功能,默认建表都是使用ACID的事务表。而spark目前还不支持hive的ACID功能,因此无法读取ACID表的数据。
另外,读取不报错,但是读取不到数据的表INPUTFORMAT=org.apache.hadoop.hive.ql.io.orc.OrcInputFormat;报错insert-only 的表 INPUTFORMAT = org.apache.hadoop.mapred.TextInputFormat 。
hive3默认开启ACID功能引起的spark和impala报错的问题
在hdp3.1.5中hive3中内部表默认开启了ACID,当spark和impala创建和读取内部表的时候会报错,网上建议的方式是舍弃掉acid特性。
hive.strict.managed.tables=false
hive.create.as.insert.only=false
metastore.create.as.acid=false
对应hdp的hive默认配置做如下修改,重启hive服务后确实可以生效:
什么是事务表
- 概念
ACID就是常见数据库事务的四大特性:Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)。
由于HDFS只支持文件的添加和删除,不支持修改,因此Hive原生并不支持原子性的行级别修改。
- 支持原子性update操作的Hive表称为事务表。
- 事务表和普通内部表的区别:
可以看到,事务表分为两种,一种是ORC格式,能完整的支持CRUD操作(增删改查),一种是普通的TextFile文件格式,只符合Insert-only特性.
Hive中支持的表类型和ACID特性如下图所示:
表类型 | ACID | 文件格式 | 插入 | 更新/删除 |
---|---|---|---|---|
托管表:CRUD事务 | 是 | ORC | 是 | 是 |
托管表:仅插入式事务 | 是 | 任意格式 | 是 | 没有 |
托管表:临时 | 没有 | 任意格式 | 是 | 没有 |
外部表 | 没有 | 任意格式 | 是 | 没有 |
其中托管表是Hive内部表。
查看表的属性,指令:desc formatted tb_name
非ACID表:
| Table Type: | MANAGED_TABLE | NULL |
| Table Parameters: | NULL | NULL |
| | COLUMN_STATS_ACCURATE | {\"BASIC_STATS\":\"true\"} |
| | numFiles | 36554 |
| | numPartitions | 4582 |
| | numRows | 656353471 |
| | rawDataSize | 3687190343914 |
| | totalSize | 191558565757 |
| | transient_lastDdlTime | 1626229162 |
ACID表:
| Table Type: | MANAGED_TABLE | NULL |
| Table Parameters: | NULL | NULL |
| | bucketing_version | 2 |
| | comment | 交易日志表 |
| | numFiles | 2131 |
| | numPartitions | 2131 |
| | numRows | 1303371497 |
| | rawDataSize | 0 |
| | totalSize | 669842162666 |
| | transactional | true |
| | transactional_properties | insert_only |
| | transient_lastDdlTime | 1613705157 |
其中:
- transactional=true,标识为事务表
- transactional_properties=insert_only
事务表的适用场景
对于数仓中的行级数据更新删除需求比较频繁的,可以考虑使用事务表。
但平常的hive表并不建议使用事务表。因为事务表的限制很多,加上由于hive表的特性,也很难满足高并发的场景。
另外,如果事务表太多,并且存在大量的更新操作,metastore后台启动的合并线程会定期的提交MapReduce Job,也会一定程度上增重集群的负担。
结论: 除非有非常迫切的行级更新需求,又只能用hive表来做,才需要去考虑事务表。
注意事项
- 不支持 BEGIN、COMMIT、ROLLBACK 等语句,所有的语句都是自动提交
- 仅支持ORC格式
设计原理与实现
文件管理格式
HDFS本身是不支持直接修改文件的,也不能保证有人追加内容时的读一致性。
因此,为了支持ACID的特性,Hive只能使用其他数据仓库常用的方法,也就是增量的形式记录更新和删除(也称做读时更新)。
存储在事务表中的数据会被分成两种类型的文件:
base文件,用来存放平常的数据
delta文件,用来存储新增、更新、删除的数据。每一个事务处理数据的结果都会单独新建一个delta文件夹用来存储数据。
在有用户要读取这个表的数据时,就会将base文件和delta文件都读取到内存,然后进行合并(就是判断哪些记录有被修改,哪些记录被删除等)。
ACID表的文件结构:${Hive_path}/db/table_name/base_{id}/file。
Found 2 items |
| -rw-rw----+ 3 hdfs hadoop 1091048351 2021-07-13 15:00 /warehouse/tablespace/managed/hive/db_name.db/tb_name/date=2021-06-30/base_0002252/000000_0
| -rw-rw----+ 3 hdfs hadoop 1091048351 2021-07-13 15:00 /warehouse/tablespace/managed/hive/db_name.db/tb_name/date=2021-06-30/delta_0000023_0000023_0000/000000_0
其中0000023
标识事务ID,0000表示序号,从上面的表中我们可以看到有一个事务的数据还未合并成base。
非ACID表,通过别的表格导入数据,文件目录为:
+----------------------------------------------------+
| DFS Output |
+----------------------------------------------------+
| 2.6 M 7.9 M /warehouse/tablespace/managed/hive/db_name.db/tb_name/day=2021-07-18/-ext-10000/000000_0 |
+----------------------------------------------------+
普通内部表文件目录:
+----------------------------------------------------+
| DFS Output |
+----------------------------------------------------+
| 7.4 M 22.1 M /warehouse/tablespace/managed/hive/db_name.db/tb_name/day=2021-07-18/000000_0 |
+----------------------------------------------------+
问题解决办法
那么如果项目一开始就没有关闭hive的ACID设置,怎么解决这个问题呢?
思路:先关闭ACID设置
使用shell脚本或者java程序连接jdbc,导出已有库的所有表的建表语句,
只保留create table 原表名_bak( col1 type, col2 type, ... )partition by (分区字段) store as ORC;
执行建表语句,新建表名,然后将数据从老表中查询出来,覆写到新表中。
然后旧表更名为 table名_old,新表面更名为原表名,这样就不影响到下游的使用。
更名操作命令:
当前Hive版本:3.1.3000.7.1.7.0-551 创建hive外部表: CREATE TABLE IF NOT EXISTS `alter_test` (a string,b string) STORED AS ORC TBLPROPERTIES("transactional"="false"); 转hive内部表:ALTER TABLE alter_test SET TBLPROPERTIES('external'='false'); 重命名:ALTER TABLE alter_test RENAME TO alter_test_bak;
设置hive表的Location的hdfs路径:
ALTER TABLE <tableName> RENAME TO <newTableName>; ALTER TABLE <newTableName> set location "hdfs://<location>";
如果在编写shell脚本时候导出的建表语句有格式化输出可以使用beeline如下命令:
如果创建的表impala还是无法查询到数据,记得可能是impala的元数据没有及时更新。可执行如下命令刷新元数据:invalidate metadata 全量刷新,性能消耗较大,主要用于 hive 当中新建数据库或者数据库表的时候来进行刷新。
refresh dbname.tablename 增量刷新,刷新某一张表的元数据,主要用于刷新 hive 当中数据表里面的数据改变的情况。
quit/exit 命令 从 Impala shell 中弹出
explain 命令 用于查看 sql 语句的执行计划
- 最佳解决方案
- 新建一个库与旧的数据库做个区分,比如旧库ODS,新库ODS_new;然后在新库下面把所有表重新建一遍。
- 下游只需更改数据源的库名就行,表名不变。
二、root 进入hive 报错:Permission denied: user=root, access=WRITE, inode=“/user/root”:hdfs:hdfs:drwxr-xr-x
使用root用户执行hive -f /home/insert.sql报错
解决办法:
进入 Ambari,
进入HDFS,
点击Configs
打开 Advanced hdfs-site,设置dfs.permissions.enabled=false.
重启HDFS,以及相关组建。
三、Hive表更名问题——RENAME TABLE
RENAME TABLE
ALTER TABLE table_name RENAME TO new_table_name
这个命令可以让用户为表更名。数据所在的位置和分区名并不改变。换而言之,老的表名并未“释放”,对老表的更改会改变新表的数据?
答案是否定的,数据所在的位置会移动到hive表默认路径下。如果建表是指定路径LOCATION,那么重名之后,路径会变更。
四、Hive 3.1ACID事务关闭,Spark.sql查询内部表报错This table may be a Hive-managed ACID table…
使用 spark2.3.2 + hive3.1.0(HDP3.1.5)
① Hive 3.x 之后,内部表是支持 ACID 特性,且只支持 ORC 格式;插入式事务(仅插入事务生效),格式没有限制,配置如下两个参数:
hive.create.as.insert.only=true
metastore.create.as.acid=true
② 在 spark-sql 创建的都是外部表,可以获取到内部表,但是插入不了数据,报错如下:
spark-sql> insert into info1 values(1,'ok');
Error in query:
Spark has no access to table `linfs`.`info1`. Clients can access this table only if
they have the following capabilities: CONNECTORREAD,HIVEFULLACIDREAD,HIVEFULLACIDWRITE,HIVEMANAGESTATS,HIVECACHEINVALIDATE,CONNECTORWRITE.
This table may be a Hive-managed ACID table, or require some other capability that Spark
currently does not implement;
③ 解决方案,统一使用外部表,修改配置如下:
hive.strict.managed.tables=false
hive.create.as.insert.only=false
metastore.create.as.acid=false
参考博客
Hive ACID和事务表支持详解_u013332124的专栏-CSDN博客
Hive ACID 增删改查 原理以及场景描述_Lara1111的博客-CSDN博客
Hive 官方手册翻译 – Hive Transactions (Hive 事务) - 范振勇 - 博客园 (cnblogs.com)
Spark 无法读取hive 3.x的表数据_u013024563的博客-CSDN博客
Hive表更名问题RENAME TABLE